Python脚本无法按预期循环运行问题求助(含pynput代码)
问题描述
编写了如下使用pynput库的Python脚本,无论是直接用Python3运行还是通过PyInstaller打包后运行,脚本都无法按预期持续循环,且无任何报错信息:
import time from pynput import keyboard from pynput.keyboard import Key, Listener STATUS=False COMBINATION={keyboard.Key.esc, keyboard.Key.alt} # The currently active modifiers current = set() def on_press(key): if key in COMBINATION: current.add(key) if all(k in current for k in COMBINATION): STATUS = False if key == keyboard.Key.esc: # Stop listener STATUS = True def on_release(key): try: current.remove(key) except KeyError: pass def hp(delay): keyboard.press(Key.f1) keyboard.release(Key.f1) time.sleep(delay) def cp(): keyboard.press(Key.f2) keyboard.release(Key.f2) def main(): with Listener( on_press=on_press, on_release=on_release) as listener: listener.join() while True: while STATUS: for i in range(3): hp(0.5) cp()
问题排查与修复
核心问题分析
- 全局变量未正确引用:
STATUS是全局变量,但on_press函数中直接赋值时,Python会将其视为局部变量,导致外部循环无法感知状态变化。 - Listener阻塞主线程:
listener.join()会一直阻塞主线程,直到Listener停止,后面的while True循环永远不会执行。 - 按键逻辑冲突:按下
esc键时同时触发两个矛盾逻辑:既设置STATUS=True,又因esc在COMBINATION中设置STATUS=False。 - 按键模拟方式不安全:直接调用
keyboard.press/release可能引发线程安全问题,因为Listener本身运行在独立线程中。
修复后的代码
import time from pynput import keyboard from pynput.keyboard import Key, Listener import threading # 全局状态变量 STATUS = False COMBINATION = {keyboard.Key.esc, keyboard.Key.alt} current = set() def on_press(key): global STATUS if key in COMBINATION: current.add(key) # 同时按下alt+esc时停止循环 if all(k in current for k in COMBINATION): STATUS = False # 单独按下esc时启动循环 elif key == keyboard.Key.esc: STATUS = True def on_release(key): try: current.remove(key) except KeyError: pass def hp(delay): # 使用Controller安全模拟按键 with keyboard.Controller() as ctrl: ctrl.press(Key.f1) ctrl.release(Key.f1) time.sleep(delay) def cp(): with keyboard.Controller() as ctrl: ctrl.press(Key.f2) ctrl.release(Key.f2) def loop_task(): """在子线程中运行循环逻辑,避免阻塞Listener""" while True: while STATUS: for _ in range(3): hp(0.5) cp() time.sleep(0.1) # 降低空循环的CPU占用 def main(): # 启动循环子线程,设置为守护线程随主线程退出 threading.Thread(target=loop_task, daemon=True).start() # 启动键盘监听器,主线程保持监听 with Listener(on_press=on_press, on_release=on_release) as listener: listener.join() if __name__ == "__main__": main()
修复说明
- 全局变量绑定:通过
global STATUS声明,确保函数内修改的是全局状态变量。 - 线程分离:将循环逻辑放到独立子线程中,避免被Listener的阻塞调用卡住。
- 逻辑修正:区分单独按
esc(启动循环)和alt+esc(停止循环)的操作,消除逻辑冲突。 - 安全按键模拟:使用
keyboard.Controller()处理按键模拟,避免跨线程操作的潜在问题。 - 性能优化:状态未激活时添加短延时,减少空循环对CPU的消耗。
内容的提问来源于stack exchange,提问作者Felipe
相关产品推荐
相关产品推荐

