2
votes

In the spirit of decoupling, rather than cramming many widgets into one giant class, I tried splitting them into separate classes.

The problem I ran into is the FileMenu class does not know about root, and so cannot call root.quit or root.destroy. On a whim I tried self.quit, and it works, but I have not seen this used before.

My questions are:

  1. Is self.quit a safe way to quit the app?
  2. Is sys.exit a safe way to quit the app?
  3. If neither are safe, is there a good way to approach decoupling the design and safely quit without passing root as a parameter everywhere or making it a global?

My example:

import Tkinter as tk

class FileMenu(tk.Menu):
    def __init__ (self, parent):
        tk.Menu.__init__(self, parent, tearoff=False)
        self.add_command(label='Exit', command=self.quit)

class MainMenu(tk.Menu):
    def __init__ (self, parent):
        tk.Menu.__init__(self, parent, tearoff=False)
        self.file_menu = FileMenu(self)
        self.add_cascade(label='File', menu=self.file_menu)

class View:
    def __init__ (self, parent):
        self.frame = tk.Frame(parent)
        self.parent = parent
        self.menu = MainMenu(self.frame)
        self.parent.configure(menu=self.menu)
        self.parent.geometry('200x200')
        self.frame.pack(fill='both', expand=True)

class App:
    def __init__ (self):
        self.root = tk.Tk()
        self.view = View(self.root)

    def run (self):
        self.root.title('Window Title')
        self.root.mainloop()

if __name__ == '__main__':
    app = App()
    app.run()
2

2 Answers

1
votes

Both use for different purpose:

sys.exit(): Close complete application.

self.quit: If you have multiple windows in your application and you don't want to close your whole application then you should use self.quit().

Both are safe to use but uses are different.

1
votes

If the class doesn't know about the root object but you expect it to act on the root object, you should pass it in.

In App, you pass the root object into View:

self.view = View(self.root)

In View, you create a MainMenu and pass it self.frame:

self.menu = MainMenu(self.frame)

Instead, also pass it the root object:

self.menu = MainMenu(self.frame, parent)

And make MainMenu expect a parameter of that object, and pass it to FileMenu:

class MainMenu(tk.Menu):
    def __init__ (self, parent, root):
        tk.Menu.__init__(self, parent, tearoff=False)
        self.file_menu = FileMenu(self, root)
        self.add_cascade(label='File', menu=self.file_menu)

Then make FileMenu expect a parameter of that root object. Now it has access to it:

class FileMenu(tk.Menu):
    def __init__ (self, parent, root):
        tk.Menu.__init__(self, parent, tearoff=False)
        self.add_command(label='Exit', command=root.destroy)

And you can use the Tkinter root object's destroy method to quit the application properly.