Typically tkinter is used with its own main loop that takes over the main thread. You need to run your app code off its callbacks or in a different thread. But you can also call tkinter to update from your own main loop without it taking over the thread.
ap_gui.py
from tkinter import *
#GLOBAL VARIABLES
root = None
lblStatus = None
txtEntryBox1 = None
######################
##### USING THIS #####
######################
#
##----- INITIALISE THE GUI -----
#ap_gui.gui_init()
#
##----- UPDATE THE GUI -----
#ap_gui.gui_update()
#if ap_gui.has_gui_been_closed():
# print("Exiting main loop")
# break
#
##----- UPDATE THE GUI STATUS TEXT -----
#ap_gui.gui_update_status_text("Running task...")
#************************************************
#************************************************
#********** CLOSE ROOT WINDOW CALLBACK **********
#************************************************
#************************************************
#User has clicked the windows close button
#We use this so tat we can set root to None and then test that from our main app to see if we need to close the app
def callback():
global root
root.destroy()
root = None
#****************************************
#****************************************
#*********** BUTTON 1 CLICKED ***********
#****************************************
#****************************************
def btnButton1Clicked():
global lblStatus
global txtEntryBox1
EnteredText = "You entered: " + txtEntryBox1.get()
lblStatus.configure(text = EnteredText)
#********************************
#********************************
#********** INITIALISE **********
#********************************
#********************************
def gui_init():
global root
global lblStatus
global txtEntryBox1
root = Tk() #Create the root window
root.protocol("WM_DELETE_WINDOW", callback) #Create callback for if the window is closed
root.title("My app")
root.geometry('350x200') #Set the window size width x height
#Create status label
lblStatus = Label(root, text = "Idle")
lblStatus.place(x=5, y=170)
#Create a Entry Field
txtEntryBox1 = Entry(root, width=10)
txtEntryBox1.place(x=5, y=5)
#Create a button
btnButton1 = Button(root, text = "Button 1", fg = "red", command=btnButton1Clicked)
btnButton1.place(x=5, y=30)
#Execute Tkinter
#root.mainloop() #<<<<This stalls execution of this thread here and hands it to the GUI until the window is closed
root.update() #For this app we are calling update in our main loop instead
#************************************
#************************************
#********** UPDATE THE GUI **********
#************************************
#************************************
#This needs to be called reguarly from the main loop to keep the GUI responsive
def gui_update():
global root
root.update()
#Note there is also this function that can be useful in some situations as its less intesive than update()
#root.update_idletasks()
#**************************************************
#**************************************************
#********** HAS ROOT WINDOW BEEN CLOSED? **********
#**************************************************
#**************************************************
#Call this from the apps main loop to see if we need to shutdown the app
def has_gui_been_closed():
if root == None:
return True
else:
return False
#********************************************
#********************************************
#********** UPDATE OUR STATUS TEXT **********
#********************************************
#********************************************
def gui_update_status_text(StatusText):
global lblStatus
lblStatus.configure(text = StatusText)
gui_update()
Feel free to comment if you can add help to this page or point out issues and solutions you have found. I do not provide support on this site, if you need help with a problem head over to stack overflow.