繁体   English   中英

在循环中连接 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 仅通过赋值和函数的参数列表在命名空间中引入新的绑定。 因此, 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__的末尾, irange(0, 10)的最后一个元素,即9 您在此 scope 中创建的所有 lambdas 都引用此i并且仅当它们被调用时,它们才会在它们被调用时获得i的值(但是, __init__的单独调用会创建引用单独变量的 lambdas。)

有两种流行的方法可以避免这种情况:

  1. 使用默认参数: lambda i=i: self._number(i) 这是因为默认参数在 function 定义时间绑定了一个值。
  2. 定义一个助手 function helper = lambda i: (lambda: self._number(i))并在循环中使用helper(i) 这是因为“外部” ii被绑定时被评估,并且 - 如前所述 - 在下一次调用helper时创建的下一个闭包将引用不同的变量。

使用Qt方式,改用QSignalMapper

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM