codeheadache
codeheadache

Reputation: 164

How to close main Tkinter window from pop up window in python?

I have a main tkinter window which launches multiple pop up windows. I am trying to close all windows by pressing the cross in the pop up window or by pressing quit. I have the following code which closes only the pop up window by pressing the quit or cross, but does not close the main window.How to refactor the code to achieve the desired output.

import tkinter as tk
from tkinter import *
      
class myclass:
    def __init__(self,master):
        self.master             = master      
        self.button            = tk.Button(self.master, text = "Function", command = self.launchFunc)        
        self.button.place(width=160,height=50, x=20, y=20)                
        self.quit         = tk.Button(self.master, text = "Quit", command = self.close_windows)  
        self.quit.place(width=160,height=50, x=20, y=80)
       
    def close_windows(self):
        self.master.destroy()
    
    def launchFunc(self):
        self.top                = tk.Toplevel(self.master)
        self.top.transient(self.master)        
        self.app                = funcClass(self.top)

class funcClass:
    def __init__(self, master):
        self.master1            = master
        self.quit         = tk.Button(self.master1, text = "Quit", command = self.close_windows)  
        self.quit.place(width=160,height=50, x=20, y=20)  
                      
    def close_windows(self):
        self.a    = myclass(self.master1) 
        self.a.close_windows()
        
if __name__ == "__main__":
    root = tk.Tk()
    myclass = myclass(root)
    root.mainloop()

Please guide me. Thank you

Upvotes: 1

Views: 866

Answers (2)

acw1668
acw1668

Reputation: 47163

You need to register a callback function which will be executed when the X button on the title bar is clicked using protocol() function. You can use the same function for the Quit button. Then close the root window inside the callback function:

class funcClass:
    def __init__(self, master):
        self.master1 = master
        self.quit = tk.Button(self.master1, text="Quit", command=self.close_windows)  
        self.quit.place(width=160, height=50, x=20, y=20)
        # for 'X' button in title bar
        master.protocol('WM_DELETE_WINDOW', self.close_windows) 
                      
    def close_windows(self):
        # self.master1 is the toplevel
        # so self.master1.master is the root window
        self.master1.master.destroy()

Upvotes: 1

Joel Toutloff
Joel Toutloff

Reputation: 484

I would recommend making your own popups using simpledialog.Dialog as a base class that you can pass special instructions like this to.

It needs to extend the base class and run the super().init with the parent and title arguments passed so that it will build the popup properly.

class MyPopup(simpledialog.Dialog):
    def __init__(self, parent, title, superClose)
        self.superClose = superClose
        super().__init__(parent, title)

I added the 'superClose' argument to save that for later. Then, we override the destroy method of that class to run that passed argument.

def destroy(self):
    super().destroy()
    self.superClose()

The super().destroy() will run the the original version of the destroy method and then we run that saved argument we passed earlier.

Make some function that closes things:

def closeOthers():
    <go through all your popups you want closed and close them>

And then to make the popup appear:

MyPopup(root, "Title", closeOthers)

When that one is destroyed, it will also call that function.

To customize that popup, overwrite the body method. (you don't need to run super().body()) This is where you should add all your tkinter widgets for the popup.

A couple other things to know: These will have a frame near the bottom that gives you an OK and Cancel button by default, that call ok() and cancel() (which you can override to choose what they do) and binds Enter and Escape to those functions as well. If you don't want this, or what this changed, overwrite the buttonbox method. The cancel function also calls the destroy method, which will run your superClose method too. If you want the cancel button (and escape key) to just close itself and not run superClose, overwrite it with this:

def cancel(self, event=None):
    if self.parent is not None:
        self.parent.focus_set()
    self.initial_focus = None
    Toplevel.destroy(self)

If you are okay with how all closing runs the superClose, you could also overwrite apply(self) instead. That will make everything in there run after the popup is destroyed, every time.

def apply(self):
    self.superClose()

It also doesn't need a super() call.

I hope that helps. Let us know if you have more questions etc.

Upvotes: 1

Related Questions