Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tkinter - Understanding how to switch frames

Im trying to teach my self how to use tkinter and I found a useful code through youtube that I don't really fully understand. Would appreciate it if some could help me understand it. Marked the things I did not understand with # ... **.

import tkinter as tk  # why not from tkinter import? **

class SampleApp(tk.Tk): # why tk.TK **

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        # the container is where we'll stack a bunch of frames
        # on top of each other, then the one we want visible
        # will be raised above the others

        container = tk.Frame(self) # **
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        for F in (StartPage, PageOne, PageTwo):
            page_name = F.__name__
            frame = F(container, self) # **
            self.frames[page_name] = frame # **

            # put all of the pages in the same location;
            # the one on the top of the stacking order
            # will be the one that is visible.
            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame("StartPage")

    def show_frame(self, page_name):
        '''Show a frame for the given page name'''
        frame = self.frames[page_name]
        frame.tkraise() # **


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent) # **
        self.controller = controller # **
        label = tk.Label(self, text="This is the start page",
                         font=TITLE_FONT)
        label.pack(side="top", fill="x", pady=10)

        button1 = tk.Button(self, text="Go to Page One",
                            command=lambda: controller.show_frame("PageOne"))
        button2 = tk.Button(self, text="Go to Page Two",
                            command=lambda: controller.show_frame("PageTwo"))
        button1.pack()
        button2.pack()
like image 898
Baller Avatar asked Dec 15 '15 23:12

Baller


1 Answers

import tkinter as tk  # why not from tkinter import? **

Why not from tkinter import *? Because that is the wrong way to do it. Global imports are bad. Tkinter tutorials tend to do it the wrong way for some reason that I don't understand.

The reason for the as tk part is so that you can do tk.Frame rather than tkinter.Frame, making the code a little easier to type and a little easier to read. It's completely optional.


class SampleApp(tk.Tk): 

tk is the name of the tkinter module that was imported. Tk is the name of a class in that module that represents the root window. Every tkinter application must have a single root window. By placing it inside of SampleApp, this creates a subclass of this widget -- a copy that has additional features.

It's not necessary to inherit from tk.Tk. You can inherit from tk.Frame, any other tkinter widget, or even object. It's a personal preference. The choice makes some things easier, some things harder.


container = tk.Frame(self)

The above creates an instance of a Frame widget, which will be used as a container for other "pages". These "pages" will all be stacked on top of each other in this container.


frame = F(container, self)

F is the loop variable. The loop is iterating over a list of classes, so each time through the loop F will be representing a class. F(...) creates an instance of the class. These classes (StartPage, PageOne, PageTwo) all require two parameters: a widget that will be the parent of this class, and an object that will server as a controller (a term borrowed from the UI patter model/view/controller).

The line of code creates an instance of the class (which itself is a subclass of a Frame widget), and temporarily assigns the frame to the local variable frame.

By passing self as the second ("controller") parameter, these new class instances will be able to call methods in the SampleApp class object.

This saves a reference to the just-created frame in a dictionary. The key to the dictionary is the page name (or more accurately, the name of the class).

This is how the show_frame method can determine the actual page widget just from the name of the class.

Creating the frames in a loop is functionally equivalent to the following:

f1 = StartPage(container, self)
f2 = PageOne(container, self)
f3 = PageTwo(container, self)

f1.grid(row=0, column=0, sticky="nsew")
f2.grid(row=0, column=0, sticky="nsew")
f3.grid(row=0, column=0, sticky="nsew")

self.frames = {"StartPage": f1, "PageOne": f2, "PageTwo": f3}

frame.tkraise() 

In nearly all GUI toolkits -- tkinter included -- there is the notion of a "stacking order": the order in which things are stacked. Some toolkits might call this the z-order. If two or more widgets are stacked on top of each other (which this code does by putting all pages in the same row and column), the widget that is on the top of the stack is the widget that will typically be visible.

tkraise is a method of a Frame object that will raise the frame to the top of the stacking order. In this line of code, frame refers to one particular instance of one of the pages.


tk.Frame.__init__(self, parent)

Because each page is a subclass of a tk.Frame class, this calls the constructor of the parent class. This is necessary to initialize all of the internal structures that make up the actual frame widget. Although a Frame can take many options, this code chooses to send in only one -- a reference to another widget which is to act as the parent of this new widget.


self.controller = controller

The above code is simply "remembering" the value of the controller variable which was passed in. In this case, the controller is the application. By saving it off, this class can call methods on the SampleApp object.


Note: the code in the question came from a tutorial that copied code from this answer: https://stackoverflow.com/a/7557028/7432. I am the author of that original code, but not the author of the tutorial. In that original answer are links to other questions related to this code.

like image 129
Bryan Oakley Avatar answered Nov 13 '22 23:11

Bryan Oakley