无法将数据从另一个 class 写入 tkinter table

Could not write data to a tkinter table from the other class

我正在尝试添加我的 GUI 并从 python 中的简单扫描仪演示应用程序获取数据。此扫描仪演示应用程序处理各种事件,如扫描事件、scanner_connected、scanner_disconnected 等

最初我在演示应用程序中调用 GUI class 来启动 GUI。 当扫描事件引发时,我获取数据并将数据传递给 Bridge class。 之后我调用一个名为 write_data 的 GUI 函数,它负责将数据打印到 table。 此 write_table 函数调用 Bridge class 以获取更新的扫描数据

#app_example.py 
import myGUI
class Bridge:  # a class to store the data from the scan_event
    var1 = "data"

    def __init__(self):
        # does
        nothing

    def update_data(self, user_info):
        global var1
        var1 = user_info

    def get_data(self):
        global var1
        return var1

# function from the APi to get the scan data
def on_scan(client: Client, event: ScanEvent) -> None:
    user_name = response_text["user"]
    b = Bridge()  
    b.update_data(user_name) # saving the scan data to a function from Bridge class
    mygui = mainGUI()
    mygui.write_table()  # calling the GUI  function to write data to a table

# function from the APi
def app_example():     
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 = mainGUI()
mygui.build_gui()  # calling the GUI 

try:
    while True:
        time.sleep(1000)
except KeyboardInterrupt:
    gateway.stop()


# myGUI.py 


class mainGUI:

    def __init__(self):
        # does
        nothing

    def build_gui(self):
        window = tk.Tk()
        window.title("Barcode Scanner 1.0")

        frame_COMinf = tk.Frame(window)
        frame_COMinf.grid(row=1, column=1)
        
        # initating a thread to the function write_table
        self.thread = Thread(target=self.write_table)
        self.thread.start()

        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(window)
        style.theme_use("clam")
        frame.grid(row=3, column=1)

        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)
        window.mainloop()

    def write_table(self):
        # this function calls the Bridge class and get the scan data
        global serNo
        bridge = Bridge() 
        user_data = bridge.get_data()
        data = [[serNo, user_data]]
        for val in data:
            self.tree.insert('', 'end', values=(val[0], val[1]), tags=('gr',))
            serNo += 1
            self.tree.tag_configure('gr', background='green')

我得到的错误是

self.tree.insert('', 'end', values = (val[0], val[1]), tags=('gr',))
   AttributeError: 'mainGUI' object has no attribute 'tree'

我对使用 python 的面向对象编程了解不多。有人可以帮忙写这个 table 的数据吗?

write_data 函数没有缩进并且不是 class mainGUI 的方法。 (已修复)

您尝试在 self.tree 变量初始化之前访问它。

您拨打self.tree的线路:

self.thread = Thread(target=self.write_table)

你正在进一步初始化它:

self.tree = ttk.Treeview(frame, columns=(1, 2, 3, 4, 5), height=20, show="headings", 
                 style="Custom.Treeview")

__init__() 中的默认 self.tree 会对您有所帮助。

如果你不能在 class 的 init 方法中设置它,那么也许这样的事情可以帮助:

for val in data:
    try:
        self.tree.insert('', 'end', values=(val[0], val[1]), tags=('gr',))
    except AttributeError:  # And do the initialization here. Pretty ugly though.
        self.tree = ttk.Treeview(frame, columns=(1, 2, 3, 4, 5), height=20, show="headings", style="Custom.Treeview")
        # ... do all other stuff with the Treeview widget.
    # ...
    # do as normal

但我不知道您首先需要在何处进行初始化。这将是您初始化它所需的绝对最新版本。

在创建 self.tree 之前,您在 build_gui() 中启动了一个到 运行 write_data() 的线程:

class mainGUI:
    def __init__(self):
        # does nothing
        pass

    def build_gui(self):
        window = tk.Tk()
        window.title("Barcode Scanner 1.0")

        frame_COMinf = tk.Frame(window)
        frame_COMinf.grid(row=1, column=1)
        
        # initating a thread to the function write_table
        self.thread = Thread(target=self.write_table)
        self.thread.start()
        ### the above code will execute `write_table()` before `self.tree` is created
        
        ...

        frame = tk.Frame(window)
        style.theme_use("clam")
        frame.grid(row=3, column=1)

        self.tree = ttk.Treeview(frame, columns=(1, 2, 3, 4, 5), height=20, show="headings",
                     style="Custom.Treeview")
        self.tree.pack(side='left')

        ...

        window.mainloop()

所以最好在 window.mainloop():

之前创建并启动线程
class mainGUI:
    ...

    def build_gui(self):
        window = tk.Tk()
        window.title("Barcode Scanner 1.0")

        frame_COMinf = tk.Frame(window)
        frame_COMinf.grid(row=1, column=1)
        
        ...

        frame = tk.Frame(window)
        style.theme_use("clam")
        frame.grid(row=3, column=1)

        self.tree = ttk.Treeview(frame, columns=(1, 2, 3, 4, 5), height=20, show="headings",
                     style="Custom.Treeview")
        self.tree.pack(side='left')

        ...

        # initating a thread to the function write_table
        self.thread = Thread(target=self.write_table)
        self.thread.start()

        window.mainloop()

另一个可疑原因:

# function from the APi to get the scan data
def on_scan(client: Client, event: ScanEvent) -> None:
    user_name = response_text["user"]
    b = Bridge()  
    b.update_data(user_name) # saving the scan data to a function from Bridge class
    mygui = mainGUI()
    ### as mygui.build_gui() is not called
    ### below line will raise the error
    mygui.write_table()  # calling the GUI  function to write data to a table

创建 class 单例解决了这个问题。

class mainGUI:

    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(mainGUI, cls).__new__(
                            cls, *args, **kwargs)
        return cls._instance

    def __init__(self):

        self.check = False

    def build_gui(self):
        window = tk.Tk()
        global x,y
        x = window.winfo_x()
        y = window.winfo_y()
        #rest of the code