tkinter mainloop() not ending the script once it completes

Question:

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")
Asked By: Justin Oberle

||

Answers:

You need something to destroy the window in order to exit the mainloop.
You could add a button to close the window, or as shown below add a line to destroy the window when you are finished:

import tkinter as tk
from tkinter import filedialog
import time   
from threading import Thread        

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."
    tk.Label(loading_window, text=text, font=('times', 12)).pack()
    loading_window.update()
    time.sleep(3)
    # destroy the window
    loading_window.destroy()
    print("The connection is complete!")

if __name__=="__main__" :
    # tk().withdraw()
    download_location = filedialog.askdirectory(title='Find and select the download folder', mustexist=tk.TRUE)
    
    loading_window = tk.Tk()
    loading_window.after(200, threading)
    loading_window.mainloop()
    
    print("Finished") 
Answered By: Jim Robinson

Update: I suggest use Event and at windows cmd script add also daemon=True:

import time
import tkinter as tk
from threading import Thread, Event

def threading(event):
    try:
        t1=Thread(target=checkProgress, daemon=True)
        t1.start()
    except:
        print("Take the error management here ..")
    finally:
        print("Thread is running")

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

if __name__=="__main__" :
    root = tk.Tk()
    event = Event()
    root.after(200, threading(event))   
    event.set()  
    root.mainloop()
Answered By: Hermann12

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")
Answered By: acw1668