Search code examples
python-3.xclasstkinterinstance-variables

How to get an attribute from another class´ instance?


I have been struggling for a week with an oop in Python while writing a tkinter application. I have simplified the app to show the problem only. I have two frames and each of them has two buttons - the button in the first row increases the frames´ number (self.top_frame_number or self.bottom_frame_number and the button in the second row should get me a number from the other frame (so the button in the top frame should get me the number from the bottom frame).

I can´t figure out how to accomplish this, this means I don´t know how to access a class´ instance attribute from another class´ instance, while both of them are attributes of the main Application class.

I´ve been searching through questions but haven´t found similar example and the answers to another questions didn´t help me in my struggle. Here is the whole code:

import tkinter as tk

class TopFrame:
    def __init__(self, parent):
        self.parent = parent
        self.topframe = tk.LabelFrame(self.parent, text="Top frame", width=300, height=100, bd=2)
        self.topframe.pack(side="top", fill="both")

        self.top_frame_number = 100

        # FIRST ROW WIDGETS
        self.topframe_tfn_label = tk.Label(self.topframe, text="number: ")
        self.topframe_tfn_label.grid(row=0, column=0, sticky="w")

        self.topframe_tfn = tk.Label(self.topframe)
        self.topframe_tfn.grid(row=0, column=1)
        self.topframe_tfn.configure(text=self.top_frame_number)

        self.topframe_tfn_button = tk.Button(self.topframe,
                                         text="increase",
                                         command=lambda: self.topframe_increase_top_frame_number())
        self.topframe_tfn_button.grid(row=0, column=2, pady=2, sticky="w")

        # SECOND ROW WIDGETS
        self.topframe_bfn_label = tk.Label(self.topframe, text="bottom frame number: ")
        self.topframe_bfn_label.grid(row=1, column=0, sticky="w")

        self.topframe_bfn = tk.Label(self.topframe)
        self.topframe_bfn.grid(row=1, column=1)
        self.topframe_bfn.configure(text="?")

        self.topframe_bfn_button = tk.Button(self.topframe,
                                         text="get bottom frame number",
                                         command=lambda: self.topframe_get_bottom_frame_number())
        self.topframe_bfn_button.grid(row=1, column=2, pady=2, sticky="e")

    def topframe_increase_top_frame_number(self):
        self.top_frame_number += 1
        self.topframe_tfn.configure(text=self.top_frame_number)

    def topframe_get_bottom_frame_number(self):
        pass
        # I AM STUCK HERE - How to get to the top frame number?

class BottomFrame:
    def __init__(self, parent):
        self.parent = parent
        self.bottomframe = tk.LabelFrame(self.parent, text="Bottom frame", width=300, height=100, bd=2)
        self.bottomframe.pack(side="top", fill="both")

        self.bottom_frame_number = 200

        # FIRST ROW
        self.bottomframe_tfn_label = tk.Label(self.bottomframe, text="number: ")
        self.bottomframe_tfn_label.grid(row=0, column=0, sticky="w")

        self.bottomframe_tfn = tk.Label(self.bottomframe)
        self.bottomframe_tfn.grid(row=0, column=1)
        self.bottomframe_tfn.configure(text=self.bottom_frame_number)

        self.bottomframe_tfn_button = tk.Button(self.bottomframe,
                                            text="increase",
                                            command=lambda: self.bottomframe_increase_bottom_frame_number())
        self.bottomframe_tfn_button.grid(row=0, column=2, pady=2, sticky="e")

        # SECOND ROW
        self.bottomframe_bfn_label = tk.Label(self.bottomframe, text="top frame number: ")
        self.bottomframe_bfn_label.grid(row=1, column=0, sticky="w")

        self.bottomframe_bfn = tk.Label(self.bottomframe)
        self.bottomframe_bfn.grid(row=1, column=1)
        self.bottomframe_bfn.configure(text="?")

        self.bottomframe_bfn_button = tk.Button(self.bottomframe,
                                            text="get top frame number",
                                            command=lambda: self.bottomframe_get_top_frame_number())
        self.bottomframe_bfn_button.grid(row=1, column=2, pady=2, sticky="e")

    def bottomframe_increase_bottom_frame_number(self):
        self.bottom_frame_number += 1
        self.bottomframe_tfn.configure(text=self.bottom_frame_number)

    def bottomframe_get_top_frame_number(self):
        pass
        # I AM STUCK HERE - How to get to the top frame number?

class Application:
    def __init__(self, master):
        self.master = master
        self.master.title("Show me numbers!")

        # -- FRAMES --
        self.top_frame = TopFrame(self.master)
        self.bottom_frame = BottomFrame(self.master)

if __name__ == "__main__":
    root = tk.Tk()

    Application(root)
    root.mainloop()

I have been trying to learn the oop from different materials over the web but with no success. So I tried:

  1. inheritance - it somehow worked but I don´t want to use inheritance as TopFrame and BottomFrame classes are equal, not parent-child
  2. composition - I wasn´t able to fix it with the composition (=initialize the other class in __init__ as it lead to an infinite recursion error.

How should I access the number in other class´ instance correctly?


Solution

  • jasonharper has pointed me to the right direction. I added the reference to the master Application class and it works. So the whole code is:

    import tkinter as tk
    
    class TopFrame:
        def __init__(self, parent, master):
            self.parent = parent
            self.master = master
            self.topframe = tk.LabelFrame(self.parent, text="Top frame", width=300, height=100, bd=2)
            self.topframe.pack(side="top", fill="both")
    
            self.top_frame_number = 100
    
            # FIRST ROW
            self.topframe_tfn_label = tk.Label(self.topframe, text="number: ")
            self.topframe_tfn_label.grid(row=0, column=0, sticky="w")
    
            self.topframe_tfn = tk.Label(self.topframe)
            self.topframe_tfn.grid(row=0, column=1)
            self.topframe_tfn.configure(text=self.top_frame_number)
    
            self.topframe_tfn_button = tk.Button(self.topframe,
                                             text="increase",
                                             command=lambda: self.topframe_increase_top_frame_number())
            self.topframe_tfn_button.grid(row=0, column=2, pady=2, sticky="w")
    
            # SECOND ROW
            self.topframe_bfn_label = tk.Label(self.topframe, text="bottom frame number: ")
            self.topframe_bfn_label.grid(row=1, column=0, sticky="w")
    
            self.topframe_bfn = tk.Label(self.topframe)
            self.topframe_bfn.grid(row=1, column=1)
            self.topframe_bfn.configure(text="?")
    
            self.topframe_bfn_button = tk.Button(self.topframe,
                                             text="get bottom frame number",
                                             command=lambda: self.topframe_get_bottom_frame_number())
            self.topframe_bfn_button.grid(row=1, column=2, pady=2, sticky="e")
    
        def topframe_increase_top_frame_number(self):
            self.top_frame_number += 1
            self.topframe_tfn.configure(text=self.top_frame_number)
    
        def topframe_get_bottom_frame_number(self):
            self.topframe_bfn.configure(text=self.master.bottom_frame.bottom_frame_number)
    
    
    class BottomFrame:
        def __init__(self, parent, master):
            self.parent = parent
            self.master = master
            self.bottomframe = tk.LabelFrame(self.parent, text="Bottom frame", width=300, height=100, bd=2)
            self.bottomframe.pack(side="top", fill="both")
    
            self.bottom_frame_number = 200
    
            # FIRST ROW
            self.bottomframe_bfn_label = tk.Label(self.bottomframe, text="number: ")
            self.bottomframe_bfn_label.grid(row=0, column=0, sticky="w")
    
            self.bottomframe_bfn = tk.Label(self.bottomframe)
            self.bottomframe_bfn.grid(row=0, column=1)
            self.bottomframe_bfn.configure(text=self.bottom_frame_number)
    
            self.bottomframe_bfn_button = tk.Button(self.bottomframe,
                                                text="increase",
                                                command=lambda: self.bottomframe_increase_bottom_frame_number())
            self.bottomframe_bfn_button.grid(row=0, column=2, pady=2, sticky="e")
    
            # SECOND ROW
            self.bottomframe_tfn_label = tk.Label(self.bottomframe, text="top frame number: ")
            self.bottomframe_tfn_label.grid(row=1, column=0, sticky="w")
    
            self.bottomframe_tfn = tk.Label(self.bottomframe)
            self.bottomframe_tfn.grid(row=1, column=1)
            self.bottomframe_tfn.configure(text="?")
    
            self.bottomframe_tfn_button = tk.Button(self.bottomframe,
                                                text="get top frame number",
                                                command=lambda: self.bottomframe_get_top_frame_number())
            self.bottomframe_tfn_button.grid(row=1, column=2, pady=2, sticky="e")
    
        def bottomframe_increase_bottom_frame_number(self):
            self.bottom_frame_number += 1
            self.bottomframe_bfn.configure(text=self.bottom_frame_number)
    
        def bottomframe_get_top_frame_number(self):
            self.bottomframe_tfn.configure(text=self.master.top_frame.top_frame_number)
    
    class Application:
        def __init__(self, master):
            self.master = master
            self.master.title("Show me numbers!")
    
            # -- FRAMES --
            self.top_frame = TopFrame(self.master, self)
            self.bottom_frame = BottomFrame(self.master, self)
    
    if __name__ == "__main__":
        root = tk.Tk()
    
        Application(root)
        root.mainloop()
    

    If there is a better way how to reference the Application class from TopFrame or BottomFrame classes without specifying self within Application´s __init__ (this means without self.top_frame = TopFrame(self.master, self) and self.bottom_frame = BottomFrame(self.master, self)) and corresponding reference in TopFrame and BottomFrame classes (self.master = master), I´m open minded...