From my main script, I want to run a thread with tkinter to visualize all the parameters of my simulation. Something relatable to this.
Since, as it is well described in the linked question, "Tkinter isn't thread safe, and the general consensus is that Tkinter doesn't work in a non-main thread", and I do not want to run everything else on threads, is there another way to display a table (not in terminal) to track the parameters that I'm interested with?
I tried to adapt my code with the solution provided here
But as suspected it never prints the last "Finished" until I close the window
import tkinter as tk
from tkinter import ttk, Button
import threading
from Brain import AGV, IDLE
import time
class my_visual():
def __init__(self, data):
self.data = data
def visualize_params(self):
### Create the object Window
self.window = tk.Tk()
self.window.geometry('900x400')
self.window.title('Table of AGVs')
#window.withdraw() # hide the window
table = ttk.Treeview(self.window, columns = ('one', 'two', 'three', 'four'), show = 'headings')
table.heading('one', text = 'Name')
table.heading('two', text = 'Battery Status')
table.heading('three',text = 'Vehicle is empty')
table.heading('four', text = 'State')
table.pack(fill = 'both', expand = True)
for i in range(len(data)):
table.insert(parent = '', index= 'end', values = self.data[i])
btn1 = Button(self.window, text = 'Refresh', command = self.refresh())
btn1.pack()
self.window.mainloop()
def refresh(self):
self.window.quit()
self.window.destroy()
self.visualize_params()
car_list = []
data = []
for i in range(0,15):
car_list.append('car_%d' %i)
car_list[i] = AGV(car_list[i], 100, True, IDLE(), True)
data.append([car_list[i].GetID(), car_list[i].GetBatteryStatus(), car_list[i].GetVehicleLoadless(), car_list[i].GetState()])
test = my_visual(data)
threading.Thread(target = test.visualize_params()).start()
time.sleep(1)
print("Finished")
And the AGV class is the following:
class AGV:
_state = None
agv_list = []
def __init__(self, ID, Battery_Status, Vehicle_Empty, Current_State: State, just_switched)-> None:
self.ID = ID
self.Battery_Status = Battery_Status
self.Vehicle_Empty = Vehicle_Empty
self.just_switched = just_switched
self.SetState(Current_State)
AGV.agv_list.append(self)
def __repr__(self):
return f"""\n Name: {self.ID}, Battery Status: {self.Battery_Status}, \
Vehicle Empty: {self.Vehicle_Empty}, Current State: {type(self._state).__name__}, Just Switched: {self.just_switched}"""
def GetID(self)->str:
return self.ID
def GetBatteryStatus(self):
return self.Battery_Status
def SetBatteryStatus(self, battery_charge):
self.Battery_Status = battery_charge
def GetVehicleLoadless(self):
return self.Vehicle_Empty
def SetVehicleLoadless(self, agv_empty):
self.Vehicle_Empty = agv_empty
def GetState(self) -> str:
return f"{type(self._state).__name__}"
def SetState(self, Current_State: State):
self._state = Current_State
self._state.agv = self
Is there a way to run tkinter on a thread or exists another way to display a table with the parameters without using tkinter nor printing in the terminal?
target = test.visualize_params()
, you are not runningvisualize_params
in a thread. You're calling it immediately, and then passingNone
to thetarget
test.visualize_params()
calls the function. This is how you call a function in python. So, it's called, and the result is passed to thetarget
parameter. This is a fairly common mistake people make with threading. Thetarget
requires a callable (roughly translated to mean the name of a function).