首页 > 解决方案 > 取消选择并选择 PyQt5 中的所有复选框

问题描述

类似于Python PyQt - 取消选中所有其他复选框的复选框

我想要一个复选框

  1. 取消选中所有选中的复选框。
  2. 和一个复选框来选择所有复选框
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView, QGridLayout, QWidget, QCheckBox
from PyQt5.QtGui import QFont
from functools import partial

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.font = QFont("Helvetica", 9)
        self.setFont(self.font)
        self.showMaximized()     
        grid = QGridLayout()
        self.setLayout(grid)

        positions = [(i,j) for i in range(5) for j in range(5)]
        print('\npostions: ', positions)
        wordlist = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y']


        for position, wordlist in zip(positions, wordlist): 
            checkB =(QCheckBox(wordlist)) 
            checkB.setStatusTip("Crawling best singals for "+wordlist+"." ) # set Statusbar
            checkB.stateChanged.connect(partial(self.checkState, wordlist))
            grid.addWidget(checkB, *position)

        checkBoxNone = QCheckBox("None Selected")
        checkBoxNone.setChecked(True)
        checkBoxNone.stateChanged.connect(self.checkState)
        grid.addWidget(checkBoxNone, 6, 1)

        checkAll = QCheckBox("Select All")
        checkAll.setChecked(False)
        checkAll.stateChanged.connect(self.selectAll)
        grid.addWidget(checkAll, 6, 2)

        widget = QWidget()
        widget.setLayout(grid) 
        self.setCentralWidget(widget)      

        self.statusBar().showMessage('Ready')

    # Here are the problems. 
    # Is it because I have create checkboxes with a for loop?

    def selectAll(self, state):
        if state == Qt.Checked:
            if self.sender() == MainWindow.checkAll:
                MainWindow.checkB.setChecked(True)

    def checkState(self, checktext, state):
        if state == Qt.Checked:
            print(checktext)
            if self.sender() == MainWindow.checkBoxNone:
                MainWindow.checkB.setChecked(False)
            elif self.sender() == MainWindow.checkB:
                MainWindow.checkBoxNone.setChecked(False)

if __name__ == '__main__':

    app = QApplication(sys.argv)
    app.setStyle('Fusion')
    mw = MainWindow()

如何使用此设置获得工作代码?

标签: pythonpython-3.xpyqtpyqt5qcheckbox

解决方案


您的代码存在各种问题。

  1. 您正在尝试访问属性,例如MainWindow.checkB,MainWindow.checkBoxNone等。它们不存在。您应该使用实例属性,例如self.checkB;
  2. 要使上述工作,您还必须设置这些属性,所以checkBoxNone = QCheckBox("None Selected")应该是self.checkBoxNone = QCheckBox("None Selected"),等等。
  3. 您正在将 连接checkBoxNone.stateChanged到一个需要两个位置参数但stateChanged()只有一个(状态)的函数;没有复选框信号返回文本;
  4. 在您的功能中,您没有选中或取消选中任何其他复选框;
  5. 即使假设您使用了正确的引用(self.checkBoxNone等),您也不应该像在代码中那样因为 stateChanged 信号而选中未选中的框(或相反的框),因为它会导致递归;
  6. QMainWindow 有自己的布局,不能自己设置;此外,您已经为中央小部件设置了它;

为了使它工作,您应该参考您创建的所有复选框,然后应用您想要的状态,同时非常小心地更改每个框的检查状态。

在以下示例中,“全选”和“全选”复选框分别显示为部分选中,如果不是所有的框都被选中,或者至少有一个被选中。

class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        grid = QGridLayout()
        # You *cannot* set a layout to a main window
        # self.setLayout(grid)

        positions = [(i,j) for i in range(5) for j in range(5)]
        wordlist = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y']


        # use a list to keep a reference to all checkboxes
        self.checkBoxes = []
        for position, word in zip(positions, wordlist): 
            checkB =(QCheckBox(word)) 
            checkB.setStatusTip("Crawling best singals for "+word+"." ) # set Statusbar
            checkB.stateChanged.connect(self.checkStates)
            grid.addWidget(checkB, *position)
            self.checkBoxes.append(checkB)

        self.checkBoxNone = QCheckBox("Select none")
        self.checkBoxNone.setCheckState(Qt.Unchecked)
        self.checkBoxNone.stateChanged.connect(
            partial(self.selectBoxes, False))
        grid.addWidget(self.checkBoxNone, 6, 1)

        self.checkBoxAll = QCheckBox("Select All")
        self.checkBoxAll.setCheckState(Qt.PartiallyChecked)
        self.checkBoxAll.stateChanged.connect(
            partial(self.selectBoxes, True))
        grid.addWidget(self.checkBoxAll, 6, 2)

        widget = QWidget()
        widget.setLayout(grid) 
        self.setCentralWidget(widget)      

        self.statusBar().showMessage('Ready')

    def checkStates(self):
        states = [c.isChecked() for c in self.checkBoxes]

        # temporarily block signals so that there is no recursive calls
        self.checkBoxAll.blockSignals(True)
        self.checkBoxNone.blockSignals(True)

        # set the "select all" fully checked too if all boxes are checked,
        # otherwise make it partially checked
        self.checkBoxAll.setCheckState(
            Qt.Checked if all(states) else Qt.PartiallyChecked)

        # set the "select none" unchecked only if all boxes are unchecked,
        # otherwise make it partially checked
        self.checkBoxNone.setCheckState(
            Qt.Unchecked if not any(states) else Qt.PartiallyChecked)

        # unblock signals back
        self.checkBoxAll.blockSignals(False)
        self.checkBoxNone.blockSignals(False)

    def selectBoxes(self, state):
        for check in self.checkBoxes:
            check.blockSignals(True)
            check.setChecked(state)
            check.blockSignals(False)
        self.checkStates()

也就是说,让我告诉你,不建议使用复选框来做你正在做的事情,因为它在视觉上和从交互的角度来看都是违反直觉的。最好使用一个按钮检查所有内容,使用另一个按钮取消选中。
另外,命名变量和属性时要小心;例如,checkAllselectAll非常相似,但它们指的是两个截然不同且非常不同的对象:复选框和函数;他们的名字也应该反映这种差异。虽然不遵循这一原则并不代表技术问题,但当对象(函数、属性等)的数量随着您的程序增长时,它可能会成为一个问题:使用清晰和描述性的名称可以提高可读性(和心理参考),这是双方发展的一个重要方面问题跟踪。
同样的概念也适用于循环:不要wordlist同时用于列表和迭代器元素。


推荐阅读