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
prevent the toplevel to come on top of the popup
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.