title: Switch from page to page with Menubar in Tkinter
body: <p>I have some issues with my tkinter IHM :
  I want to be able to switch from one frame to another with a menu bar that always stays in place.
  I tried Bryan Oakley's method. It is Ok with simple widgets Pages, but not with Pages involving more complex widgets.</p>
  <p>In my example : ZoomConstructorPage is always visible, never hidden by other pages.
  Moreover, I don't know how to include my Zoom_constructor_Frame widget in a PanedWindow.</p>
  <p>I want StartPage to be displayed when opening the App, then switching to PageTwo when clicking on 'Charger un jeu de données', or displaying ZoomConstructorPage in a PanedWindow when clicking on 'Définir un zoom'</p>
  <p>I'm new in tkinter, any help would be greatly appreciated.</p>
  <p>Here my main code : `</p>
  <pre><code>import tools
  import tkinter as tk
  from tkinter import ttk
  from tkinter import messagebox
  from tkinter import filedialog
      
      
  ###############################################################################        
  
  
  class MainWindow(tools.FullscreenWindow):
  &quot;&quot;&quot; Fenêtre principale &quot;&quot;&quot;
      def __init__(self, title, *args, **kwargs):
          super().__init__(*args, **kwargs)
          
          self.wm_title(title)
          # self.iconbitmap('drone.ico')
          
          container = ttk.Frame(self)
          container.grid()
          container.grid_rowconfigure(0, weight=1)
          container.grid_columnconfigure(0, weight=1)
                          
          # Affiche la Barre de Menu      
          '''MenuBar(self).grid()
          self.columnconfigure(0, weight=1)'''
          
  ###############################################################################
          # Barre de Menu principal
          menu_bar = tk.Menu(self)
          menu_bar.add_command(label='Traiter nouveau fichier', 
                               command=self.explorer_fichier)
          
          
          menu_bar.add_command(label='Charger un jeu de données', 
                               command=self.charger_data)
          
          
          menu_bar.add_command(label='Définir un zoom', 
                               command=self.definir_zoom)
          
          
          # Sous menu liste des enregistrements
          submenu1 = tk.Menu(menu_bar, tearoff=0)
          menu_bar.add_cascade(label='Enregistrements', menu=submenu1)
          submenu1.add_command(label='Liste complète')
          
                  
          # Quitter l'application
          menu_bar.add_command(label='Quitter', 
                               command=self.close_application)
                         
          
          # Affiche le Menu 
          self.config(menu=menu_bar)
  ###############################################################################        
          
          # Déroule les différentes pages        
          self.frames = {}    
          for F in (StartPage, ZoomConstructorPage, PageTwo):
              frame = F(container, self)
              self.frames[F] = frame
              frame.grid(row=0, column=0, sticky=&quot;nsew&quot;)            
  
          self.show_frame(StartPage)
  
  
      def show_frame(self, cont):
          frame = self.frames[cont]
          frame.tkraise()
                 
          
      def inscrire(self):
          messagebox.showinfo(&quot;Info&quot;, &quot;Wait a minute&quot;)
          
          
      def explorer_fichier(self):
          file_path = filedialog.askopenfilename()
          print(file_path)
  
          
      def close_application(self):    
          reponse = messagebox.askyesnocancel(&quot;Quit&quot;, 
                    &quot;êtes vous sûr de vouloir quitter l'application ?&quot;)
          if reponse:
            self.destroy()
            
            
      def charger_data(self):
          self.show_frame(PageTwo)
      
      
      def definir_zoom(self):        
          self.show_frame(ZoomConstructorPage)
          
      
      
            
  class Zoom_constructor_Frame(ttk.Frame):
      &quot;&quot;&quot; Frame et controller des 2 Scales qui permettent de définir une section
      zoommée&quot;&quot;&quot;
      def __init__(self, parent, xmin, xmax, *args, **kwargs):
          super().__init__(*args, **kwargs)
          
          self.parent = parent
          
          label = ttk.Label(text='Choisir le début et la fin de la section à zoomer')
          self.start_slider = tools.Slider(parent, self, 'start', xmin, xmin, xmax, 
                                     name='Début du zoom')
          self.end_slider = tools.Slider(parent, self, 'end', xmax, xmin, xmax,
                                   name='Fin du zoom')
          
          label.grid(column=0, row=0, pady=5)
          self.start_slider.grid(column=0, row=1, pady=8)
          self.end_slider.grid(column=0, row=2, pady=8)
  
          
  
      def slider_command(self, command_parameter):
          &quot;&quot;&quot; commande les deux sliders &quot;&quot;&quot;
          # Récupère les valeurs des 2 sliders
          self.get_sliders_value()
          
          print('\nentrée', command_parameter, self.new_start_value, self.new_end_value)
          
          if command_parameter == 'start':            
              self.border_start_value()
              
          if command_parameter == 'end':
              self.border_end_value()
          
          # Récupére les valeurs éventuellement actualisées
          self.get_sliders_value()
          
      
      def get_sliders_value(self):
          &quot;&quot;&quot; Récupère les valeurs actualisées des 2 sliders &quot;&quot;&quot;
          self.new_start_value = self.start_slider.value.get()
          self.new_end_value = self.end_slider.value.get()
          
      
      def border_start_value(self):
          &quot;&quot;&quot; Limite la valeur de start_value à end_value - 1 &quot;&quot;&quot;
          if self.new_start_value &gt; self.new_end_value:
              self.start_slider.value.set(self.new_end_value - 1)
             
              
      def border_end_value(self):
          &quot;&quot;&quot;Limite la valeur de end_value à start_value + 1 &quot;&quot;&quot;        
          if self.new_start_value &gt; self.new_end_value:
              self.end_slider.value.set(self.new_start_value + 1)
  
  ###############################################################################            
  #   *****   PAGES   *****
  class StartPage(tk.Frame):
      def __init__(self, parent, controller):
          super().__init__(parent)
          label = tk.Label(self, text=&quot;Start Page  This is a Start Page&quot;)
          label.pack(pady=10, padx=10)        
  
  
  class ZoomConstructorPage(tk.Frame):
      def __init__(self, parent, controller):
          super().__init__(parent)
          
          # Valeurs à définir avec la data
          self.start_value = 0
          self.end_value = 5850
          
          label = tk.Label(self, text=&quot;This is Zoom ConstructPage&quot;)
          label.grid(column=0, row=0, pady=8)
          
          
          # Affiche la fenêtre de gauche              
          my_paned_window_1 = tk.PanedWindow()
          my_paned_window_1.grid(column=1, row=1)
          my_paned_window_2 = tk.PanedWindow(my_paned_window_1, orient=tk.VERTICAL)
          my_paned_window_1.add(my_paned_window_2)
          
          self.left_frame = Zoom_constructor_Frame(my_paned_window_2,
                                            self.start_value, 
                                            self.end_value,
                                            height=400, width=500,
                                            relief='groove', borderwidth=4)
          
          my_paned_window_2.add(self.left_frame)
          bottom_pane_text = tk.Text(my_paned_window_2, height=30, width=60)
          my_paned_window_2.add(bottom_pane_text)
          right_pane_text = tk.Text(my_paned_window_1, height=30, width=3)
          my_paned_window_1.add(right_pane_text)
                  
              
  
  class PageTwo(tk.Frame):
  
      def __init__(self, parent, controller):
          tk.Frame.__init__(self, parent)
          label = tk.Label(self, text=&quot;Page Two&quot;)
          label.pack(pady=10, padx=10)
  
          button1 = tk.Button(self, text=&quot;Back to Home&quot;,
                          command=lambda: controller.show_frame(StartPage))
          button1.pack()
          button2 = tk.Button(self, text=&quot;Page One&quot;,
                          command=lambda: controller.show_frame(ZoomConstructorPage))
          button2.pack()
  ###############################################################################
  
          
  if __name__ == '__main__':
      app = MainWindow(title='Instrument analyser')
      app.mainloop()
  </code></pre>
  <p>and some classes from tools module :</p>
  <pre><code>class FullscreenWindow(tk.Tk):
      &quot;&quot;&quot; Une Window qui se met directement en plein écran (wm_state = 'zoomed')
      et que l'on peut réduire par touche &quot;escape&quot;, rezoomer par F11 &quot;&quot;&quot;
      
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
          #self.parent = parent
          self.initUI()
          
  
      def initUI(self):       
          #self.pack(fill=&quot;both&quot;, expand=True, side=&quot;top&quot;)
          self.grid()
          self.bind(&quot;&lt;F11&gt;&quot;, self.fullscreen_toggle)
          self.bind(&quot;&lt;Escape&gt;&quot;, self.fullscreen_cancel)
          self.fullscreen_toggle()
          
  
      def fullscreen_toggle(self, event=&quot;none&quot;):
          self.wm_state('zoomed')
          self.focus_set()
          #self.parent.wm_attributes(&quot;-topmost&quot;, 1)
          
  
      def fullscreen_cancel(self, event=&quot;none&quot;):        
          self.reduceWindow()
          self.wm_state('normal')
          #self.parent.wm_attributes(&quot;-topmost&quot;, 0)
          
  
      def reduceWindow(self):
          &quot;&quot;&quot; Récupére largeur et hauteur d'écran. Réduit la taille de la 
          fenêtre à 70% &quot;&quot;&quot;
          sw = self.winfo_screenwidth()
          sh = self.winfo_screenheight()
          
          w = sw*0.7
          h = sh*0.7
          
          x = (sw-w)/2
          y = (sh-h)/2
          
          self.geometry(&quot;%dx%d+%d+%d&quot; % (w, h, x, y))
  
  
  class Slider(tk.Frame):
      &quot;&quot;&quot; Associe un Entry et un Scale, dont les valeurs sont liées à un 
      IntVar unique : value. Les modifications de value sont pris en compte en
      temps réel et retournées au widget controller par la fonction slider_command &quot;&quot;&quot;
      def __init__(self, parent, controller, command_parameter, 
                   start, xmin, xmax, 
                   orientation='horizontal', name='Slider', *args, **kwargs):
          super().__init__(*args, **kwargs)
          
          if start &lt; xmin or start &gt; xmax:
              raise ValueError(&quot;Start is out of bounds&quot;)
              
          self.parent = parent
          self.controller = controller
          self.command_parameter = command_parameter
          self.start = start
          self.min = xmin
          self.max = xmax
          self.orientation = orientation
          self.name = name
          self.value = tk.IntVar()
          
          # Calcul de l'intervalle entre 2 ticks pour le Scale 
          nb_data = self.max - self.min
          nb_interval = 5
          tickinterval = tick_interval(nb_data, nb_interval)
          
          # Création des deux widgets
          self.entry = ttk.Entry(self, width= 10, textvariable=self.value)
          
          self.slider = tk.Scale(self, 
                            from_=self.min, 
                            to=self.max, 
                            orient=self.orientation,
                            resolution=1, 
                            tickinterval=tickinterval, 
                            length=300, 
                            variable=self.value,
                            label=self.name,
                            command = self.slider_command)
          
          # Initialisation de la valeur de départ               
          self.slider.set(self.start)        
          
          # Affichage des widgets
          self.entry.grid(column=0, row=0, sticky='e', padx=10)        
          self.slider.grid(column=0, row=1, columnspan=1, padx=8)       
          #self.entry.pack()
          #self.slider.pack()
          
      def slider_command(self, value):
          self.controller.slider_command(self.command_parameter)
  
  
  
  
          
  def tick_interval(x, nb_ticks=10):
      ''' Calcul l'intervalle entre 2 ticks pour un ensemble de x data, arrondi
      à la puissance de 10 la plus proche '''
      # intervalle réel
      interval = x/nb_ticks
      # nombre de chiffres de l'intervalle
      nb_digit = len(str(interval))
      # puissance de dix la plus proche
      power = 10**(nb_digit - 3)
      # intervalle arrondi à la puissance de 10 la plus proche
      interval = round(interval/power) * power    
      return interval
  </code></pre>