Python 在回路中连接PyQt4中的插槽和信号
我正在尝试用PyQt4构建一个计算器,但是连接按钮的“clicked()”信号并没有按预期工作。 我正在为for循环中的数字创建按钮,之后我会尝试连接它们Python 在回路中连接PyQt4中的插槽和信号,python,pyqt4,signals-slots,Python,Pyqt4,Signals Slots,我正在尝试用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()'), l
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仅通过赋值和函数的参数列表在名称空间中引入新绑定<因此,code>i实际上不是在
lambda
的名称空间中定义的,而是在的名称空间中定义的。lambda中i
的名称查找结果是在\uuuu init\uuuu()
的命名空间中,其中i
最终绑定到9
。这被称为“关闭”
您可以通过将i
作为带有默认值的关键字参数传递,来解决这些不太直观(但定义良好)的语义。如上所述,参数列表中的名称在本地名称空间中引入了新的绑定,因此lambda
中的i
将独立于中的i
一个更具可读性、不那么神奇的选择是functools.partial
:
self._numberButtons[i].clicked.connect(partial(self._number, i))
我在这里使用新型的信号和插槽语法只是为了方便起见,旧式语法的工作原理是一样的。您正在创建闭包。闭包真正捕获的是变量,而不是变量的值。在\uuu init\uuu
的末尾,i
是范围(0,10)
的最后一个元素,即9
。在此范围内创建的所有lambda都引用此i
,只有在调用它们时,它们才会在被调用时获得i
的值(但是,单独调用\uuu init\uuu
创建lambda引用单独的变量!)
有两种常用的方法可以避免这种情况:
使用默认参数:lambda i=i:self.\u编号(i)
。这是因为默认参数在函数定义时绑定了一个值
定义助手函数helper=lambda i:(lambda:self.\u number(i))
并在循环中使用helper(i)
。这是因为“外部”i
是在绑定i
时计算的,正如前面提到的,在下一次调用helper
时创建的下一个闭包将引用不同的变量
使用Qt
方式,改用。谢谢。我会选择部分解决方案,请不要<代码> QSaleMalPoper-<代码>是C++ 98的残余,它没有lambda或部分函数,并且这种处理是必要的邪恶。然而,在Python中,QSignalMapper
与functools.partial()
或lambda相比只是多余的,而且非常臃肿。使用partial()
或lambda
的解决方案比QSignalMapper
更短、更优雅。哦,这是关于选择的问题,我会选择QSignalMapper
来实现Qt/C++兼容性。当然,这是关于选择的问题,但我根本不理解您的选择,我也无法理解。我使用Python来QT应用程序,因为它不是C++,如果我放弃所有Python的特殊特性来维护或达到C++兼容性,我也可以使用C++。
self._numberButtons[i].clicked.connect(partial(self._number, i))