pythonuser-interfacepyqt5qcheckbox

QCheckBox to check/uncheck all QCheckboxes from another class in PyQt5


I have two QWidget classes: Widget1 and Widget2, and I would like to have a QCheckBox in the first class (Widget1) that can check/uncheck all the QcheckBoxes generated by the second class (Widget2). Is there a way to do this? Many thanks in advance for your help.

class Widget1(QWidget):
    def __init__(self, ids):
        super().__init__()
        self.ids = ids  # ids is a list of list [[1, 1], [2, 2], [3, 3], ..., [n, n] generated elsewhere in the code 
        self.initUI()

    def initUI(self):
        self.widget1_layout = QVBoxLayout()
        self.setLayout(self.Widget1_Layout)

        self.master_checkbox = QCheckBox("Select all")
        self.master_checkbox.stateChanged.connect(self.selectAll)
        self.widget1_layout.addWidget(self.master_checkbox)

        for i, id in enumerate(self.ids):
            self.singleID_checkbox = Widget2(self, i, id)
            self.widget1_layout.addWidget(self.singleID_checkbox)

     def selectAll(self):
        if self.master_checkbox.isChecked():
            function_that_check_all_Widget2_checkboxes()
        else:
            function_that_UNcheck_all_Widget2_checkboxes()



class Widget2(QWidget):
    def __init__(self, parent, i, id, *args, **kwargs):
        super().__init__(parent, *args, **kwargs)
        self.i = i
        self.id = id
        self.initUI()
    
    def initUI(self):
        self.singleIDcheckbox_layout = QGridLayout(self)
        self.singleIDcheckbox = QCheckBox(str(self.id))
        self.singleIDcheckbox_layout.addWidget(self.singleIDcheckbox, self.i, 0)

The two functions

function_that_check_all_Widget2_checkboxes()

and

function_that_UNcheck_all_Widget2_checkboxes()

do not exist.

They are are here as examples to better present my problem, as I guess that this is where I should put some code to do what I'd like to achieve.


Solution

  • The simplest solution is to add each widget to a list whenever you're creating them, then check/uncheck them according to the "master" state.

    Note that:

    1. when creating multiple objects there's no use in setting them as instance members: the purpose of an instance attribute is to have a persistent reference to an object, if you continously overwrite that reference you lose that benefit;
    2. most signals provide arguments, especially those relating "changes"; calling isChecked() is unnecessary, as stateChanged already returns the current state, you just need to add the argument to the function;
    3. unless you're requiring a tristate checkbox, the correct signal is toggled (which returns a bool state), not the stateChanged (which returns a Qt.CheckState flag that has 2 for the checked state);
    class Widget1(QWidget):
        # ...
        def initUI(self):
            self.widget1_layout = QVBoxLayout(self)
    
            self.master_checkbox = QCheckBox("Select all")
            self.master_checkbox.toggled.connect(self.selectAll)
            self.widget1_layout.addWidget(self.master_checkbox)
    
            self.checkboxes = []
            for i, id in enumerate(self.ids):
                singleID_checkbox = Widget2(self, i, id)
                self.widget1_layout.addWidget(singleID_checkbox)
                self.checkboxes.append(singleID_checkbox)
    
        def selectAll(self, state):
            for check in self.checkboxes:
                check.setChecked(state)
    
    
    class Widget2(QWidget):
        # ...
        def setChecked(self, state):
            self.singleIDcheckbox.setChecked(state)
    

    Note that, since you're using a specialized class, you could also use findChildren, which returns all child objects that are instances of the specified class:

        def selectAll(self, state):
            for check in self.findChildren(Widget2):
                check.setChecked(state)
    

    Use the above with care, though, as by default it looks recursively through the whole object tree, so using a list is still a better solution.