有没有办法 "freeze" PyQt5 中复选框的状态?

Is there a way to "freeze" the state of a checkbox in PyQt5?

我希望当我选中另一个复选框时自动选中一个复选框,并且在我取消选中另一个复选框之前不会取消选中。 我认为“冻结”是一种简单的方法,但我也对其他解决方案感兴趣。 尝试这个的示例程序是:

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QCheckBox

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setGeometry(500, 50, 500, 500)
        self.box1 = QCheckBox('Box1', self)
        self.box1.setGeometry(250, 10, 50, 50)
        self.box1.stateChanged.connect(self.dothething)
        self.box2 = QCheckBox('Box2', self)
        self.box2.setGeometry(10, 10, 50, 50)
        self.show()
    
    def dothething(self):
        if not self.box2.isChecked():
            self.box2.toggle()
            self.box2.freeze()
        else: 
            self.box2.unfreeze()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ui = Window()
    sys.exit(app.exec_())

显然 .freeze().unfreeze() 函数不存在。

我也试过 .setCheckable 但是 CheckBox 只能冻结在未选中状态,所以这与我想要的相反。

有多种可用选项。

最明显的是禁用复选框,这是最好的选择,因为它向用户表明它不能取消选中,同时仍然显示它选中。

classWindow(QMainWindow):
    def __init__(自我):
        超级().__init__()
        self.setGeometry(500, 50, 500, 500)
        中央= QWidget()
        self.setCentralWidget(中央)
        self.box1 = QCheckBox('Box1', self)
        <b>self.box1.toggled.connect(self.dothething)</b>
        self.box2 = QCheckBox('Box2', self)
        layout = QHBoxLayout(中央)
        layout.addWidget(self.box1)
        layout.addWidget(self.box2)
        self.show()
    
    def dothething(自我,检查):
        如果选中:
            自我。box2.setChecked(真)
            self.box2.setEnabled(假)
        别的:
            self.box2.setEnabled(真)

请注意,在上面的示例中,我使用了中央小部件并设置了布局;这是因为您应该 总是 在 QMainWindow 中使用中央小部件,还因为使用固定的几何形状有 很多 的问题和布局经理应该总是优先考虑的;在这种情况下,例如,我无法区分 box1 和 box2,因为您用于它们的几何形状导致将数字隐藏在它们的标签中。请记住,您在屏幕上看到的内容 很少 是其他人会看到的内容。

更简单的替代方法是,如果选中了另一个框,则再次选中该框;这在任何情况下都有效,无论用户使用鼠标还是键盘:

class Window(QMainWindow):
    def __init__(self):
        # ...

        self.box2.toggled.connect(self.verifyBox2)

    def verifyBox2(self, checked):
        if not checked and self.box1.isChecked():
            self.box2.setChecked(True)

    def dothething(self, checked):
        if checked:
            self.box2.setChecked(True)

为了完整起见,还有其他两种可能性,但请记住,这两种可能性都依赖于 鼠标 事件,因此如果用户使用键盘导航(通过选项卡或助记符)并按下 Space 复选框仍会被切换。

第一种可能是设置WA_TransparentForMouseEvents标志,这将忽略所有鼠标事件:

class Window(QMainWindow):
    # ...
    def dothething(self, checked):
        if checked:
            self.box2.setChecked(True)
            self.box2.setAttribute(Qt.WA_TransparentForMouseEvents, True)
        else:
            self.box2.setAttribute(Qt.WA_TransparentForMouseEvents, False)

或者,您可以使用事件过滤器忽略左键单击事件(包括双击,因为 QCheckBox 也可以与它们切换):

class Window(QMainWindow):
    def __init__(self):
        # ...
        self.box2.installEventFilter(self)

    def eventFilter(self, source, event):
        if source == self.box2:
            if event.type() in (QEvent.MouseButtonPress, QEvent.MouseButtonDblClick):
                return event.button() == Qt.LeftButton and self.box1.isChecked()
        return super().eventFilter(source, event)

    def dothething(self, checked):
        if checked:
            self.box2.setChecked(True)