QListView 中的复选框选择
Checkbox selection in QListView
我开发了一个带有复选框的简单对话框,允许用户select 列表中的一个或多个项目。除了标准的 OK 和 Cancel 按钮外,它还添加了 Select All 和 Unselect All 按钮,允许用户一次 check/uncheck 所有项目(这对于大型列表很方便)。
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]))
上面的代码有效,但我对 QListView 的奇怪行为感到困扰:当对话框显示时“check”参数变为 True,所有复选框都显示 select带有“X”标记(如预期)。但是,当单击 Select All 按钮时,复选框会变成灰色(尽管项目已正确 selected)。我希望它们显示有“X”标记,以便向用户呈现一致的外观。
见下图。
一般来说,我的问题是:如何控制复选框在 QListView 中的显示方式?
问题是因为aQCheckBox
的states是3:
Qt::Unchecked 0 The item is unchecked.
Qt::PartiallyChecked 1 The item is partially checked. Items in
hierarchical models may be partially checked if some, but not all, of
their children are checked.
Qt::Checked 2 The item is checked.
如您所见,它们是整数值,当您传递 True 的值时,它会转换为 1,这相当于 Qt::PartiallyChecked
那些您得到矩形而不是十字的人。
解决方案是将正确的值传递给它:QtCore.Qt.Checked
如下所示:
import 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()
for string in stringlist:
item = QtGui.QStandardItem(string)
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(self)
vbox.addWidget(self.listView)
vbox.addStretch(1)
vbox.addLayout(hbox)
self.setWindowTitle(self.name)
if self.icon:
self.setWindowIcon(self.icon)
self.okButton.clicked.connect(self.onAccepted)
self.cancelButton.clicked.connect(self.reject)
self.selectButton.clicked.connect(self.select)
self.unselectButton.clicked.connect(self.unselect)
def onAccepted(self):
self.choices = [self.model.item(i).text() for i in
range(self.model.rowCount())
if self.model.item(i).checkState()
== QtCore.Qt.Checked]
self.accept()
def select(self):
for i in range(self.model.rowCount()):
item = self.model.item(i)
item.setCheckState(QtCore.Qt.Checked)
def unselect(self):
for i in range(self.model.rowCount()):
item = self.model.item(i)
item.setCheckState(QtCore.Qt.Unchecked)
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_() == QtGui.QDialog.Accepted:
print '\n'.join([str(s) for s in form.choices])
这里是 PyQt5 实现:
from PyQt5 import QtWidgets, QtGui, QtCore
class ChecklistDialog(QtWidgets.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 = QtWidgets.QListView()
for string in stringlist:
item = QtGui.QStandardItem(string)
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 = QtWidgets.QPushButton('OK')
self.cancelButton = QtWidgets.QPushButton('Cancel')
self.selectButton = QtWidgets.QPushButton('Select All')
self.unselectButton = QtWidgets.QPushButton('Unselect All')
hbox = QtWidgets.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(self.okButton)
hbox.addWidget(self.cancelButton)
hbox.addWidget(self.selectButton)
hbox.addWidget(self.unselectButton)
vbox = QtWidgets.QVBoxLayout(self)
vbox.addWidget(self.listView)
vbox.addStretch(1)
vbox.addLayout(hbox)
self.setWindowTitle(self.name)
if self.icon:
self.setWindowIcon(self.icon)
self.okButton.clicked.connect(self.onAccepted)
self.cancelButton.clicked.connect(self.reject)
self.selectButton.clicked.connect(self.select)
self.unselectButton.clicked.connect(self.unselect)
def onAccepted(self):
self.choices = [self.model.item(i).text() for i in
range(self.model.rowCount())
if self.model.item(i).checkState()
== QtCore.Qt.Checked]
self.accept()
def select(self):
for i in range(self.model.rowCount()):
item = self.model.item(i)
item.setCheckState(QtCore.Qt.Checked)
def unselect(self):
for i in range(self.model.rowCount()):
item = self.model.item(i)
item.setCheckState(QtCore.Qt.Unchecked)
if __name__ == '__main__':
import sys
fruits = [
'Banana',
'Apple',
'Elderberry',
'Clementine',
'Fig',
'Guava',
'Mango',
'Honeydew Melon',
'Date',
'Watermelon',
'Tangerine',
'Ugli Fruit',
'Juniperberry',
'Kiwi',
'Lemon',
'Nectarine',
'Plum',
'Raspberry',
'Strawberry',
'Orange',
]
app = QtWidgets.QApplication(sys.argv)
form = ChecklistDialog('Fruit', fruits, checked=True)
if form.exec_() == QtWidgets.QDialog.Accepted:
print('\n'.join([str(s) for s in form.choices]))`
我开发了一个带有复选框的简单对话框,允许用户select 列表中的一个或多个项目。除了标准的 OK 和 Cancel 按钮外,它还添加了 Select All 和 Unselect All 按钮,允许用户一次 check/uncheck 所有项目(这对于大型列表很方便)。
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]))
上面的代码有效,但我对 QListView 的奇怪行为感到困扰:当对话框显示时“check”参数变为 True,所有复选框都显示 select带有“X”标记(如预期)。但是,当单击 Select All 按钮时,复选框会变成灰色(尽管项目已正确 selected)。我希望它们显示有“X”标记,以便向用户呈现一致的外观。
见下图。
一般来说,我的问题是:如何控制复选框在 QListView 中的显示方式?
问题是因为aQCheckBox
的states是3:
Qt::Unchecked 0 The item is unchecked.
Qt::PartiallyChecked 1 The item is partially checked. Items in hierarchical models may be partially checked if some, but not all, of their children are checked.
Qt::Checked 2 The item is checked.
如您所见,它们是整数值,当您传递 True 的值时,它会转换为 1,这相当于 Qt::PartiallyChecked
那些您得到矩形而不是十字的人。
解决方案是将正确的值传递给它:QtCore.Qt.Checked
如下所示:
import 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()
for string in stringlist:
item = QtGui.QStandardItem(string)
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(self)
vbox.addWidget(self.listView)
vbox.addStretch(1)
vbox.addLayout(hbox)
self.setWindowTitle(self.name)
if self.icon:
self.setWindowIcon(self.icon)
self.okButton.clicked.connect(self.onAccepted)
self.cancelButton.clicked.connect(self.reject)
self.selectButton.clicked.connect(self.select)
self.unselectButton.clicked.connect(self.unselect)
def onAccepted(self):
self.choices = [self.model.item(i).text() for i in
range(self.model.rowCount())
if self.model.item(i).checkState()
== QtCore.Qt.Checked]
self.accept()
def select(self):
for i in range(self.model.rowCount()):
item = self.model.item(i)
item.setCheckState(QtCore.Qt.Checked)
def unselect(self):
for i in range(self.model.rowCount()):
item = self.model.item(i)
item.setCheckState(QtCore.Qt.Unchecked)
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_() == QtGui.QDialog.Accepted:
print '\n'.join([str(s) for s in form.choices])
这里是 PyQt5 实现:
from PyQt5 import QtWidgets, QtGui, QtCore
class ChecklistDialog(QtWidgets.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 = QtWidgets.QListView()
for string in stringlist:
item = QtGui.QStandardItem(string)
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 = QtWidgets.QPushButton('OK')
self.cancelButton = QtWidgets.QPushButton('Cancel')
self.selectButton = QtWidgets.QPushButton('Select All')
self.unselectButton = QtWidgets.QPushButton('Unselect All')
hbox = QtWidgets.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(self.okButton)
hbox.addWidget(self.cancelButton)
hbox.addWidget(self.selectButton)
hbox.addWidget(self.unselectButton)
vbox = QtWidgets.QVBoxLayout(self)
vbox.addWidget(self.listView)
vbox.addStretch(1)
vbox.addLayout(hbox)
self.setWindowTitle(self.name)
if self.icon:
self.setWindowIcon(self.icon)
self.okButton.clicked.connect(self.onAccepted)
self.cancelButton.clicked.connect(self.reject)
self.selectButton.clicked.connect(self.select)
self.unselectButton.clicked.connect(self.unselect)
def onAccepted(self):
self.choices = [self.model.item(i).text() for i in
range(self.model.rowCount())
if self.model.item(i).checkState()
== QtCore.Qt.Checked]
self.accept()
def select(self):
for i in range(self.model.rowCount()):
item = self.model.item(i)
item.setCheckState(QtCore.Qt.Checked)
def unselect(self):
for i in range(self.model.rowCount()):
item = self.model.item(i)
item.setCheckState(QtCore.Qt.Unchecked)
if __name__ == '__main__':
import sys
fruits = [
'Banana',
'Apple',
'Elderberry',
'Clementine',
'Fig',
'Guava',
'Mango',
'Honeydew Melon',
'Date',
'Watermelon',
'Tangerine',
'Ugli Fruit',
'Juniperberry',
'Kiwi',
'Lemon',
'Nectarine',
'Plum',
'Raspberry',
'Strawberry',
'Orange',
]
app = QtWidgets.QApplication(sys.argv)
form = ChecklistDialog('Fruit', fruits, checked=True)
if form.exec_() == QtWidgets.QDialog.Accepted:
print('\n'.join([str(s) for s in form.choices]))`