How to use the after method to make a callback run periodically?
Note: the following code is written and tested in Python 3.5. Minor changes might be needed, for instance, when calling super
.
The documentation describes the Widget.after
method as follows:
after(delay_ms, callback=None, *args)
Registers an alarm callback that is called after a given time.
Scheduling a function
The after
method is primarily used to schedule a function call after a given delay. For instance, the following code schedules a call to a function after one second:
import tkinter as tk
def speak():
print("Hello world!")
root = tk.Tk()
root.after(1000, speak)
# Output
Hello world!
Making a function run periodically
In order to make a function run periodically, one can make it call itself at the end of its own body. However, after
is a method from the Widget
class, so a widget is needed. Therefore, the best choice is generally to put the scheduled function inside of a class extending Widget
.
The following code prints "Hello world!"
every other second in the console.
import tkinter as tk
class Foo(tk.Tk):
def periodically_speak(self):
print("Hello world!")
self.after(2000, self.periodically_speak)
foo = Foo()
foo.periodically_speak()
Using parameters
One might want to pass parameters to a method that runs periodically. For this purpose, the after
method unpacks every parameter after the callback as the parameters to pass to the callback. For instance, root.after(1000, foo, a, b, c)
will schedule a call to foo(a, b, c)
. The following example shows a use of this feature to determine the behaviour of the function.
import tkinter as tk
class Foo(tk.Tk):
def periodically_speak(self, text):
print(text)
self.after(2000, self.periodically_speak, text)
foo = Foo()
foo.periodically_speak("Good night world!")
Canceling a call
The after
methods returns a string, that corresponds to the call's id. It can be passed to the after_cancel
method, in order to cancel a call that was scheduled.
The following example will start printing "Hello world!"
every second, but will stop when pressing the button.
import tkinter as tk
class Foo(tk.Tk):
def __init__(self):
super().__init__()
self.callId = None
self.button = tk.Button(self, text="Stop", command=self.stop)
self.button.pack()
def periodically_speak(self):
print("Hello world!")
self.callId = self.after(2000, self.periodically_speak)
def stop(self):
if self.callId is not None:
self.after_cancel(self.callId)
foo = Foo()
foo.periodically_speak()
Side notes
The following points should be kept in mind.
- The
after
method does not guarantee that the callback will be called *exactly* after the given delay, but *at least* after it. As a consequence,after
should not be used where precision is required. - It might be tempting to use
time.sleep
in order to schedule or periodically run a function. This must be avoided when working on a GUI, because `sleep` will pause the current thread, which most of the time is the main thread. For example, this could halt the refresh of the widgets, the program would stop responding.