Today, I will explain how to use Threading to improve the responsiveness when creating a GUI application.
Design a simple GUI screen as shown in the figure below. When the button at the top is clicked, it is displayed in order from 0 to 9 at the bottom. Let's write the code in Tkinter right away.
First, I wrote the following code. When the button is pressed, _main_func () is executed. _main_func () is a function that displays from 0 to 9 using the For statement.
We used two types of labels, a Tkinter label and a print statement, to display the results.
tkinter_wo_threading.py
import tkinter as tk
from tkinter import ttk
from tkinter import font
import time
class Application(tk.Frame):
def __init__(self,master):
super().__init__(master)
self.pack()
self.master.geometry("300x300")
self.master.title("Tkinter Freezes after clicking Buttons")
self.font_lbl_big = font.Font( family="Meiryo UI", size=30, weight="bold" )
self.font_lbl_middle = font.Font( family="Meiryo UI", size=15, weight="bold" )
self.font_lbl_small = font.Font( family="Meiryo UI", size=12, weight="normal" )
self.create_widgets()
def create_widgets(self):
# Frame
self.main_frame = tk.LabelFrame(self.master, text ='', font = self.font_lbl_small)
self.main_frame.place(x=25,y=25)
self.main_frame.configure(height = 250, width=250)
self.main_frame.grid_propagate(0)
self.main_frame.grid_columnconfigure(0, weight = 1)
# Start Button
self.btn_Start = ttk.Button( self.main_frame)
self.btn_Start.configure( text='Start' )
self.btn_Start.configure( command=self._main_func)
self.btn_Start.grid( column=0, row=0, pady=10 , sticky='NESW')
# Label Title
self.lbl_title = ttk.Label( self.main_frame)
self.lbl_title.configure( text='Calculation Results Shown Here' )
self.lbl_title.grid( column=0, row=1, padx = 20, pady=20 ,sticky='EW')
# Label Result
self.lbl_result = ttk.Label( self.main_frame )
self.lbl_result.configure( text='' )
self.lbl_result.grid( column=0, row=2, padx = 100, pady=10 ,sticky='EW')
def _main_func(self):
for i in range(10):
print(i)
self.lbl_result.configure(text = i, font = self.font_lbl_big)
time.sleep(0.1)
def main():
root = tk.Tk()
app = Application(master=root)#Inherit
app.mainloop()
if __name__ == "__main__":
main()
Let's run this code. When you press the button, the program becomes unresponsive until the for statement loop ends. The print statement prints the results continuously, but the GUI remains stationary. And when the loop of the for statement ends, only the last number is displayed. It's not what you expected at first.
This is because Python executes the code line by line. If this is left as it is, the response will be poor because other events cannot even start until one event is processed by the GUI program.
At this time, Threading is used. For the contents related to Threading, refer to the reference material.
First, we will introduce threading.
import threading
In turn, change the button's callback function to _start_thread (). In that _start_thread (), put the function to start _main_func () as a thread. It is an image that you can specify as a target and write a start.
def _start_thread(self):
self.thread_main = threading.Thread(target = self._main_func)
self.thread_main.start()
The whole code.
tkinter_with_threading.py
import tkinter as tk
from tkinter import ttk
from tkinter import font
import time
import threading
class Application(tk.Frame):
def __init__(self,master):
super().__init__(master)
self.pack()
self.master.geometry("300x300")
self.master.title("Tkinter Freezes after clicking Buttons")
self.font_lbl_big = font.Font( family="Meiryo UI", size=30, weight="bold" )
self.font_lbl_middle = font.Font( family="Meiryo UI", size=15, weight="bold" )
self.font_lbl_small = font.Font( family="Meiryo UI", size=12, weight="normal" )
self.create_widgets()
def create_widgets(self):
# Frame
self.main_frame = tk.LabelFrame(self.master, text ='', font = self.font_lbl_small)
self.main_frame.place(x=25,y=25)
self.main_frame.configure(height = 250, width=250)
self.main_frame.grid_propagate(0)
self.main_frame.grid_columnconfigure(0, weight = 1)
# Start Button
self.btn_Start = ttk.Button( self.main_frame)
self.btn_Start.configure( text='Start' )
self.btn_Start.configure( command=self._start_thread)
self.btn_Start.grid( column=0, row=0, pady=10 , sticky='NESW')
# Label Title
self.lbl_title = ttk.Label( self.main_frame)
self.lbl_title.configure( text='Calculation Results Shown Here' )
self.lbl_title.grid( column=0, row=1, padx = 20, pady=20 ,sticky='EW')
# Label Result
self.lbl_result = ttk.Label( self.main_frame )
self.lbl_result.configure( text='' )
self.lbl_result.grid( column=0, row=2, padx = 100, pady=10 ,sticky='EW')
#-----------------------------------------------------------
# Start Thread
# -----------------------------------------------------------
def _start_thread(self):
self.thread_main = threading.Thread(target = self._main_func)
self.thread_main.start()
def _main_func(self):
for i in range(10):
print(i)
self.lbl_result.configure(text = i, font = self.font_lbl_big)
time.sleep(0.1)
def main():
root = tk.Tk()
app = Application(master=root)#Inherit
app.mainloop()
if __name__ == "__main__":
main()
It is the execution result of the code using Threading.
I was able to confirm that the program works as originally intended. The program is more responsive and can handle other events.
I was able to improve the responsiveness of the GUI program by using threads.
1.python#Threading 2. Complete understanding of Python threading and multiprocessing 3. Implement in [Python] thread