2

With tkinter, how can I use the after method to make a function run periodically?

For instance, I have a speak function that only prints something in the console:

def speak():
    print("Hello, world!")

How can I use the after method to call the speak function every second?

Right leg
  • 16,080
  • 7
  • 48
  • 81
  • 2
    This question is intended to be canonical for the questions related to the `after` method in tkinter. Please check the answer below that comes with it. Examples of hardly-canonical questions related to that topic: [How to use Tkinter after() method?](http://stackoverflow.com/questions/37748729/how-to-use-tkinter-after-method), [tkinter: how to use after method](http://stackoverflow.com/questions/25753632/tkinter-how-to-use-after-method) – Right leg May 20 '17 at 11:54
  • This question has been asked and answered many times. Instead of creating a new question, why not edit one of the existing answers to be more canonical? – Bryan Oakley May 20 '17 at 12:32
  • @BryanOakley Well, I wanted to do so, but the questions were not quite canonical, and generally related to a precise problem, which it would have not been appropriated to edit. So, I decided to write a straightforward question. – Right leg May 20 '17 at 13:17

1 Answers1

4

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.
Community
  • 1
  • 1
Right leg
  • 16,080
  • 7
  • 48
  • 81
  • Your very first answer isn't very canonical. You're using a lambda, when lambda is unnecessary and possibly misleading. Beginners may grab ahold of that first example and think that's how you're supposed to use `after`. – Bryan Oakley May 20 '17 at 12:34
  • @BryanOakley Fair point. A few of us in the [SO Python chat room](http://chat.stackoverflow.com/rooms/6/python) who are familiar with Tkinter intend to look at this Q+A pair over the next few days and offer constructive criticism to bring it up to scratch. – PM 2Ring May 20 '17 at 12:56
  • @BryanOakley You're right, I edited that point. As PM 2Ring said, feel free to edit this post, I only want this to be helpful :) – Right leg May 20 '17 at 13:20