-1

I posted a question about this topic a few days ago but since my sample code in that question was wrong I deleted that topic to come up with a cleaner sample code. what's the best practice to navigate through different pages/windows in a GUI built by Tkinter? simply, I want to be able to go through different pages in my App, via commands from my menubar. I want to avoid stacking pages on top of each other and a method in which you use grid_remove() or pack_forget() is preferable to me. The only other tutorial which I found here uses the stacking method and lift(). is there any other better way?

import tkinter as tk
from tkinter import *


class MainWin(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        self.page_1 = Page1(self.parent)
        self.page_2 = Page2(self.parent)

        self.init_UI()

    def init_UI(self):
        menubar = Menu(self.parent)
        self.parent.config(menu=menubar)
        self.parent.title('Frame Switching test app')
        file_menu = Menu(menubar)
        pages_menu = Menu(menubar)
        menubar.add_cascade(label='File', menu=file_menu)
        file_menu.add_command(label='Exit', command=self.on_exit)
        menubar.add_cascade(label='Pages', menu=pages_menu)
        pages_menu.add_command(label='Pages 1', command=self.page_1.show)
        pages_menu.add_command(label='Page 2', command=self.page_2.show)

    def on_exit(self):
        self.quit()


class Page1(LabelFrame):
    def __init__(self, parent):
        LabelFrame.__init__(self, parent)
        self.parent = parent
        self.config(text='This is page 1 label Frame')
        self.sample_text = Label(self, text='You are viewing Page 1')

    def show(self):
        self.pack(fill=BOTH, expand=1)
        self.sample_text.grid(in_=self)
        self.lift()

    def close(self):
        self.pack_forget()


class Page2(LabelFrame):
    def __init__(self, parent):
        LabelFrame.__init__(self, parent)
        self.parent = parent
        self.config(text='This is page 2 label Frame')
        self.sample_text = Label(self, text='You are viewing Page 2')

    def show(self):
        self.pack(fill=BOTH, expand=1)
        self.sample_text.grid(in_=self)
        self.lift()

    def close(self):
        self.pack_forget()


def main():
    root = tk.Tk()
    app = MainWin(root)
    root.mainloop()


if __name__ == '__main__':
    main()
Pouria
  • 5
  • 1
  • 2
  • Do you mean something like [`ttk.tkinter.Notebook`](https://docs.python.org/3.4/library/tkinter.ttk.html?highlight=notebook#tkinter.ttk.Notebook)? – TigerhawkT3 Jan 13 '16 at 10:49
  • not exactly @TigerhawkT3. I need a solution for navigation via a top level menubar and menu buttons for now. – Pouria Jan 13 '16 at 10:56
  • if you prefer `pack_forget()` then use it and you can delete this question. If you need something more then create `PagesManager(Frame)` which use `pack()` and `pack_forget()` to show/hide frames. You get something similar to `Notebook`. – furas Jan 13 '16 at 12:23
  • btw: I think you can use menubar to navigate in `Notebook` too. – furas Jan 13 '16 at 12:26
  • "better" and "best" are highly subjective. – Bryan Oakley Jan 13 '16 at 15:00
  • out of curiosity, why do you want to avoid using `lift`? Are you concerned about the memory of the hidden pages, or perhaps concerned about the startup performance of creating all of the pages ahead of time? – Bryan Oakley Jan 13 '16 at 15:20
  • yes @BryanOakley, I thought that perhaps having those pages stacked on top of each other could lead to memory issue. of course this is just a suspicion. again, I know that 'gird_remove()' or 'pack_forget()' also just hide the widgets and don't destroy them so probably they are no different in terms of memory issues. but still I feel removing the pages is the _cleaner_ approach! – Pouria Jan 13 '16 at 16:56

1 Answers1

2

There's already a question and answer that shows how to stack frames. To switch to a mode where you use grid_forget or pack_forget you only have to change the code that calls lift to instead call the appropriate "forget" method on the current page (which you'll need to keep track of), and then add the new window.

If you want to create the pages on demand, and destroy them when they aren't in use, that's easy too. The only real difference is that you don't create the page until it is asked for, and delete it when you are done. Otherwise the implementation is identical.

Following is an example of creating the pages on demand. Starting with the code in this answer, modify the SampleApp class to look like this:

class SampleApp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        # the container is where we'll pack the current page
        self.container = tk.Frame(self)
        self.container.pack(side="top", fill="both", expand=True)
        self.current_frame = None

        self.show_frame("StartPage")

    def show_frame(self, page_name):
        '''Show a frame for the given page name'''

        # destroy the old page, if there is one
        if self.current_frame is not None:
            self.current_frame.destroy()

        # create the new page and pack it in the container
        cls = globals()[page_name]
        self.current_frame = cls(self.container, self)
        self.current_frame.pack(fill="both", expand=True)
Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685