Home > Enterprise >  How to reach and modify child class's variables from parent class when child class is one of ma
How to reach and modify child class's variables from parent class when child class is one of ma

Time:09-08

I am writing a Python GUI application with Tkinter where I have several frames that I want to manage separately. I want to put these "child" frames in separate classes (and ultimately in different files) to make the overall code more manageable. Each child class is basically a Tkinter frame with input elements. Based on selections from the main GUI, the relevant child class frame is shown. This is achieved using container and tkraise(). I want to reach child class variables from the main class but I cannot with my current code which is given below. I believe there is a problem with the initialization of child classes and/or the inheritance scheme of my app.

What is the correct way to structure a Python application in a setting where you have child classes being shown with container and tkraise() scheme and you want to reach child class variables form the main class? I appreciate your help.

import tkinter as tk
from tkinter import ttk
import math

 

# Padding values.
tab_padx = (10, 0)
tab_pady = (20, 0)

# Font settings.
font_1 = ("Arial", 13, "bold")

# Main class.
class Main_GUI(tk.Tk):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.title("DEMO")

        self.frame_blue_circle = BlueCircle(self, self)
        self.frame_green_square = GreenSquare(self, self)


        # Available shapes.
        self.available_shapes = ["CIRCLE", "SQUARE"]
        # Available colors.
        self.available_colors = ["BLUE", "GREEN"]

        # Function to run when color is changed.
        def color_change(*args):
            self.color = self.option_var_color.get()
            if self.color == "BLUE" and self.shape == "CIRCLE":
                self.type = "BlueCircle"
                self.show_frame("BlueCircle")
            elif self.color == "GREEN" and self.shape == "SQUARE":
                self.show_frame("GreenSquare")
            else:
                self.show_frame("Unimplemented")
            print(f"{self.color} {self.shape}")

        # Function to run when shape is changed.
        def shape_change(*args):
            self.shape = self.option_var_shape.get()
            if self.color == "BLUE" and self.shape == "CIRCLE":
                self.show_frame("BlueCircle")
            elif self.color == "GREEN" and self.shape == "SQUARE":
                self.show_frame("GreenSquare")
            else:
                self.show_frame("Unimplemented")
            print(f"{self.color} {self.shape}")


        #GUI tabs
        self.nb = ttk.Notebook(self)
        self.nb.grid(row=1, column=0, sticky="w", padx=10, pady=10)

        #GUI tab1 - Type selection.
        self.tab1 = tk.Frame(self.nb)
        self.nb.add(self.tab1, text="Type")

        #GUI tab2 - Unput for selected type.
        self.tab2 = tk.Frame(self.nb)
        self.nb.add(self.tab2, text="Input")

        #GUI tab3 - Calculate result for selected type with its specific inputs.
        self.tab3 = tk.Frame(self.nb)
        self.nb.add(self.tab3, text="Result")

        # Tab-1 types.
        # Shapes.
        self.Label_shape = tk.Label(self.tab1, text = "Shape: ", font=font_1)
        self.Label_shape.grid(row=10, column=0, padx=tab_padx, pady=tab_pady, sticky="W")

        # Setup variable for disk type dropdown menu.
        self.option_var_shape= tk.StringVar()
        self.option_var_shape.set(self.available_shapes[0])
        self.option_var_shape.trace("w", shape_change)
        self.shape = self.option_var_shape.get()

        self.shape_dropdown_menu = tk.OptionMenu(self.tab1, self.option_var_shape, *self.available_shapes)
        self.shape_dropdown_menu.grid(row=10, column=1, sticky="WE", padx=tab_padx, pady=tab_pady)
        self.shape_dropdown_menu.config(font=font_1, width=20)
        self.shape_dropdown_menu["menu"].config(font=font_1)


        # Colors.
        self.Label_color = tk.Label(self.tab1, text = "Color: ", font=font_1)
        self.Label_color.grid(row=20, column=0, padx=tab_padx, pady=tab_pady, sticky="W")

        # Setup variable for disk type dropdown menu.
        self.option_var_color= tk.StringVar()
        self.option_var_color.set(self.available_colors[0])
        self.option_var_color.trace("w", color_change)
        self.color = self.option_var_color.get()

        self.color_dropdown_menu = tk.OptionMenu(self.tab1, self.option_var_color, *self.available_colors)
        self.color_dropdown_menu.grid(row=20, column=1, sticky="WE", padx=tab_padx, pady=tab_pady)
        self.color_dropdown_menu.config(font=font_1, width=20)
        self.color_dropdown_menu["menu"].config(font=font_1)


        # Tab-2. Show frame based on selection in Tab-1.
        # Container for frames.
        container = tk.Frame(self.tab2)
        container.grid(row=0, column=0)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

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

        self.show_frame("BlueCircle")


        # Tab-3. Calculate and display result based on Tab-1 and Tab-2.
        # Label to display result.
        result_text = "Result will be displayed here."
        self.Label_result = tk.Label(self.tab3, text = result_text, font=font_1, fg="RED")
        self.Label_result.grid(row=10, column=0, padx=tab_padx, pady=tab_pady, sticky="W")

        self.button = tk.Button(self.tab3, text=f"Print", command=self.print_info)
        self.button.grid(row=20, column=0, sticky="W")

        # print(self.Label_result)



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

    def print_info(self):
        bc_text = f"Blue circle radius: {self.frame_blue_circle.radius}"
        print(bc_text)



# Class defining GUI for BlueCircle.
class BlueCircle(tk.Frame):

    def __init__(self, parent, controller, *args, **kwargs):

        super().__init__(*args, **kwargs)

        self.parent = parent
        self.radius = 0

        # Function to run when rim radius is changed.
        def Entry_change(*args):
            value = self.Entry_var_radius.get()
            
            if value == "":
                self.Entry_var_radius.set(".0")
            else:
                try:
                    self.radius = float(value)
                    print(self.radius)
                except ValueError:
                    self.Entry_var_radius.set("")
                    print(f"Warning! Floating point number only!")


        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.label = tk.Label(self, text="Blue Circle", font=font_1, fg="BLUE")
        self.label.grid(row=0, column=0)
        self.label = tk.Label(self, text="Radius:")
        self.label.grid(row=1, column=0)

        # Setup variable for entry to use in callback trace.
        self.Entry_var_radius = tk.StringVar()
        self.Entry_var_radius.trace("w", lambda name, index, mode, sv=self.Entry_var_radius: Entry_change(self.Entry_var_radius))
        # Entry.
        self.Entry_radius = tk.Entry(self, font=font_1, textvariable=self.Entry_var_radius)
        self.Entry_radius.grid(row=1, column=1)
        self.radius = self.Entry_radius.get()


 
# Class defining GUI for GreenSquare.
class GreenSquare(tk.Frame):

    def __init__(self, parent, controller):

        super().__init__()
        self.parent = parent

        # Function to run when rim radius is changed.
        def Entry_change(*args):
            value = self.Entry_var_lenght.get()
            if value == "":
                self.Entry_var_lenght.set(".0")
            else:
                try:
                    self.lenght = float(value)
                    self.green_square_area = self.lenght**2
                    # print(f"Side lenght: {self.lenght}. Area: {self.green_square_area:.2f}")
                except ValueError:
                    self.Entry_var_lenght.set("")
                    print(f"Warning! Floating point number only!")


        # Inıtialize variable.
        self.green_square_area = 0


        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.label = tk.Label(self, text="Green Squire", font=font_1, fg="GREEN")
        self.label.grid(row=0, column=0)
        self.label = tk.Label(self, text="Side lenght:")
        self.label.grid(row=1, column=0)

        # Setup variable for entry to use in callback trace.
        self.Entry_var_lenght = tk.StringVar()
        self.Entry_var_lenght.trace("w", lambda name, index, mode, sv=self.Entry_var_lenght: Entry_change(self.Entry_var_lenght))
        # Entry.
        self.lenght = tk.Entry(self, font=font_1, textvariable=self.Entry_var_lenght)
        self.lenght.grid(row=1, column=1)
        self.lenght = self.Entry_var_lenght.get()


# Class defining GUI for unimplemented options.
class Unimplemented(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.label = tk.Label(self, text="To be implemented...", font=font_1, fg="RED")
        self.label.grid(row=0, column=0)



if __name__ == "__main__":
    app = Main_GUI()
    app.mainloop()

CodePudding user response:

In your __init__() method you have: self.frame_blue_circle = BlueCircle(self, self). This means that anywhere in your Main_GUI class (I mean typically other methods) you can access this and then its radius attribute.

In fact, you already do this here:

    def print_info(self):
        bc_text = f"Blue circle radius: {self.frame_blue_circle.radius}"
        print(bc_text)

CodePudding user response:

Note that self.frame_blue_circle is not the instance of BlueCircle shown inside the notebook, so self.frame_blue_circle.radius is not the one input inside the "Input" tab.

The correct instance should be self.frames['BlueCircle'], so you need to use self.frames['BlueCircle'].radius instead:

def print_info(self):
    bc_text = f"Blue circle radius: {self.frames['BlueCircle'].radius}"
    print(bc_text)
  • Related