Python: Tkinter: Why is it root.mainloop() and not app.mainloop()
I'm not sure if you'll find this answer satisfying, but you call root.mainloop()
because root
is the object that has the mainloop
method. In the code you've given, App
has no mainloop
function.
In simpler terms, this is just how tkinter works -- you always end your script by calling the mainloop
method of the root window. When that method returns, your program will exit.
Let's start at the beginning. The simplest, non-OO Tkinter program is going to look like the following example. Note that this is a python 2.x example, and I do not use a global import since I think global imports are bad.
import Tkinter as tkroot = tk.Tk()<your widgets go here>root.mainloop()
Many people find that a pure procedural style is not an effective way to write code, so they might choose to write this in an object-oriented style. It's natural to think of "the app" as a singleton object. There are many ways to do this -- the one in your question is, unfortunately, not one of the clearer ways to do it.
A slightly better way, IMO, would be to structure the code like this:
class App(tk.Tk): def __init__(self, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) <your widgets go here>app = App()app.mainloop()
In this case, mainloop
is still being called, though now it's a method of App
since App
inherits from Tk
. It is conceptually the same as root.mainloop()
since in this case, app
is the root window even though it goes by a different name.
So, in both cases, mainloop
is a method that belongs to the root window. And in both cases, it must be called for the GUI to function properly.
There is a third variation which is what the code you picked is using. And with this variation, there are several ways to implement it. The variation is your question uses a class to define the GUI, but does not inherit from Tk
. This is perfectly fine, but you still must call mainloop
at some point. Since you don't create (or inherit) a mainloop
function in your class, you must call the one associated with the root window. The variations I speak of are how and where the instance of App
is added to the root window, and how mainloop
is ultimately called.
Personally I prefer that App
inherits from Frame
, and that you pack the app outside the definition of the app. The template I use looks like this:
class App(tk.Frame): def __init__(self, *args, **kwargs): tk.Frame.__init__(self, *args, **kwargs) <your widgets go here>if __name__ == "__main__": root = tk.Tk() app = App(root) app.pack(fill="both", expand=True) root.mainloop()
In this final example, app
and root
are two completely different objects. app
represents a frame that exists inside the root window. Frames are commonly used this way, as a container for groups of other widgets.
So, in all cases, mainloop
must be called. where it is called, and how, depends a bit on your coding style. Some people prefer to inherit from the root window, some don't. In either case, you must call the mainloop
function of the root window.
I tested both like you see:
One is written with "app." + ".pack()" and one calls "mainframe." + ".grid()"
#-*- coding: utf-8 -*-#THIS IS THE "MAINFRAME." - PARTfrom Tkinter import *import ttkdef show(): p = password.get() #get password from entry print(p)root = Tk()root.title("Ingos first program")mainframe = ttk.Frame(root, padding="30 30 60 12")mainframe.grid(column=0, row=0, sticky=(N, W, E, S))mainframe.columnconfigure(0, weight=1)mainframe.rowconfigure(0, weight=1)password = StringVar() #Password variablepassEntry = Entry(mainframe, textvariable=password, show='*').grid(column=3, row=3, sticky=S)submit = Button(mainframe, text='Show Console',command=show).grid(column=3, row=4, sticky=S)root.mainloop()def show(): p = password.get() #get password from entry print(p)#THIS IS THE "APP."-PART. BOTH WORKS FINE.app = Tk()app.title("Ingos first program")password = StringVar() #Password variablepassEntry = Entry(app, textvariable=password, show='#').pack()submit = Button(app, text='Show Console',command=show).pack()app.mainloop()
This instance works fine with python 2.7. In that test even app. can handle "mainloop()"That script opens 2 windows, one after another (if you close the first one) and the first programm is formatted, didn't tryed to write the colum=3... stuff in the pack() clamps.
I still started the Tkinter so don't fight me, just trying.. Hope I could help to answer your question.All the best, Ingo
The App
object is just your app code, and the reason you call App(root)
is to make an instance with your class, which then has access to your root window.
It receives this reference in the __init__
method:
def __init__(self, master): # master refers to the root window now ...
You can see the entire definition of the App
object (given by the block beginning with class App:
), and it doesn't even have a mainloop
method, so to start the main Tkinter loop, you have to call it on the root window.
In the example in the Python2 documentation, they do call it as you suspected should be done, but note that their example class subclasses the Tk object Frame
. In your example code, App
is an old-style class that doesn't inherit anything.