在循环中连接 PyQt4 中的插槽和信号

问题描述:

我正在尝试使用 PyQt4 构建一个计算器,但连接来自按钮的clicked()"信号没有按预期工作.我在 for 循环中为数字创建按钮,然后我尝试将它们连接起来.

Im trying to build a calculator with PyQt4 and connecting the 'clicked()' signals from the buttons doesn't work as expected. Im creating my buttons for the numbers inside a for loop where i try to connect them afterwards.

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".为什么会这样,我该如何解决?

When I click on the buttons all of them print out '9'. Why is that so and how can i fix this?

这就是 Python 中如何定义作用域、名称查找和闭包.

This is just, how scoping, name lookup and closures are defined in Python.

Python 仅通过赋值和函数参数列表在命名空间中引入新的绑定.因此 i 实际上不是在 lambda 的命名空间中定义的,而是在 __init__() 的命名空间中定义的.在 lambda 中对 i 的名称查找最终在 __init__() 的命名空间中结束,其中 i 最终绑定到 9代码>.这称为关闭".

Python only introduces new bindings in namespace through assignment and through parameter lists of functions. i is therefore not actually defined in the namespace of the lambda, but in the namespace of __init__(). The name lookup for i in the lambda consequently ends up in the namespace of __init__(), where i is eventually bound to 9. This is called "closure".

您可以通过将 i 作为具有默认值的关键字参数传递来解决这些公认的不直观(但定义明确)的语义.如前所述,参数列表中的名称在本地命名空间中引入了新的绑定,因此 lambda 中的 i 然后独立于 中的 i.__init__():

You can work around these admittedly not really intuitive (but well-defined) semantics by passing i as a keyword argument with default value. As said, names in parameter lists introduce new bindings in the local namespace, so i inside the lambda then becomes independent from i in .__init__():

self._numberButtons[i].clicked.connect(lambda i=i: self._number(i))

一个更易读、更少魔法的替代方案是functools.partial:

A more readable, less magic alternative is functools.partial:

self._numberButtons[i].clicked.connect(partial(self._number, i))

我在这里使用新式信号槽语法只是为了方便起见,旧式语法的工作原理是一样的.

I'm using new-style signal and slot syntax here simply for convenience, old style syntax works just the same.