Связывание radioButton и checkBox

У меня есть форма, которая хранит в себе radioButton и 4 checkBox'а.
Я пытался связать их так, чтобы при нажатии на radioButton, ставились флажки на всех checkBox, а при отжимании одного из флажков, radioButton потухала, но проблема в том, что когда нажимаю на radioButton флажки ставятся на все checkBox кроме первого,
а чтобы флажок поставился на первый checkBox приходится повторно нажимать на radioButton.

Кроме этого, при снятии флажка с одного из checkBox, флажки снимаются со всех.

Прошу, подскажите, как это можно вылечить?

Код:

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


class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(152, 237)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.radioButton = QtWidgets.QRadioButton(self.centralwidget)
        self.radioButton.setObjectName("radioButton")
        self.verticalLayout.addWidget(self.radioButton)
        self.checkBox = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox.setObjectName("checkBox")
        self.verticalLayout.addWidget(self.checkBox)
        self.checkBox_2 = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox_2.setObjectName("checkBox_2")
        self.verticalLayout.addWidget(self.checkBox_2)
        self.checkBox_3 = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox_3.setObjectName("checkBox_3")
        self.verticalLayout.addWidget(self.checkBox_3)
        self.checkBox_4 = QtWidgets.QCheckBox(self.centralwidget)
        self.checkBox_4.setObjectName("checkBox_4")
        self.verticalLayout.addWidget(self.checkBox_4)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 152, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.radioButton.setText(_translate("MainWindow", "ALL"))
        self.checkBox.setText(_translate("MainWindow", "A"))
        self.checkBox_2.setText(_translate("MainWindow", "B"))
        self.checkBox_3.setText(_translate("MainWindow", "C"))
        self.checkBox_4.setText(_translate("MainWindow", "D"))


class Test(QtWidgets.QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None):
        super(Test, self).__init__(parent)

        self.setupUi(self)
        self.radioButton.toggled.connect(self.onClicked)
        self.checkBox.toggled.connect(self.onChecked)
        self.checkBox_2.toggled.connect(self.onChecked)
        self.checkBox_3.toggled.connect(self.onChecked)
        self.checkBox_4.toggled.connect(self.onChecked)

    def onClicked(self):
        if self.radioButton.isChecked():
            self.checkBox.setChecked(True)
            self.checkBox_2.setChecked(True)
            self.checkBox_3.setChecked(True)
            self.checkBox_4.setChecked(True)
        elif not self.radioButton.isChecked():
            self.checkBox.setChecked(False)
            self.checkBox_2.setChecked(False)
            self.checkBox_3.setChecked(False)
            self.checkBox_4.setChecked(False)

    def onChecked(self):
        check = []
        checkBoxs = self.findChildren(QCheckBox)
        for checkBox in checkBoxs:
            if checkBox.isChecked():
                check.append(1)
        if len(check) == 4:
            self.radioButton.setChecked(True)
        else:
            self.radioButton.setChecked(False)


if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    ui = Test()
    ui.show()
    sys.exit(app.exec_())

Ответы (2 шт):

Автор решения: Sergey Tatarincev

Заглядываем в документацию и отмечаем

This may be the result of a user action, click() slot activation, or because setChecked() is called.

А поскольку в onClicked для радиобатона у вас четыре раза вызывается setChecked, на каждый вызов будет сгенерирован и обработан сигнал toggled, в котором вы опять меняете checkState чекбоксов

→ Ссылка
Автор решения: S. Nick

Попробуйте так:

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


class Custom_QGroupBox(QtWidgets.QGroupBox):
    checkAllIfAny = True
    
    def __init__(self, *args, **kwargs):
        super(Custom_QGroupBox, self).__init__(*args, **kwargs)
        self.setCheckable(True)
        self.checkBoxes = []
        self.toggled.connect(self.toggleCheckBoxes)

    def addCheckBox(self, cb):
        self.checkBoxes.append(cb)
        cb.toggled.connect(self.update)
        cb.destroyed.connect(lambda: self.removeCheckBox(cb))

    def removeCheckBox(self, cb):
        try:
            self.checkBoxes.remove(cb)
            cb.toggled.disconnect(self.update)
        except:
            pass

    def allStates(self):
        return [cb.isChecked() for cb in self.checkBoxes]

    def toggleCheckBoxes(self):
        if self.checkAllIfAny:
            state = not all(self.allStates())
        else:
            state = not any(self.allStates())

        for widget in self.children():
            if not widget.isWidgetType():
                continue
            if not widget.testAttribute(QtCore.Qt.WA_ForceDisabled):
                # восстановить включенное состояние, чтобы переопределить 
                # поведение setChecked(False) по умолчанию; 
                # предыдущие явные вызовы setEnabled(False) для целевого виджета 
                # будут игнорироваться
                widget.setEnabled(True)
                if widget in self.checkBoxes:
                    widget.setChecked(state)

    def paintEvent(self, event):
        opt = QtWidgets.QStyleOptionGroupBox()
        self.initStyleOption(opt)
        states = self.allStates()
        if all(states):
            # force the "checked" state
            opt.state |= QtWidgets.QStyle.State_On
            opt.state &= ~QtWidgets.QStyle.State_Off
        else:
            # force the "not checked" state
            opt.state &= ~QtWidgets.QStyle.State_On
            if any(states):
                # force the "not unchecked" state and set the tristate mode
                opt.state &= ~QtWidgets.QStyle.State_Off
                opt.state |= QtWidgets.QStyle.State_NoChange
            else:
                # force the "unchecked" state
                opt.state |= QtWidgets.QStyle.State_Off
        painter = QtWidgets.QStylePainter(self)
        painter.drawComplexControl(QtWidgets.QStyle.CC_GroupBox, opt)


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        
        self.groupBox = Custom_QGroupBox('ALL')
        layout = QtWidgets.QGridLayout(self.groupBox)
        
        cb_text_labels = ["A", "B", "C", "D"]
        for r, text in enumerate(cb_text_labels):
            cb = QtWidgets.QCheckBox(f' {text}')
            self.groupBox.addCheckBox(cb)
            layout.addWidget(cb, r, 0)

        main_layout = QtWidgets.QGridLayout(self)
        main_layout.addWidget(self.groupBox)
    

if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setFont(QtGui.QFont("Times", 12, QtGui.QFont.Bold))
    w = MainWindow()
    w.resize(140, 240)
    w.show()
    sys.exit(app.exec_())

введите сюда описание изображения введите сюда описание изображения введите сюда описание изображения

→ Ссылка