Переключение QAction затруднено QMenu mousePressEvent

У меня есть вложенные пункты меню, в которых я пытаюсь сделать все элементы доступными для проверки. Первоначально переключение не работает для основного элемента (QMenu, установленного как QAction), но работает для подэлементов.

Но при использовании mousePressEvent переключение теперь работает для основного элемента, но не для подэлементов.

Пытался воспроизвести то, что я сделал для основного элемента для функции подэлемента, но переключение по-прежнему не работает. И похоже, он вызывается дважды в файле _callActionItem().

Однако по некоторым причинам, если окно подэлемента находится в режиме отрыва, переключение возможно, но не так, если вы делаете контекстное меню в самом инструменте.

Кроме того, если я отключу mousePressEvent в QCustomMenu, я вернусь к исходной точке, где переключение работает для подэлементов, но не для основных элементов.

class QSubAction(QtGui.QAction):
    def __init__(self, text="", parent=None):
        super(QSubAction, self).__init__(text, parent)
        self.setCheckable(True)
        self.setChecked(True)


class QAddAction(QtGui.QAction):
    def __init__(self, icon=None, text="Add Item", parent=None):
        if icon:
            super(QAddAction, self).__init__(icon, text, parent)
        else:
            super(QAddAction, self).__init__(text, parent)

class QCustomMenu(QtGui.QMenu):
    """Customized QMenu."""

    def __init__(self, title, parent=None):
        super(QCustomMenu, self).__init__(title=str(title), parent=parent)
        self.setup_menu()

    def mousePressEvent(self, event):
        action = self.activeAction()
        if isinstance(action, QtGui.QAction):
            action.trigger()
        return QtGui.QMenu.mousePressEvent(self, event)

    def setup_menu(self):
        self.setContextMenuPolicy(QtCore.Qt.DefaultContextMenu)

    def contextMenuEvent(self, event):
        no_right_click = [QAddAction]
        if any([isinstance(self.actionAt(event.pos()), instance) for instance in no_right_click]):
            return
        pos = event.pos()

    def addAction(self, action):
        super(QCustomMenu, self).addAction(action)


class Example(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Example, self).__init__(parent)
        self.initUI()

    def initUI(self):         
        self.setGeometry(300, 300, 300, 200)
        self.setWindowTitle('Context menu')   

        self.qmenu = QCustomMenu(title='', parent=self)
        add_item_action = QtGui.QAction('Add Main item', self,
            triggered=self.add_new_item)
        self.qmenu.addAction(add_item_action)

    def contextMenuEvent(self, event):
        action = self.qmenu.exec_(self.mapToGlobal(event.pos()))

    def add_new_item(self):
        main_menu_name, ok = QtGui.QInputDialog.getText(
            self,
            'Main Menu',
            'Name of new Menu Item:'
        )
        if ok:
            self._addMenuItemTest(main_menu_name)

    def _addMenuItemTest(self, main_menu_name):
        icon_path = '/user_data/add.png'

        base_qmenu = QCustomMenu(title=main_menu_name, parent=self)
        base_qmenu.setTearOffEnabled(True)                     

        add_item_action = QAddAction(None, 'Add Sub Item', base_qmenu)
        slot = functools.partial(self.add_sub_item, base_qmenu)
        add_item_action.triggered.connect(slot)
        base_qmenu.addAction(add_item_action)

        test_action = QtGui.QAction(main_menu_name, self)
        test_action.setMenu(base_qmenu)
        test_action.setCheckable(True)
        test_action.setChecked(True)

        self.connect(
            test_action,
            QtCore.SIGNAL("triggered(bool)"),
            self.main_toggling
        )

        self.qmenu.addAction(test_action)

    def main_toggling(self, check_state):
        sender_obj = self.sender()
        if isinstance(sender_obj, QtGui.QAction):
            sender_obj.setChecked(check_state)

    def add_sub_item(self, base_menu):
        sub_menu_name, ok = QtGui.QInputDialog.getText(
            self,
            'Sub Menu',
            'Name of new Sub Item:'
        )
        if ok:
            action = QSubAction(sub_menu_name, self)
            slot = functools.partial(
                self._callActionItem,
                action
            )
            # action.toggled.connect(slot)
            # from pprint import pprint
            # pprint(help(action))
            # action.connect(action, QtCore.SIGNAL("triggered(bool)"), self._callActionItem)

            base_menu.addAction(action)

    def _callActionItem(self, action):
        # This is called twice, False and True again
        print '>>> sub check-state : ', action.isChecked()


if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = Example()
    window.show()
    sys.exit(app.exec_())

person Teh Ki    schedule 03.04.2019    source источник


Ответы (1)


По умолчанию QMenu позволяет проверять только QAction без дочерних элементов, поэтому вы сделали трюк, чтобы включить функциональность другого QAction, который не соответствует вышеизложенному, что может повлиять на операцию, и это то, что происходит в вашем случае. Решение состоит в том, чтобы определить, какой тип QAction нажат:

def mousePressEvent(self, event):
    action = self.activeAction()
    if not isinstance(action, QSubAction) and action is not None:
        action.trigger()
        return
    return QtGui.QMenu.mousePressEvent(self, event)
person eyllanesc    schedule 03.04.2019
comment
Извините, что комментировать немного поздно, но я пытаюсь понять, почему action возвращается дважды - один как QSubAction и None. Особенно откуда берётся None? - person Teh Ki; 11.04.2019