I am following the MVC model that Brian made on the answer to “Switch between two frames in tkinter“. He stacks the frames on top of each other (all are made at the very beginning) and then we just show them at our will.
I try to add another frame on the run. I know it is made (because you get an error before creating it and no error after), but I am cannot show it. What am I missing?
Thanks
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
#import Tkinter as tk # python 2
#import tkFont as tkfont # python 2
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# 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(parent=container, controller=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()
def add_new(self):
''' Create a new frame on the run '''
self.frames["PageNew"] = PageNew(parent=tk.Frame(self), controller=self)
self.frames["PageNew"].grid(row=0, column=0, sticky="nsew")
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=controller.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"))
button3 = tk.Button(self, text="Go to a New Page ",
command=lambda: controller.show_frame("PageNew"))
button1.pack()
button2.pack()
button3.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Create a new page and go to the start page",
command=self.on_click)
button.pack()
def on_click(self):
self.controller.add_new()
self.controller.show_frame("StartPage")
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button1 = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button2 = tk.Button(self, text="Go to the new page",
command=lambda: controller.show_frame("PageNew"))
button1.pack()
button2.pack()
class PageNew(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the new page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Your problem is the parent of your PageNew:
self.frames["PageNew"] = PageNew(parent=tk.Frame(self), controller=self)
Its parent is a Frame you create, but it is not packed inside the GUI so you won't see it, nor its child PageNew. In order for PageNew to be like the other pages, you need to give it the same parent, which is container here.
Since you will need container outside of __init__, you need to make it an attribute of SampleApp, i.e. replace container = tk.Frame(self) by self.container = tk.Frame(self).
And now, in add_new, you can create your PageNew with
self.frames["PageNew"] = PageNew(parent=self.container, controller=self)
Related
I was reading this tutorial of StackOverflow on how to get variables among classes, but I'm not able to make it work. Basically what i want to do is to pass an entry value from StartPage to PageOne where i need it. I also tried "the wrong way" using global variables, but I need an int and I can't covert the string entry to an int. Down below there's my code, can you help me please?
import tkinter as tk # python 3
from tkinter import font as tkfont
from typing_extensions import IntVar # python 3
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# 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):
page_name = F.__name__
frame = F(parent=container, controller=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()
def get_page(self, classname):
'''Returns an instance of a page given it's class name as a string'''
for page in self.frames.values():
if str(page.__class__.__name__) == classname:
return page
return None
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="Start Page: insert value", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
self.valore = tk.IntVar()
entry = tk.Entry(self, textvariable=self.valore).pack()
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()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
start_page = self.controller.get_page("StartPage")
value = start_page.entry.get()
print ('The value stored in StartPage entry = %s' % value)
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
The example below has previously been used to describe the functionality of classes and switching pages in Tkinter.
Although I don't understand:
Why classes PageOne and PageTwo need to inherit from tk.Frame?
Where the equivilant of root = Tk() is (since in other beginner tutorials I watched, this step is essential).
Code:
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
#import Tkinter as tk # python 2
#import tkFont as tkfont # python 2
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# 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(parent=container, controller=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=controller.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()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Why classes PageOne and PageTwo need to inherit from tk.Frame?
They don't have to inherit from tk.Frame. Because they contain other widgets, tk.Frame is the natural choice. You could use tk.Canvas or literally any other widget (though, using widgets like tk.Button or tk.Scrollbar make no sense)
Where the equivilant of root = Tk()
It is this line of code:
app = SampleApp()
SampleApp inherits from tk.Tk, so it behaves exactly the same. You're free to rename app to root if you wish.
I use following code:
Switch between two frames in tkinter
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
# 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):
page_name = F.__name__
frame = F(parent=container, controller=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=controller.title_font)
label.pack(side="top", fill="x", pady=10)
self.giris = tk.Entry(self)
self.giris.pack()
self.yazi = tk.Label(self, text="Buraya GİRİLEN VERİ gelecek.")
self.yazi.pack()
button2 = tk.Button(self, text="ae",
command=lambda: [self.alinanmetin(), controller.show_frame("PageOne")])
button2.pack()
def alinanmetin(self):
il = self.giris.get()
self.yazi.config(text="Girdiğiniz il: %s" % il)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.geometry("600x400")
app.mainloop()
I want to change my frame according to the value I get from the entry.
if(il =="ali") i want change my frame and
if( il != "ali") i dont want do anything.
I am currently running two functions at the same time.How can i change frame after check value.
I've tried to change the window size for this GUI but i'm struggling. I tried to use root.geometry("1080x800+200+200") but that doesnt seem to work as well. Can someone explain why? i'm currently just practicing using tkinter. thanks
import tkinter as tk # python3
TITLE_FONT = ("Helvetica", 18, "bold")
class SampleApp(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(parent=container, controller=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()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame("StartPage"))
button.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
You do not have a root declared in your program so you will not be able to call root.geometry. If you change your code to something like this then you will be able to call root.geometry and change the size of your GUI window, also by using root you can pass it in as a parameter to your other page classes and set different sizes for them all if you wish.
if __name__ == "__main__":
root = tk.Tk()
root.geometry("1080x800+200+200")
app = SampleApp(root)
root.mainloop()
In first class SampleApp you can just use self.geometry('500x555') #for example.
So I have this code, running multiple GUI windows in tkinter, I also have the second code that contains a certain function assigned to a button, that seems not to work. Im extremely tired and can't find the solution, I'm sure it's some basic thing. The function I mean is the toggle_text1 command in the PageOne class. I'll appreciate every help, thanks!
import Tkinter as tk
TITLE_FONT = ("Helvetica", 18, "bold")
class SampleApp(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=False)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (StartPage, PageOne, PageTwo, Page3, Page4):
frame = F(container, self)
self.frames[F] = 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, c):
'''Show a frame for the given class'''
frame = self.frames[c]
frame.tkraise()
def toggle_text1():
if button1["text"] == "WL":
button1["text"] = "WYL"
label1["bg"] = "green"
#wiringpi.pinMode(91,0)
#wiringpi.digitalWrite(91,0)
else:
button1["text"] = "WL"
label1["bg"] = "red"
#wiringpi.pinMode(91,1)
#wiringpi.digitalWrite(91,0)
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="This is the start page", font=TITLE_FONT)
label.pack(side="left", 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))
button3 = tk.Button(self, text="Go to Page 3",
command=lambda: controller.show_frame(Page3))
button4 = tk.Button(self, text="Go to Page 4",
command=lambda: controller.show_frame(Page4))
button1.pack(pady=10)
button2.pack(pady=10)
button3.pack(pady=10)
button4.pack(pady=10)
Oh crap, I've deleted the widget earlier and forgot to paste it again, my bad. How about now?
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="This is page 1", font=TITLE_FONT)
label.pack(side="top", fill="x", pady=10)
button = tk.Button(self, text="Go to the start page",
command=lambda: controller.show_frame(StartPage))
button1 = tk.Button(self, text='WL', command=toggle_text1)
button.pack()
button1.pack()
label1.pack()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
Your function and button are in different classes if you want to use a function from one class in another, then you need to pass it into your constructor. Which is what you have done.
for F in (StartPage, PageOne, PageTwo, Page3, Page4):
frame = F(container, self)
You have passed it in as controller
class PageOne(tk.Frame):
def __init__(self, parent, controller):
Therefore when assigning the function you need to prefix it with controller
button1 = tk.Button(self, text='WL', command=controller.toggle_text1)
Which you have done for all your lambda functions. Also you need to add self as an argument to your function