You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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_())

关键修改说明

  1. 让Ui_MainWindow继承QtCore.QObject,新增坐标更新信号pos_updated,用于子线程回调和UI更新的中转。
  2. 将listener的初始化从类属性改为实例属性,放在setupUi中创建,绑定当前实例的on_click方法,自动传入self参数解决参数缺失报错。
  3. 修正监听停止逻辑,调用当前实例self.listener.stop()停止监听。
  4. 新增update_pos槽函数接收信号更新UI,避免子线程直接操作UI的闪退问题。
  5. 点击启动监听前先停止已运行的监听、重新创建实例,避免重复启动异常。

内容的提问来源于stack exchange,提问作者guy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 04:24:05