如何避免执行函数时Toplevel窗口(启动屏)出现冻结?
解决Tkinter启动屏与进度条冻结问题
你的问题核心是耗时操作阻塞了Tkinter的主事件循环,导致启动屏和进度条无法刷新。即使尝试了threading.Thread,如果没正确处理UI线程的限制,依然会失效。Tkinter要求所有UI操作必须在主线程执行,子线程只能处理耗时逻辑,再通过主线程更新UI状态。
以下是修正后的代码:
import tkinter as tk from tkinter import ttk import threading def loading_window(): toplevel_window = tk.Toplevel(root) toplevel_window.overrideredirect(1) # 让启动屏居中显示(可选优化) window_width, window_height = 220, 80 screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() x = (screen_width // 2) - (window_width // 2) y = (screen_height // 2) - (window_height // 2) toplevel_window.geometry(f"{window_width}x{window_height}+{x}+{y}") label = tk.Label(toplevel_window, text='请稍候') label.pack(padx=10, pady=5) progressbar = ttk.Progressbar(toplevel_window, orient=tk.HORIZONTAL, length=200, mode="indeterminate") progressbar.start() progressbar.pack(padx=10, pady=10) def check_process_status(): if is_process_over.get(): progressbar.stop() toplevel_window.destroy() return toplevel_window.after(100, check_process_status) toplevel_window.after(100, check_process_status) toplevel_window.grab_set() def func2(): # 模拟耗时操作 for _ in range(250000000): pass # 子线程不能直接操作UI,用after通知主线程更新状态 root.after(0, lambda: is_process_over.set(True)) root.after(0, lambda: label.configure(text="完成")) def func1(): is_process_over.set(False) loading_window() # 将耗时操作放到子线程执行 threading.Thread(target=func2, daemon=True).start() root = tk.Tk() is_process_over = tk.BooleanVar(value=False) btn = tk.Button(root, text="执行任务", command=func1) btn.pack(padx=10, pady=20) label = tk.Label(root, text="等待任务开始") label.pack(padx=10, pady=20) root.mainloop()
关键修正点:
- 耗时操作移至子线程:用
threading.Thread把func2放到独立线程,避免阻塞主事件循环。 - 子线程通过
after更新UI:子线程不能直接修改Tkinter组件,必须通过root.after(0, ...)将UI操作委托给主线程执行。 - 简化启动屏更新逻辑:不再手动调用
progressbar.update(),而是通过after定期检查任务状态,同时依赖进度条自身的start()方法实现动画。 - 启动屏居中优化:添加了窗口居中代码,提升用户体验(可选)。
注意事项:
- 子线程中绝对不能直接调用任何Tkinter的UI方法,否则会导致程序崩溃或异常。
- 使用
daemon=True标记子线程,确保程序退出时子线程会自动终止。
内容的提问来源于stack exchange,提问作者duruburak
相关产品推荐
相关产品推荐

