2011-05-21 91 views
1

我有一本字典。
我需要創建一個鍵名按鈕和clicked插槽基於價值:PyQt - 從字典中創建按鈕

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) 

我有正確的名稱所有按鈕。最後創建的按鈕的行爲是正確的。
但所有其他按鈕'clicked插槽也連接到最後創建的按鈕do('333')

如何讓所有按鈕的行爲不同?

回答

1

我認爲問題是,當你調用lambda:doit(dic [key])時,它確實是這樣做的,並且查找dic [key],並且在那個時候key被設置爲最後一個項迭代被

試試這個:

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) 
+0

怎麼會呢?這是如何與'for'連接的?這個竅門有沒有名字?如果沒有,則不需要這種愚蠢的功能。我認爲'lambda:'解決了所有這些問題。但情況變得更糟。 – Qiao 2011-05-21 21:42:16

3

,直至函數被調用的匿名函數lambda: doit(dic[key])不評估key。那時候,for-loop已經完成,並且for-loop變量key引用了dic中的最後一個鍵。

當調用匿名函數(當您按下按鈕時),在全局命名空間中查找key,並返回當前值key

爲了避免這一缺陷,可以在lambda表達式中使用默認參數:在定義時而不是在當拉姆達被調用的時候

for key in dic: 
    btn = QPushButton(key, self) 
    btn.clicked.connect(lambda key=key: doit(dic[key])) 
    vbox.addWidget(btn) 

默認參數進行評估。通過這樣做,key在匿名函數的本地名稱空間中查找,而不是在全局名稱空間中查找,並且由於key的本地名稱空間值被設置爲每次通過for-loop不同的默認值,您獲得key的正確值。

這也在這SO answer解釋。

+0

謝謝你的回答和鏈接,我開始明白了。 Bwmat的方法正在工作,但'lambda key = key:'出錯了,'lambda key = key:self.label.setText(key)'返回'TypeError:QLabel.setText(str):argument 1 has unexpected type' bool''。沒有'key = key',它就像問題一樣工作。我不明白'bool'是從哪裏來的。 – Qiao 2011-05-21 22:23:34

+0

@Qiao:使用'lambda key = key:'如果* no *參數傳遞給lambda函數,則起作用。這允許'key'採取默認值。如果一個參數傳遞給lambda函數,那麼'key'會被賦值。它看起來像一個布爾被傳遞給你的lambda函數,並被分配給'key'。 – unutbu 2011-05-21 23:28:21

1

你的迭代需要字典dic的鍵和值。 您可以使用dict.iteritems()方法。
如果lambda變得混亂,那麼最好使用partial。

試試這個:

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_()