Python从其他类向tkinter表格写数据报AttributeError如何解决?
报错根因
- 执行顺序错误:
build_gui方法中先启动了write_table的线程,之后才初始化self.tree属性,线程执行时self.tree还不存在 - 多实例错误:
on_scan回调中每次都会新建mainGUI实例,该实例未执行build_gui方法,自然没有self.tree属性,且和你实际打开的GUI窗口不是同一个实例,写数据也不会显示到可见窗口中 - 跨线程操作问题:Tkinter的GUI组件不允许在非主线程中操作,直接在线程中调用
self.tree.insert后续会出现随机崩溃问题 - Bridge类变量使用错误:
update_data中使用global var1指向的是模块全局变量,而非Bridge类的类属性,数据存储和读取会出现不一致
修复代码
修改 app_example.py
import myGUI import time import logging # 自行补充你缺失的GatewayMessageHandler、Client、ScanEvent等相关导入 logger = logging.getLogger(__name__) serNo = 1 # 初始化全局序列号 mygui = None # 全局单例GUI实例 class Bridge: # 修复类变量读写逻辑 _user_data = "data" @classmethod def update_data(cls, user_info): cls._user_data = user_info @classmethod def get_data(cls): return cls._user_data # API提供的用于获取扫描数据的函数 def on_scan(client: Client, event: ScanEvent) -> None: global serNo # 补充扫码结果获取逻辑,你原有代码漏了这步,根据实际API替换即可 response_text = event.json() user_name = response_text["user"] Bridge.update_data(user_name) # 调用全局GUI实例的写入方法,用after把操作丢到GUI主线程执行 if mygui: mygui.window.after(0, mygui.write_table) # 补充你缺失的其他事件回调函数示例,按需实现 def on_connected(client): logger.info("scanner connected") def on_disconnected(client): logger.info("scanner disconnected") def on_error(client, error): logger.error(f"error occur: {error}") def on_button_pressed_event(client, event): logger.info("button pressed") # API提供的函数 def app_example(): global mygui handler = GatewayMessageHandler( on_scanner_connected=on_connected, on_scanner_disconnected=on_disconnected, on_scan=on_scan, on_error=on_error, on_button_pressed=on_button_pressed_event ) logger.info('application started, press Ctrl-C to exit') mygui = myGUI.mainGUI() mygui.build_gui() # 调用GUI try: while True: time.sleep(1000) except KeyboardInterrupt: gateway.stop()
修改 myGUI.py
import tkinter as tk from tkinter import ttk from app_example import Bridge, serNo class mainGUI: def __init__(self): # 提前声明属性避免不存在报错 self.window = None self.tree = None def build_gui(self): self.window = tk.Tk() self.window.title("Barcode Scanner 1.0") frame_COMinf = tk.Frame(self.window) frame_COMinf.grid(row=1, column=1) # 删除原有的write_table线程启动逻辑,避免跨线程操作GUI style = ttk.Style() style.theme_use("clam") style.element_create("Custom.Treeheading.border", "from", "default") style.layout("Custom.Treeview.Heading", [ ("Custom.Treeheading.cell", {'sticky': 'nswe'}), ("Custom.Treeheading.border", {'sticky': 'nswe', 'children': [ ("Custom.Treeheading.padding", {'sticky': 'nswe', 'children': [ ("Custom.Treeheading.image", {'side': 'right', 'sticky': ''}), ("Custom.Treeheading.text", {'sticky': 'we'}) ]}) ]}), ]) frame = tk.Frame(self.window) style.theme_use("clam") frame.grid(row=3, column=1) # 先完成tree组件初始化 self.tree = ttk.Treeview(frame, columns=(1, 2, 3, 4, 5), height=20, show="headings", style="Custom.Treeview") self.tree.pack(side='left') style.configure("Custom.Treeview.Heading", background="blue", foreground="white", relief="flat") style.map("Custom.Treeview.Heading", relief=[('active', 'groove'), ('pressed', 'sunken')]) self.tree.heading(1, text="Ser.No") self.tree.heading(2, text="user") self.tree.column(1, width=50, anchor='center') self.tree.column(2, width=100, anchor='center') self.scroll = tk.Scrollbar(frame, orient="vertical", command=self.tree.yview) self.scroll.pack(side='right', fill=tk.Y) self.tree.configure(yscrollcommand=self.scroll.set) self.window.mainloop() def write_table(self): global serNo # 加判断避免tree未初始化时报错 if not self.tree: return user_data = Bridge.get_data() self.tree.insert('', 'end', values=(serNo, user_data), tags=('gr',)) serNo += 1 self.tree.tag_configure('gr', background='green')
内容的提问来源于stack exchange,提问作者Ghani
相关产品推荐
相关产品推荐

