如何用Python复刻AutoHotkey多定时循环按键脚本
Python复刻AutoHotkey多定时循环启停功能
需求说明
需要通过Python实现:按下指定键盘按键,切换多个独立定时循环的启停;启动循环后,pynput需持续监听按键,支持随时启停任意循环。核心难点是用asyncio复刻AutoHotkey中SetTimer的功能——该功能可按指定间隔重复调用函数,同时不阻塞其他操作。
现有Python代码
import pyautogui,time import random from random import randint import time from time import sleep from pynput import keyboard from pynput.keyboard import Key, Listener import threading from datetime import datetime import asyncio toggle1 = False def start_loop1(): start_time = time.time() async def stuff(): await asyncio.sleep(random.random() * 3) print(round(time.time() - start_time, 1), "Finished doing stuff") async def do_stuff_periodically(interval, periodic_function): while True: print(round(time.time() - start_time, 1), "Starting periodic function") await asyncio.gather( asyncio.sleep(interval), periodic_function(), ) global toggle1 print("toggle1 STATE:", toggle1) toggle1 = not toggle1 if toggle1== False: task.cancel() task= asyncio.run(do_stuff_periodically(5, stuff)) def on_press(key): # print('{0} pressed'.format(key)) if key.char in ("a"): global toggle1 toggle1 = not toggle1 print("toggle1 STATE:", toggle1) if toggle1: start_loop1() return False def on_release(key): print('{0} release'.format(key)) if key == Key.esc: # Stop listener return False # Collect events until released with Listener( on_press=on_press, on_release=on_release) as listener: listener.join()
目标AutoHotkey代码
Random(Min := "", Max := "") { Random Out, % Min, % Max return out } Toggle := 0 *a:: Toggle := !Toggle if Toggle { send {a down} settimer, Loop1, 10130, on settimer, Loop2, 6185, on sleep,% Random(30,60 ) send {q} sleep,% Random(30,60 ) send {r} } else { settimer, Loop1, off settimer, Loop2, off send {a up} } return Loop1: send {q} SetTimer, Loop1,off SetTimer, Loop1, % Random(10200,10230 ),on return Loop2: send {r} SetTimer, Loop2,off SetTimer, Loop2, % Random(6220,6245 ),on return
解决方案实现
核心思路
- 用asyncio模拟SetTimer:每个定时任务封装为独立的异步函数,支持动态修改间隔、启停控制;
- 多任务管理:用字典存储各个定时任务的状态(是否运行)和任务对象,方便按键触发时快速操作;
- 非阻塞监听:将pynput的按键监听放在单独线程,避免阻塞asyncio事件循环,保证定时任务和监听同时运行。
完整代码
import asyncio import random from pynput import keyboard from pynput.keyboard import Key, Listener import pyautogui import threading # 全局任务管理器:键为任务名,值为{"task": 异步任务对象, "running": 运行状态, "interval_func": 间隔生成函数} timers = {} global_toggle = False def random_interval(min_ms, max_ms): """生成随机间隔(转换为秒)""" return random.randint(min_ms, max_ms) / 1000 async def timer_task(name, interval_func, callback): """模拟AHK的SetTimer循环执行逻辑""" while timers[name]["running"]: # 执行回调 await callback() # 生成下一次间隔 interval = interval_func() await asyncio.sleep(interval) def start_timer(name, interval_func, callback): """启动定时任务""" if name not in timers or not timers[name]["running"]: timers[name] = { "running": True, "interval_func": interval_func, "task": asyncio.create_task(timer_task(name, interval_func, callback)) } def stop_timer(name): """停止定时任务""" if name in timers and timers[name]["running"]: timers[name]["running"] = False timers[name]["task"].cancel() del timers[name] async def loop1_callback(): """对应AHK的Loop1逻辑""" pyautogui.press('q') async def loop2_callback(): """对应AHK的Loop2逻辑""" pyautogui.press('r') async def on_toggle_enable(): """启用时执行的初始化操作""" pyautogui.keyDown('a') # 启动两个定时任务 start_timer("Loop1", lambda: random_interval(10200, 10230), loop1_callback) start_timer("Loop2", lambda: random_interval(6220, 6245), loop2_callback) # 初始的q和r发送 await asyncio.sleep(random_interval(30, 60)/1000) pyautogui.press('q') await asyncio.sleep(random_interval(30, 60)/1000) pyautogui.press('r') def on_toggle_disable(): """禁用时执行的操作""" pyautogui.keyUp('a') stop_timer("Loop1") stop_timer("Loop2") def on_press(key): """按键按下回调""" global global_toggle try: if key.char == 'a': global_toggle = not global_toggle if global_toggle: # 在事件循环中执行启用逻辑 asyncio.create_task(on_toggle_enable()) print("已启用所有定时循环") else: on_toggle_disable() print("已停止所有定时循环") except AttributeError: pass def on_release(key): """按键释放回调""" if key == Key.esc: # 退出时清理所有任务 for name in list(timers.keys()): stop_timer(name) return False def run_listener(): """启动按键监听线程""" with Listener(on_press=on_press, on_release=on_release) as listener: listener.join() async def main(): """主事件循环""" # 启动监听线程 listener_thread = threading.Thread(target=run_listener, daemon=True) listener_thread.start() # 保持事件循环运行 await asyncio.Event().wait() if __name__ == "__main__": asyncio.run(main())
代码说明
- 任务管理器:
timers字典统一管理所有定时任务,记录运行状态和任务对象,方便启停控制; - timer_task函数:模拟AHK的SetTimer,循环执行回调并动态生成下一次间隔;
- 按键监听:放在单独线程运行,避免阻塞asyncio事件循环,按下
a键切换全局启停状态,按下esc键退出; - 复刻AHK逻辑:完全对应目标脚本中的初始操作(按下a不放、发送q/r)和定时循环(Loop1和Loop2的随机间隔)。
内容的提问来源于stack exchange,提问作者Shiranui Okami
相关产品推荐
相关产品推荐

