Switch between two frames in tkinter?
One way is to stack the frames on top of each other, then you can simply raise one above the other in the stacking order. The one on top will be the one that is visible. This works best if all the frames are the same size, but with a little work you can get it to work with any sized frames.
Note: for this to work, all of the widgets for a page must have that page (ie: self
) or a descendant as a parent (or master, depending on the terminology you prefer).
Here's a bit of a contrived example to show you the general concept:
try:
import tkinter as tk # python 3
from tkinter import font as tkfont # python 3
except ImportError:
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()
If you find the concept of creating instance in a class confusing, or if different pages need different arguments during construction, you can explicitly call each class separately. The loop serves mainly to illustrate the point that each class is identical.
For example, to create the classes individually you can remove the loop (for F in (StartPage, ...)
with this:
self.frames["StartPage"] = StartPage(parent=container, controller=self)
self.frames["PageOne"] = PageOne(parent=container, controller=self)
self.frames["PageTwo"] = PageTwo(parent=container, controller=self)
self.frames["StartPage"].grid(row=0, column=0, sticky="nsew")
self.frames["PageOne"].grid(row=0, column=0, sticky="nsew")
self.frames["PageTwo"].grid(row=0, column=0, sticky="nsew")
Over time people have asked other questions using this code (or an online tutorial that copied this code) as a starting point. You might want to read the answers to these questions:
- Understanding parent and controller in Tkinter __init__
- Tkinter! Understanding how to switch frames
- How to get variable data from a class
- Calling functions from a Tkinter Frame to another
- How to access variables from different classes in tkinter?
- How would I make a method which is run every time a frame is shown in tkinter
- Tkinter Frame Resize
- Tkinter have code for pages in separate files
- Refresh a tkinter frame on button press
How to switch between two tkinter frames
The mistake is in the following lines:
HR.pack(fill='BOTH', expand=1)
Schain.pack(fill='BOTH', expand=1)
You must either directly give the string in lowercase as 'both'
or use the pre-defined tkinter constant BOTH
. On changing 'BOTH'
to BOTH
in both the places, your code works properly.
Working Code:
from tkinter import *
root= Tk()
root.title("HOSPITAL MANAGEMENT SYSTEM")
root.geometry('600x350')
#=======frames====
HR=Frame(root)
Schain=Frame(root)
#=========================functions for switching the frames========
def change_to_HR():
HR.pack(fill=BOTH, expand=1)
Schain.pack_forget()
def change_to_Schain():
Schain.pack(fill=BOTH, expand=1)
HR.pack_forget()
#=====================Add heading logo in the frames======
labelHR=Label(HR, text="HR DEPARTMENT")
labelHR.pack(pady=20)
labelSchain=Label(Schain, text="SUPPLY CHAIN DEPARTMENT")
labelSchain.pack(pady=20)
#===============================add button to switch between frames=====
btn1=Button(root, text="HR", command=change_to_HR)
btn1.pack(pady=20)
btn2=Button(root, text="SUPPLY CHAIN", command=change_to_Schain)
btn2.pack(pady=20)
root.mainloop()
How to switch between frames in tkinter
You have five major problems:
- you are calling a function immediately (
command=CharacterCreate.lift(1)
) rather than at the time the button is clicked (command=CharacterCreate.lift
), - you are passing an invalid argument to lift - you are passing
1
, but the argument tolift
must be another widget, - you are calling
lift
on a class rather than an instance of a class. - you never create an instance of
CharacterCreate
- your classes inherit from
Frame
but you never use the classes as frames -- they each place their widgets directly incontainer
Switching between pages usually involves one of two techniques: create all the frames at startup and then lift the active frame above the others, or destroy the current frame and recreate the active frame. You seem to be attempting to do the latter, so this answer will show you how to do that.
Since fixing your program is going to require many changes, I am instead going to show you a template that you can use to start over.
Let's start with an import, and then the definition of your pages. To keep the example short, each class will have a single label so that you can distinguish between them (note: importing tkinter "as tk" is done simply to make the code a bit easier to read and type):
import tkinter as tk
class CharacterCreate(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="I am CharacterCreate")
label.pack(padx=20, pady=20)
class MainMenu(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text="I am MainMenu")
label.pack(padx=20, pady=20)
Your original code created a container, so we'll do that next. We need to create the root window, too:
root = tk.Tk()
container = tk.Frame(root)
container.pack(fill="both", expand=True)
Now we need to create an instance of each page, giving them the container as the parent. As a rule of thumb, the code that creates a widget should be the code that calls pack
, place
, or grid
on the widget, so we have to do that too. We need to make sure that grid
is configured to give all weight to row 0 column 0.
main = MainMenu(container)
cc = CharacterCreate(container)
main.grid(row=0, column=0, sticky="nsew")
cc.grid(row=0, column=0, sticky="nsew")
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
We need a way to lift one of the classes above the other. That's best handled by a function. To make the code easier to understand we'll save the pages in a dictionary so we can reference them by name. This name will be the argument to the function.
pages = {"cc": cc, "main": main}
def switch(name):
page = pages[name]
page.lift()
Finally, we need to start with the main menu on top, and we need to start the event loop:
switch('main')
root.mainloop()
With that, you have a program that runs and displays the main menu. To finish the example lets add a button to the menu to switch to the create page, and create a button in the create page to switch back to the menu.
First, inside the __init__
of MainMenu
add the following after the code that creates the label. Notice that because we need to pass an argument to switch
, we use lambda
:
button = tk.Button(self, text="Go to creater", command=lambda: switch('cc'))
button.pack()
And next, inside the __init__
of CharacterCreate
add the following after the code that creates the label:
button = tk.Button(self, text="Go to main menu", command=lambda: switch('main'))
button.pack()
With that, you now have the basic structure to create as many pages as you want, and easily switch to them by name.
How do I switch frame using place in tkinter?
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)
Related Topics
Why Do Some Regex Engines Match .* Twice in a Single Input String
How to Convert String Representation of List to a List
String Formatting: % Vs. .Format Vs. F-String Literal
How to Check Whether a File Exists Without Exceptions
Best Way to Structure a Tkinter Application
Printing Lists as Tabular Data
How to Remove Accents (Normalize) in a Python Unicode String
Access Nested Dictionary Items Via a List of Keys
Converting Datetime.Date to Utc Timestamp in Python
Parsing a Date That Can Be in Several Formats in Python
How to Filter Pandas Dataframe Using 'In' and 'Not In' Like in Sql
Importing Files from Different Folder
How to Provide a Reproducible Copy of Your Dataframe With To_Clipboard()
How to Read/Process Command Line Arguments
How to Add Sequential Counter Column on Groups Using Pandas Groupby
What Is the Purpose of the Return Statement? How Is It Different from Printing
Why Can a Function Modify Some Arguments as Perceived by the Caller, But Not Others