Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Tkinter: Why is it root.mainloop() and not app.mainloop()

I'm a new member to Stack Overflow. I found this thread, but was not allowed to comment or ask questions on it, so I thought I'd just reference it here: How can I make a in interactive list in Python's Tkinter complete with buttons that can edit those listings?

from tkinter import *
import os
import easygui as eg

class App:

    def __init__(self, master):
        frame = Frame(master)
        frame.pack()

        # character box
        Label(frame, text = "Characters Editor").grid(row = 0, column = 0, rowspan = 1, columnspan = 2)
        charbox = Listbox(frame)
        for chars in []:
            charbox.insert(END, chars)
        charbox.grid(row = 1, column = 0, rowspan = 5)
        charadd = Button(frame, text = "   Add   ", command = self.addchar).grid(row = 1, column = 1)
        charremove = Button(frame, text = "Remove", command = self.removechar).grid(row = 2, column = 1)
        charedit = Button(frame, text = "    Edit    ", command = self.editchar).grid(row = 3, column = 1)

    def addchar(self):
        print("not implemented yet")
    def removechar(self):
        print("not implemented yet")
    def editchar(self):
        print("not implemented yet")


root = Tk()
root.wm_title("IA Development Kit")
app = App(root)
root.mainloop()

Could somebody explain to me why the very last line is root.mainloop()? Being a novice with Python, and coming from a background that's procedural-oriented with no object-orient experience, I would have thought it would have been app.mainloop().

In fact app = App(root) , app is never used again in the rest of the code! I'm having trouble understanding why root.mainloop() still works.

like image 752
Nuuk Avatar asked Jan 11 '23 22:01

Nuuk


1 Answers

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 tk
root = 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.

like image 139
Bryan Oakley Avatar answered Jan 31 '23 08:01

Bryan Oakley