python – Tkinter understanding mainloop

python – Tkinter understanding mainloop

tk.mainloop() blocks. It means that execution of your Python commands halts there. You can see that by writing:

while 1:
    print(hello)   #NEW CODE

You will never see the output from the print statement. Because there is no loop, the ball doesnt move.

On the other hand, the methods update_idletasks() and update() here:

while True:

…do not block; after those methods finish, execution will continue, so the while loop will execute over and over, which makes the ball move.

An infinite loop containing the method calls update_idletasks() and update() can act as a substitute for calling tk.mainloop(). Note that the whole while loop can be said to block just like tk.mainloop() because nothing after the while loop will execute.

However, tk.mainloop() is not a substitute for just the lines:


Rather, tk.mainloop() is a substitute for the whole while loop:

while True:

Response to comment:

Here is what the tcl docs say:

Update idletasks

This subcommand of update flushes all currently-scheduled idle events
from Tcls event queue. Idle events are used to postpone processing
until “there is nothing else to do”, with the typical use case for
them being Tks redrawing and geometry recalculations. By postponing
these until Tk is idle, expensive redraw operations are not done until
everything from a cluster of events (e.g., button release, change of
current window, etc.) are processed at the script level. This makes Tk
seem much faster, but if youre in the middle of doing some long
running processing, it can also mean that no idle events are processed
for a long time. By calling update idletasks, redraws due to internal
changes of state are processed immediately. (Redraws due to system
events, e.g., being deiconified by the user, need a full update to be

APN As described in Update considered harmful, use of update to handle
redraws not handled by update idletasks has many issues. Joe English
in a comp.lang.tcl posting describes an alternative:

So update_idletasks() causes some subset of events to be processed that update() causes to be processed.

From the update docs:

update ?idletasks?

The update command is used to bring the application “up to date” by
entering the Tcl event loop repeatedly until all pending events
(including idle callbacks) have been processed.

If the idletasks keyword is specified as an argument to the command,
then no new events or errors are processed; only idle callbacks are
invoked. This causes operations that are normally deferred, such as
display updates and window layout calculations, to be performed

KBK (12 February 2000) — My personal opinion is that the [update]
command is not one of the best practices, and a programmer is well
advised to avoid it. I have seldom if ever seen a use of [update] that
could not be more effectively programmed by another means, generally
appropriate use of event callbacks. By the way, this caution applies
to all the Tcl commands (vwait and tkwait are the other common
culprits) that enter the event loop recursively, with the exception of
using a single [vwait] at global level to launch the event loop inside
a shell that doesnt launch it automatically.

The commonest purposes for which Ive seen [update] recommended are:

  1. Keeping the GUI alive while some long-running calculation is
    executing. See Countdown program for an alternative. 2) Waiting for a window to be configured before doing things like
    geometry management on it. The alternative is to bind on events such
    as that notify the process of a windows geometry. See
    Centering a window for an alternative.

Whats wrong with update? There are several answers. First, it tends
to complicate the code of the surrounding GUI. If you work the
exercises in the Countdown program, youll get a feel for how much
easier it can be when each event is processed on its own callback.
Second, its a source of insidious bugs. The general problem is that
executing [update] has nearly unconstrained side effects; on return
from [update], a script can easily discover that the rug has been
pulled out from under it. Theres further discussion of this
phenomenon over at Update considered harmful.


Is there any chance I can make my program work without the while loop?

Yes, but things get a little tricky. You might think something like the following would work:

class Ball:
    def __init__(self, canvas, color):
        self.canvas = canvas = canvas.create_oval(10, 10, 25, 25, fill=color)
        self.canvas.move(, 245, 100)

    def draw(self):
        while True:
           self.canvas.move(, 0, -1)

ball = Ball(canvas, red)

The problem is that ball.draw() will cause execution to enter an infinite loop in the draw() method, so tk.mainloop() will never execute, and your widgets will never display. In gui programming, infinite loops have to be avoided at all costs in order to keep the widgets responsive to user input, e.g. mouse clicks.

So, the question is: how do you execute something over and over again without actually creating an infinite loop? Tkinter has an answer for that problem: a widgets after() method:

from Tkinter import *
import random
import time

tk = Tk()
tk.title = Game
tk.wm_attributes(-topmost, 1)

canvas = Canvas(tk, width=500, height=400, bd=0, highlightthickness=0)

class Ball:
    def __init__(self, canvas, color):
        self.canvas = canvas = canvas.create_oval(10, 10, 25, 25, fill=color)
        self.canvas.move(, 245, 100)

    def draw(self):
        self.canvas.move(, 0, -1)
        self.canvas.after(1, self.draw)  #(time_delay, method_to_execute)


ball = Ball(canvas, red)
ball.draw()  #Changed per Bryan Oakleys comment

The after() method doesnt block (it actually creates another thread of execution), so execution continues on in your python program after after() is called, which means tk.mainloop() executes next, so your widgets get configured and displayed. The after() method also allows your widgets to remain responsive to other user input. Try running the following program, and then click your mouse on different spots on the canvas:

from Tkinter import *
import random
import time

root = Tk()
root.title = Game
root.wm_attributes(-topmost, 1)

canvas = Canvas(root, width=500, height=400, bd=0, highlightthickness=0)

class Ball:
    def __init__(self, canvas, color):
        self.canvas = canvas = canvas.create_oval(10, 10, 25, 25, fill=color)
        self.canvas.move(, 245, 100)

        self.canvas.bind(<Button-1>, self.canvas_onclick)
        self.text_id = self.canvas.create_text(300, 200, anchor=se)
        self.canvas.itemconfig(self.text_id, text=hello)

    def canvas_onclick(self, event):
            text=You clicked at ({}, {}).format(event.x, event.y)

    def draw(self):
        self.canvas.move(, 0, -1)
        self.canvas.after(50, self.draw)


ball = Ball(canvas, red)
ball.draw()  #Changed per Bryan Oakleys comment.
while 1:

… is (very!) roughly similar to:


The difference is, mainloop is the correct way to code and the infinite loop is subtly incorrect. I suspect, though, that the vast majority of the time, either will work. Its just that mainloop is a much cleaner solution. After all, calling mainloop is essentially this under the covers:

while the_window_has_not_been_destroyed():
    event = event_queue.pop()

… which, as you can see, isnt much different than your own while loop. So, why create your own infinite loop when tkinter already has one you can use?

Put in the simplest terms possible: always call mainloop as the last logical line of code in your program. Thats how Tkinter was designed to be used.

python – Tkinter understanding mainloop

Im using an MVC / MVA design pattern, with multiple types of views. One type is a GuiView, which is a Tk window. I pass a view reference to my window object which does things like link buttons back to view functions (which the adapter / controller class also calls).

In order to do that, the view object constructor needed to be completed prior to creating the window object. After creating and displaying the window, I wanted to do some initial tasks with the view automatically. At first I tried doing them post mainloop(), but that didnt work because mainloop() blocked!

As such, I created the window object and used tk.update() to draw it. Then, I kicked off my initial tasks, and finally started the mainloop.

import Tkinter as tk

class Window(tk.Frame):
    def __init__(self, master=None, view=None ):
        tk.Frame.__init__( self, master )
        self.view_ = view       
         Setup window linking it to the view... 

class GuiView( MyViewSuperClass ):

    def open( self ):
        self.tkRoot_ = tk.Tk()
        self.window_ = Window( master=None, view=self )

    def onOpen( self ):        
         Do some initial tasks... 

    def refresh( self ):        

Leave a Reply

Your email address will not be published. Required fields are marked *