Search code examples
pythonpyqtpyqt5qtreewidgetqradiobutton

PyQt [QTreeWidget]: How to add Radiobutton for items?


To this question I am referring to the answer from @Andy PyQt Tree Widget, adding check boxes for dynamic removal

There @Andy shows how to add CheckBox into the QTreeWidget, which works perfect.

I would like ask here, how to add RadioButton into QTreeWidget? ----And, which is more difficult for me, how to make only one item selectable, although they are in different groups?

I rewrite the code from @Andy into PyQt5:

from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
import sys

def main(): 
    app     = QApplication (sys.argv)
    tree    = QTreeWidget ()
    headerItem  = QTreeWidgetItem()
    item    = QTreeWidgetItem()

    for i in range(3):
        parent = QTreeWidgetItem(tree)
        parent.setText(0, "Parent {}".format(i))
        parent.setFlags(parent.flags() | Qt.ItemIsTristate | Qt.ItemIsUserCheckable)
        for x in range(5):
            child = QTreeWidgetItem(parent)
            child.setFlags(child.flags() | Qt.ItemIsUserCheckable)
            child.setText(0, "Child {}".format(x))
            child.setCheckState(0, Qt.Unchecked)
    tree.show() 
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

The running result of the code above:

enter image description here

UPDATE: The desired result should be like as followings... enter image description here

Any help will be highly appreciated! Thanks!


Solution

  • If you want to establish a QRadioButton and it is exclusive in a group, a possible solution is to implement a delegate, in this delegate we will overwrite the paint method to draw the QRadioButton and the editorEvent method to catch the click event and change the status of the other items according to be the case.

    from PyQt5.QtWidgets import *
    from PyQt5.QtGui import *
    from PyQt5.QtCore import *
    import sys
    
    
    class Delegate(QStyledItemDelegate):
        def paint(self, painter, option, index):
            if not index.parent().isValid():
                QStyledItemDelegate.paint(self, painter, option, index)
            else:
                widget = option.widget
                style = widget.style() if widget else QApplication.style()
                opt = QStyleOptionButton()
                opt.rect = option.rect
                opt.text = index.data()
                opt.state |= QStyle.State_On if index.data(Qt.CheckStateRole) else QStyle.State_Off
                style.drawControl(QStyle.CE_RadioButton, opt, painter, widget)
    
        def editorEvent(self, event, model, option, index):
            value = QStyledItemDelegate.editorEvent(self, event, model, option, index)
            if value:
                if event.type() == QEvent.MouseButtonRelease:
                    if index.data(Qt.CheckStateRole) == Qt.Checked:
                        parent = index.parent()
                        for i in range(model.rowCount(parent)):
                            if i != index.row():
                                ix = parent.child(i, 0)
                                model.setData(ix, Qt.Unchecked, Qt.CheckStateRole)
    
            return value
    
    
    def main():
        app = QApplication(sys.argv)
        tree = QTreeWidget()
        tree.setItemDelegate(Delegate())
    
        for i in range(3):
            parent = QTreeWidgetItem(tree)
            parent.setText(0, "Parent {}".format(i))
            for x in range(5):
                child = QTreeWidgetItem(parent)
                child.setFlags(child.flags() | Qt.ItemIsUserCheckable)
                child.setText(0, "Child {}".format(x))
                child.setCheckState(0, Qt.Unchecked)
    
        tree.expandAll()
        tree.show()
        sys.exit(app.exec_())
    
    
    if __name__ == '__main__':
        main()
    

    enter image description here