Pynput绑定鼠标X2键触发左键连按异常:仅按下不释放
鼠标侧键X2触发连按左键后无法释放的问题
我用pynput库给游戏绑定鼠标额外按键:Button.x1设为触发式连按空格键,Button.x2设为触发式连按左键,都是开关模式。X1功能正常,但X2触发后表现为按住左键不松手,已经试过用管理员身份运行脚本,问题依旧。
我的代码如下:
from pynput.mouse import Listener, Button, Controller as MController from pynput.keyboard import Key, Controller as KController from time import sleep from threading import Thread from random import uniform C_keyboard = KController() C_mouse = MController() state = [False, False] def bind(i, key, c): global state while state[i]: c.press(key) sleep(round(uniform(0.5, 1.0), 10)) c.release(key) def onClick(x, y, button, pressed): global state global C_keyboard global C_mouse if pressed: if button == Button.x1: state[0] = not state[0] Thread(target=bind, args=(0, Key.space, C_keyboard)).start() elif button == Button.x2: state[1] = not state[1] Thread(target=bind, args=(1, Button.left, C_mouse)).start() print("%s is %s (%s, %s)" % (button, ("Pressed" if pressed else "Released"), state[0],state[1])) def main(): with Listener(on_click=onClick) as listener: listener.join() Thread(target=main).start()
按下X2键一次后,onClick函数的输出如下:
Button.x2 is Pressed (False, True)
Button.left is Pressed (False, True)
Button.left is Released (False, True)
Button.x2 is Released (False, True)
问题原因
- pynput模拟的左键按下事件会被自身的鼠标监听器捕获,触发
onClick函数,导致逻辑混乱,系统误以为左键处于持续按下状态。 - 每次触发开关都启动新线程,关闭开关时旧线程可能仍在执行
press操作,无法及时释放按键。
修复方案
以下是修复后的代码,解决了模拟事件干扰和线程控制问题:
from pynput.mouse import Listener, Button, Controller as MController from pynput.keyboard import Key, Controller as KController from time import sleep from threading import Thread, Event from random import uniform C_keyboard = KController() C_mouse = MController() # 用Event对象精准控制线程启停 space_running = Event() left_click_running = Event() def auto_space(): while space_running.is_set(): C_keyboard.press(Key.space) sleep(round(uniform(0.5, 1.0), 10)) C_keyboard.release(Key.space) sleep(0.01) # 让线程及时响应停止信号 def auto_left_click(): while left_click_running.is_set(): C_mouse.press(Button.left) sleep(round(uniform(0.5, 1.0), 10)) C_mouse.release(Button.left) sleep(0.01) # 停止时强制释放左键,避免残留状态 C_mouse.release(Button.left) def on_click(x, y, button, pressed, is_synthetic): # 过滤pynput自身模拟的事件 if is_synthetic: return if pressed: if button == Button.x1: if space_running.is_set(): space_running.clear() else: space_running.set() Thread(target=auto_space, daemon=True).start() elif button == Button.x2: if left_click_running.is_set(): left_click_running.clear() else: left_click_running.set() Thread(target=auto_left_click, daemon=True).start() print(f"{button} is {'Pressed' if pressed else 'Released'} ({space_running.is_set()}, {left_click_running.is_set()})") def main(): # 启用pass_is_synthetic参数,获取事件是否为模拟产生 with Listener(on_click=on_click, pass_is_synthetic=True) as listener: listener.join() if __name__ == "__main__": main()
关键改动说明
- 用
Event替代全局state列表,线程启停控制更可靠,避免多线程状态冲突。 - 添加
pass_is_synthetic=True并过滤模拟事件,防止自身模拟的左键触发监听器。 - 线程设置为守护线程,主程序退出时自动终止。
- 停止连按时强制释放左键,确保系统中无残留按键状态。
- 循环中添加短暂延迟,让线程能及时响应停止信号。
内容的提问来源于stack exchange,提问作者Abrham
相关产品推荐
相关产品推荐

