0

Consider the following PySide2 program (python version 3.8.6 and PySide2 version 5.15.1):

from PySide2.QtWidgets import QApplication, QLineEdit

def setup(w, sigs):
    for name, f in sigs:
        getattr(w, name).connect(lambda *args: f(*args))

app = QApplication()
win = QLineEdit()

setup(win, [("textChanged",   lambda *args: print("  text", args)),
            ("returnPressed", lambda *args: print("return", args))])

win.show()
app.exec_()

And this is the output when I type "asdf" and press ENTER:

return ('a',)
return ('as',)
return ('asd',)
return ('asdf',)
return ()

I would expect this to be

  text ('a',)
  text ('as',)
  text ('asd',)
  text ('asdf',)
return ()

Why is PySide connecting the later callback function on the QLineEdit? If I change the setup function to

def setup(w, sigs):
    for name, f in sigs:
        getattr(w, name).connect(f)

It works as expected. The following also works:

def const(f):
    return lambda *args: f(*args)

def setup(w, sigs):
    for name, f in sigs:
        getattr(w, name).connect(const(f))

1 Answers1

1

This has nothing to do with PySide, but with closures.

The scope of lambda is within the function in which it's created, so when you're creating multiple lambdas in a loop, any variable that was not referenced in its arguments is the last value of the variable known in that scope. In your case, f is always the last function of the list.

By the way, in your case you're making things more complex than they shoud: just connect to the function, there's absolutely no need to create another lambda:

def setup(w, sigs):
    for name, f in sigs:
        getattr(w, name).connect(f)
musicamante
  • 41,230
  • 6
  • 33
  • 58
  • Oh, I thought that lambda captured the variables values instead of a reference to the loop variable. Thanks! – Ian Liu Rodrigues Nov 30 '20 at 15:23
  • @IanLiuRodrigues what exists *within* the lambda is only evaluated once it's run, since that "inner" lambda you created has no other reference of the `f` function, it will use what the scope of the function it was created provides (so, the last value assigned to `f` in the loop). When a fixed number of positional arguments is used, you could create "static" references with keywords in the lambda: `connect(lambda argument, func=f: func(*args))`, but in your case it won't work, as `returnPressed` has no arguments. – musicamante Nov 30 '20 at 15:31