Python Tkinter开发自动化bot运行卡死、启停失效崩溃问题求助
问题根因
你的程序卡死、崩溃、报属性错误一共是4个核心问题:
- GUI阻塞问题:你把bot的死循环逻辑直接绑在了按钮的command上,Tkinter的主循环是单线程的,点启动后bot的while循环直接占住了主线程,GUI连刷新、响应按钮点击的资源都没有,自然点停止没反应、切回来就崩。
- P键显隐报错问题:你写的
main_window.win根本不存在,main_window本身就是Tk生成的窗口对象,直接调用它的withdraw/deiconify方法就行,多写个.win当然找不到属性。 - 线程写法错误:设置守护线程的方法是给线程对象的
daemon属性赋值True,不是调用.Daemon(True)方法,你原来的写法线程根本没被正确设为守护进程,关窗口的时候线程还在后台跑容易抛异常。另外Tkinter的控件操作只能在主线程做,你原来的add()函数每次循环都新建Label往窗口堆,既不销毁旧标签,跨线程改GUI还会偶发崩溃。 - 逻辑bug:你把全局的
start函数、starter函数和按钮变量重名了,按钮赋值之后原来的函数名被覆盖,点启动的时候实际调用的是Button对象不是你写的函数。还有stopp标志位初始值设的True,窗口一加载没点启动bot就可能跑起来。
修复方案
- 把bot的运行逻辑单独放到子线程里,启动按钮只负责创建子线程启动循环,绝对不能占Tk主循环的线程
- 删掉
main_window.win的多余写法,直接对main_window调用窗口显隐方法 - 修正守护线程的写法,所有GUI控件的更新都通过
after()方法调度回主线程执行,不要在子线程里直接创建/修改Label - 把函数名和按钮变量名分开,不要重名;修正计数标签的逻辑,固定两个标签用来显示书本、青金石数量,不要每次循环新建Label堆在窗口上
- 给图像识别加容错,找不到图片的时候直接跳过本轮循环,不要因为找不到坐标直接抛异常崩溃
修复后的完整代码
from pynput.keyboard import Key, Controller import pyautogui keyboards = Controller() import keyboard as kb from threading import Thread from tkinter import * import time # 全局变量 bookss = 0 lapiss = 0 main_window = Tk() stopp = False # 初始状态设为停止,避免窗口加载就自动跑 bot_thread = None # 存bot线程对象,避免重复点启动开多个线程 # 启停控制 def stopper(): global stopp stopp = False def start_bot(): global stopp, bot_thread if stopp: # 已经在运行就不重复开线程 return stopp = True bot_thread = Thread(target=bot_loop, daemon=True) bot_thread.start() # 计数更新,固定用两个标签,不要每次新建 book_label = Label(main_window, text=f"已使用书本: 0") lapis_label = Label(main_window, text=f"已使用青金石: 0") def update_count(): global bookss, lapiss bookss += 1 lapiss += 3 book_label.config(text=f"已使用书本: {bookss}") lapis_label.config(text=f"已使用青金石: {lapiss}") # bot功能函数,加找不到图片的容错 def book(): try: cord = pyautogui.locateCenterOnScreen('book1.png', region=(630, 530, 1290, 840), confidence=0.8) if not cord: return pyautogui.moveTo(cord, duration=.25) keyboards.press(Key.shift) pyautogui.leftClick(cord) except Exception as e: print(f"找书本出错: {e}") def lapis(): try: coord = pyautogui.locateCenterOnScreen('lapis_lazuli1.png', region=(630, 530, 1290, 840), confidence=0.8) if not coord: return pyautogui.moveTo(coord, duration=.25) keyboards.press(Key.shift) pyautogui.leftClick(coord, duration = .25) except Exception as e: print(f"找青金石出错: {e}") # bot主循环,跑在子线程 def bot_loop(): global stopp while stopp: time.sleep(4) if not stopp: break book() pyautogui.moveTo(1, 1, duration=0) time.sleep(.05) if not stopp: break lapis() pyautogui.moveTo(1, 1, duration=0) time.sleep(.05) keyboards.release(Key.shift) pyautogui.moveTo(1060, 465, duration= 0) pyautogui.leftClick(1060, 465, duration= 0) pyautogui.moveTo(702, 447, duration= 0) time.sleep(.05) keyboards.press('q') keyboards.release('q') # 用after把GUI更新调度回主线程,避免跨线程操作控件崩溃 main_window.after(0, update_count) # P键显隐窗口逻辑,修正win属性错误 def keybinds(): isopen = True while True: if kb.is_pressed("p"): if isopen: # 直接操作main_window,不要写不存在的.win属性 main_window.after(0, main_window.withdraw) isopen = False else: main_window.after(0, main_window.deiconify) main_window.after(0, main_window.focus_force) isopen = True time.sleep(0.5) time.sleep(0.01) # 加小休眠避免占满CPU # GUI布局 Label(main_window, text= "Enchant-a-bot 4.0").pack() Button(main_window, text= "quit", command= main_window.quit).pack() Button(main_window, text = "stop", command = stopper).pack() # 注意函数名和按钮变量不要重名 Button(main_window,text="start", command = start_bot).pack() book_label.pack() lapis_label.pack() # 启动快捷键线程,修正daemon设置方式 keybinds_thread = Thread(target=keybinds, daemon=True) keybinds_thread.start() # GUI主循环 if __name__ == "__main__": main_window.mainloop()
额外注意点
- 用
pyautogui.locateCenterOnScreen的时候建议装opencv-python,加confidence参数提升识别准确率,不然稍微有点色差就找不到图直接抛错 - 循环里每个耗时操作前都加一下
stopp标志位判断,不然点停止之后要等当前整轮循环跑完才会停,响应会很慢 - 所有涉及Tk控件的操作,不管是改文字还是显隐窗口,都用
main_window.after(0, 要执行的函数)扔回主线程跑,不然跨线程改Tk控件在Windows上大概率会直接无响应崩溃
内容的提问来源于stack exchange,提问作者Victor Voznyuk
相关产品推荐
相关产品推荐

