开发者

PyQt - creating buttons from dictionary [duplicate]

This question already has answers here: Creating functions (开发者_StackOverflowor lambdas) in a loop (or comprehension) (6 answers) Closed 7 months ago.

I have a dictionary.

I need to create buttons with keys name, and clicked 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_()
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜