Search code examples
pythontkintercustomtkinter

How to access methods from a parent class which enables the frame swapping?


This is the window provides the container and methods which allow frame swapping:

class Login_Window(ctk.CTk):
    def __init__(self, *args, **kwargs):
        super().__init__()

        self.geometry('400x400')
        self.title('Music Mayhem')
        self.resizable(False, False)

        container = ctk.CTkFrame(master=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 (LoginFrame, RegEmailFrame):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(row=0, column=0, sticky= 'nsew')
            frame.grid_columnconfigure(0,weight=1)
            frame.grid_rowconfigure(0,weight=1)
             

        self.show_frame(LoginFrame) 

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

In order to swap the frames, a button has to be created in the frame that is going to be swapped. How would i go about creating an instance of another class within these frames which will also call the show_frame method? Here is the code for the frames- this could be ran as long as you have tkinter and custom tkinter installed. The only aspect that should supposedly not work are the buttons in the menu frame. Yes, in this situation the menu frame is not needed but this is just a simple example because the actual code is way too long to be included here. I have tried adding the menu frame into the list of frames to be swapped (in the class above) and giving it the same parent and controller attributes as the other frame but that required a parent and controller argument to be passed through when it is called in the Login and Register frames.

Is there a way to get round this or a simpler method that could be implemented instead?

class LoginFrame (tk.Frame):
    def __init__(self,parent, controller):
        tk.Frame.__init__(self, parent)

        self.menu = Menu(self)
        self.menu.grid(row=0, column=0) 

 
        self.loginBtn = ctk.CTkButton(master=self, width=100, height = 20,text='Login', 
                                state='normal',
                                command=lambda: controller.show_frame(RegEmailFrame)  
        self.loginBtn.grid(row=1, column=0)

class RegEmailFrame(tk.Frame):
    def __init__(self, parent, controller,header_name="Register Email"):
        tk.Frame.__init__(self, parent)

        self.menu = Menu(self)
        self.menu.grid(row=0, column=0)
    
        self.emailLabel = ctk.CtKLabel(master=self,width=100, height=20 text='Frame swapped') 
        self.emailLabel.grid(row=1, column=0) 


class Menu(tk.Frame):  
        def __init__(self, *args, header_name="Logo Frame",
                     width=175, height=175,**kwargs):
                super().__init__(*args, width=width, height=height, **kwargs)

            self.menuloginBtn = ctk.CTkButton(master=self, width=100, height = 20,text='Login', 
                                    state='normal',
                                    command=lambda: controller.show_frame(LoginFrame)  
            self.menuloginBtn.grid(row=0, column=0)

            self.menuRegBtn = ctk.CTkButton(master=self, width=100, height = 20,text='Login', 
                                    state='normal',
                                    command=lambda: controller.show_frame(RegEmailFrame)  
            self.menuRegBtn.grid(row=1, column=0)

Solution

  • In the current implementation, the Menu class does not have access to the controller object that is used to switch between frames in the Login_Window class. One way to fix this would be to pass the controller object to the Menu class during instantiation. You can do this by adding a parameter called controller in the Menu class constructor and then passing it as an argument when creating an instance of the Menu class in the LoginFrame and RegEmailFrame classes.

    For example, in the LoginFrame class:

    def __init__(self,parent, controller):
        tk.Frame.__init__(self, parent)
    
        self.menu = Menu(self, controller)
        self.menu.grid(row=0, column=0) 
    

    And in the Menu class constructor:

    def __init__(self, parent, controller, *args, header_name="Logo Frame",
                 width=175, height=175,**kwargs):
        super().__init__(parent, *args, width=width, height=height, **kwargs)
        self.controller = controller
    

    With this changes, the Menu class now has access to the controller object and can use it to switch between frames using the show_frame method.

    You should also make the same changes in the RegEmailFrame class and in the constructor of the Menu class.

    Hope this helps!