I am trying to implement Multithreading while using a GUI in tkinter. I came along this solution, but i am failing to implement it.
So basically i need to know:
How do i need to alter my Code to make the Progressbar interact nice and smoothly, i.e. not going in unresponsive mode when the GUI loses focus?
This is my code boiled down:
from tkinter import *
import queue
import threading
from tkinter.ttk import *
class ThreadedTask(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
# Gui class
class MyGui(Frame):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.queue = queue.Queue()
self.init_ui()
# define a button to fulfill long task
def init_ui(self):
self.frame = Frame(self, relief=RAISED, borderwidth=1)
self.frame.grid(row=0, column=0, columnspan=1, sticky='ns')
self.status_frame = Frame(self, relief=RAISED, borderwidth=1, height=20)
self.status_frame.grid(row=1, column=0, columnspan=3, sticky='nesw')
self.status_frame.grid_configure(padx=3, pady=3)
self.button = Button(self.frame, text='do Stuff', command=self.do_stuff)
self.button.grid(padx=3, pady=3)
self.progress = Progressbar(self.status_frame, orient="horizontal", length=80, mode="determinate")
self.progress.grid(row=1, column=0, pady=3, sticky='nesw')
self.grid()
# start ThreadedTask here
def do_stuff(self):
ThreadedTask(self.queue).start()
self.queue_process(DoStuffClass(ui=self))
def queue_process(self, process, retry_time=10):
self.master.after(retry_time, process)
def update_status(self):
self.parent.update_idletasks()
class DoStuffClass:
def __init__(self, ui=None):
self.ui = ui
self.long_task()
def long_task(self):
# do stuff here and update the progressbar from MyGui
self.ui.progress['maximum'] = 10000
# some lengthy task ...
for i in range(10000):
print(i)
self.ui.progress.step()
self.ui.parent.update_idletasks()
# main
root = Tk()
root.geometry("150x80+50+50")
MyGui(root)
root.mainloop()
root.quit()
I think right now my problem is the wrong implementation of queues and Threading, i.e. self.queue_process(DoStuffClass(ui=self))... the behaviour is the same as if i wouldn't use a queue and Multithread at all. The progressbar works, as long as it stays "in focus", which means me not clicking anything else on the desktop. As soon as i'm clicking elsewhere on the desktop and the GUI loses focus, the GUI goes in "unresponsive" mode and the progress bar does not update anymore. Also, sometimes Tcl closes the wrong Thread, which makes the whole program crash.
AttributeError: 'MyGui' object has no attribute 'preview_button'from lineself.preview_button.grid(padx=3, pady=3). BTW, the main thread is the one that is executed first by the Python interpreter.self.queue_process(DoStuffClass(ui=self))which is the part i am uncertain with. However, you do see a progressbar moving while doing the lengthy task, and when u change focus of the minigui, it will go in unresponsive mode (and for my original program -- sometimes crash).ThreadedTaskclass anywhere you want. Threads aren't spawned from it, instances of it are separate threads. The basic thing to keep in mind is that only one thread (usually the main one) should be updating the GUI (viaafter()). Other threads need to put stuff inQueues and the main thread should check them and update the GUI depending on what it finds.