Switch between two frames in tkinter

I built my first few scripts with a small graphical interface on them, as the lessons showed me, but none of them concerns what needs to be done for a more complex program.

If you have something with a "start menu" for your start screen and user selection, you go to another section of the program and redraw the screen accordingly, what is an elegant way to do this?

Is there only a .destroy() frame "Start menu", and then create a new one, filled with widgets for another part? And change this process when they click the back button?

+73
python tkinter frame
Sep 25 '11 at 14:14
source share
3 answers

One way is to stack frames on top of each other, then you can simply lift one on top of the other in the stacking order. The one above will be the one that is visible. This works best if all frames are the same size, but with little work you can make it work with frames of any size.

Note : for this to work, all widgets for the page must have this page ( self .: self ) or a child as the parent (or master, depending on the terminology you use).

Here is a little contrived example to show you the general concept:

 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() 

start page page 1 page 2

If you find the concept of creating an instance in a class incomprehensible, or if different pages require different arguments during construction, you can explicitly call each class separately. The cycle serves primarily to illustrate that each class is identical.

For example, to create 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 asked other questions using this code (or the online manual 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 that runs every time a frame is displayed in tkinter
  • Tkinter frame resize
  • Tkinter has code for pages in separate files
  • Updating the tkinter frame when a button is pressed
+124
Sep 26 '11 at 14:55
source share

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() 
+23
Jul 24. '15 at 6:24
source share

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

I modified Brian 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.3 import tkinter as tk class 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 the old _frame if it exists, and then replaces it with a new frame.
  • Other frames added with .pack() , such as menubars, will not be affected.
  • It 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 initialized v2.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. 
+20
Mar 16 '18 at 16:42
source share



All Articles