Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 264
artomason Avatar asked Sep 26 '18 21:09

artomason


1 Answers

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_())
like image 143
eyllanesc Avatar answered Nov 15 '22 00:11

eyllanesc