How to get the name of a QMenu Item when clicked?
Asked Answered
H

1

6

I have a few actions in a QMenu that I'm trying to connect to a single method; there are additional actions that are unrelated.

import sys
from PyQt5.QtWidgets import *


class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        layout = QGridLayout(self)
        self.menu_bar = QMenuBar()
        self.menu = QMenu('MENU', self)

        self.menu_action = QAction('Option #1', self)
        self.menu_action.setData('option1')
        self.menu_action.triggered.connect(self.actionClicked)

        self.menu.addAction(self.menu_action)
        self.menu_bar.addMenu(self.menu)
        layout.addWidget(self.menu_bar)

    def actionClicked(self, action):
        print(action)

        try:
            print(action.text())
        except AttributeError as e:
            print(e)

        try:
            print(action.data())
        except AttributeError as e:
            print(e)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 300, 100)
    window.show()
    sys.exit(app.exec_())

I was wondering how I can tell which of the actions was clicked when the method is called. Currently I'm trying to use self.menu_action.setData() to give the action a cleaner name for the receiving method, but that does not seem to be working properly.

Haskel answered 26/9, 2018 at 21:8 Comment(0)
D
11

A possible solution is to use the sender() method that returns the QObject that emits the signal, in this case the QAction:

import sys
from PyQt5 import QtCore, QtWidgets


class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()

        layout = QtWidgets.QGridLayout(self)
        menubar = QtWidgets.QMenuBar()
        filemenu = menubar.addMenu('MENU')

        filemenu.addAction('Option #1', self.actionClicked)
        filemenu.addAction('Option #2', self.actionClicked)
        layout.addWidget(menubar)

    @QtCore.pyqtSlot()
    def actionClicked(self):
        action = self.sender()
        print('Action: ', action.text())


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 300, 100)
    window.show()
    sys.exit(app.exec_())

If you are going to connect all the QActions of the QMenu then you can use the triggered signal of the QMenu, this sends the QAction that was pressed.

import sys
from PyQt5 import QtCore, QtWidgets


class Window(QtWidgets.QWidget):
    def __init__(self):
        super(Window, self).__init__()

        layout = QtWidgets.QGridLayout(self)
        menubar = QtWidgets.QMenuBar()
        filemenu = menubar.addMenu('MENU')
        filemenu.triggered.connect(self.actionClicked)
        filemenu.addAction('Option #1')
        filemenu.addAction('Option #2')
        layout.addWidget(menubar)

    @QtCore.pyqtSlot(QtWidgets.QAction)
    def actionClicked(self, action):
        print('Action: ', action.text())


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 300, 100)
    window.show()
    sys.exit(app.exec_())

Update:

The QAction triggered signal is different from the QMenu triggered signal, in the case of the first one it sends a Boolean value that indicates if the QAction is checked (by default a QAction is not checkable, if you want to activate it you must use setCheckable(True)) and in the second case it sends the QAction that was pressed that belongs to the QMenu. That's why you always get False, if you do not want to have that problem you have to use my first method using sender(). On the other hand in Qt very rarely you will have to use try-except, in reality you should never use it in the world of Qt since that has an additional cost that Qt does not want, and on the other hand it hides the cause of the errors , as a recommendation use try-except when there is no other solution, and in the case of Qt there will almost always be another option.

import sys
from PyQt5.QtWidgets import *


class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()
        layout = QGridLayout(self)
        self.menu_bar = QMenuBar()
        self.menu = QMenu('MENU', self)

        self.menu_action = QAction('Option #1', self)
        self.menu_action.setData('option1')
        self.menu_action.triggered.connect(self.actionClicked)

        self.menu.addAction(self.menu_action)
        self.menu_bar.addMenu(self.menu)
        layout.addWidget(self.menu_bar)

    def actionClicked(self, checked):
        action = self.sender()
        print(action.text())
        print(action.data())


if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = Window()
    window.setGeometry(600, 100, 300, 100)
    window.show()
    sys.exit(app.exec_())
Diplocardiac answered 26/9, 2018 at 21:15 Comment(8)
I think the first option should work good. I tried using `.setData('Option #1')' initially, but all it would do is return false in the method; I couldn't get anything to work.Haskel
@AaronTomason where did you do setData() ?, if my answer helped you do not forget to mark it as correctDiplocardiac
I set one of the menu actions as self.option1 = QAction1('Option #1', self) then self.option1.setData(QVariant('option1')) and connected it using self.option1.triggered.connect(self.actionClicked). When I tried to print action, it was only a False value.Haskel
@AaronTomason I understand how you used, but since you get False, that is, you had to do something like print(xxx), what is xxx?Diplocardiac
I tried print(action). When I attempted to use print(action.text()) or print(action.data()) python said there was no xxx for Boolean object.Haskel
@AaronTomason where does the code use print(action.data())?, on the other hand do not use QVariant, save it directly: self.option1.setData('option1')Diplocardiac
@AaronTomason You could edit your question and put a complete example of what you point out, I think you have used an inappropriate method, or you have used it in the wrong place, but if I do not see the complete code I will not understand you.Diplocardiac
Edited to reflect this conversation and provide a clearer intention.Haskel

© 2022 - 2024 — McMap. All rights reserved.