Switch between two frames in tkinter Switch between two frames in tkinter tkinter tkinter

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 3except ImportError:    import Tkinter as tk     # python 2    import tkFont as tkfont  # python 2class 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()

start page page 1 page 2

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:


Here is another simple answer, but without using classes.

from tkinter import *def raise_frame(frame):    frame.tkraise()root = Tk()f1 = Frame(root)f2 = Frame(root)f3 = Frame(root)f4 = Frame(root)for frame in (f1, f2, f3, f4):    frame.grid(row=0, column=0, sticky='news')Button(f1, text='Go to frame 2', command=lambda:raise_frame(f2)).pack()Label(f1, text='FRAME 1').pack()Label(f2, text='FRAME 2').pack()Button(f2, text='Go to frame 3', command=lambda:raise_frame(f3)).pack()Label(f3, text='FRAME 3').pack(side='left')Button(f3, text='Go to frame 4', command=lambda:raise_frame(f4)).pack(side='left')Label(f4, text='FRAME 4').pack()Button(f4, text='Goto to frame 1', command=lambda:raise_frame(f1)).pack()raise_frame(f1)root.mainloop()


Note: According to JDN96, the answer below may cause a memory leak by repeatedly destroying and recreating frames. However, I have not tested to verify this myself.

One way to switch frames in tkinter is to destroy the old frame then replace it with your new frame.

I have modified Bryan Oakley's answer to destroy the old frame before replacing it. As an added bonus, this eliminates the need for a container object and allows you to use any generic Frame class.

# Multi-frame tkinter application v2.3import tkinter as tkclass SampleApp(tk.Tk):    def __init__(self):        tk.Tk.__init__(self)        self._frame = None        self.switch_frame(StartPage)    def switch_frame(self, frame_class):        """Destroys current frame and replaces it with a new one."""        new_frame = frame_class(self)        if self._frame is not None:            self._frame.destroy()        self._frame = new_frame        self._frame.pack()class StartPage(tk.Frame):    def __init__(self, master):        tk.Frame.__init__(self, master)        tk.Label(self, text="This is the start page").pack(side="top", fill="x", pady=10)        tk.Button(self, text="Open page one",                  command=lambda: master.switch_frame(PageOne)).pack()        tk.Button(self, text="Open page two",                  command=lambda: master.switch_frame(PageTwo)).pack()class PageOne(tk.Frame):    def __init__(self, master):        tk.Frame.__init__(self, master)        tk.Label(self, text="This is page one").pack(side="top", fill="x", pady=10)        tk.Button(self, text="Return to start page",                  command=lambda: master.switch_frame(StartPage)).pack()class PageTwo(tk.Frame):    def __init__(self, master):        tk.Frame.__init__(self, master)        tk.Label(self, text="This is page two").pack(side="top", fill="x", pady=10)        tk.Button(self, text="Return to start page",                  command=lambda: master.switch_frame(StartPage)).pack()if __name__ == "__main__":    app = SampleApp()    app.mainloop()

Start page Page one Page two

Explanation

switch_frame() works by accepting any Class object that implements Frame. The function then creates a new frame to replace the old one.

  • Deletes old _frame if it exists, then replaces it with the new frame.
  • Other frames added with .pack(), such as menubars, will be unaffected.
  • Can be used with any class that implements tkinter.Frame.
  • Window automatically resizes to fit new content

Version History

v2.3- Pack buttons and labels as they are initializedv2.2- Initialize `_frame` as `None`.- Check if `_frame` is `None` before calling `.destroy()`.v2.1.1- Remove type-hinting for backwards compatibility with Python 3.4.v2.1- Add type-hinting for `frame_class`.v2.0- Remove extraneous `container` frame.    - Application now works with any generic `tkinter.frame` instance.- Remove `controller` argument from frame classes.    - Frame switching is now done with `master.switch_frame()`.v1.6- Check if frame attribute exists before destroying it.- Use `switch_frame()` to set first frame.v1.5  - Revert 'Initialize new `_frame` after old `_frame` is destroyed'.      - Initializing the frame before calling `.destroy()` results        in a smoother visual transition.v1.4- Pack frames in `switch_frame()`.- Initialize new `_frame` after old `_frame` is destroyed.    - Remove `new_frame` variable.v1.3- Rename `parent` to `master` for consistency with base `Frame` class.v1.2- Remove `main()` function.v1.1- Rename `frame` to `_frame`.    - Naming implies variable should be private.- Create new frame before destroying old frame.v1.0- Initial version.