首页 > 解决方案 > 从 QAction 触发器访问委托类

问题描述

我有一个 QListWidget 对项目及其子项目进行分类(它们都是可检查的)。

目前,在选中/取消选中类别项目时,子项目可以正常工作,我在上下文菜单中介绍了该选项的工作方式与您选中和取消选中类别项目的方式相同。

但是在我update_opts的上下文菜单中的 QAction 中,虽然它允许我检查/取消选中类别项目,但它不会影响子项目的检查状态。

此外,似乎QListWidgetItem没有我可以用来触发委托类中的triggeror信号。toggleeditorEvent

IsCategoryRole = QtCore.Qt.UserRole
ParentRole = QtCore.Qt.UserRole + 1


class CategoryDelegate(QtGui.QStyledItemDelegate):
    def editorEvent(self, event, model, option, index):
        old_state = model.data(index, QtCore.Qt.CheckStateRole)
        res = super(CategoryDelegate, self).editorEvent(
            event, model, option, index
        )
        current_state = model.data(index, QtCore.Qt.CheckStateRole)
        if old_state != current_state:
            if index.data(IsCategoryRole):
                pix = QtCore.QPersistentModelIndex(index)
                for i in range(model.rowCount()):
                    ix = model.index(i, 0)
                    if pix == ix.data(ParentRole):
                        model.setData(
                            ix, current_state, QtCore.Qt.CheckStateRole
                        )
        return res


class ListWidget(QtGui.QListWidget):
    def __init__(self, parent=None):
        super(ListWidget, self).__init__(parent)
        delegate = CategoryDelegate(self)
        self.setItemDelegate(delegate)

    def addCategory(self, text):
        item = ListWidget.create_checkable_item(text)
        item.setData(IsCategoryRole, True)
        self.addItem(item)
        return item

    def addItemToCategory(self, category_item, text):
        item = ListWidget.create_checkable_item(text)
        item.setData(IsCategoryRole, False)
        ix = self.indexFromItem(category_item)
        pix = QtCore.QPersistentModelIndex(ix)
        item.setData(ParentRole, pix)
        self.addItem(item)
        return item

    @staticmethod
    def create_checkable_item(text):
        item = QtGui.QListWidgetItem(text)
        item.setFlags(
            item.flags()
            | QtCore.Qt.ItemIsUserCheckable
            | QtCore.Qt.ItemIsEditable
        )
        item.setCheckState(QtCore.Qt.Unchecked)
        return item

    def mousePressEvent(self, event):
        if event.button() == QtCore.Qt.RightButton:
            list_menu = QtGui.QMenu(self)
            selected_item = self.currentItem()
            if selected_item.data(IsCategoryRole):
                update_cat_items = QtGui.QAction("Check all or none", self)
                update_cat_items.triggered.connect(self.update_opts)
                list_menu.addAction(update_cat_items)

            list_menu.exec_(QtGui.QCursor().pos())

        else:
            super(ListWidget, self).mousePressEvent(event)

    def update_opts(self):
        selected_item = self.currentItem() 
        state = selected_item.checkState() # It returns eith 0 or 2, instead of QtCore.Qt.Unchecked or QtCore.Qt.Checked

        if state == QtCore.Qt.Checked:
            # If opt is checked in the first place, set it to uncheck
            selected_item.setCheckState(QtCore.Qt.Unchecked)

        else:
            # If opt is unchecked in the first place, set it to check
            selected_item.setCheckState(QtCore.Qt.Checked)


class TestDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        super(TestDialog, self).__init__()
        self.listWidget = ListWidget()

        all_num = self.listWidget.addCategory("-- All Nums --")
        num_items = ["One", "Two", "Three"]
        for num in num_items:
            self.listWidget.addItemToCategory(all_num, num)

        all_letters = self.listWidget.addCategory("-- All Letters --")

        letter_items = ["A", "B", "C"]
        for letter in letter_items:
            self.listWidget.addItemToCategory(all_letters, letter)

        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.listWidget)




if __name__ == "__main__":
    import sys

    app = QtGui.QApplication(sys.argv)
    w = TestDialog()
    w.show()
    sys.exit(app.exec_())

标签: pythonpyqtpyqt4

解决方案


在我之前的回答中,考虑到 OP 只希望仅在用户按下复选框时才允许该行为,现在使用新方法将扩展该行为,为此不需要委托,而是创建自定义 QListWidgetItem 方法被覆盖 setData() 方法:

from PyQt4 import QtCore, QtGui

IsCategoryRole = QtCore.Qt.UserRole
ParentRole = QtCore.Qt.UserRole + 1


class ListWidgetItem(QtGui.QListWidgetItem):
    def setData(self, role, value):
        if role == QtCore.Qt.CheckStateRole and self.data(IsCategoryRole):
            lw = self.listWidget()
            index = lw.indexFromItem(self)
            pix = QtCore.QPersistentModelIndex(index)
            for r in range(lw.count()):
                print(r)
                it = lw.item(r)
                if pix == it.data(ParentRole):
                    it.setData(role, value)
        super(ListWidgetItem, self).setData(role, value)


class ListWidget(QtGui.QListWidget):
    def addCategory(self, text):
        item = ListWidget.create_checkable_item(text)
        item.setData(IsCategoryRole, True)
        self.addItem(item)
        return item

    def addItemToCategory(self, category_item, text):
        item = ListWidget.create_checkable_item(text)
        item.setData(IsCategoryRole, False)
        ix = self.indexFromItem(category_item)
        pix = QtCore.QPersistentModelIndex(ix)
        item.setData(ParentRole, pix)
        self.addItem(item)
        return item

    @staticmethod
    def create_checkable_item(text):
        item = ListWidgetItem(text)
        item.setFlags(
            item.flags()
            | QtCore.Qt.ItemIsUserCheckable
            | QtCore.Qt.ItemIsEditable
        )
        item.setCheckState(QtCore.Qt.Unchecked)
        return item

    def contextMenuEvent(self, event):
        list_menu = QtGui.QMenu()
        it = self.itemAt(self.viewport().mapFromGlobal(QtGui.QCursor().pos()))
        if it.data(IsCategoryRole):
            update_cat_items = QtGui.QAction("Check all or none", self)
            update_cat_items.triggered.connect(self.update_opts)
            list_menu.addAction(update_cat_items)
        list_menu.exec_(QtGui.QCursor().pos())

    def update_opts(self):
        it = self.itemAt(self.viewport().mapFromGlobal(QtGui.QCursor().pos()))
        it.setCheckState(
            QtCore.Qt.Unchecked
            if it.checkState() == QtCore.Qt.Checked
            else QtCore.Qt.Checked
        )


class TestDialog(QtGui.QDialog):
    def __init__(self, parent=None):
        super(TestDialog, self).__init__()
        self.listWidget = ListWidget()

        all_num = self.listWidget.addCategory("-- All Nums --")
        num_items = ["One", "Two", "Three"]
        for num in num_items:
            self.listWidget.addItemToCategory(all_num, num)

        all_letters = self.listWidget.addCategory("-- All Letters --")

        letter_items = ["A", "B", "C"]
        for letter in letter_items:
            self.listWidget.addItemToCategory(all_letters, letter)

        layout = QtGui.QVBoxLayout(self)
        layout.addWidget(self.listWidget)


if __name__ == "__main__":
    import sys

    app = QtGui.QApplication(sys.argv)
    w = TestDialog()
    w.show()
    sys.exit(app.exec_())

推荐阅读