PyQt6中button.clicked.connect绑定Lambda回调输出异常问题
PyQt6按钮回调lambda失效问题解析
问题场景
编写了如下PyQt6计算器代码,预期点击除“C”和“=”之外的按钮时,打印对应的按键内容,但实际点击任意数字或运算符按钮时,始终输出“=”。使用functools.partial绑定回调则功能正常,需要排查回调的问题。
import sys from functools import partial from PyQt6.QtCore import Qt from PyQt6.QtWidgets import ( QApplication, QWidget, QMainWindow, QGridLayout, QVBoxLayout, QLineEdit, QPushButton ) ErrorMessage = "ERROR" class CalculatorUi(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Calculator") self.setFixedSize(300, 300) self.generatLayout = QVBoxLayout() central_widget = QWidget() central_widget.setLayout(self.generatLayout) self.setCentralWidget(central_widget) self._createDisplay() self._createButtons() def _createDisplay(self): self.display = QLineEdit() self.display.setReadOnly(True) self.display.setMaxLength(12) self.display.setAlignment(Qt.AlignmentFlag.AlignCenter) self.display.setFixedHeight(50) self.generatLayout.addWidget(self.display) def _createButtons(self): self.buttonMap = {} buttonLayout = QGridLayout() keyboard = [ ["7", "8", "9", "/", "C"], ["4", "5", "6", "*", "("], ["1", "2", "3", "-", ")"], ["0", "00", ".", "+", "="] ] for row, keys in enumerate(keyboard): for col,key in enumerate(keys): self.buttonMap[key] = QPushButton(key) self.buttonMap[key].setFixedSize(50, 50) buttonLayout.addWidget(self.buttonMap[key], row, col) self.generatLayout.addLayout(buttonLayout) def _getDisplayText(self): return self.display.text() def _setText(self, text): self.display.setText(text) def _clearDisplay(self): self.display.setText('') class Calculate: def __init__(self, model, view): self._evaluate = model self._view = view self._connectSignalsAndSLot() def _connectSignalsAndSLot(self): for key, button in self._view.buttonMap.items(): if key not in ("C", "="): button.clicked.connect( lambda: print(key) ) def evaluate_expression(expression): try: result = str(eval(expression)) except Exception: result = ErrorMessage return result def main(): app = QApplication([]) calculator = CalculatorUi() calculator.show() Calculate(evaluate_expression, calculator) sys.exit(app.exec()) if __name__ == "__main__": main()
问题根源
问题出在lambda的变量延迟绑定特性上:
- 在循环中定义lambda时,lambda并没有直接捕获当前循环迭代的
key值,而是保存了对key变量的引用。 - 当按钮被点击时,lambda才会去读取
key的值,而此时循环已经执行完毕,key变量最终指向的是循环最后一次迭代的元素——也就是“=”(尽管代码里过滤了C和=,但循环遍历了所有按钮,最后一个元素是“=”,lambda引用的是全局的key变量)。
解决方法
方法1:使用functools.partial绑定参数
这是你已经验证可行的方式,通过partial将当前循环的key值直接绑定到回调函数中,避免延迟引用:
def _connectSignalsAndSLot(self): for key, button in self._view.buttonMap.items(): if key not in ("C", "="): button.clicked.connect( partial(print, key) )
方法2:给lambda添加默认参数捕获当前值
通过给lambda设置默认参数,让每次循环时都将当前的key值赋值给默认参数,形成闭包保存该值:
def _connectSignalsAndSLot(self): for key, button in self._view.buttonMap.items(): if key not in ("C", "="): button.clicked.connect( lambda k=key: print(k) )
内容的提问来源于stack exchange,提问作者mind overflow
相关产品推荐
相关产品推荐

