提问人:lukad 提问时间:1/2/2011 最后编辑:lukad 更新时间:7/1/2022 访问量:6250
在 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 仅通过赋值和函数的参数列表在命名空间中引入新的绑定。 因此,实际上并没有在 的命名空间中定义,而是在 的命名空间中定义。因此,lambda 中的名称查找最终会出现在 的命名空间中,其中最终绑定到 。这称为“闭合”。i
lambda
__init__()
i
__init__()
i
9
您可以通过将具有默认值的关键字参数作为关键字参数传递来解决这些公认的不太直观(但定义明确)的语义。如前所述,参数列表中的名称在本地命名空间中引入了新的绑定,因此 then 内部独立于 in:i
i
lambda
i
.__init__()
self._numberButtons[i].clicked.connect(lambda checked, i=i: self._number(i))
更新:clicked
有一个默认的选中
参数,该参数将覆盖 i
的值,因此必须将其添加到关键字值之前的参数列表中。
一个更具可读性、不那么神奇的替代方案是:functools.partial
self._numberButtons[i].clicked.connect(partial(self._number, i))
为了方便起见,我在这里使用新式的信号和时隙语法,旧式语法的工作方式是一样的。
评论
您正在创建闭包。闭包实际上捕获的是变量,而不是变量的值。在 的末尾,是 的最后一个元素,即 。您在此作用域中创建的所有 lambda 都引用了此值,并且仅在调用它们时,它们才会获得调用时的值(但是,对 create lambda 的单独调用引用了单独的变量!__init__
i
range(0, 10)
9
i
i
__init__
有两种流行的方法可以避免这种情况:
- 使用默认参数:.之所以这样做,是因为默认参数在函数定义时绑定了一个值。
lambda i=i: self._number(i)
- 定义帮助程序函数并在循环中使用。这是有效的,因为“外部”是在绑定时计算的,并且 - 如前所述 - 在下一次调用中创建的下一个闭包将引用不同的变量。
helper = lambda i: (lambda: self._number(i))
helper(i)
i
i
helper
使用方式,请改用 QSignalMapper
。Qt
评论
QSignalMapper
QSignalMapper
functools.partial()
partial()
lambda
QSignalMapper
QSignalMapper
评论