PyQt - creating buttons from dictionary [duplicate]
I have a dictionary.
I need to create buttons with keys name, andclicked
slot based on value:
dic = {'a':'111', 'b':'222', 'c':'333'}
for key in dic:
btn = QPushButton(key, self)
btn.clicked.connect(lambda: doit(dic[key]))
vbox.addWidget(btn)
I have all buttons with right name. And last created button behave rightly.
But all others buttons'clicked
slots are also connected to the last created button do('333')
.
How can I make all buttons behave differently?
The anonymous function lambda: doit(dic[key])
does not evaluate key
until the function is called. By that time, the for-loop
has completed, and the for-loop
variable key
references the last key in dic
.
When the anonymous function is called (when you press a button), key
is looked up in the global namespace, and the current value of key
is returned.
To avoid this pitfall, you can use a default argument in the lambda
expression:
for key in dic:
btn = QPushButton(key, self)
btn.clicked.connect(lambda key=key: doit(dic[key]))
vbox.addWidget(btn)
Default arguments are evaluated at definition-time instead of at the time when the lambda is called. By doing this, key
is looked up in the local namespace of the anonymous function, rather than in the global namespace, and since the local namespace value for key is set to the default value which is different for each pass through the for-loop, you obtain the right value for key
.
This is also explained in this SO answer.
I think the problem is that when you call lambda: doit(dic[key]), it does literally that, and looks up dic[key], and at that time key is set to whatever the last item iterated through was
try this:
dic = {'a':'111', 'b':'222', 'c':'333'}
def create_connect(x):
return lambda: doit(x)
for key in dic:
btn = QPushButton(key, self)
btn.clicked.connect(create_connect(dic[key]))
vbox.addWidget(btn)
your iteration needs keys and values of dictionary dic.
You can use dict.iteritems() method.
If lambda is getting confusing, then is better to use partial.
Try this:
from PyQt4.QtGui import QApplication, QWidget, QVBoxLayout, QPushButton
from functools import partial
class MainWidget(QWidget):
def __init__(self):
super(MainWidget, self).__init__()
dic = {'a':'111', 'b':'222', 'c':'333'}
vbox = QVBoxLayout(self)
for key,val in dic.iteritems():
btn = QPushButton(key, self)
btn.clicked.connect(partial(self.doit, val))
vbox.addWidget(btn)
def doit(self, text):
print "%s" % text
if __name__ == "__main__":
app = QApplication([])
w = MainWidget()
w.show()
app.exec_()
精彩评论