Threads and tkinter Threads and tkinter tkinter tkinter

Threads and tkinter


All Tcl commands need to originate from the same thread. Due to tkinter'sdependence on Tcl, it's generally necessary to make all tkinter gui statementsoriginate from the same thread. The problem occurs because mainWindow is instantiated in the tkinterGui thread, but -- because mainWindow is an attribute of tkinterGui -- is not destroyed until tkinterGui is destroyed in the main thread.

The problem can be avoided by not making mainWindow an attribute of tkinterGui-- i.e. changing self.mainWindow to mainWindow. This allows mainWindow to be destroyed when the run method ends in the tkinterGui thread. However, often you can avoid threads entirely by using mainWindow.after calls instead:

import time, threadingfrom tkinter import *from tkinter import messageboxdef infinite_process():    print("Infinite Loop")    mainWindow.after(3000, infinite_process)mainWindow = Tk()mainWindow.geometry("200x200")mainWindow.title("My GUI Title")lbCommand = Label(mainWindow, text="Hello world", font=("Courier New", 16)).place(x=20, y=20)mainWindow.after(3000, infinite_process)mainWindow.mainloop()

If you want to define the GUI inside a class, you can still do so:

import time, threadingfrom tkinter import *from tkinter import messageboxclass App(object):    def __init__(self, master):        master.geometry("200x200")        master.title("My GUI Title")        lbCommand = Label(master, text="Hello world",                           font=("Courier New", 16)).place(x=20, y=20)def tkinterGui():      global finish    mainWindow = Tk()    app = App(mainWindow)    mainWindow.mainloop()    #When the GUI is closed we set finish to "True"    finish = Truedef InfiniteProcess():    while not finish:        print("Infinite Loop")        time.sleep(3)finish = FalseGUI = threading.Thread(target=tkinterGui)GUI.start()Process = threading.Thread(target=InfiniteProcess)Process.start()GUI.join()Process.join()

or even simpler, just use the main thread to run the GUI mainloop:

import time, threadingfrom tkinter import *from tkinter import messageboxclass App(object):    def __init__(self, master):        master.geometry("200x200")        master.title("My GUI Title")        lbCommand = Label(master, text="Hello world",                           font=("Courier New", 16)).place(x=20, y=20)def InfiniteProcess():    while not finish:        print("Infinite Loop")        time.sleep(3)finish = FalseProcess = threading.Thread(target=InfiniteProcess)Process.start()mainWindow = Tk()app = App(mainWindow)mainWindow.mainloop()#When the GUI is closed we set finish to "True"finish = TrueProcess.join()


The fix here is simple, but hard to discover:

Call mainWindow.quit() immediately after mainwindow.mainloop(), so that the cleanup happens on the same thread as the one that created the tk UI, rather than on the main thread when python exits.