Trying to update tkinter 'textvariable' running on a thread with 'main' function variable. I implemented a thread based solution so the code following the tkinter mainloop can run: https://stackoverflow.com/a/1835036/15409926.
Please share how I can resolve this error. If this is not the simplest method to update 'textvariable', please share alternatives.
Code:
from tkinter import *
from threading import Thread
class GUI(Thread):
def __init__(self):
Thread.__init__(self)
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.root.mainloop()
gui = GUI()
def main():
for i in range(1000):
gui.var.set(f'Current Iteration: {i}')
if __name__ == '__main__':
main()
Window doesn't update: Tkinter window displays initial 'textvariable'
Error:
Traceback (most recent call last):
File "test.py", line 36, in <module>
main()
File "test.py", line 33, in main
gui.var.set(f'Current Iteration: {i}')
AttributeError: 'GUI' object has no attribute 'var'
CodePudding user response:
Most GUIs don't like to change values in widgets in separate thread.
You should rather use queue
to send value to thread and it should use root.after(time, function)
to run periodically function which will get value from queue and update value in GUI.
import tkinter as tk # PEP8: `import *` is not preferred
from threading import Thread
import queue
import time # simulate show program
class GUI(Thread):
def __init__(self, queue):
super().__init__()
self.queue = queue
self.start()
def run(self):
self.root = tk.Tk()
self.var = tk.StringVar()
self.var.set("Initiated")
screen_width = self.root.winfo_screenwidth()
screen_height = self.root.winfo_screenheight()
width = int(screen_width*.12)
height = int(screen_height)
x = int(screen_width - width)
y = int(screen_height*.025)
self.root.geometry(f'{width}x{height} {x} {y}')
label = tk.Label(self.root, textvariable=self.var)
label.pack()
# run first time after 100ms (0.1 second)
self.root.after(100, self.check_queue)
self.root.mainloop()
def check_queue(self):
#if not self.queue.empty():
while not self.queue.empty():
i = self.queue.get()
self.var.set(f'Current Iteration: {i}')
# run again after 100ms (0.1 second)
self.root.after(100, self.check_queue)
def main():
q = queue.Queue()
gui = GUI(q)
for i in range(1000):
q.put(i)
# simulate show program
time.sleep(0.5)
if __name__ == '__main__':
main()