[英]Connecting slots and signals in PyQt4 in a loop
我試圖用 PyQt4 構建一個計算器並連接來自按鈕的“clicked()”信號沒有按預期工作。 我為 for 循環中的數字創建了我的按鈕,之后我嘗試將它們連接起來。
def __init__(self):
for i in range(0,10):
self._numberButtons += [QPushButton(str(i), self)]
self.connect(self._numberButtons[i], SIGNAL('clicked()'), lambda : self._number(i))
def _number(self, x):
print(x)
當我單擊按鈕時,所有按鈕都會打印出“9”。 為什么會這樣,我該如何解決?
這就是 Python 中定義范圍、名稱查找和閉包的方式。
Python 僅通過賦值和函數的參數列表在命名空間中引入新的綁定。 因此, i
實際上並未在lambda
的命名空間中定義,而是在__init__()
的命名空間中定義。 因此,在 lambda 中查找i
的名稱最終在__init__()
的命名空間中,其中i
最終綁定到9
。 這被稱為“關閉”。
您可以通過將i
作為具有默認值的關鍵字參數傳遞來解決這些公認的不是很直觀(但定義明確)的語義。 如前所述,參數列表中的名稱在本地命名空間中引入了新的綁定,因此lambda
中的i
然后獨立於.__init__()
中的i
:
self._numberButtons[i].clicked.connect(lambda checked, i=i: self._number(i))
更新: clicked
有一個默認的checked
參數,它會覆蓋i
的值,所以它必須在關鍵字值之前添加到參數列表中。
functools.partial
是一個更易讀、更少魔法的替代方案:
self._numberButtons[i].clicked.connect(partial(self._number, i))
我在這里使用新式的信號和槽語法只是為了方便,舊式的語法是一樣的。
您正在創建閉包。 閉包確實捕獲了一個變量,而不是變量的值。 在__init__
的末尾, i
是range(0, 10)
的最后一個元素,即9
。 您在此 scope 中創建的所有 lambdas 都引用此i
並且僅當它們被調用時,它們才會在它們被調用時獲得i
的值(但是, __init__
的單獨調用會創建引用單獨變量的 lambdas。)
有兩種流行的方法可以避免這種情況:
lambda i=i: self._number(i)
。 這是因為默認參數在 function 定義時間綁定了一個值。helper = lambda i: (lambda: self._number(i))
並在循環中使用helper(i)
。 這是因為“外部” i
在i
被綁定時被評估,並且 - 如前所述 - 在下一次調用helper
時創建的下一個閉包將引用不同的變量。使用Qt
方式,改用QSignalMapper
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.