QListWidget 复选框与项目选择同步

QListWidget checkbox sync with item selection

我在尝试设置 QListWidget 时遇到了一个奇怪的问题,我可以在其中单击 select 项目的复选框。我目前编写的代码不会使这种模式保持一致。喜欢下面的table。

例如,当我单击“苹果”复选框时,我希望“苹果”项会被 selected,但有时“橙色”会被删除selected。有时它起作用了我想要的方式,但它是不可预测的table。我已经阅读了几乎所有关于这个问题的 Whosebug 线程,但是 none 解决了它。 我的环境是MacBig Sur,Python3.7.9,PySide6 6.2.0.

我下面有一个MVE,谁能帮我看看?欣赏它。卡在这里好久了

from PySide6.QtCore import *
from PySide6.QtGui import *
from PySide6.QtWidgets import *
import sys


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("My App")
        self.list = QListWidget(self)
        self.list.setIconSize(QSize(12, 12))

        self.list.setSelectionMode(QAbstractItemView.MultiSelection)
        self.list.itemSelectionChanged.connect(
            self.on_selection_changed)
        self.list.itemChanged.connect(self.on_checkbox_clicked)

        # Set the central widget of the Window.
        self.setCentralWidget(self.list)

        for sheet in ['apple', 'orange', 'banana', 'pearl']:
            item = QListWidgetItem(sheet)
            self.list.addItem(item)
            item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
            item.setCheckState(Qt.Unchecked)

    def on_checkbox_clicked(self, item):
        print(item.text())
        print(item.checkState())
        if item.checkState() is Qt.CheckState.Checked:
            item.setSelected(True)
            # self.list.setCurrentItem(
            #     item, QItemSelectionModel.Select)
            print(f'select: {item.text()}')
        else:
            item.setSelected(False)
            # self.list.setCurrentItem(
            #     item, QItemSelectionModel.Deselect)
            print(f'unselect: {item.text()}')

    def on_selection_changed(self):
        self.list.blockSignals(True)
        for index in range(self.list.count()):
            item = self.list.item(index)
            if item.isSelected():
                item.setCheckState(Qt.CheckState.Checked)
            elif not item.isSelected():
                item.setCheckState(Qt.CheckState.Unchecked)
        self.list.blockSignals(False)


app = QApplication(sys.argv)

window = MainWindow()
window.show()

app.exec()

更新: 第一个答案非常复杂,因为我深入挖掘了更多东西要学习。最后,我找到了一个更容易实施的替代解决方案。我 post 核心部分供将来可能需要它的任何人使用。

listwidget = QListWidget()
listwidget.setSelectionMode(QAbstractItemView.MultiSelection)

listwidget.itemSelectionChanged.connect(self.on_selection_changed)

def on_selection_changed()
    for index in range(listwidget.count()):
        item = listwidget.item(index)
        if item.isSelected():
            item.setIcon(QIcon("path/to/your/check_icon.png"))
        else:
            item.setIcon(QIcon("path/to/your/uncheck_icon.png"))

据我了解,OP 希望同步,如果用户选择了一个项目,那么它就会被选中,反之亦然。考虑到这一点,一个可能的解决方案是消除委托对复选框矩形所做的验证。另一方面,用户也可以使用键盘更改复选框的状态,因此一个可能的解决方案是使用 itemChange 信号来更新选择的状态。

class Delegate(QStyledItemDelegate):
    def editorEvent(self, event, model, option, index):
        last_state = index.data(Qt.ItemDataRole.CheckStateRole)
        ret = super().editorEvent(event, model, option, index)
        if event.type() in (
            QEvent.Type.MouseButtonPress,
            QEvent.Type.MouseButtonDblClick,
        ):
            return False
        elif event.type() == QEvent.Type.MouseButtonRelease and last_state is not None:
            flags = model.flags(index)
            if flags & Qt.ItemFlag.ItemIsUserTristate:
                state = (last_state + 1) % 3
            else:
                state = (
                    Qt.CheckState.Unchecked
                    if last_state == Qt.CheckState.Checked
                    else Qt.CheckState.Checked
                )
            model.setData(index, state, Qt.ItemDataRole.CheckStateRole)
            return False
        return ret


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setWindowTitle("My App")
        self.listwidget = QListWidget(self)
        self.listwidget.setIconSize(QSize(12, 12))
        self.listwidget.setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection)
        self.setCentralWidget(self.listwidget)
        self.listwidget.setItemDelegate(Delegate(self.listwidget))
        self.listwidget.itemChanged.connect(self.handle_itemChanged)

        for sheet in ["apple", "orange", "banana", "pearl"]:
            item = QListWidgetItem(sheet)
            self.listwidget.addItem(item)
            item.setFlags(item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
            item.setCheckState(Qt.CheckState.Unchecked)

    def handle_itemChanged(self, item):
        item.setSelected(item.checkState() == Qt.CheckState.Checked)