I developed a simple dialog with a checkbox, which allows the user to select one or several items from a list. Besides the standard OK and Cancel buttons, it adds Select All and Unselect All buttons, allowing the user to check/uncheck all items at once (this comes handy for large lists).
import os, sys
from PyQt4 import Qt, QtCore, QtGui
class ChecklistDialog(QtGui.QDialog):
def __init__(self, name, stringlist=None, checked=False, icon=None, parent=None):
super(ChecklistDialog, self).__init__(parent)
self.name = name
self.icon = icon
self.model = QtGui.QStandardItemModel()
self.listView = QtGui.QListView()
if stringlist is not None:
for i in range(len(stringlist)):
item = QtGui.QStandardItem(stringlist[i])
item.setCheckable(True)
check = QtCore.Qt.Checked if checked else QtCore.Qt.Unchecked
item.setCheckState(check)
self.model.appendRow(item)
self.listView.setModel(self.model)
self.okButton = QtGui.QPushButton("OK")
self.cancelButton = QtGui.QPushButton("Cancel")
self.selectButton = QtGui.QPushButton("Select All")
self.unselectButton = QtGui.QPushButton("Unselect All")
hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(self.okButton)
hbox.addWidget(self.cancelButton)
hbox.addWidget(self.selectButton)
hbox.addWidget(self.unselectButton)
vbox = QtGui.QVBoxLayout()
vbox.addWidget(self.listView)
vbox.addStretch(1)
vbox.addLayout(hbox)
self.setLayout(vbox)
#self.setLayout(layout)
self.setWindowTitle(self.name)
if self.icon is not None: self.setWindowIcon(self.icon)
self.okButton.clicked.connect(self.accept)
self.cancelButton.clicked.connect(self.reject)
self.selectButton.clicked.connect(self.select)
self.unselectButton.clicked.connect(self.unselect)
def reject(self):
QtGui.QDialog.reject(self)
def accept(self):
self.choices = []
i = 0
while self.model.item(i):
if self.model.item(i).checkState():
self.choices.append(self.model.item(i).text())
i += 1
QtGui.QDialog.accept(self)
def select(self):
i = 0
while self.model.item(i):
item = self.model.item(i)
if not item.checkState():
item.setCheckState(True)
i += 1
def unselect(self):
i = 0
while self.model.item(i):
item = self.model.item(i)
item.setCheckState(False)
i += 1
if __name__ == "__main__":
fruits = ["Banana", "Apple", "Elderberry", "Clementine", "Fig",
"Guava", "Mango", "Honeydew Melon", "Date", "Watermelon",
"Tangerine", "Ugli Fruit", "Juniperberry", "Kiwi", "Lemon",
"Nectarine", "Plum", "Raspberry", "Strawberry", "Orange"]
app = QtGui.QApplication(sys.argv)
form = ChecklistDialog("Fruit", fruits, checked=True)
if form.exec_():
print("\n".join([str(s) for s in form.choices]))
The above code works, but I am bothered by the strange behaviour of QListView: when the dialog is display with the "check" parameter turned to True, all checkbozes appear selected with an "X" mark (as expected). However, when the Select All button is clicked, the checkboxes are instead grayed (although the items are correctly selected). I would prefer that they be displayed with an "X" mark, in order to present a consistent appearance to the user.
See the figures below.
In general terms, my question is: how to control the way the checkboxes are displayed in a QListView?