0

I'm trying to create a number of context menu items inside a loop. Each of them has its own name and should be connected to a specific function (when clicked). I found that if I write something like:

from PyQt5 import QtCore, QtWidgets, QtSvg
from PyQt5.QtGui import *
import sys


class MyMainWidget(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.setGeometry(300, 300, 500, 500)
        self.show()

    def myMenuHandler(self, name):
        print('from', name)

    def contextMenuEvent(self, event):
        menu = QtWidgets.QMenu(self)

        # Method 1
        action = QtWidgets.QAction('outside1', menu)
        action.triggered.connect(lambda: self.myMenuHandler('outside1'))
        menu.addAction(action)

        action = QtWidgets.QAction('outside2', menu)
        action.triggered.connect(lambda: self.myMenuHandler('outside2'))
        menu.addAction(action)

        # Method 2
        for s in ['item1', 'item2', 'item3']:
            action = QtWidgets.QAction(s, menu)
            action.triggered.connect(lambda: self.myMenuHandler(s))
            menu.addAction(action)

        action = menu.exec_(self.mapToGlobal(event.pos()))


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    widget = MyMainWidget()
    sys.exit(app.exec_())

Then There are five items when I right click on the widget, when I click them in order:

What I expected from the output:

from outside1
from outside1
from item1
from item2
from item3

Actual output:

from outside1
from outside2
from item3
from item3
from item3

Notice that whatever item I clicked on the actions created inside the for loop, they all triggered the last item.

I inspected the call stack and found that those three outputs were all triggered by item3, which is exactly the last item created inside the loop.

Why adding an action inside and outside behave differently? Does that have something to do with Python's variable scope or the PyQt5 itself?

  • change to `action.triggered.connect(lambda *args, s=s: self.myMenuHandler(s))` – eyllanesc Nov 09 '20 at 04:57
  • @eyllanesc This works. could you plz explain what's happening when adding `*args` and argument `s=s` as lambda's parameters? I don't know why it doesn't work if I either omit `*args` or omit `s=s`. – PromethiumL Nov 09 '20 at 05:14
  • The triggered signal carries the "checked" state of the QAction so "* args" represents that information that should be omitted. Please read the docs: https://doc.qt.io/qt-5/qaction.html#triggered – eyllanesc Nov 09 '20 at 05:16

0 Answers0