簡體   English   中英

在循環中連接 PyQt4 中的插槽和信號

[英]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__的末尾, irange(0, 10)的最后一個元素,即9 您在此 scope 中創建的所有 lambdas 都引用此i並且僅當它們被調用時,它們才會在它們被調用時獲得i的值(但是, __init__的單獨調用會創建引用單獨變量的 lambdas。)

有兩種流行的方法可以避免這種情況:

  1. 使用默認參數: lambda i=i: self._number(i) 這是因為默認參數在 function 定義時間綁定了一個值。
  2. 定義一個助手 function helper = lambda i: (lambda: self._number(i))並在循環中使用helper(i) 這是因為“外部” ii被綁定時被評估,並且 - 如前所述 - 在下一次調用helper時創建的下一個閉包將引用不同的變量。

使用Qt方式,改用QSignalMapper

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM