跟随视频编写的pynput自定义热键脚本运行报KeyError失效
核心报错来源
这个KeyError和德语特殊字符、替换按键没有关系,是代码逻辑里两个必然触发的漏洞叠加导致的:
- 大小写状态错位:Windows处理大写按键时,如果按组合键过程中先松开Shift,那么松开P键的事件上报的字符会从
P变成小写p。这个值确实在定义的组合列表里,但是因为之前按下时存进集合的是大写P,当前这个小写p根本不存在于按下状态集合中,直接执行remove就会抛出键不存在的错误。 - 移除逻辑无容错:现有代码只要判断到松开的按键属于任意自定义组合,就直接删除集合元素,不校验元素是否真的在集合内——一旦出现事件上报的按键状态和按下时的状态不匹配,程序就会直接崩溃。
另外现有代码还存在两个未暴露的隐性问题:
- 按住热键不松手时,会高频次重复触发打印,无法停止
- 将大小写P拆成两个独立组合的写法冗余,同一个物理键不需要做两套识别逻辑
可直接运行的修正版本
from pynput import keyboard # 统一识别小写p即可,不需要拆分大小写组合 COMBINATIONS = [ {keyboard.Key.ctrl, keyboard.Key.alt, keyboard.KeyCode(char='p')} ] current_pressed = set() trigger_lock = False # 防重复触发的锁 def run_action(): global trigger_lock if not trigger_lock: print("Scheiße!") trigger_lock = True # 按键值归一化,统一处理大小写差异 def format_key(key): # 仅对可输出字符的按键做小写转换,功能键不做处理 if hasattr(key, 'char') and key.char: return keyboard.KeyCode(char=key.char.lower()) return key def on_press(key): norm_key = format_key(key) # 归一化后判断按键是否属于热键组合 if any(norm_key in combo for combo in COMBINATIONS): current_pressed.add(norm_key) # 所有热键都按下,且未触发过时执行动作 if any(all(k in current_pressed for k in combo) for combo in COMBINATIONS) and not trigger_lock: run_action() def on_release(key): global trigger_lock norm_key = format_key(key) # 先确认元素在集合内再删除,从根源避免KeyError if norm_key in current_pressed: current_pressed.remove(norm_key) # 所有热键都松开后重置触发锁,支持下一次热键触发 if len(current_pressed) == 0: trigger_lock = False with keyboard.Listener(on_press=on_press, on_release=on_release) as listener: listener.join()
关键修正说明
- 新增统一按键归一化函数,不管按的是大写P还是小写p,全部转成小写值做匹配,彻底解决系统上报的按键字符和按下时不匹配的问题。
- 集合移除操作前先判断元素是否存在,不会再触发
KeyError。 - 加入触发锁机制,按住热键不会无限刷屏;等所有组合键松开后,锁自动重置,下次按热键可正常触发。
- 删除冗余的大写P组合定义,减少无效判断开销。
内容的提问来源于stack exchange,提问作者user14655231
相关产品推荐
相关产品推荐

