Home > other >  How to get instance attribute value from parent class using OOP? Tkinter, Python
How to get instance attribute value from parent class using OOP? Tkinter, Python

Time:02-11

I try to add a custom message box using the class MessageboxYesNo. But when trying to do so I get an attribute error from the parent class.

import tkinter as tk
from sys import platform

class MainApp(tk.Frame):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.parent = parent
        self.parent.title("My app")
        self.set_app_size_and_position()

        self.messagebox1 = MessageboxYesNo(self.parent, "QUIT", "Are you sure you want to quit?")
   
    def set_app_size_and_position(self):
        # get screen width and height
        self.screen_width = int(self.parent.winfo_screenwidth()) 
        self.screen_height = int(self.parent.winfo_screenheight())
        
        self.app_width_mac = int(self.screen_width/2)
        self.app_height_mac = int(self.screen_height)
        self.app_x_pos_mac = int(self.screen_width/2)
        self.app_y_pos_mac = 0

        if platform == "darwin":
            self.parent.geometry(f"{self.app_width_mac}x{self.app_height_mac} {self.app_x_pos_mac} {self.app_y_pos_mac}")

class MessageboxYesNo(MainApp):
    def __init__(self, parent, title, text):
        MainApp.__init__(self, parent)
        #inherits the MainApp to be able to get the parent's instance variable value from app_x_pos_mac and app_y_pos_mac
        
        self.parent = parent
        
        self.title = title
        self.text = text
        
        self.toplevel = tk.Toplevel(self.parent)
        self.toplevel.title(self.title)
        
        #set x- and y-coordinates for centering the messagebox
        self.x_pos_messagebox = int(self.app_x_pos_mac (self.app_x_pos_mac/2))
        self.y_pos_messagebox = 0

        #position the messagebox in the center of the app
        self.toplevel.geometry(f"500x300 {self.x_pos_messagebox} {self.y_pos_messagebox}") 
    
        #add labels and buttons to messagebox
        self.l1=tk.Label(self.toplevel, image="::tk::icons::question")
        self.l1.grid(row=0, column=0, pady=(7, 0), padx=(10, 30), sticky="e")

        self.l2=tk.Label(self.toplevel,text=self.text)
        self.l2.grid(row=0, column=1, columnspan=3, pady=(7, 10), sticky="w")
    
        self.b1=tk.Button(self.toplevel,text="Yes",width = 10)
        self.b1.grid(row=1, column=1, padx=(2, 35), sticky="e")

        self.b2=tk.Button(self.toplevel,text="No", width = 10)
        self.b2.grid(row=1, column=2, padx=(2, 35), sticky="e")  
           
def main():
    root = tk.Tk()
    app = MainApp(root)
    Driver(app.parent)
    app.pack()
    
    root.mainloop()
      
if __name__ == "__main__":
    main()

I get this traceroute below. It seems like the parent that gets passed to MainApp.init(self, parent) method in the MessageboxYesNo class, gets confused somehow.

Traceback (most recent call last):
  File "//AcademyApp.py", line 307, in <module>
    main()
  File "//AcademyApp.py", line 300, in main
    app = MainApp(root)
  File "//AcademyApp.py", line 78, in __init__
    self.messagebox1 = MessageboxYesNo(self.parent, "QUIT", "Are you sure you want to quit?")
  File "//AcademyApp.py", line 263, in __init__
    MainApp.__init__(self, parent)
  File "//AcademyApp.py", line 68, in __init__
    self.parent.title("My app")
AttributeError: 'Frame' object has no attribute 'title'

It seems that the self.parent in the class MessageboxYesNo gets confused with the self.parent in class MainApp. How can we solve this?

If I try the code without any of my other app code. I get this error instead:

File "/.py", line 12, in __init__
    self.messagebox1 = MessageboxYesNo(self.parent, "QUIT", "Are you sure you want to quit?")
  File "/.py", line 29, in __init__
    MainApp.__init__(self, parent)
  File "/.py", line 12, in __init__
    self.messagebox1 = MessageboxYesNo(self.parent, "QUIT", "Are you sure you want to quit?")
  File "/.py", line 29, in __init__
    MainApp.__init__(self, parent)
  File "/.py", line 7, in __init__
    tk.Frame.__init__(self, parent, *args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tkinter/__init__.py", line 3116, in __init__
    cnf = _cnfmerge((cnf, kw))
  File "/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/tkinter/__init__.py", line 105, in _cnfmerge
    elif isinstance(cnfs, (type(None), str)):
RecursionError: maximum recursion depth exceeded in __instancecheck__

It is like it is looping, but don't know why?

If I change the MessageboxYesNo class to this it works, but then I can't figure out how to get the value from the instance variable app_x_pos_mac?

class MessageboxYesNo(tk.Frame):
    def __init__(self, parent, title, text):
        tk.Frame.__init__(self, parent)
        
        self.parent = parent
        
        self.title = title
        self.text = text
        
        self.toplevel = tk.Toplevel(self.parent)
        self.toplevel.title(self.title)
        
        print(self.parent.app_x_pos_mac) #this can't get the variable value from the MainApp class

CodePudding user response:

It appears the reason it is confusing the parents is because you're passing MainApp's parent to MessageboxYesNo. Instead, write:

self.messagebox1 = MessageboxYesNo(self, "QUIT", "Are you sure you want to quit?")

But you will also need to remove the MainApp.__init__(self, parent) line from your MessageboxYesNo class, otherwise it will continue to recursively loop.

  • Related