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)
# ...
我有一个 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)
# ...