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

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)


问题原因

  1. pynput模拟的左键按下事件会被自身的鼠标监听器捕获,触发onClick函数,导致逻辑混乱,系统误以为左键处于持续按下状态。
  2. 每次触发开关都启动新线程,关闭开关时旧线程可能仍在执行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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 17:33:26