Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
584 views
in Technique[技术] by (71.8m points)

python 3.x - 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()
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)
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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...