1

I have a gui application where the navbar has buttons to switch between frames. On the startpage there is an entry widget to get a file name and a listbox to display all the saved filenames. When a filename is selected from the listbox another page opens which displays the selected filename.enter image description here

I believe that the answer here creates frame objects in the beginning, stack them and raise whichever frame is called. But I think that my window needs to update every time it is called so that it can display the different filename. Also it is placed at exactly the same position like the other frames so that I can still use to navbar buttons for switching to other home and other windows. I found something similar to this here but I cannot understand it.

(my acutal application is a bit more complex than this but i think that this is a small and precise representation of my problem)

Edit:

import tkinter as tk

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

        container = tk.Frame(self)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(1, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames={}
        for F in (StartPage,PageOne):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame
            frame.grid(row=1, column=0, sticky="nsew")

        self.show_frame("StartPage")

    def show_frame(self, page_name):
        frame = self.frames[page_name]
        frame.tkraise()


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)
        self.list_box = tk.Listbox(self)
        self.list_box.pack()
        for item in ["file 1", "file 2", "file 3"]:
            self.list_box.insert(tk.END, item)
        button1 = tk.Button(self, text='Go to next Page', command=lambda: self.getvalue(controller))
        button1.pack()

    def getvalue(self, controller):
        clicked_item=self.list_box.curselection()
        selected_file=self.list_box.get(clicked_item)
        print(selected_file)
        controller.show_frame('PageOne')

class PageOne(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        # ....selected file name to be displayed here....
        button1 = tk.Button(self, text="Back to Home", command=lambda: controller.show_frame('StartPage'))
        button1.pack()


app = MainApplication()
app.geometry("400x400")
app.mainloop()

  • Relevant [Stack Frames on top of each other](https://stackoverflow.com/a/7557028/7414759) – stovfl Jun 20 '20 at 11:22
  • @stovfl The answer that you suggested works fine but I have one problem, From what I understand what happens is that the frames are stacked on top of each other, then simply raised one above the other in the stacking order. However in my application what I want is that when I switch back from Window 2 to Window 1 the contents of that window are updated or refreshed and not remain the same as before. Do you know how can I do this? – Shwetanshu RAj Jun 21 '20 at 11:58
  • @stovfl I have added the code...I dont know how to pass the selected_file name each time I want to go to PageOne from StartPage – Shwetanshu RAj Jun 22 '20 at 05:44

2 Answers2

2

Question: how to pass the selected_file name each time I want to go to PageOne from StartPage.


Core point:

Overload the class methode Frame.tkraise to update the PageOne widgets on every call of .tkraise from the controller

Note: I show only the changed parts of your example code, compare carefully and copy to your real code.


  1. No changes in class MainApplication(tk.Tk):

    class MainApplication(tk.Tk):
       ...
    
  2. class StartPage(tk.Frame):

    class StartPage(tk.Frame):
        def __init__(self, parent, controller):
            ...
            .Button(..., command=lambda: controller.show_frame('PageOne'))
    
        def getvalue(self):
            ...
            return selected_file
    
    
  3. class PageOne(tk.Frame):

    class StartPage(tk.Frame):
        def __init__(self, parent, controller):
            tk.Frame.__init__(self, parent)
            self.controller = controller
            # ....selected file name to be displayed here....
            self.label = tk.Label(self, text='')
            self.label.pack()
    
        def tkraise(self, aboveThis=None):
            # Get a reference to StartPage
            start_page = self.controller.frames['StartPage']
    
            # Get the selected item from start_page
            self.label.configure(text=start_page.getvalue())
    
            # Call the real .tkraise
            super().tkraise(aboveThis)
    
    

Alternatives:

stovfl
  • 14,998
  • 7
  • 24
  • 51
1

#Multiple Screens

from tkinter import *

class screen(Frame):
    """
    A screen is area
    for content in a program
    """
    def __init__(self,master,name):
        Frame.__init__(self,master)
        #Attributes
        self.master=master
        self.name=name
        #Initalise with master
        self.master.addScreen(self)
    def show(self):
        """
        Method will show screen
        """
        self.master.showScreen(self.name)

class screenController(Frame):
    """
    Screen Controller
    will manage screens 
    in the program
    """
    def __init__(self,parent):
        Frame.__init__(self,parent)
        #Configure
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)
        #Attributes
        self.allScreens={}
        self.currentScreen=None

    def addScreen(self,screenObject):
        """
        Adds a Screen object to the screenController
        """
        #Place the screen
        screenObject.grid(row=0, column=0, sticky="nsew")
        #Add to dictionary
        self.allScreens[screenObject.name]=screenObject

    def showScreen(self,screenName):
        if screenName in self.allScreens:
            #Display
            self.allScreens[screenName].tkraise()
            #Update variable
            self.currentScreen=screenName
            
    
#Create a Tkinter Window
window=Tk()
window.title("Multiple Screens")
window.geometry("400x300")
window.columnconfigure(0,weight=1)
window.rowconfigure(1,weight=1)


#Create a Controller for the screens
screenMaster=screenController(window)
screenMaster.grid(row=1,column=0,sticky="NSEW")

#Create SCREEN 1
screen1=screen(screenMaster, "S1")
Label(screen1,text="This is screen 1").grid(row=0,column=0) 
screen1.config(bg="red")

#Create SCREEN 2
screen2=screen(screenMaster, "S2")
Label(screen2,text="This is screen 2").grid(row=0,column=0) 
screen2.config(bg="blue")

#Create SCREEN 3
screen3=screen(screenMaster, "S3")
Label(screen3,text="This is screen 3").grid(row=0,column=0) 
screen3.config(bg="green")

#Create a navigation bar
navBar=Frame(window)
navBar.grid(row=0,column=0,sticky="EW")
navBar.config(bg="#F1F0F2")

b1=Button(navBar,text="Screen 1",command=lambda: screen1.show())
b1.grid(row=0,column=0)

b1=Button(navBar,text="Screen 2",command=lambda: screen2.show())
b1.grid(row=0,column=1)

b1=Button(navBar,text="Screen 3",command=lambda: screen3.show())
b1.grid(row=0,column=2)


#Show screen 1 by default
screen1.show()

window.mainloop()

This is the way in which I would use OO Programming to solve this, the navigation bar would remain static whichever "Screen" you were on and by simply raising the screens above each other you don't need to keep creating new objects and save a lot of memory.

This solution is also very modular and simply allows you to add screens as if they were Tkinter Frames.

A.Goody
  • 71
  • 1