QAction 知道在 QTableView 中触发包含它的上下文菜单的项目

QAction aware of the item where the context menu containing it is triggered in a QTableView

我有一个 QTableView/QAbstractTableModel 框架,我在其中实现了一个上下文菜单,最多有 2 个操作,具体取决于请求菜单的索引。对于这两个操作之一,一旦触发,我想发出请求上下文菜单的索引。我有一个可行的解决方案,但我不喜欢它。

代码:

class MyModel(QAbstractTableModel):

    ...

    def _restore_all(self):
        print('restore all')

    def _restore_index(self, index):
        print('restore index at: row = ' , index.row(), ', column = ', index.column())
    ...

class _ItemAwareAction(QAction):

    _mytriggered = pyqtSignal('QModelIndex')

    def __init__(self, *args, index=None, **kwargs):
        super().__init__(*args, **kwargs)
        self._index = index
        self.triggered.connect(self._emit_index)

    def _emit_index(self):
        self._mytriggered.emit(self._index)

class MyTableView(QTableView):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        model = MyModel(self)
        self.setModel(model)
        # Attributes
        restore_all_action = QAction('Restore all values', self)
        self._restore_all_action = restore_all_action
        self._menu = QMenu(self)
        # Connexions
        self.customContextMenuRequested.connect(self._item_context_menu)
        restore_all_action.triggered.connect(model._restore_all)

    def _item_context_menu(self, pos):
        menu = self._menu
        menu.clear()
        index = self.indexAt(pos)
        model = self.model()
        originals = model._originals
        if not originals:
            return
        menu.addAction(self._restore_all_action)
        references = model._index_to_references(index)
        if references in originals:
            action = _ItemAwareAction(
                'Restore this value', parent=self, index=index
            )
            action._mytriggered.connect(model._restore_index)
            menu.addAction(action)
        menu.popup(self.viewport().mapToGlobal(pos))
    ...

我发现我的 _ItemAwareAction class 是一个不必要的循环。或许您可以指出一个更简单直接的解决方案。

更简单的解决方案是使用 lambda 或 partials:

    def _item_context_menu(self, pos):
        # ...
        if references in originals:
            action = QAction('Restore this value', parent=self)
            action.triggered.connect(lambda: model._restore_index(index))
        # ...

由于您没有进行排序循环,您甚至可以只检查触发的操作是否是应该重置的操作,这是通过使用 exec() 完成的(直到菜单为 triggered/closed) 而不是立即返回:

    def _item_context_menu(self, pos):
        # ...
        if references in originals:
            restoreAction = QAction('Restore this value', parent=self)
        else:
            # cancelled menu returns None, we need to compare with something else
            restoreAction = -1
        if menu.exec_(self.viewport().mapToGlobal(pos)) == restoreAction:
            model._restore_index(index)

另一种方法是使用 data() 操作,然后您可以连接到菜单 triggered 操作。

class MyTableView(QTableView):
    def __init__(self, *args, **kwargs):
        # ...
        self._menu = QMenu(self)
        self._menu.triggered.connect(self._menu_triggered)

    def _menu_triggered(self, action):
        if isinstance(action.data(), QtCore.QModelIndex):
            self.model()._restore_index(action.data())

    def _item_context_menu(self, pos):
        # ...
        if references in originals:
            action = QAction('Restore this value', parent=self)
            action.setData(index)
        # ...