0

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
3
  • It appears to me that it is looping recursively because you are creating an instance of MessageboxYesNo from MainApp and then calling MainApp from MessageboxYesNo...which then creates another instance of MessageboxYesNo...and on and on. Can you simply pass app_x_pos_mac to MessageboxYesNo as an additional parameter? Commented Feb 8, 2022 at 14:44
  • Are there any other way to get the instance variable value app_x_pos_mac directly inside the init method of MessageboxYesNo without passing it as a parameter and without using the MainApp.__init__(self, parent) method ? Commented Feb 8, 2022 at 14:47
  • I believe my answer below addresses this question -- you should then be able to accerss whatever properties inside MessageboxYesNo with parent.propertyOfInterest. If it does not, I apologize -- I'm not as experienced as some/most around here! Commented Feb 8, 2022 at 14:49

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

This works. Thanks. How is self and self.parent different? I know that self.parent is root. But is not the self also root? Confused. Is self the app instance?
Glad to hear it! self is always the current object, or in other words the present instance of the class in which you are using it.
(If the answer works for you, please accept it!) For more instruction/examples of the self keyword: delftstack.com/howto/python/self-in-python or geeksforgeeks.org/self-in-python-class

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.