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


问题内容

我试图用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__()。对于名称查找i在lambda中的命名空间从而结束了__init__(),在i最终必然9。这称为“关闭”。

您可以通过传递i带有默认值的关键字参数来解决这些公认的非直觉(但定义明确)的语义。如前所述,参数列表中的名称在本地名称空间中引入了新的绑定,因此theni内部lambda变得独立于iin
.__init__()

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

更具可读性,更少魔术性的替代方法是functools.partial

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

为了方便起见,我在这里使用新型信号和插槽语法,而老式语法的工作原理相同。