PyQt - create buttons from a dictionary

I have a dictionary.
I need to create buttons with key names and clicked slot based on the 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 the buttons with the correct name. And the last button created behaves correctly.
But all the other buttons of the ' clicked slots' clicked also connected to the last created do('333') button do('333') .

How to make all buttons behave differently?

+1
source share
3 answers

I think the problem is that when you call lambda: doit (dic [key]), it does it literally and looks for dic [key], and at that time the key is set to the last element,

try the following:

 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) 
+1
source

The anonymous lambda: doit(dic[key]) function lambda: doit(dic[key]) does not evaluate key until the function is called. By this time, for-loop , and the for-loop key variable refers to the last key in dic .

When an anonymous function is called (when you click the button), key scanned in the global namespace and the current key value is returned.

To avoid this error, you can use the 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) 

The default arguments are evaluated at the time of definition, not at the time of calling the lambda. By doing so, key looked up in the local namespace of the anonymous function, and not in the global namespace, and since the value of the local namespace is set to the default value, which is different for each pass through the for-loop, you will get the correct value key .

This is also explained in this SO answer .

+3
source

your iteration needs the keys and values โ€‹โ€‹of the dic dictionary. You can use the dict.iteritems () method.
If lambda becomes confusing, then it is better to use partial.

Try the following:

 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_() 
+1
source

All Articles