Search code examples
pythonpython-3.xpyqtpyqt5qcheckbox

QCheckbox to check all other QCheckBoxes


My question is very similar to this post, Python PyQt - Checkbox to uncheck all other checkboxes. However, I am trying to check all other boxes when main checkbox is selected and at the same time, if any of the other boxes are selected independently, then I would like to deselect the main checkbox. I tried modifying the answer provided, but not able to put my head around the 'self.sender' signal. I am not able to change the selection when I deselect a checkbox. Here is the code that I modified using the solution provided by @ eyllanesc.Any help is greatly appreciated, thanks!

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

class Test(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
       self.checkBoxAll = QCheckBox("Select All")
       self.checkBoxA    = QCheckBox("Select A")
       self.checkBoxB    = QCheckBox("Select B")

        self.checkBoxAll.setChecked(False)
        self.checkBoxAll.stateChanged.connect(self.onStateChange)
        self.checkBoxA.stateChanged.connect(self.onStateChange)
        self.checkBoxB.stateChanged.connect(self.onStateChange)

        grid = QGridLayout(self)

        grid.addWidget(self.checkBoxAll, 1, 0)
        grid.addWidget(self.checkBoxA, 2, 0)
        grid.addWidget(self.checkBoxB, 3, 0)
        self.setWindowTitle('Test')
        self.show()

    @pyqtSlot(int)
    def onStateChange(self, state):
        if state == Qt.Checked:
            if self.sender() == self.checkBoxAll:
                self.checkBoxA.setChecked(True)
                self.checkBoxB.setChecked(True)
            elif self.sender() in (self.checkBoxA, self.checkBoxB):
                self.checkBoxAll.setChecked(False)

Solution

  • With the logic that you have you are creating a loop since the change of state of any element to change the state of another element, the idea is to block the emission of signals when the change of state is implemented in the slot with blockSignals():

    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class Test(QtWidgets.QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
    
        def initUI(self):
           self.checkBoxAll = QtWidgets.QCheckBox("Select All")
           self.checkBoxAll.setChecked(False)
           self.checkBoxAll.stateChanged.connect(self.onStateChangePrincipal)
    
           self.checkBoxA   = QtWidgets.QCheckBox("Select A")
           self.checkBoxB   = QtWidgets.QCheckBox("Select B")
           self.checkboxes = [self.checkBoxA, self.checkBoxB]
    
           for checkbox in self.checkboxes:
                checkbox.stateChanged.connect(self.onStateChange)
    
           grid = QtWidgets.QGridLayout(self)
           grid.addWidget(self.checkBoxAll, 1, 0)
           grid.addWidget(self.checkBoxA, 2, 0)
           grid.addWidget(self.checkBoxB, 3, 0)
           self.setWindowTitle('Test')
    
        @QtCore.pyqtSlot(int)
        def onStateChangePrincipal(self, state):
            if state == QtCore.Qt.Checked:
                for checkbox in self.checkboxes:
                    checkbox.blockSignals(True)
                    checkbox.setCheckState(state)
                    checkbox.blockSignals(False)
    
        @QtCore.pyqtSlot(int)
        def onStateChange(self, state):
            self.checkBoxAll.blockSignals(True)
            self.checkBoxAll.setChecked(QtCore.Qt.Unchecked)
            self.checkBoxAll.blockSignals(False)
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = Test()
        w.show()
        sys.exit(app.exec_())