similar to Python PyQt - Checkbox to uncheck all other checkboxes
I want to have a checkbox to
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()
How can I get a working code with this setup?
There various problems with your code.
MainWindow.checkB
, MainWindow.checkBoxNone
, etc. They do not exist. You should use instance attributes, using things like self.checkB
;checkBoxNone = QCheckBox("None Selected")
should be self.checkBoxNone = QCheckBox("None Selected")
, etc.checkBoxNone.stateChanged
to a function that requires two positional arguments, but stateChanged()
only has one (the state); no check box signal returns the text;self.checkBoxNone
, etc), you should never check an unchecked box (or the opposite) as a result of a stateChanged signal as you did in your code, because it will result in a recursion;To make it work you should have some reference to all checkboxes you have created, and then apply the state you want, while being very careful about changing the check state of each box.
In the following example the "select all" and "select none" check boxes appear partially checked if, respectively, not all boxes are checked, or at least one is.
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()
That said, let me tell you that using a checkbox to do what you are doing is not suggested, as it is counterintuitive, both visually and from the point of view of interaction. It is better to use a button for checking everything and another one for unchecking.
Also, be careful when naming variables and attributes; for example, checkAll
and selectAll
are very similar, but they refer to two distinct and very different objects: a checkbox and a function; their names should reflect that difference also. While not following this principle does not represent a technical issue, it can become a problem when the number of objects (functions, attributes, etc) grows along with your program: using clear and descriptive names improves readability (and mental reference), which is an important aspect for both developing and issue tracking.
The same concept goes for loops: don't use wordlist
for both the list and the iterator element.