Home > Back-end >  How to set tkinter textvariable running on separate Thread?
How to set tkinter textvariable running on separate Thread?

Time:10-28

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

PEP 8 -- Style Guide for Python Code

  • Related