在 PyQt4 中循环连接插槽和信号
- 2024-12-26 08:43:00
- admin 原创
- 105
问题描述:
我正在尝试使用 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”。为什么会这样?我该如何解决这个问题?
解决方案 1:
这只是 Python 中范围、名称查找和闭包的定义方式。
Python 仅通过赋值和函数的参数列表在命名空间中引入新绑定。 i
因此,实际上不是在 的命名空间中定义的lambda
,而是在 的命名空间中定义的__init__()
。因此,lambda 中的名称查找i
最终会在 的命名空间中结束__init__()
,其中i
最终绑定到9
。这称为“闭包”。
您可以通过传递具有默认值的关键字参数来解决这些显然不太直观(但定义明确)的语义i
。如前所述,参数列表中的名称在本地命名空间中引入了新的绑定,因此theni
内部lambda
变得独立于i
in .__init__()
:
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))
我在这里使用新式信号和槽语法只是为了方便,旧式语法的工作原理是一样的。
解决方案 2:
您正在创建闭包。闭包实际上捕获的是变量,而不是变量的值。在 的末尾__init__
,i
是 的最后一个元素range(0, 10)
,即9
。您在此范围内创建的所有 lambda 都引用此,并且只有在调用它们时,它们才会在调用时i
获得 的值(但是,创建 lambda 的单独调用引用单独的变量!)。i
`__init__`
有两种常用的方法可以避免这种情况:
使用默认参数:
lambda i=i: self._number(i)
。这是因为默认参数在函数定义时绑定了一个值。定义辅助函数并在循环中
helper = lambda i: (lambda: self._number(i))
使用。这样做之所以有效,是因为“外部”是在绑定时进行评估的,并且 - 如前所述 - 在下一次调用中创建的下一个闭包将引用不同的变量。helper(i)
`ii
helper`
解决方案 3:
用Qt
方法,用QSignalMapper
代替。