Skip to main content

Here we will learn to use the .after() method from tkinter(ttkbootstrap) method to automatically update the tkinter GUI  widgets like Labels, Textboxes at regular intervals without user intervention to create a responsive and usable GUI that can multitask effectively without freezing.

When you are building a GUI with tkinter/ttkbootstrap ,you may want to check the status of certain variable or functions continuously at regular interval and update those variables on the tkinter GUI like on a textbox or Label widget without freezing the GUI.

 

 

Here, we are using ttkbootstrap theme extension along with tkinter and Python. 

Make sure that you have ttkbootstrap theme extension installed on your system, otherwise codes below will not run.

If you are new to tkinter/ttkbootstrap ,Do check out our tutorial on Python GUI design using ttkbootstrap and tkinter 

 

Consider the  example below,

We have a tkinter GUI (below image) with two buttons and a textbox. You want to display the data coming from a sensor on the tkinter/ttkbootstrap Text Box GUI every 1 second .

How do I create an automatically updating GUI using Tkinter in Python

The Text Box GUI needs to be constantly updated with new data every second so user can view the latest data. The tkinter GUI must query the sensor function in background at specific intervals to get data and then update that data to the GUI automatically.

At the same time the GUI should respond to other GUI elements like user pressing buttons without the interface getting unresponsive, so for such applications we need to use the 

  • .after() method  of the tkinter window

 to constantly run a specific function in the background at periodic intervals.Here we can put the function that query the sensor inside  the .after() function.

 

Syntax of the .after() method is 

root = ttkb.Window()
root.after(delay_in_milli_seconds,function_to_run_periodically) 

#here "delay_in_milli_seconds" defines the period in milliseconds at which the function runs the "function_to_run_periodically" function
#here "function_to_run_periodically" is the function that will be run

 

Here is a very basic code for running background tasks using .after() method to update tkinter/ttkbootstrap GUI in realtime.

import ttkbootstrap as ttkb

def run_periodic_background_func():
   print('Read from Sensor/Update textbox') #Put update function here
   
   root.after(1000,run_periodic_background_func)
   
root = ttkb.Window()
root.geometry('400x400')

run_periodic_background_func() # call the update function once

root.mainloop()

Here we will create a tkinter/ttkbootstrap Window object and assign to root. Create a window of size 400X400.

After which we call the run_periodic_background_func()  once to start it over. This step is  important to start the .after() method.

Inside the function

def run_periodic_background_func():

   print('Read from Sensor/Update textbox') #Put update function here
   #read_from_sensor()
   root.after(1000,run_periodic_background_func)

 

we will print 'Read from Sensor/Update textbox'  then it will reach the root.after() function.

root.after() will wait 1000 milliseconds or 1 second and then it will call the run_periodic_background_func() and the cycle repeats every 1000 milliseconds.

You can change the wait period to any value you want ,depending on your application.

 

using root.after() method to update tkinter gui in real time

You can put your "own update function", instead of the print() statement, every one second that "your own update function" will be called and run.

Here is the full source code for a GUI update function.

#run periodic back ground tasks from tkinter gui
from tkinter import *
import ttkbootstrap as ttkb
from ttkbootstrap.scrolled import ScrolledText
import tkinter as tk
from time import sleep  
  
count = 0

def update_function():
   global count
   count = count + 1
   
   background_actions_textbox.insert(END,f'Read from Dummy Sensor,Value = {count}\n')
   background_actions_textbox.see(tk.END) #for auto  scrolling
   # sleep(5)
   
   root.after(100, update_function) # run itself again after 100 ms

def button_1_handler():
   my_button_text.insert(END,f'You Pressed Button 1\n')# add text
   my_button_text.see(tk.END) #for auto  scrolling

def button_2_handler():
   my_button_text.insert(END,f'You Pressed Button 2\n')# add text
   my_button_text.see(tk.END) #for auto  scrolling
  
root = ttkb.Window(themename = 'superhero') # theme = superhero
root.geometry('600x500')
root.title('Running Periodic background tasks in ttkbootstrap/tkinter')

#create textbox for button actions
my_button_text = ScrolledText(root,height = 4,width = 50,wrap = WORD,autohide = True)
my_button_text.pack(padx = 20,pady = 20)

#create button 
button_1 = ttkb.Button(text = 'Button 1',command = button_1_handler).pack(pady =10)
button_2 = ttkb.Button(text = 'Button 2',command = button_2_handler).pack(pady =10)

#create textbox for background actions
background_actions_textbox = ScrolledText(root,height = 10,width = 50,wrap = WORD,autohide = True)
background_actions_textbox.pack(padx = 20,pady = 20)

update_function() #call update function to start root.after() method

root.mainloop()

When you run this code. you will get a window as shown below. Make sure that ttkbootstrap  is installed on your system.

ttkbootstrap running backgound tasks using root.after() function
 

The above method is used to update data coming from serial port in our python logger software  

Here background_actions_textbox is used to display the results of the scheduled action that is activated by root.after() method.

In our case ,it will simply print the text string "Read from Dummy Sensor,Value =  " along with a count number every 100 milliseconds.

count = 0

def update_function():
   global count
   count = count + 1
   
   background_actions_textbox.insert(END,f'Read from Dummy Sensor,Value = {count}\n')
   background_actions_textbox.see(tk.END) #for auto  scrolling
   # sleep(5)
   
   root.after(100, update_function) # run itself again after 100 ms

Here count is declared global and is constantly incremented every time the function ( update_function() ) calls itself using root.after() method .

Data is inserted into the text box along with updated value using

background_actions_textbox.insert(END,f'Read from Dummy Sensor,Value = {count}\n')

then 

background_actions_textbox.see(tk.END) #for auto  scrolling

ensures that the last value that is inserted into the textbox remains in focus, giving the appearance that the textbox is scrolling.

root.after(100, update_function) # run itself again after 100 ms

ensures that the update_function is called every 100ms

 
Limitations of .after() method

One issue with the .after() method is that ,if the function you are running inside the .after() method takes too long to return. The response of your GUI will be slow and sluggish.

You can see that in the above code by uncommenting the sleep() function inside the after() method

# sleep(5) #to simulate a function that takes too long
   root.after(100, update_function) # run itself again after 100 ms

One way to improve the responsiveness of functions that takes too long to run is to use threading

The GUI which runs as the main thread will create a separate thread and run that function inside that thread concurrently, thereby the performance of the GUI is not effected. 

The data generated from the long running Python thread can be passed on to the thread handling the tkinter GUI (main thread) through various data structures like queue's, deque's or stacks.

If you are interested in learning about threading in Python. Do check out our Video on Python threading here.

 

References