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

如何用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

解决方案实现

核心思路

  1. 用asyncio模拟SetTimer:每个定时任务封装为独立的异步函数,支持动态修改间隔、启停控制;
  2. 多任务管理:用字典存储各个定时任务的状态(是否运行)和任务对象,方便按键触发时快速操作;
  3. 非阻塞监听:将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 23:35:23