Search code examples
pythonpython-3.xtkinterttkbootstrap

the frame are not showing when calling tkraise() from seperate class


I have the test app (below) where navigation buttons are in a separate frame/class: NavBar(). Each frame is also in a separate class as well as the main frame.

When I press the button I want the respective frame to show in the MainView frame.

I have built the below code, and it is not giving me any errors, but the frames are not being raised to the front. Any ideas about what I am doing wrong here?

As you can probably notice I am new to the OOP and Tkinter so any suggestions are welcome. Thanks

enter image description here

import ttkbootstrap as tb
from ttkbootstrap.constants import *

# run this to see all widgets | python -m ttkbootstrap

class NavBar(tb.Frame):
    def __init__(self, parent, *args, **kwargs):
        tb.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        
        MainViewObject = MainView(self)
             
        txt_logo = tb.Label(
            self, 
            text='LOGO', 
            style='TLabel'

        )
        txt_logo.pack(fill=X, pady=30)
        txt_logo.configure(anchor="center")
        
        nav_separator = tb.Separator(
            self, 
            orient='horizontal', 
            style='secondary.horizontal.TSeparator'
        )
        nav_separator.pack(fill=X)
        
        cb_frame1 = tb.Button(
            master= self,
            text="Frame One",
            width=20,
            command= lambda: MainViewObject.show_frame("frameOneView")
        )
        cb_frame1.pack(fill=X, pady=(30, 0), padx = (10))
        
        
        cb_frame2 = tb.Button(
            master= self,
            text="Frame Two",
            command=lambda: MainViewObject.show_frame("frameTwoView")
        )
        cb_frame2.pack(fill=X, pady=(20,0), padx = (10))
        
        
        cb_frame3 = tb.Button(
            master= self,
            text="Frame Three",
            command=lambda: MainViewObject.show_frame("frameThreeView")
        )
        cb_frame3.pack(fill=X, pady=(20,0), padx = (10))
             
class MainView(tb.Frame):
    def __init__(self, parent, *args, **kwargs):
        tb.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        
        container = tb.Frame(self,bootstyle ="info") # **
        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 (frameOneView, frameTwoView, frameThreeView):
             frame_name = F.__name__
             frame = F(container, self) # **
             self.frames[frame_name] = frame # **
             frame.grid(row=0, column=0, sticky="nsew")
          
        self.show_frame("frameOneView")
        #print(self, frame_name)
        
    def show_frame(self, frame_name):
        #print(frame_name)
        frame = self.frames[frame_name]
        #print(frame)
        frame.tkraise()

class frameOneView(tb.Frame):
    def __init__(self, parent, controller):
        tb.Frame.__init__(self, parent)
        self.controller = controller
        
        lb1 = tb.Label(self, text="This is the FRAME 1", bootstyle="inverse-dark")
        lb1.pack(side="top", fill="both", pady=10, expand=True)
        lb1.configure(anchor="center")
        
class frameTwoView(tb.Frame):
    def __init__(self, parent, controller):
        tb.Frame.__init__(self, parent)
        self.controller = controller
        
        lb2 = tb.Label(self, text="This is the FRAME 2", bootstyle="inverse-dark")
        lb2.pack(side="top", fill="both", pady=10, expand=True)
        lb2.configure(anchor="center")

class frameThreeView(tb.Frame):
    def __init__(self, parent, controller):
        tb.Frame.__init__(self, parent)
        self.controller = controller
        
        lb3 = tb.Label(self, text="This is the FRAME 3", bootstyle="inverse-dark")
        lb3.pack(side="top", fill="both", pady=10, expand=True)
        lb3.configure(anchor="center")   
         
class MainApplication(tb.Frame):
    def __init__(self, parent, *args, **kwargs):
        tb.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        
        self.navbar = NavBar(self) #,bootstyle ="info")
        self.navbar.pack(side="left", fill="y")
        
        self.main = MainView(self, bootstyle ="sucess")
        self.main.pack(side="right", fill="both", expand=True)

if __name__ == "__main__":
    root = tb.Window(themename="darkly")
    root.state('zoomed')
    MainApplication(root).pack(side="top", fill="both", expand=True)
    root.mainloop()

Solution

  • You are creating two instances of MainView, and thus two instances of each inner frame. The one you see on the screen and the one triggered by the buttons are two different frames.

    You should create only one instance of MainView. You need to remove the one in NavBar, and have your nav bar reference the other one. It also helps if you create the frames before the navbar so that they exist by the time you create the navbar.

    Step 1: change the order that you create items in MainApplication:

    class MainApplication(tb.Frame):
        def __init__(self, parent, *args, **kwargs):
            tb.Frame.__init__(self, parent, *args, **kwargs)
            self.parent = parent
    
            self.main = MainView(self, bootstyle ="sucess")
            self.navbar = NavBar(self) #,bootstyle ="info")
    
            self.navbar.pack(side="left", fill="y")
            self.main.pack(side="right", fill="both", expand=True)
    

    Step 2: reference this version of MainView inside of NavBar by removing the definition of MainViewObject and replacing all uses with parent.main:

    class NavBar(tb.Frame):
        def __init__(self, parent, *args, **kwargs):
            ...
            cb_frame1 = tb.Button(
                ...,
                command= lambda: parent.main.show_frame("frameOneView")          
            )
            ...