[英]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.