[PYTHON] [Tkinter] Improve GUI responsiveness

1.First of all

Today, I will explain how to use Threading to improve the responsiveness when creating a GUI application.

2. What you want to do

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. image.png Let's write the code in Tkinter right away.

3. Unresponsive code

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.

3.1. Programming code (1)

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

3.2. Execution result

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. with_thread.gif

4. Code that uses Threading to improve responsiveness

At this time, Threading is used. For the contents related to Threading, refer to the reference material.

4.1. Programming code with improved responsiveness (1)

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

4.2. Execution result

It is the execution result of the code using Threading. without_thread.gif

I was able to confirm that the program works as originally intended. The program is more responsive and can handle other events.

Summary

I was able to improve the responsiveness of the GUI program by using threads.

Reference material

1.python#Threading 2. Complete understanding of Python threading and multiprocessing 3. Implement in [Python] thread

Recommended Posts

[Tkinter] Improve GUI responsiveness
Easy GUI app with Tkinter Text
GUI creation in python using tkinter 2
About building GUI using TKinter of Python
Make GUI apps super easy with tkinter
Create a GUI app with Python's Tkinter