PyQt5用pynput监听鼠标报on_click缺失pressed参数如何解决?
问题排查与修复方案
核心错误原因
- 你将
listener定义为类属性,声明时直接绑定on_click实例方法,此时方法未与实例对象绑定,触发回调时不会自动传入self参数,回调实际传入的4个参数(x、y、button、pressed)会被依次对应到方法定义的self、x、y、button参数位,最终缺少pressed参数触发报错。 - 停止监听的调用方式错误,不能直接调用
mouse.Listener.stop()类方法,需要调用当前运行的listener实例的stop方法。 - 额外注意:pynput的事件回调运行在独立子线程,PyQt禁止子线程直接修改UI组件,后续放开label赋值的注释会直接闪退,需要通过信号槽中转更新UI。
修复后完整代码
from PyQt5 import QtCore, QtGui, QtWidgets from PIL import ImageGrab from pynput import mouse import pyautogui import time # 继承QObject支持自定义信号 class Ui_MainWindow(QtCore.QObject): # 自定义坐标更新信号,用于子线程和UI线程中转 pos_updated = QtCore.pyqtSignal(int, int) def setupUi(self, MainWindow): super().__init__() MainWindow.setObjectName("MainWindow") MainWindow.resize(306, 145) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName("centralwidget") self.label_x = QtWidgets.QLabel(self.centralwidget) self.label_x.setGeometry(QtCore.QRect(70, 90, 71, 41)) self.label_x.setScaledContents(False) self.label_x.setAlignment(QtCore.Qt.AlignCenter) self.label_x.setWordWrap(False) self.label_x.setObjectName("label_x") self.label_y = QtWidgets.QLabel(self.centralwidget) self.label_y.setGeometry(QtCore.QRect(160, 90, 71, 41)) self.label_y.setScaledContents(False) self.label_y.setAlignment(QtCore.Qt.AlignCenter) self.label_y.setWordWrap(False) self.label_y.setObjectName("label_y") self.button = QtWidgets.QPushButton(self.centralwidget) self.button.setGeometry(QtCore.QRect(90, 20, 111, 61)) self.button.setObjectName("button") MainWindow.setCentralWidget(self.centralwidget) self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) self.button.clicked.connect(self.onPosButtonClick) # 绑定信号到UI更新槽函数 self.pos_updated.connect(self.update_pos) # listener改为实例属性,绑定当前实例的on_click方法 self.listener = mouse.Listener(on_click=self.on_click) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate("MainWindow", "Bot")) self.label_x.setText(_translate("MainWindow", "0")) self.label_y.setText(_translate("MainWindow", "0")) self.button.setText(_translate("MainWindow", "Pick Location")) def update_pos(self, x, y): # 主线程更新UI _translate = QtCore.QCoreApplication.translate self.label_x.setText(_translate("MainWindow", str(x))) self.label_y.setText(_translate("MainWindow", str(y))) def on_click(self, x, y, button, pressed): if pressed: print('{0} at {1}'.format( 'Pressed' if pressed else 'Released', (x, y))) # 发送信号通知UI更新 self.pos_updated.emit(x, y) # 调用当前实例的stop方法停止监听 self.listener.stop() def onPosButtonClick(self): # 先停止已运行的监听,避免重复启动异常 if self.listener.running: self.listener.stop() # 重新创建listener实例,避免二次启动报错 self.listener = mouse.Listener(on_click=self.on_click) self.listener.start() if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) MainWindow = QtWidgets.QMainWindow() ui = Ui_MainWindow() ui.setupUi(MainWindow) MainWindow.show() sys.exit(app.exec_())
关键修改说明
- 让
Ui_MainWindow继承QtCore.QObject,新增坐标更新信号pos_updated,用于子线程回调和UI更新的中转。 - 将
listener的初始化从类属性改为实例属性,放在setupUi中创建,绑定当前实例的on_click方法,自动传入self参数解决参数缺失报错。 - 修正监听停止逻辑,调用当前实例
self.listener.stop()停止监听。 - 新增
update_pos槽函数接收信号更新UI,避免子线程直接操作UI的闪退问题。 - 点击启动监听前先停止已运行的监听、重新创建实例,避免重复启动异常。
内容的提问来源于stack exchange,提问作者guy
相关产品推荐
相关产品推荐

