如何在Python程序运行期间检测物理按键是否按下
在Python中边运行耗时操作边检测物理按键
这个场景太常见了!单线程下确实没法同时跑耗时操作和检测按键——因为Python会把CPU资源全给那个耗时的main()函数,根本腾不出手去检查按键。解决思路就是用多线程把这两个任务分开,让它们各自跑互不干扰。下面给你两种实用的实现方案:
方案一:用第三方库pynput(跨平台推荐)
pynput是专门处理输入设备的库,跨平台支持Windows/macOS/Linux,用它监听按键非常方便。
步骤1:安装库
pip install pynput
步骤2:完整代码示例
import threading from pynput import keyboard # 线程安全的停止信号(比直接用全局变量更可靠) stop_event = threading.Event() # 原代码中的全局标志a a = True def main(): global a try: # 把你的10秒耗时操作拆成可中断的小步骤 for step in range(10): # 每次循环检查是否要停止 if stop_event.is_set(): print("检测到按键,立即终止操作!") break # 模拟单步耗时1秒的操作(替换成你的实际业务逻辑) import time time.sleep(1) print(f"核心操作进行中...第 {step+1} 秒") finally: # 无论正常结束还是中断,都把a设为False a = False def on_key_press(key): """按键按下时触发的回调函数""" print(f"\n检测到按键: {key}") # 设置停止信号,通知main()终止 stop_event.set() # 停止按键监听(可选,如果你只想响应一次按键) return False def start_key_listener(): """启动后台按键监听线程""" with keyboard.Listener(on_press=on_key_press) as listener: listener.join() if __name__ == "__main__": # 启动核心操作线程 main_thread = threading.Thread(target=main) main_thread.start() # 启动按键监听线程 listener_thread = threading.Thread(target=start_key_listener) listener_thread.start() # 等待核心操作线程结束 main_thread.join() print("\n程序已结束")
代码说明
- 把
main()的耗时操作拆成循环步骤,每次循环检查stop_event,这样按键按下后能快速响应中断 pynput的Listener会在后台线程监听按键,完全不阻塞核心操作stop_event是线程安全的同步原语,避免了直接操作全局变量可能导致的竞态问题
方案二:用系统原生库(无需第三方依赖)
如果不想安装第三方库,可以根据你的操作系统用对应的标准库实现:
Windows平台(用msvcrt)
import threading import msvcrt import time stop_event = threading.Event() a = True def main(): global a try: for step in range(10): if stop_event.is_set(): print("检测到按键,终止操作!") break time.sleep(1) print(f"核心操作进行中...第 {step+1} 秒") finally: a = False def listen_for_keys(): """循环检查按键(非阻塞)""" while not stop_event.is_set(): # 检查是否有按键按下 if msvcrt.kbhit(): # 获取按下的按键 key = msvcrt.getch().decode('utf-8') print(f"\n检测到按键: {key}") stop_event.set() # 短暂休眠,避免占用过多CPU资源 time.sleep(0.1) if __name__ == "__main__": main_thread = threading.Thread(target=main) main_thread.start() key_thread = threading.Thread(target=listen_for_keys) key_thread.start() main_thread.join() print("\n程序已结束")
Linux/macOS平台(用termios和sys)
import threading import sys import termios import tty import time stop_event = threading.Event() a = True def get_key(): """非阻塞获取按键""" fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setraw(sys.stdin.fileno()) key = sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return key def main(): global a try: for step in range(10): if stop_event.is_set(): print("检测到按键,终止操作!") break time.sleep(1) print(f"核心操作进行中...第 {step+1} 秒") finally: a = False def listen_for_keys(): while not stop_event.is_set(): try: key = get_key() print(f"\n检测到按键: {key}") stop_event.set() except: pass time.sleep(0.1) if __name__ == "__main__": main_thread = threading.Thread(target=main) main_thread.start() key_thread = threading.Thread(target=listen_for_keys) key_thread.start() main_thread.join() print("\n程序已结束")
关键注意点
- 一定要把耗时操作拆成可中断的小片段,否则即使触发了停止信号,也要等到整个
main()执行完才会响应 - 优先用线程安全的同步工具(比如
threading.Event),而不是直接修改全局变量,避免多线程下的竞态问题 - 如果你的
main()里是调用第三方库的耗时函数(没法拆成循环),尽量找支持中断的库函数,避免用threading.Thread的terminate()方法(该方法不安全,可能导致资源泄漏)
内容的提问来源于stack exchange,提问作者Stalemate
相关产品推荐
相关产品推荐

