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