如何在终端外终止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("宏已停止")
关键改动说明
- 全局热键:
keyboard.add_hotkey("f12", stop_macro)绑定F12为全局终止键,无论程序处于操作还是sleep状态,都能触发终止指令。 - 终止标志:全局变量
stop_flag控制循环生命周期,热键触发时将其设为True。 - 拆分长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.
相关产品推荐
相关产品推荐

