Search code examples
pythonlambdapyqtpysidegetattr

PyQt/Pyside - Create & Connect Dynamically - capturing function and arguments in lambda


I'm trying to recreate the windows note pad.

I'm currently messing with the QMenuBar

I made a dictionary with all the menus and actions it will have in the following pattern:

menus = {'File':[['New', 'Ctrl+n'],
                 ['Open', 'Ctrl+o'],
                 ['Save', 'Ctrl+s'],
                 ['Save as...', None],
                 'Separator', 
                 # and so on

Then I iterate over that dict, and created the menus and actions successfully and stored them in a second dictionary.

Now I'm trying to connect each action[new, open, save, …] to a instance method of the same name.

I'm doing like so:

for action in menus[m]:
    action = menu.addAction(action[0])

    if action[1]:
        action.setShortcut(QKeySequence(action[1]))

    if isinstance(action, QAction):
        fname = action[0].lower() 
        # and some other string manipulations
        func = getattr(self,fname)
        action.triggered.connect(lambda arg=action: func(arg))

It connects successfully, but if I try to use any of the actions it does nothing.

I had previously connected actions using lambda functions, but this is the first time I'm using getattr() and lambda together.


Solution

  • Found my mistake:

    Problem was thatfunc was getting overwritten on every iteration over menus.

    fixed it by changing:

    action.triggered.connect(lambda arg=action:func(arg))
    

    to

    action.triggered.connect(lambda f=func,arg=a:f(arg))
    

    The latter works because a reference to func is stored is taken immediately by default argument in lambda.