如何对运行在独立线程中的tkinter的textvariable进行更新?
问题根因
- 主线程创建
GUI实例后立刻执行main()函数,此时子线程内的run()方法还没完成self.var的初始化,直接访问gui.var就会抛出AttributeError - Tkinter 不是线程安全的组件,所有控件、关联变量的修改操作必须在运行
mainloop的线程内执行,跨线程直接修改StringVar就算不报错,也不会触发界面刷新,严重时会导致程序崩溃。
修复方案
我们用线程安全的队列传递更新内容,同时加初始化等待逻辑,符合Tkinter的线程操作规范:
from tkinter import * from threading import Thread import queue import time class GUI(Thread): def __init__(self, update_queue): Thread.__init__(self) self.update_queue = update_queue self.init_ok = False # 初始化完成标记 self.start() def run(self): self.root = Tk() self.var = StringVar() self.var.set("Initiated") screen_width = self.root.winfo_screenwidth() screen_height = self.root.winfo_screenheight() width = screen_width*.12 height = screen_height x = screen_width - width y = screen_height*.025 self.root.geometry('%dx%d+%d+%d' % (width, height, x, y)) label = Label(self.root, textvariable= self.var) label.pack() self.init_ok = True # 标记初始化完成 self.check_queue() # 启动队列轮询 self.root.mainloop() def check_queue(self): # 定时检查队列里有没有更新内容 while not self.update_queue.empty(): new_text = self.update_queue.get() self.var.set(new_text) self.root.after(100, self.check_queue) # 每100ms轮询一次 update_queue = queue.Queue() gui = GUI(update_queue) # 等待GUI初始化完成再执行后续逻辑 while not gui.init_ok: time.sleep(0.01) def main(): for i in range(1000): # 把更新内容塞队列,由GUI线程自己取了更新 update_queue.put(f'Current Iteration: {i}') time.sleep(0.1) # 加延时方便看到更新效果,可根据需求调整 if __name__ == '__main__': main()
更简单的替代实现
更符合Tkinter开发惯例的写法是把GUI主线程跑mainloop,业务逻辑放子线程,不需要额外加初始化等待逻辑,代码更简洁:
from tkinter import * from threading import Thread import time def business_logic(var): # 业务逻辑放在子线程运行 for i in range(1000): # 用after方法把更新操作提交到GUI主线程执行 var.after(0, var.set, f'Current Iteration: {i}') time.sleep(0.1) if __name__ == '__main__': root = Tk() var = StringVar(value="Initiated") screen_width = root.winfo_screenwidth() screen_height = root.winfo_screenheight() width = screen_width*.12 height = screen_height x = screen_width - width y = screen_height*.025 root.geometry('%dx%d+%d+%d' % (width, height, x, y)) label = Label(root, textvariable=var) label.pack() # 启动业务逻辑子线程 Thread(target=business_logic, args=(var,), daemon=True).start() root.mainloop()
内容的提问来源于stack exchange,提问作者555
相关产品推荐
相关产品推荐

