Search code examples
pythonpyside2

Uncheck all boxes in exclusive group


I have two check boxes which are part of a button group. I want to be able to select one, the other, or none. To this end, I have the following:

import sys
from PySide2 import QtCore, QtWidgets


class MainWindow(QtWidgets.QMainWindow):

    def __init__(self):
        super().__init__()

        self.setWindowTitle('Check Problems')

        self.init_widgets()
        self.init_layout()

    def init_widgets(self):

        self.check1_label = QtWidgets.QLabel('Check1')
        self.check1 = QtWidgets.QCheckBox()
        self.check2_label = QtWidgets.QLabel('Check2')
        self.check2 = QtWidgets.QCheckBox()

        self.check_buttongroup = QtWidgets.QButtonGroup()
        self.check_buttongroup.addButton(self.check1)
        self.check_buttongroup.addButton(self.check2)
        self.check_buttongroup.buttonPressed.connect(self.check_buttongroup_pressed)

    def init_layout(self):

        check1_layout = QtWidgets.QHBoxLayout()
        check1_layout.addWidget(self.check1_label)
        check1_layout.addWidget(self.check1)

        check2_layout = QtWidgets.QHBoxLayout()
        check2_layout.addWidget(self.check2_label)
        check2_layout.addWidget(self.check2)

        check_layout = QtWidgets.QVBoxLayout()
        check_layout.addLayout(check1_layout)
        check_layout.addLayout(check2_layout)

        # Central widget
        centralWidget = QtWidgets.QWidget()
        centralWidget.setLayout(check_layout)
        self.setCentralWidget(centralWidget)

    def check_buttongroup_pressed(self, button):
        print('\nIs this check1? ', button == self.check1, flush=True)
        print('Status when pressed: ', button.isChecked(), flush=True)
        if button.isChecked():
            print('Button isChecked: ', button.isChecked(), flush=True)
            print('Changing button check state...', flush=True)
            button.group().setExclusive(False)
            button.setChecked(False)
            print('Button isChecked: ', button.isChecked(), flush=True)
            button.group().setExclusive(True)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    mainWin = MainWindow()
    mainWin.show()
    sys.exit(app.exec_())

No boxes are checked initially. Clicking check1 produces:

Is this check1?  True
Status when pressed:  False

This is expected. The box was unchecked initially. Having clicked it, however, the box is now checked. When I click check1 again, I expect it to uncheck, leaving both boxes unchecked. Now, clicking check1, I see:

Is this check1?  True
Status when pressed:  True
Button isChecked:  True
Changing button check state...
Button isChecked:  False

This is as expected. What is unexpected, however, is check1 is still checked!

Clicking a third time produces:

Is this check1?  True
Status when pressed:  True
Button isChecked:  True
Changing button check state...
Button isChecked:  False

Why does check1 not uncheck as expected? Is button.isChecked() lying to me or is setChecked(False) not doing its job? Am I mistaken somewhere?

I have tried this with PySide2==5.15.0, PySide2==5.13, PySide6, and PyQt5==5.15 with the same result, so I assume this is how it's supposed to behave and not some Qt implementation error.


Solution

  • The solution is to change the property exclusive to False if when the buttonPressed signal is emitted the button is checked and set that property to true when the buttonClicked is emitted.

    def init_widgets(self):
        # ...
        self.check_buttongroup = QtWidgets.QButtonGroup()
        self.check_buttongroup.addButton(self.check1)
        self.check_buttongroup.addButton(self.check2)
    
        self.check_buttongroup.buttonPressed.connect(self.handle_pressed)
        self.check_buttongroup.buttonClicked.connect(self.handle_clicked)
    
    def handle_pressed(self, button):
        button.group().setExclusive(not button.isChecked())
    
    def handle_clicked(self, button):
        button.group().setExclusive(True)