为ListView创建切换“全部检查”复选框

时间:2016-02-24 19:24:53

标签: python pyqt pyqt4

我有一个包含可检查项目的ListView。我想在ListView上面放置一个三态“全部检查”复选框,我希望这个复选框是双向的。

也就是说,如果用户切换check all复选框,我想要所有ListView的项目镜像检查所有选项。但是如果用户手动检查或取消选中ListView中的项目,我希望select all复选框反映该状态(即检查是否所有ListView项目都已选中,如果未选中则取消选中,或部分检查是否有部分ListView检查项目。)

This answer显示了如何连接第一部分(检查/取消选中select all框将其状态传播到列表视图的项目)。但是,我对如何连接另一个方向感到难过。

这就是我将Check All复选框传播到ListView的方式:

self.layout = QtGui.QVBoxLayout()

self.select_all_cb = QtGui.QCheckBox('Check All', self.ui.tab)
self.select_all_cb.setChecked(True)
self.select_all_cb.setStyleSheet('margin-left: 5px; font: bold')
self.select_all_cb.stateChanged.connect(self.selectAllCheckChanged)
self.layout.addWidget(select_all_cb)

self.listview = QtGui.QListView(self.ui.tab)
self.listview.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
self.listview.setSelectionMode(QtGui.QAbstractItemView.NoSelection)
self.listview.setSelectionRectVisible(False)

model = QStandardItemModel()
for checkItem in self.checkItems:
    item = QStandardItem(checkItem)
    item.setCheckable(True)
    item.setSelectable(False)
    item.setCheckState(QtCore.Qt.Checked)
    model.appendRow(item)
self.listview.setModel(model)
self.layout.addWidget(listview)


def selectAllCheckChanged(self):
    model = self.listview.model()
    for index in range(model.rowCount()):
        item = model.item(index)
        if item.isCheckable():
            if self.select_all_cb.isChecked():
                item.setCheckState(QtCore.Qt.Checked)
            else:
                item.setCheckState(QtCore.Qt.Unchecked)

关于如何走另一条道路的任何建议?

2 个答案:

答案 0 :(得分:2)

您可以连接到itemChanged上的QStandardItemModel信号并测试所有复选框的状态。

from itertools import product

self.model.itemChanged.connect(self.test_check)

def test_check(self, item):
    items = [self.model.item(r,c) for r, c in product(range(self.model.rowCount()), range(self.model.columnCount())]

    if all(item.checkState() == Qt.Checked for item in items)
        state = Qt.Checked
    elif any(item.checkState() == Qt.Checked for item in items):
        state = Qt.PartiallyChecked
    else:
        state = Qt.Unchecked

    if self.select_all_cb.checkState() != state:
        self.select_all_cb.setCheckState(state)

如果你有非常多的复选框,你可以通过缓存每个项目的检查状态并在项目状态发生变化时更新缓存,然后检查缓存而不是从每个项目中拉出来优化它每一次。

如果您知道您将同时对多个项目进行更改,则应该在模型上阻止信号,然后在进行所有更改后手动运行此功能。

selectAllCheckChanged处理程序中,您还应该阻止模型上的信号,以便它不会触发此处理程序

def selectAllCheckChanged(self):
    model = self.listview.model()
    model.blockSignals(True)
    try:
        for index in range(model.rowCount()):
            item = model.item(index)
            if item.isCheckable():
                if self.select_all_cb.isChecked():
                    item.setCheckState(QtCore.Qt.Checked)
                else:
                    item.setCheckState(QtCore.Qt.Unchecked)
    finally:
        model.blockSignals(False)

答案 1 :(得分:1)

如果它会帮助其他人,这就是我如何在我的代码中结合Brendan的答案。差异是三态功能仅在需要时启用(因此用户无法启用部分检查状态),并且我使用clicked信号而不是stateChange连接它以避免{{1}由selectAllCheckChanged触发。当然,listviewCheckChanged也有效,但使用model.blockSignals对我来说似乎更加炽热。

clicked