Home > other >  How to get instance attribute value and do logic based on that? Python, Tkinter
How to get instance attribute value and do logic based on that? Python, Tkinter

Time:02-11

I have created a custom message box that returns True if the yes button is pressed and False if the no button is pressed. Now I want to do logic based on that in the init method of the class MainApp. How can I do this? How can I get the instance attribute value of answer from the class MessageboxYesNo? I am fairly new to OOP.

from sys import platform
import tkinter as tk


class MainApp(tk.Frame):
    
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs) 
        self.parent = parent #save root window object here when instantiating from this class

        self.set_app_size_and_position()
   
        #Exit button
        self.exitButton = tk.Button(self.parent, text="Exit", command=lambda: MessageboxYesNo(self, "QUIT", "Are you sure you want to quit?"), bg="red", fg="black")
        self.exitButton.pack(side="top", anchor='ne', padx=15, pady=10)

    #Want to do logic here based on the answer from the messagebox. How can I access the answer from the MessageboxYesNo class/instance?
    # if answer:
    #    do something

    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
        
        #set the app window to half of the screens width and to the right side of the screen 
        
        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(tk.Frame):
    def __init__(self, parent, title, text):
        tk.Frame.__init__(self, parent)
        
        self.parent = parent #this will be root
        
        self.title = title
        self.text = text
        self.answer = None
        
        self.toplevel = tk.Toplevel(self.parent)
        
        #get the messagebox on top of the app window
        self.toplevel.focus_force()     # Get focus
        
        self.toplevel.title(self.title)
        
        #set x- and y-coordinates for centering the messagebox
        #self.parent.app_x_pos_mac will get the instance attribute value from the class MainApp
        self.x_pos_messagebox = int(self.parent.app_x_pos_mac (self.parent.app_x_pos_mac/2))
        self.y_pos_messagebox = int(self.parent.app_height_mac/2)

        #position the messagebox near 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, command=lambda: self.answerMessagebox(True))
        self.b1.grid(row=1, column=1, padx=(2, 35), sticky="e")

        self.b2=tk.Button(self.toplevel,text="No", width = 10, command=lambda: self.answerMessagebox(False))
        self.b2.grid(row=1, column=2, padx=(2, 35), sticky="e")
        
    def answerMessagebox(self, answerOnQuestion):
        
        if answerOnQuestion == True:
            print("You pressed yes")
            self.answer = True
        
        if answerOnQuestion == False:  
            print("You pressed no")
            self.answer = False 
    
        print(self.answer)
        self.toplevel.destroy() #close messagebox
        return self.answer
    

def main():
    root = tk.Tk()
    app = MainApp(root)
    app.pack()
    
    root.mainloop()
      
if __name__ == "__main__":
    main()

CodePudding user response:

In my opinion, button commands should nearly always call a custom function, and never directly instantiate some other object. Functions are cheap and easier to debug. This is especially true if you need the return value from the function or class.

So, to start, I would do just that and move #Want to do logic here based on the answer from the messagebox. into a function that is called by the button.

class MainApp(tk.Frame):
    
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs) 
        self.parent = parent #save root window object here when instantiating from this class

        self.set_app_size_and_position()
   
        #Exit button
        self.exitButton = tk.Button(self.parent, text="Exit", command=self.confirm_exit)

    def confirm_exit(self):
        result = MessageboxYesNo(self, "QUIT", "Are you sure you want to quit?").show()
        if result:
            <put your code here>

The next part of the problem is to get MessageboxYesNo to not return until you have the result. Tkinter provides functions that can help with that: wait_window and wait_variable. wait_window will wait until a window is destroyed, and wait_variable will wait until a tkinter variable is set.

Since the instantiation of a class needs to return the instance rather than something else, you should create a new function for the dialog that displays the dialog and then waits for it to be destroyed.

class MessageboxYesNo(tk.Frame):
    def __init__(self, parent, title, text):
        ...

    def show(self):
        self.wait_window(self.toplevel)
        return self.answer

With this, when you create the messagebox and call show (or call it wait if you want...), the code will wait until the window has been destroyed and then return the value selected by the user. You can then use that result in the calling function.

  • Related