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

如何在终端外终止Python BTD6宏的while循环?

终端外按键终止BTD6宏循环的解决方案

我用Python的pydirectinput和pyautogui编写《气球塔防6》(BTD6)宏程序时,遇到了终端外无法按键终止while循环的问题:

  • 尝试keyboard.is_pressed检测按键,但程序执行time.sleep()(尤其是长sleep)时无法捕捉终止指令
  • 尝试try-except KeyboardInterrupt,仅在终端内按Ctrl+C有效,终端外操作无反应

以下是两种可行的解决方案:


方案1:全局热键+终止标志(推荐)

利用keyboard库的全局热键功能,配合终止标志变量,同时将长time.sleep()拆分为多次短sleep循环,确保全程能及时响应终止按键。

修改后的完整代码

import pyautogui
import pydirectinput
import keyboard
import time

print("""
Welcome too...
 ____ _____ ____   __   __  __ _  __  __  __                      
| __ )_   _|  _ \ / /_ |  \/  | |/ / |  \/  | __ _  ___ _ __ ___  
| _ \ | | | | | | '_ \| |\/| | ' /  | |\/| |/ _` |/ __| '__/ _ \ 
| |_) || | | |_| | (_) | |  | | . \  | |  | | (_| | (__| | | (_) |
|____/ |_| |____/ \___/|_|  |_|_|\_\ |_|  |_|\__,_|\___|_|  \___/    
                                                       By MMW Studios      
""")

print("\nTo Begin, Get to the BTD6 Home screen.")

# 塔的快捷键映射
dart_monkey = "q"
boomerang_monkey = "w"
bomb_shooter = "e"
tack_shooter = "r"
ice_monkey = "t"
glue_gunner = "y"
sniper_monkey = "z"
submarine_monkey = "x"
buccaneer_monkey = "c"
ace_monkey = "v"
helicopter_monkey = "b"
mortar_monkey = "n"
dartling_gunner = "m"
wizard_monkey = "a"
super_monkey = "s"
ninja_monkey = "d"
alchemist = "f"
druid = "g"
mermonkey = "o"
banana_farm = "h"
spike_factory = "j"
monkey_village = "k"
engineer_monkey = "l"
beast_handler = "i"
hero = "u"

def click(position, clicks=1):
    pyautogui.moveTo(position, duration=0.2)
    time.sleep(0.2)
    pyautogui.click(clicks=clicks, interval=0.2)

def summon_tower(monkey, position):
    pydirectinput.press(monkey)
    time.sleep(0.2)
    pyautogui.moveTo(position, duration=0.2)
    time.sleep(0.2)
    pyautogui.click()

def upgrade_tower(position, upgrades):
    pyautogui.moveTo(position, duration=0.2)
    time.sleep(0.2)
    pyautogui.click()
    pydirectinput.press(",", presses=upgrades[0], interval=0.2)
    pydirectinput.press(".", presses=upgrades[1], interval=0.2)
    pydirectinput.press("/", presses=upgrades[2], interval=0.2)

# 全局终止标志
stop_flag = False

def stop_macro():
    global stop_flag
    stop_flag = True
    print("终止指令已触发,宏将在下一轮操作前停止")

# 设置全局终止热键(示例用F12,可自定义)
keyboard.add_hotkey("f12", stop_macro)

print("Press the spacebar to begin")
print("按F12可在任何时候终止宏")
keyboard.wait("space")
time.sleep(1)

# 带终止检查的循环逻辑
while not stop_flag:
    # 执行原宏操作
    click((825, 925))
    click((1350, 950))
    click((1400, 550))
    click((630, 400))
    click((1300, 450))
    
    # 替换原time.sleep()为带检查的短sleep循环(原代码time.sleep()默认1秒)
    for _ in range(10):
        if stop_flag:
            break
        time.sleep(0.1)
    if stop_flag:
        break
    
    click((965, 750))
    summon_tower(monkey_village, (1582, 673))
    upgrade_tower((1582, 673), [2, 0, 2])
    summon_tower(sniper_monkey, (1527, 595))
    upgrade_tower((1527, 595), [0, 2, 4])
    summon_tower(alchemist, (1603, 592))
    upgrade_tower((1603, 592), [4, 2, 0])
    click((1830, 1000), 2)
    
    # 处理长sleep(300),拆分为300次1秒检查
    for _ in range(300):
        if stop_flag:
            break
        time.sleep(1)
    if stop_flag:
        break
    
    click((960, 900))
    click((725, 850))

print("宏已停止")

关键改动说明

  1. 全局热键:keyboard.add_hotkey("f12", stop_macro)绑定F12为全局终止键,无论程序处于操作还是sleep状态,都能触发终止指令。
  2. 终止标志:全局变量stop_flag控制循环生命周期,热键触发时将其设为True。
  3. 拆分长sleep:把原代码中的time.sleep(300)拆分为300次time.sleep(1),每次sleep后检查终止标志,确保最多等待1秒就能响应终止指令;短sleep也做同样处理,消除响应死角。

方案2:用keyboard.wait替代长sleep(简洁版)

如果不需要在等待期间执行其他操作,可直接用keyboard.wait的超时参数替代长sleep,在等待时直接捕捉终止按键:

# 替换原time.sleep(300)为以下代码
try:
    # 等待300秒,或直到F12被按下
    keyboard.wait("f12", timeout=300)
    stop_flag = True
except KeyboardInterrupt:
    pass

这种方法更简洁,但灵活性不如方案1,适合不需要后台操作的场景。

内容的提问来源于stack exchange,提问作者Maddox W.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 21:58:14