I'm implementing a GUI with Tkinter following this topic: Switch between two frames in tkinter
Trying to adapting it to my problem:
This is the controller:
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title('VOICED/UNVOICED SPEECH DETECTION')
self.resizable(RESIZABLE, RESIZABLE)
self.geometry(str(WIDTH_WINDOW) + 'x' + str(HEIGHT_WINDOW))
self.configure(bg=BACK_GROUND_COLOR)
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 (HomePage, MenuPage, GraphicPage):
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("HomePage")
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
This is the home page that shows the lateral bar:
And the corresponding code:
class HomePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.bg = BACK_GROUND_COLOR
label = tk.Label(self, text="This is the start page", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
# -----------ROOT WINDOWS-------------
self.configure(bg=BACK_GROUND_COLOR)
self.position = 0
# ----------Menu Bar-------------------------
self.menu_frame = tk.Frame(master=self, height=HEIGHT_WINDOW, width=50,
borderwidth=5, relief='groove', highlightbackground="black",
highlightcolor="black", highlightthickness=1, bg=BACK_GROUND_COLOR).place(x=0, y=0)
# Menu Button
self.menu_img = tk.PhotoImage(file='frontend/Widget/icons/menu.png')
self.menu_button = tk.Button(master=self.menu_frame, image=self.menu_img,
height=25, width=25,
command=lambda: controller.show_frame("MenuPage"),
bg=BACK_GROUND_COLOR).place(x=10, y=10)
# Back Button
self.back_img = tk.PhotoImage(file='frontend/Widget/icons/back.png')
self.back_button = tk.Button(master=self.menu_frame, image=self.back_img, height=25, width=25,
bg=BACK_GROUND_COLOR).place(x=10, y=46)
# Home Button
self.home_img = tk.PhotoImage(file='frontend/Widget/icons/home.png')
self.home_button = tk.Button(master=self.menu_frame, image=self.home_img, height=25, width=25,
bg=BACK_GROUND_COLOR).place(x=10, y=82)
When I click the menu button (the first in the lateral bar), I obtain the menu option (That I called MenuPage):
And the corresponding code:
class MenuPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.bg = BACK_GROUND_COLOR
# --------------------------------------------------toolbar--------------------------------------
self.toolbar_frame = tk.Frame(master=self, height=70, width=WIDTH_WINDOW - 54,
borderwidth=5, relief='groove', highlightbackground="black",
highlightcolor="black",
highlightthickness=1, bg=BACK_GROUND_COLOR).place(x=51, y=0)
self.button_graphics_img = tk.PhotoImage(file='frontend/Widget/icons/graphic.png')
self.button_graphic = tk.Button(master=self, text='Test/Train',
image=self.button_graphics_img, height=25, width=25, bg=BACK_GROUND_COLOR,
command=lambda: controller.show_frame("GraphicPage")).place(x=75, y=5)
self.label_graphic = tk.Label(master=self, text='Plot Parameters',
height=0, width=12, bg=BACK_GROUND_COLOR).place(x=55, y=40)
As you can notice, when I click on the menu button the lateral frame disappears. This is because in the MenuPage there aren't the methods of the lateral bar, but trying to put it also in the MenuPage, the buttons (back arrow, home) don't work.
So I don't get how to hold fixed the lateral bar for all my pages and make sure that the buttons do their job.
Any suggestions will be appreciated.
Thanks in advance
UPDATE:
By placing the sidebar in the Main Windows, I solved the fact that the sidebar disappeared when I opened the menu.
The other problem is:
How can I do with the back arrow to come back to the previous Frame? For Instance, if I open another page with a button in the MenuPage, and then I'd want to come back to from that page to the MenuPage, how should I do?
I've already tried to re-place the buttons in the page opened from menu changing the command to open the previous page (putting lambda: controller.show_frame("MenuPage") ) but doesn't works.
Related
I'm new to Tkinter and python. I'm following Switch between two frames in tkinter to see switch frames and it worked. Next, I'm trying to write the code for switch frames inside Page One, but I don't have any idea how to do it.
Below is the code:
from tkinter import *
import tkinter as tk
from tkinter import font as tkfont
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)
width = 1350 # 1280
height = 720
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x = (screen_width / 2) - (width / 2)
y = (screen_height / 2) - (height / 2)
self.geometry(f'{width}x{height}+{int(x)}+{int(y)}')
self.resizable(False,False)
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)
button1 = tk.Button(self, text="Go to Page One",command=lambda: controller.show_frame("PageOne"))
button1.pack()
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
LeftFrame=tk.Frame(self,relief=RIDGE, bd=2)
LeftFrame.place(x=0,y=0,width=160,height=720)
button = tk.Button(LeftFrame, text="Go to the start page",font=("bold"),command=lambda: controller.show_frame("StartPage"))
button.grid(row=0)
buttonBlue = tk.Button(LeftFrame, text="Go to the blue page",bg="blue",fg="white",command=self.blue,activebackground="blue",activeforeground="white")
buttonBlue.grid(row=1)
buttonRed = tk.Button(LeftFrame, text="Go to the red page",bg="red",fg="white",command=self.red,activebackground="red",activeforeground="white")
buttonRed.grid(row=2)
buttonYellow = tk.Button(LeftFrame, text="Go to the yellow page",bg="yellow",fg="black",command=self.yellow,activebackground="yellow",activeforeground="black")
buttonYellow.grid(row=3)
def blue(self):
# self.hide_all_frames()
blueFrame=tk.Frame(self,relief=RIDGE,bd=1 ,bg="blue")
blueFrame.place(x=160,y=0,width=1190,height=720)
def red(self): # Do I need to put self here? It still worked without putting self here
# self.hide_all_frames()
redFrame=tk.Frame(self,relief=RIDGE,bd=1 ,bg="red")
redFrame.place(x=200,y=0,width=1150,height=720)
def yellow(self):
# self.hide_all_frames()
yellowFrame=tk.Frame(self,relief=RIDGE,bd=1 ,bg="yellow")
yellowFrame.place(x=240,y=0,width=1110,height=720)
def hide_all_frames(self):
self.blue.withdraw()
if __name__ == "__main__":
app = SampleApp()
app.mainloop()
When I switch to Page One, I create a couple of buttons to switch color frames. They all overlap every time I switch between colors like the image below.
I'm finding a way to switch color frames without overlapping each other. And, when I go back to the Start Page, all color frames should be destroyed/hidden. Please help me. Thank you so much.
For the frame switching inside PageOne, you need to hide the current frame before showing the requested frame. Also it is better to create the three color frames in the __init__() and show it in the corresponding function:
class PageOne(tk.Frame):
def __init__(self, parent, controller):
...
# create the three color frames with initially hidden
self.blueFrame = tk.Frame(self, relief=RIDGE, bd=1, bg="blue")
self.redFrame = tk.Frame(self, relief=RIDGE, bd=1, bg="red")
self.yellowFrame = tk.Frame(self, relief=RIDGE, bd=1, bg="yellow")
def blue(self):
self.hide_all_frames()
self.blueFrame.place(x=160, y=0, width=1190, height=720)
def red(self):
self.hide_all_frames()
self.redFrame.place(x=200, y=0, width=1150, height=720)
def yellow(self):
self.hide_all_frames()
self.yellowFrame.place(x=240, y=0, width=1110, height=720)
def hide_all_frames(self, event=None):
self.redFrame.place_forget()
self.blueFrame.place_forget()
self.yellowFrame.place_forget()
If you want to hide all color frames after switching frames, i.e. PageOne -> MainPage -> PageOne, you can notify the PageOne using virtual event when it is raised. Then PageOne hides all the color frames upon receiving such virtual event:
class SampleApp(tk.Tk):
...
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
# notify the raised frame via virtual event
frame.event_generate('<<Raised>>')
...
class PageOne(tk.Frame):
def __init__(self, parent, controller):
...
self.blueFrame = tk.Frame(self, relief=RIDGE, bd=1, bg="blue")
self.redFrame = tk.Frame(self, relief=RIDGE, bd=1, bg="red")
self.yellowFrame = tk.Frame(self, relief=RIDGE, bd=1, bg="yellow")
self.bind('<<Raised>>', self.hide_all_frames)
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 have a tkinter app in which I have a main canvas with multiple pages (all of which are frames). I pull up the different pages by rasing them with the frame.tkraise() command. I now want to add a scrollbar to the whole thing. The scrollbar appears but without a slider and I am not sure if it cna recognize the change of page.
import tkinter as tk
from tkinter import ttk
class Economics(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
tk.Tk.columnconfigure(self, 0, weight=1)
tk.Tk.rowconfigure(self, 0, weight=1)
self.container = tk.Canvas()
self.container.grid(row=0, column=0, sticky="nsew")
self.container.columnconfigure("all", weight=1)
self.container.rowconfigure("all", weight=1)
self.vscrollbar = tk.Scrollbar(orient="vertical", command=self.container.yview)
self.container.configure(yscrollcomman=self.vscrollbar.set)
self.vscrollbar.grid(row=0, column=1, sticky="ns")
self.frames = {}
for F in (StartPage, ExamplePage1, ExamplePage2): # TUPLE OF PAGES
frame = F(self.container, self)
self.frames[F] = frame
self.show_frame(StartPage)
def show_frame(self, cont):
self.container.delete("all")
frame = self.frames[cont]
self.container.create_window(0, 0, anchor="nw", window=frame)
class StartPage(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
button_1 = ttk.Button(self, text="Example Page 1",
command=lambda: controller.show_frame(ExamplePage1))
button_1.grid(row=0, column=0)
button_2 = ttk.Button(self, text="Example Page 2",
command=lambda: controller.show_frame(ExamplePage2))
button_2.grid(row=1, column=0)
class ExamplePage1(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
for i in range(50):
label = tk.Label(self, text="Button {} of 50".format(i+1))
label.grid(row=i, column=0)
button_back = ttk.Button(self, text="Back",
command=lambda: controller.show_frame(StartPage))
button_back.grid(row=0, column=1)
class ExamplePage2(tk.Frame):
def __init__(self, parent, controller):
super().__init__(parent)
for i in range(35):
label = tk.Label(self, text="Button {} of 35".format(i+1))
label.grid(row=i, column=0)
button_back = ttk.Button(self, text="Back",
command=lambda: controller.show_frame(StartPage))
button_back.grid(row=0, column=1)
app = Economics()
app.geometry("800x600")
app.resizable(True, True)
app.mainloop()
In this example file you can see the basic structure of my app with some example widgets and buttons. The scrollbar shows but without the slider. What do I have to change to get a working scrollbar for all pages.
Later on I'm planning to get a horizontal scrollbar as well.
You can't scroll items added to a canvas with pack, place, or grid. A canvas can only scroll items added via the canvas create_* functions, such as create_window.
I am trying to make a tkinter widow with multiple frames, but also the functions of notebook, like multiple widows. The problem is I am kind of unfamiliar with tkinter and am not sure how to do that. This is my current code, and it doesn't work, and would love to know what I should do to make it work. Again, the dream end result would be that I would have a first widow, which says the test text, and then the 2nd window which has multiple tabs.
from tkinter import ttk
import tkinter as tk
Font= ("Verdana", 8)
LargeFont = ("Verdana", 12)
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
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):
frame = F(container, self)
self.frames[F] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self,parent)
label = tk.Label(self, text="Info", font=LargeFont)
label.pack(pady=2,padx=10)
text = tk.Label(self, text="testtestestetetqwegfegeg\ntestwegwegwegweg", font=Font)
text.pack(pady=2,padx=2)
button = tk.Button(self, text="Go to the Card",
command=lambda: controller.show_frame(PageOne))
button.pack(fill="x")
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
nb = ttk.Notebook(ttk.Frame())
nb.grid(row=1, column=0, columnspan = 50, rowspan=49, sticky='nesw')
p1 = (nb)
nb.add(p1, text='test')
label = tk.Label(self, text="", font=LargeFont)
label.pack(pady=10,padx=10)
button1 = tk.Button(self, text="Back to Home",
command=lambda: controller.show_frame(StartPage))
button1.pack()
app = App()
app.mainloop()
The error that I eventually get is that it creates a third frame that is displayed with the test tab. Everything else works.
Thanks for your help
I know exactly what you mean because I'm trying the same. To me
nb = ttk.Notebook(self)
worked.
best
Pkanda
Taubate Brazil
I've been stuck for few days trying to figure out how I can resize the frame in TKInter dynamically using this approach.
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(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()
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()
I copied this code from Switch between two frames in tkinter, because I'm following the same approach.
The problem which I'm facing is that using this approach all frames are stacked in a container and the size of this container is the size of its' largest frame. Moving from one frame to another doesn't resize the respective window dynamically and leads to huge free space in small frames. I tried different techniques to make all frames in the container dynamically resizable but without much success. Can somebody suggest what I can do?
Instead of stacking the frames, make sure only one is ever managed by grid at a time. You can do this by calling grid_remove() of the current frame and then grid() on the new frame. Or, being lazy you can call grid_remove() on everything so that you don't have to remember which page is current.
def show_frame(self, page_name):
'''Show a frame for the given page name'''
for frame in self.frames.values():
frame.grid_remove()
frame = self.frames[page_name]
frame.grid()
Note: the automatic resizing will stop working if you give the main window a fixed size with the geometry method on the root window, or if the user manually resizes the window. This is because tkinter assumes that if something explicitly requests a window size, that size should be honored.
If you always want the window to resize, you should reset the geometry to an empty string. You can add this as the last statement in the show_frame method:
frame.winfo_toplevel().geometry("")