When do I need to call mainloop in a Tkinter application?
Asked Answered
P

6

48

Every tkinter tutorial I have seen claims that tkinter.mainloop must be called for windows to be drawn and events to be processed, and they always call this function, even in hello world programs. However, when I try these out in the interactive shell, windows are drawn correctly without having to call mainloop. This example of embedding matplotlib graphics in tkinter produces a relatively complex application, with buttons for panning, zooming and resizing a plot within a tkinter window, and again, this all works if you remove the call to mainloop and run the code in the interactive shell. Of course, if I run the script (with mainloop removed) outside the interactive shell, the program ends too quickly to see what happens, but if I add a call to input to hold the program open everything works correctly (I'm running python 3.2.2 on linux).

So what exactly does mainloop do, and when is it necessary to call it?

EDIT: To clarify, if I open up the GNOME terminal and type

$python3
>>> import tkinter
>>> root = tkinter.Tk()

a window immediately appears without having to call mainloop, and more complex tkinter functionality seems to work as well (for example, adding buttons to the window). In IDLE, a call to mainloop is necessary. It was my understanding that nothing should be drawn, and no events should be processed, until mainloop is called.

Passacaglia answered 30/12, 2011 at 19:27 Comment(0)
O
56

The answer to your main question is, you must call mainloop once and only once, when you are ready for your application to run.

mainloop is not much more than an infinite loop that looks roughly like this (those aren't the actual names of the methods, the names merely serve to illustrate the point):

while True:
    event=wait_for_event()
    event.process()
    if main_window_has_been_destroyed(): 
        break

In this context, "event" means both the user interactions (mouse clicks, key presses, etc) and requests from the toolkit or the OS/window manager to draw or redraw a widget. If that loop isn't running, the events don't get processed. If the events don't get processed, nothing will appear on the screen and your program will likely exit unless you have your own infinite loop running.

So, why don't you need to call this interactively? That's just a convenience, because otherwise it would be impossible to enter any commands once you call mainloop since mainloop runs until the main window is destroyed.

Obidiah answered 31/12, 2011 at 1:54 Comment(0)
A
10

Compare a program with an interactive GUI to a program that calculates the hundredth Fibonacci number. All the latter program has to go through a series of steps in order, top to bottom. The set of steps and their sequencing can be known in advance, and it'll remain constant no matter how many times you run the program.

But the GUI program is different: at any given moment, it has to be able to handle all sorts of different kinds of events and interactions. This requirement is often implemented using a programming construct called an event loop. An event loop is the central control structure of a program. It waits for an event to happen, and then dispatches the appropriate handler.

You didn't mention which interactive shell you're using, but I'm guessing it's IDLE. IDLE itself is a Tkinter program, and it already has an event loop going. So possibly the Tkinter code you are typing into the shell is getting bound to IDLE's event loop.

Anthelion answered 30/12, 2011 at 21:29 Comment(2)
Sorry, I should have mentioned: I am just using the standard python shell (not IDLE) in the GNOME terminal (which is apparently written in C). So as far as I can see, nothing apart from my own code should be doing anything affecting tkinter.Passacaglia
I have just tried the same thing in IDLE, and I don't get the same behaviour - no windows appear until I call mainloop.Passacaglia
D
1

When you execute your code, the tkinter window will refuse to open without there being a mainloop function.

For example this will not work:

from tkinter import*
root=Tk()

This, however, will work:

from tkinter import*
root=Tk()
root.mainloop()
Dauphine answered 24/10, 2019 at 18:27 Comment(0)
M
-1

If you’re using python interective shell, you don’t need to call the root.mainloop() function. But if you are coding in a file, suppose one in IDLE, you need to call out the mainloop() function in order for your program to work as follows:

from tkinter import *

root = Tk()

# Elements in the GUI must go here eg:

lbl = Label(root, text=“Text”)
lbl.pack()

# At the end of the program, do this:

root.mainloop()
Malikamalin answered 14/10, 2021 at 6:29 Comment(0)
T
-4

As follows:

from tkinter import *

tk = Tk()
canvas = Canvas(tk, width=500, height=500)
canvas.pack()
canvas.create_line(0, 0, 500, 500)

mainloop()
Terni answered 11/3, 2016 at 13:41 Comment(0)
P
-5

I've decided that, instead of sticking a call directly to mainloop anywhere in my script, I'll just add it as part of atexit - that is, when the Python interpreter decides it's time to start closing down, it's going to enter Tk's mainloop. This then prevents it from finishing the shut down sequence until the user actually tells Tk to quit (IE, with command-Q on a Mac, or by clicking on the red X in Windows.)

from Tkinter import Tk
root = Tk()

import atexit
atexit.register(root.mainloop)

There seems to be no need to call mainloop from a system command line. The Python interpreter will continue running without it, because it's waiting for further input from you (until you run exit()).

Pterodactyl answered 16/1, 2016 at 4:29 Comment(6)
"It seems to me that the only thing calling mainloop does is prevent your application from automatically closing": that is an incorrect assessment. It manages the event queue, which is vastly more than just preventing the application from exiting. Also, you're not even doing what you think you're doing: you're actually calling mainloop() immediately rather than at exit time.Obidiah
@BryanOakley - Whoops, I have fixed that accidental call to mainloop() - now it's only registered, as it should be. Anyways, I believe you are incorrect. In your answer, you say the fact that you don't need to call mainloop in an interactive interactive is "just a convenience". Uh, what? If it's a convenience thing, then something, either the interactive interpreter or the Tkinter module has to contain special code which allows this. I don't believe that code exists. There's no documentation that says it does, either. The burden is on you to prove mainloop is magic as you say.Pterodactyl
@ArtOfWarfare: I realize I left you hanging on your last couple of comments. The internal tkinter and tcl/tk code does indeed have special code to handle interactive use. There is a global tcl variable named tcl_interactive that controls this behavior. This is set in cpython/Modules/_tkinter.c, which also has some conditional code based on whether it's set or not. You will need to step through the code yourself to see precisely how interactive use is different from non-interactive. Regardless, there is a special code path for interactive use.Obidiah
Also, you can look in python's Modules/_tkinter.c for the definition of mainloop (specifically, _tkinter_tkapp_mainloop_impl) where you can see that mainloop does much more than "prevent your application from closing". It is indeed a loop that processes events while also checking other things between each event (such as windows being destroyed). I'm not sure if this addresses all your concerns or not.Obidiah
@BryanOakley - My apologies. But if your script has to call mainloop at the end, what's wrong with calling it from using atexit.register to call it? I've removed my claims that mainloop does nothing.Pterodactyl
I would say it's because that's simply not what atexit is for. atexit is for code that should be run after the program completes. Per the documentation: "The atexit module defines a single function to register cleanup functions". mainloop isn't a cleanup function, it's an integral part of the running application. One could argue that because it works, there's no harm in doing it that way. However, we should strive for clarity in our code, which includes not using features for a purpose that they weren't designed for.Obidiah

© 2022 - 2024 — McMap. All rights reserved.