5

First read this. It is about lambda x=x: foo(x) catching x even in for loop.

This is a window with label and two buttons generated in for loop. When button is clicked, it name appears in label.

If we use usual lambda: label.setText("button -- " + str(i)), then the result is last i in the loop, no matter what button is pressed:
lambda:foo(i)
And this is right.

When we change to lambda i=i: label.setText("button -- " + str(i)) (snipet) and expect that now it will be everything ok, the result is:
lambda i=i:foo(i)]
False!

Where this False comes from?

import sys
from PyQt4.QtGui import *

class MainWindow(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        vbox = QVBoxLayout(self)

        # label for action
        label = QLabel('')
        vbox.addWidget(label)

        # adding buttons
        for i in range (1, 3):
            btn = QPushButton(str(i))
            btn.clicked.connect( lambda i=i: label.setText("button " + str(i)) )
            vbox.addWidget(btn)

app = QApplication(sys.argv)
myapp = MainWindow()
myapp.show()
sys.exit(app.exec_())

Why this solution is not working as it should be? What this false means?

I know that you can make foo_factory, as in first link, but the question is what is wrong with lambda i=i: foo(i)

Community
  • 1
  • 1
Qiao
  • 15,903
  • 28
  • 85
  • 117
  • It starts from here - http://stackoverflow.com/questions/6084331/pyqt-creating-buttons-from-dictionary/6084655#6084655 – Qiao May 28 '11 at 01:13

2 Answers2

4

I don't have PyQt4 installed to test at this very instant, but it seems clear to me that when your lambda callback is called, it's being given an argument. i is then equal to whatever the argument is, instead of the default value. Try this and tell me if it works (or if it at least changes the output):

btn.clicked.connect( lambda throw_away=0, i=i: label.setText("button " + str(i)) )
senderle
  • 136,589
  • 35
  • 205
  • 230
  • 3
    @Qiao, by the way -- some people might advocate the use of `_` as the name for a `throw_away` variable. I won't take a position on that, except to say that it is one recognized convention. – senderle May 28 '11 at 02:11
  • Ok, got it. But there is on more little question that bothers me - who (and when) adds argument to this lambda. It is someone in PyQt. But it is other question. – Qiao May 28 '11 at 02:24
  • @Qiao, I don't know PyQt very well, but it's common for GUI libraries, when an event triggers a callback, to pass that event to the callback. My guess is that something like that is happening. Why it shows up as 'False' when converted to a string is a mystery to me. – senderle May 28 '11 at 04:22
  • @Qiao, see [pedrotech's answer](http://stackoverflow.com/questions/6159021/lambda-i-i-fooi-in-for-loop-not-working/6162445#6162445) for a fuller explanation of what is happening. – senderle May 28 '11 at 22:23
2

Signal "clicked" passes a boolean argument to your connected lambda slot.
Documentation

What you are trying to accomplish is better done by this:

btn.clicked.connect( lambda clicked, i=i : label.setText("button " + str(i)) )
pedrotech
  • 1,331
  • 10
  • 19
  • Ah, thanks & +1 for the link -- I did a halfhearted documentation search but didn't find what I was looking for. – senderle May 28 '11 at 22:18