Search code examples
pythontkinterpython-multithreadingmainloop

tkinter mainloop() not ending the script once it completes


For some reason the mainloop() is not ending. The final print statement never gets triggered but everything else does. Any idea what is causing this or how to resolve it? It happens even without the threading.

import time
from tkinter.filedialog import askdirectory
from tkinter import *

def threading():
    t1=Thread(target=checkProgress)
    t1.start()

def checkProgress():
    loading_window.geometry = ("500x500")
    text = "The device is being reset. This will take a minute."
    Label(loading_window, text=text, font=('times', 12)).pack()
    loading_window.update()
    time.sleep(3)
    print("The connection is complete!")

Tk().withdraw()
download_location = askdirectory(title='Find and select the download folder', mustexist=TRUE)

loading_window = Tk()
loading_window.after(200, threading())
loading_window.mainloop()

print("Finished")

Solution

  • Since there are two instances of Tk():

    • Tk().withdraw() (hidden instance)
    • loading_window = Tk()

    Even you close the second instance, mainloop() will still be running because there is a hidden instance of Tk().

    You should use only one instance of Tk() instead:

    import time
    from threading import Thread
    from tkinter.filedialog import askdirectory
    from tkinter import *
    
    def threading():
        t1=Thread(target=checkProgress)
        t1.start()
    
    def checkProgress():
        loading_window.geometry("500x500")
        text = "The device is being reset. This will take a minute."
        Label(loading_window, text=text, font=('times', 12)).pack()
        loading_window.update()
        loading_window.deiconify()  # show the window
        time.sleep(3)
        print("The connection is complete!")
        loading_window.destroy()  # destroy the window
    
    # only create one instance of Tk()
    loading_window = Tk()
    loading_window.withdraw() # hide the window
    download_location = askdirectory(title='Find and select the download folder', mustexist=TRUE)
    loading_window.after(200, threading)
    loading_window.mainloop()
    
    print("Finished")
    

    Note that most people said it is not recommended to update tkinter widgets directly in a child thread because tkinter is not thread safe.

    Below is the modified code to minimize direct update tkinter widgets in a thread:

    import time
    from threading import Thread
    from tkinter.filedialog import askdirectory
    from tkinter import *
    
    def threading():
        loading_window.deiconify()  # show the window
        t1=Thread(target=checkProgress)
        t1.start()
    
    def checkProgress():
        time.sleep(3)
        print("The connection is complete!")
        loading_window.after(10, loading_window.destroy)  # destroy the window
    
    
    loading_window = Tk()
    loading_window.geometry("500x500")
    text = "The device is being reset. This will take a minute."
    Label(loading_window, text=text, font=('times', 12)).pack()
    
    loading_window.withdraw() # hide the window
    download_location = askdirectory(title='Find and select the download folder', mustexist=TRUE)
    
    loading_window.after(200, threading)
    loading_window.mainloop()
    
    print("Finished")