1

Is there any way to disable all windows when a messagebox popup is created in tkinter?

Here's the code:

from tkinter import *
from tkinter import messagebox

def show():
    messagebox.showinfo("Test Popup", "Hello world")

root = Tk()
root.title("Main Window")
root.geometry("500x500")

toplevel = Toplevel(root)
toplevel.title("Toplevel Window")
toplevel.geometry("300x300")

show_button = Button(root , text = "Show popup" , command = show)
show_button.place(x = 200 , y = 200)

mainloop()

Here when the messagebox pops up, I don't want the user to be able to interact with any other Tk or Toplevel windows until that popup is destroyed.

(I tried using the parent attribute of the messagebox, but it only disables one window.)

Is there any way to achieve this in tkinter?

It would be great if anyone could help me out.

Lenovo 360
  • 569
  • 1
  • 7
  • 27
  • Here https://stackoverflow.com/q/15363923/12198502 – JacksonPro Mar 10 '21 at 11:40
  • @JacksonPro: I don't think there are such kind of attributes for a tkinter `messagebox`. – Lenovo 360 Mar 10 '21 at 11:42
  • By "any other window" do you mean any other program on the computer? Tkinter only has control over the `Tk` root window and its children, not on the other opened programs. So apart from making the tkinter window fullscreen and forcing it to stay on top of every other window I don't think you can prevent the user to interact with an external program. – j_4321 Mar 10 '21 at 12:41
  • @j_4321: No, I do not mean any other program, I only want to disable my Tk and Toplevel windows when a messagebox pops up. – Lenovo 360 Mar 10 '21 at 14:50
  • @Lenovo360 Ok, then I don't understand why `parent=root` does not solve your issue, because if I add buttons in the Toplevel, they become disabled when the popup is displayed. – j_4321 Mar 10 '21 at 15:01
  • @j_4321: This is because, when I use `parent=root`, it only disables the `root` window and I am still able to interact with the other `Toplevel` windows. – Lenovo 360 Mar 10 '21 at 15:05
  • @Lenovo360 That's weird because when I use `parent=root`, all the widgets in both `toplevel` and `root` are disabled. However, `toplevel` can still be closed and when I click on it, it goes on top of the popup: Is this what you want to prevent? – j_4321 Mar 10 '21 at 15:28
  • @j_4321: Yes, that's exactly what I want to prevent, I don't want any window to be clickable or interactable when the popup is created. – Lenovo 360 Mar 10 '21 at 15:42

2 Answers2

1

I don't think it is possible to prevent all interactions with the windows like moving them around (except if you use .overrideredirect(True) which will make the window decoration disappear and the widow will stop being handled by the window manager).

However, it is possible to

  1. prevent the toplevel to come on top of the popup

  2. disable the "close" button of both the root window and toplevel when the popup is displayed

For both I use the following general idea in show():

def show():
    # modify state of root and toplevel to make them less interactive
    # ...
    messagebox.showinfo("Test Popup", "Hello world", parent=root)
    # put root and toplevel back in their normal state
    # ...

For 1. I use root.attributes('-topmost', True) before displaying the popup, which inherits this property from root and therefore will stay on top of toplevel.

For 2. I use window.protocol("WM_DELETE_WINDOW", lambda: quit(window)) which calls quit(window) when the user clicks on the close button of window. In quit(), I check whether the popup is opened before destroying the window:

def quit(window):
    if not popup:
        window.destroy()

popup is a global variable which value is changed in show().

Full code:

import tkinter as tk
from tkinter import messagebox

def quit(window):
    if not popup:  # destroy the window only if popup is not displayed
        window.destroy()

def show():
    global popup
    popup = True
    root.attributes('-topmost', True)
    messagebox.showinfo("Test Popup", "Hello world", parent=root)
    root.attributes('-topmost', False)
    popup = False

root = tk.Tk()
popup = False
root.protocol("WM_DELETE_WINDOW", lambda: quit(root))
root.title("Main Window")
root.geometry("500x500")

toplevel = tk.Toplevel(root)
toplevel.protocol("WM_DELETE_WINDOW", lambda: quit(toplevel))
toplevel.title("Toplevel Window")

show_button = tk.Button(root, text="Show popup", command=show)
show_button.pack()

root.mainloop()

You can probably add some more stuff in show(), e.g. .resizable(False, False) if you don't want the user to be able to resize the windows when the popup is displayed.

j_4321
  • 15,431
  • 3
  • 34
  • 61
1

After experimenting for a few days, I finally found the solution.

The basic idea here is to get all the child widgets of a window, check whether the child is an instance of Tk or Toplevel, and apply the -disabled attribute to them.

Here's the implementation:

from tkinter import *
from tkinter import messagebox

def disable_windows(window):
    for child in window.winfo_children(): # Get all the child widgets of the window
        if isinstance(child, Tk) or isinstance(child, Toplevel): # Check if the child is a Tk or Toplevel window so that we can disable them
            child.attributes('-disabled', True)
            disable_windows(child)

def enable_windows(window):
    for child in window.winfo_children(): # Get all the child widgets of the window
        if isinstance(child , Tk) or isinstance(child , Toplevel): # Check if the child is a Tk or Toplevel window so that we can enable them
            child.attributes('-disabled' , False)
            enable_windows(child)

def increase_popup_count():
    global popup_count
    popup_count += 1
    if popup_count > 0: # Check if a popup is currently active so that we can disable the windows
        disable_windows(root)
    else: # Enable the windows if there is no active popup
        enable_windows(root)

def decrease_popup_count():
    global popup_count
    popup_count -= 1
    if popup_count > 0: # Check if a popup is currently active so that we can disable the windows
        disable_windows(root)
    else: # Enable the windows if there is no active popup
        enable_windows(root)

def showinfo(title, message): # A custom showinfo funtion
    increase_popup_count() # Increase the 'popup_count' when the messagebox shows up
    messagebox.showinfo(title , message)
    decrease_popup_count() # Decrease the 'popup_count' after the messagebox is destroyed

def show():
    showinfo("Test Popup", "Hello world")

root = Tk()
root.title("Main Window")
root.geometry("500x500")

popup_count = 0

toplevel = Toplevel(root)
toplevel.title("Toplevel Window")
toplevel.geometry("400x400")

toplevel_2 = Toplevel(toplevel)
toplevel_2.title("Toplevel Window of Another Toplevel")
toplevel_2.geometry("300x300")

show_button = Button(root , text = "Show popup" , command = show)
show_button.place(x = 200 , y = 200)

mainloop()
Lenovo 360
  • 569
  • 1
  • 7
  • 27