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()
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With