tkinter类与frame架构下GUI无响应,求多线程同步更新进度条方案
Tkinter调用耗时服务端方法GUI无响应问题解决
我是Python/tkinter新手,使用Python 3.8版本开发时遇到如下问题:调用运行在服务端的CSF.addOrders方法时,该方法根据订单数量不同,执行时长在10秒到5分钟不等,期间应用标题栏会显示「未响应」,且进度条pb1无法更新,用户无法感知程序仍在正常运行。
我需要实现CSF.addOrders运行期间pb1缓慢更新、且该方法执行完成后立即终止进度条更新的效果。我查阅了大量tkinter无响应相关的帖子,但由于我的代码采用类和frame架构,未找到适配的解决方案。
修复后可运行完整代码
import tkinter as tk import tkinter.ttk as ttk import ClientServer_Functions as CSF import threading import queue import logging import time # Log File logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) formatter = logging.Formatter('%(asctime)s:%(message)s', datefmt='%Y-%m-%d %H:%M:%S') # create the handlers and call logger.addHandler(logging_handler) if not logger.handlers: file_handler = logging.FileHandler('GUI_Logger.log') file_handler.setLevel(logging.INFO) file_handler.setFormatter(formatter) stream_handler = logging.StreamHandler() stream_handler.setFormatter(formatter) logger.addHandler(file_handler) logger.addHandler(stream_handler) logger.propagate = False class sample(tk.Tk): def __init__(self): tk.Tk.__init__(self) self.shared_data = { "fName": tk.StringVar(), # userInfo[0][0] "username": tk.StringVar(), # userInfo[0][1] "password": tk.StringVar(), # userInfo[0][2] "custSym": tk.StringVar(), # userInfo[0][3] "email": tk.StringVar(), # userInfo[0][4] "sPath": tk.StringVar(), # userInfo[0][5] "FTP_User": tk.StringVar(), # userInfo[0][6] "FTP_Password": tk.StringVar(), # userInfo[0][7] "fType": tk.StringVar() } container = tk.Frame(self) container.pack(side="top", fill="both", expand=True) container.grid_rowconfigure(0, weight=1) container.grid_columnconfigure(0, weight=1) global bgColor bgColor = "black" self.frames = {} for F in (Trade, blank): #there are other frames I create that are not listed frame = F(container, self) self.frames[F] = frame frame.config(bg=bgColor) frame.grid(row=0, column=0, sticky="nsew") self.show_frame(Trade) def get_page(self, page_class): return self.frames[page_class] def show_frame(self, c): frame = self.frames[c] frame.tkraise() class Trade(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller=controller tk.Label(self, bg=bgColor).pack() tk.Label(self, text="TRADES", bg=bgColor, fg="yellow").pack(pady=20) tk.Button(self, text="Orders", command=lambda: [self.setFType("Orders"), self.files()]).pack(pady=10) self.statusbar = tk.Label(self) self.statusbar.pack(side=tk.BOTTOM, fill=tk.X) def setFType(self, fType): self.controller.shared_data["fType"].set(fType) def aOrders(self, ls): l = ls[0] print("starting t1") time.sleep(1) result, msg, FTP_outFile = CSF.addOrders(l,"u", "p", "fp", "sp", "c") return result, msg, FTP_outFile def files(self): fType = self.controller.shared_data["fType"].get() self.statusbar['text'] = "Running..." pb1 = ttk.Progressbar(self, orient=tk.HORIZONTAL, length=250, mode='determinate') pb1['value'] += 10 pb1.place(relx=.25, rely=.88) que = queue.Queue() t1 = threading.Thread(target = lambda q, arg : q.put(self.aOrders(arg)), args = (que, [logger, 2])) t1.start() for x in range(100): if t1.is_alive(): print("update pb1") pb1['value'] += 2 tk.Tk.update(self) time.sleep(1) else: print(que.get()) break print("do balance of program...") class blank(tk.Frame): def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller=controller if __name__ == "__main__": app = sample() app.geometry("500x500+500+150") app.mainloop()
核心实现逻辑
- 把耗时的
CSF.addOrders调用放到独立子线程执行,避免阻塞Tkinter主线程的GUI渲染 - 主线程循环检测子线程运行状态,子线程存活时逐秒更新进度条数值,调用
tk.Tk.update()主动刷新GUI - 子线程执行完成后立即终止进度条更新,通过队列获取子线程的返回结果
内容的提问来源于stack exchange,提问作者Hedgebox
相关产品推荐
相关产品推荐

