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

Posted

技术标签:

【中文标题】在循环中连接 PyQt4 中的插槽和信号【英文标题】:Connecting slots and signals in PyQt4 in a loop 【发布时间】:2019-07-25 08:29:55 【问题描述】:

我试图用 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 作为具有默认值的关键字参数传递来解决这些公认的不是很直观(但定义明确)的语义。如前所述,参数列表中的名称在本地命名空间中引入了新的绑定,因此lambda 中的i 然后独立于.__init__() 中的i

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

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

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

我在这里使用新风格的信号和槽语法只是为了方便,旧风格的语法是一样的。

【讨论】:

谢谢。我将使用 functools.partial 解决方案。【参考方案2】:

您正在创建闭包。闭包确实捕获了一个变量,而不是变量的值。在__init__的末尾,irange(0, 10)的最后一个元素,即9。您在此范围内创建的所有 lambdas 都引用此 i 并且仅当它们被调用时,它们才会在它们被调用时获得 i 的值(但是,__init__ 的单独调用会创建引用单独的 lambdas变量!)。

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

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

【讨论】:

【参考方案3】:

使用Qt方式,改用QSignalMapper

【讨论】:

请不要。 QSignalMapper 是 C++ 98 的残余,它没有 lambda 或部分函数,​​并且这种变通方法是必要的。然而,在 Python 中,QSignalMapperfunctools.partial() 或 lambda 相比是多余的,而且确实臃肿。使用partial()lambda 的解决方案比QSignalMapper 更短、更优雅。 哦,关于选择,我会选择 QSignalMapper 以实现 Qt/C++ 兼容性。 当然是关于选择的问题,但我根本不明白你的选择,我也无法遵循。我将 Python 用于 Qt 应用程序正是因为它不是 C++,如果我放弃 Python 的所有特殊功能来维护或达到 C++ 兼容性,我也可以使用 C++ ;)

以上是关于在循环中连接 PyQt4 中的插槽和信号的主要内容,如果未能解决你的问题,请参考以下文章

跟随发出的信号到其连接的插槽?

Qt 4.8:来自不同线程的两个信号和一个插槽之间的连接行为

信号 - 回路内的插槽连接

如何使用 Qt Creator 将按钮单击信号(“触发”信号)与工具栏中的用户按钮的动作/插槽功能连接起来?

在代码中连接信号和插槽并在 QtCreator“信号和插槽编辑器”窗口中设置它们有啥区别?

如何将信号和插槽与 qt 中的另一个对象连接 - 已解决