Переключение QAction мешает QMenu mousePressEvent - PullRequest
0 голосов
/ 03 апреля 2019

У меня есть вложенные пункты меню, в которых я пытаюсь сделать все элементы проверяемыми. Первоначально переключение не работает для основного элемента (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_())

Ответы [ 2 ]

1 голос
/ 03 апреля 2019

QtGui.QAction.toggle () Это вспомогательная функция для свойства checked ().Подключитесь к нему, чтобы изменить проверенное состояние на противоположное.

и

Если я выполню ваш код, возникла ошибка TypeError.(После добавления подменю и щелчка по элементам действия подменю)

TypeError: _callActionItem() takes 3 positional arguments but 4 were given

Похоже, вы подготовили богатые аргументы для _callActionItem ().

Но это ловушка.

Поскольку для переключения требуется аргумент для bool.

Итак, вы должны изменить аргументы для _callActionItem () на _callActionItem(self,checked,action)

и toggled послеtoggling.

И toggle вызывается QSubAction.

Вы меняете, например, mousePressEvent.

1.

def mousePressEvent(self,event):
    action = self.activeAction()
    if not isinstance(action,QSubAction) and action is not None:
        action.trigger()
        return
    elif isinstance(action,QSubAction):
        action.toggle()
        return
    return QtGui.QMenu.mousePressEvent(self,event)

и

2.

_callActionItem(self,checked,action)

и весь измененный код здесь.


import functools
import sys
from PyQt4 import QtGui, QtCore


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 not isinstance(action,QSubAction) and action is not None:
            action.trigger()
            return
        elif isinstance(action,QSubAction):
            action.toggle()
            return
        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)
        # self.qmenu.addMenu(base_qmenu)

        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.unsetCheck_action)


        self.qmenu.addAction(test_action)

    def unsetCheck_action(self,checked):

        sender_obj = self.sender()
        if isinstance(sender_obj,QtGui.QAction): 


            sender_obj.setChecked(checked)




    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.setCheckable(True)
            action.setChecked(True)
            action.toggled.connect(slot)
            base_menu.addAction(action)

    def _callActionItem(self, checked, 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_())
1 голос
/ 03 апреля 2019

По умолчанию 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)
...