Home > Software design >  Could not write data to a tkinter table from the other class
Could not write data to a tkinter table from the other class

Time:10-29

I am trying to add my GUI and get data from the simple scanner demo application in python. This scanner demo application handles various events like scan event, scanner_connected, scanner_disconnected etc.

Initially I'm calling the GUI class in the demo application to initiate the GUI. When the scan event raises , I take the data and pass the data to a Bridge class. After that I call a GUI function called write_data which is responsible to print the data to a table. This write_table function calls the Bridge class to get the updated scan data

#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')

The error I get is

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

I do not have much knowledge in object-oriented programming using python. Could someone help how to write the data for this table?

CodePudding user response:

You started a thread to run write_data() inside build_gui() before creating self.tree:

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()

So better create and start the thread before 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()

Another suspect cause:

# 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

CodePudding user response:

The write_data function is not indented and is not a method of the class mainGUI. (This was fixed)

You try to access the self.tree variable before it is initialized.

The line where you call self.tree:

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

And you are initializing it further down:

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

A default self.tree in __ init__() would help you.

If you cannot set it in the init method of the class then maybe something like this can help:

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

But I don't know where exactly you need this initialized in the first place. This would be the absolute latest you need to initialize it.

  • Related