Shibli
Shibli

Reputation: 6139

How to prevent closing of parent window before child window is closed

I know that, from here,

grab_set() [#]: Routes all events for this application to this widget.

OK but it doesn't prevent the user to close the parent window with X button. In addition to other events, how can I route "X button close" event as well?

MWE:

from Tkinter import *

class Window(Toplevel):

    def __init__(self, master):
        Toplevel.__init__(self)


class MainWindow(Tk):

    def __init__(self):
        Tk.__init__(self)
        Button(self, command=self.open_window).grid(row=0, column=0)

    def open_window(self):
        win = Window(self)
        win.grab_set()


app = MainWindow()
app.mainloop()

Upvotes: 1

Views: 1126

Answers (2)

Mike - SMT
Mike - SMT

Reputation: 15226

For windows you can use:

root.protocol("WM_DELETE_WINDOW", your_function_here)

I don't know about other operating systems as I have not tested it on anything other than windows.

Works fine in a class as well.

self.root.protocol("WM_DELETE_WINDOW", self.your_method_here)

I have added some code to your post to illustrate its use. Note when you press the X button the program will rune the close_program method.

Edit: Without being able to test on Mac I have added what I think will fix your problem. I have added an if statement in the close_program method to check if the top level exist before attempting to close.

Update: I added a check for the variable win to deal with possible errors if toplevel was never opened but still trying to close root window.

from tkinter import *

class Window(Toplevel):

    def __init__(self, master):
        Toplevel.__init__(self)
        self.protocol("WM_DELETE_WINDOW", self.close_program)

    def close_program(self):
        print ("destroying Window")
        self.destroy()


class MainWindow(Tk):

    def __init__(self):
        Tk.__init__(self)
        Button(self, command=self.open_window).grid(row=0, column=0)
        self.protocol("WM_DELETE_WINDOW", self.close_program)

    def open_window(self):
        global win
        win = Window(self)
        win.grab_set()

    def close_program(self):
        if 'win' in globals():
            if win.winfo_exists() == 1:
                print("top level still active")
            else:
                print("destroying MainWindow")
                self.destroy()
        else:
            print("destroying MainWindow")
            self.destroy()


app = MainWindow()
app.mainloop()

Upvotes: 1

Tadhg McDonald-Jensen
Tadhg McDonald-Jensen

Reputation: 21453

Lets say that if the child window exists, trying to close the parent window will put it into focus, this would require a few changes:

  1. override the close protocol with .protocol("WM_DELETE_WINDOW") so it checks for a child window.
  2. Keep a reference to a child window
class MainWindow(Tk):

    def __init__(self):
        Tk.__init__(self)
        Button(self, text="hello there", command=self.open_window).grid(row=0, column=0)
        self.protocol("WM_DELETE_WINDOW", self.try_to_close)
        self.win = None

    def open_window(self):
        if self.win is None or not self.win.winfo_exists():
            self.win = Window(self)
        self.win.lift(self)
        self.win.focus_set()

    def try_to_close(self):
        if self.win is not None and self.win.winfo_exists():
            self.win.lift()
        else:
            self.destroy()

Upvotes: 1

Related Questions