1

I am trying create a indeterminate progress bar in python 3 in new top level window for some process and then starting the thread for that process. What I want is that the progress bar starts and the thread is also started in background and as soon as the thread completes executing, some message is shown that the task is complete.

Code :

class myThread(threading.Thread):
    def __init__(self, threadID):
        threading.Thread.__init__(self)
        self.threadID = threadID


    def run(self):
        print("Starting the thread")
        func()
        print("Ending the thread")

def func():
    some task

...
new_top = Toplevel()
new_top.title("New Top Level")
new_top.geometry("400x170")

label = Label(new_top, text='Doing some work', justify=CENTER, bg="#CBFDCB").place(x=43,y=30)

progress_bar = ttk.Progressbar(new_top, orient="horizontal", mode="indeterminate", takefocus=True, length=320)
progress_bar.place(x=40, y=80)
progress_bar.start()

thread1 = myThread(1)
thread1.start()
thread1.join()

...

Perform post thread operations

What my problem is, the top level window with label and progress bar never appears if thread1.join() is called and if i skip this part , then the operations post thread execution does not run

2 Answers2

0

TKinter works by using an infinite main loop to wait for and process events - intercept presses, redraw elements etc. (Take a look here for some more info).

When you call join(), you force your main thread (in which tkinter runs) to wait until your newly started thread finished executing before proceeding, hence it is unable to draw your toplevel window with a progress bar. Thus, this is not really an option.

On the other hand, you need to know when your child thread has finished executing. You could check if the thread is still alive by using mythread.isAlive(), but then you can't do it in a loop, as it will again stop the tkinter's main loop from execution and thus drawing the interface. I suggest looking at the answer here to deal with this problem.

Dmytro Chekunov
  • 192
  • 1
  • 10
0

It can be a little tricky doing threading stuff with Tkinter. Here's some code that does what you want. My first version didn't work properly because I tried to destroy the Tkinter window from inside the thread's .run method. That didn't work: the window closed, but the .run method didn't progress after the root.destroy call. So now we have a function that checks every 100 milliseconds to see if the thread is still alive, and if it's not alive we close the Tkinter window.

import threading
import tkinter as tk
from tkinter import ttk
from time import sleep

class myThread(threading.Thread):
    def __init__(self, threadID):
        threading.Thread.__init__(self)
        self.threadID = threadID

    def run(self):
        print("Starting the thread")
        func()
        print("Ending the thread")

def func():
    for i in range(10):
        print(i)
        sleep(1)

def check_thread(th):
    if not th.isAlive():
        root.destroy()
    root.after(100, check_thread, th)

root = tk.Tk()
root.title("New Top Level")
root.geometry("400x170")

tk.Label(root, text='Doing some work', justify=tk.CENTER, bg="#CBFDCB").place(x=43, y=30)
progress_bar = ttk.Progressbar(root, orient="horizontal", 
    mode="indeterminate", takefocus=True, length=320)
progress_bar.place(x=40, y=80)
progress_bar.start()

thread1 = myThread(1)
thread1.start()
root.after(100, check_thread, thread1)
root.mainloop()

print("Doing post-thread stuff")
PM 2Ring
  • 54,345
  • 6
  • 82
  • 182