Disable window controls when a messagebox is created in tkinter - python
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.
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.
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()
Related
force Toplevel Widget on top of root widget
I have a tkinter app with a Toplevel widget that I want to create when the window is starting. The issue I have is that the Toplevel window always ends up behind the main window. Is there a way to force it in front of the root window?
To expand on #acw1668's comment, here's an example of how to create a transient window that sits on top of the root window. Note that a transient window will only have a close button [X], and no minimize / maximize buttons. import tkinter as tk from tkinter import ttk class App(tk.Tk): def __init__(self): super().__init__() self.new_window_button = ttk.Button( self, text='Open New Window', command=self.new_window, ) self.new_window_button.pack() def new_window(self): self.dialog_window = tk.Toplevel(self) self.dialog_window.transient(self) # place this window on top of the root window if __name__ == '__main__': app = App() app.mainloop() One important thing to consider is that you might want to prevent the user from interacting with the root window while the dialog is open. Otherwise, in the case of this example, the user could keep clicking the button and spawning new windows. You can do this by calling grab_set() on the dialog (thanks #acw1668 for reminding me) def new_window(self): self.dialog_window = tk.Toplevel(self) self.dialog_window.transient(self) # place this window on top of the root window self.dialog_window.grab_set() # hold focus
Python Tkinter destroy toplevel window missing argument
My Code opens a window with a button. When the button gets clicked a toplevel window is created and the root window gets destroyed. When the button on the toplevel window gets clicked a messagebox opens. I want the the Toplevel Window to be closed when the user presses the ok button of the messagebox. Pressing Ok causes the TypeError: destroy() missing 1 required positional argument: 'self' I really don't understand why it dosn't work since the toplevel window gets passed as an argument to the destroy method. import tkinter as tk from tkinter import messagebox def main(): root = tk.Tk() root.title("Hauptmenü") mainmenue(root) root.mainloop() def mainmenue(root): button_rennen = tk.Button(root, text="New Window", width=20, command=lambda: call_window(root)) button_rennen.pack() def call_window(root): root.destroy() rframe = tk.Toplevel button = tk.Button(text="Wette platzieren", command=lambda: question(rframe)) button.pack() def question(rframe): dialog = tk.messagebox.askokcancel(message="Destroy Window?") if dialog is True: rframe.destroy() main()
def call_window(root): root.destroy() rframe = tk.Tk() button = tk.Button(rframe, text="Wette platzieren", command=lambda: question(rframe)) button.pack() replace toplevel with Tk window and it works fine.
Keeping Toplevel above root after calling filedialog.askdirectory()
I currently have a root window and a Toplevel initialized, and I want the Toplevel to always be above root root = Tk() root.geometry("500x150") root.resizable(0, 0) root.title("test") root.config(background=BACKGROUND_COLOR) def defaultFrameNoResize(geom, title): output = Toplevel() output.geometry(geom) output.resizable(0, 0) output.title(title) output.config(background=BACKGROUND_COLOR) return output defaultsWindow = defaultFrameNoResize("500x150", "Change default settings") And in defaultsWindow I have a button with the function: lambda: text1Ptr.set(filedialog.askdirectory( initialdir=currentWorkspace )) And when I press the button, the askdirectory interface comes up normally, but the root window goes above the Toplevel window. I've tried using defaultsWindow.lift() and root.wm_attributes("-topmost") as well but to no avail Thanks!
How to snap the tkinter canvas to the top left of the screen? [duplicate]
How can I make a frame in Tkinter display in fullscreen mode? I saw this code, and it's very useful…: >>> import Tkinter >>> root = Tkinter.Tk() >>> root.overrideredirect(True) >>> root.geometry("{0}x{1}+0+0".format(root.winfo_screenwidth(), root.winfo_screenheight())) …but is it possible to edit the code so that hitting Esc automatically makes the window "Restore down"?
I think this is what you're looking for: Tk.attributes("-fullscreen", True) # substitute `Tk` for whatever your `Tk()` object is called You can use wm_attributes instead of attributes, too. Then just bind the escape key and add this to the handler: Tk.attributes("-fullscreen", False) An answer to another question alluded to this (with wm_attributes). So, that's how I found out. But, no one just directly went out and said it was the answer for some reason. So, I figured it was worth posting. Here's a working example (tested on Xubuntu 14.04) that uses F11 to toggle fullscreen on and off and where escape will turn it off only: import sys if sys.version_info[0] == 2: # Just checking your Python version to import Tkinter properly. from Tkinter import * else: from tkinter import * class Fullscreen_Window: def __init__(self): self.tk = Tk() self.tk.attributes('-zoomed', True) # This just maximizes it so we can see the window. It's nothing to do with fullscreen. self.frame = Frame(self.tk) self.frame.pack() self.state = False self.tk.bind("<F11>", self.toggle_fullscreen) self.tk.bind("<Escape>", self.end_fullscreen) def toggle_fullscreen(self, event=None): self.state = not self.state # Just toggling the boolean self.tk.attributes("-fullscreen", self.state) return "break" def end_fullscreen(self, event=None): self.state = False self.tk.attributes("-fullscreen", False) return "break" if __name__ == '__main__': w = Fullscreen_Window() w.tk.mainloop() If you want to hide a menu, too, there are only two ways I've found to do that. One is to destroy it. The other is to make a blank menu to switch between. self.tk.config(menu=self.blank_menu) # self.blank_menu is a Menu object Then switch it back to your menu when you want it to show up again. self.tk.config(menu=self.menu) # self.menu is your menu.
This creates a fullscreen window. Pressing Escape resizes the window to '200x200+0+0' by default. If you move or resize the window, Escape toggles between the current geometry and the previous geometry. import Tkinter as tk class FullScreenApp(object): def __init__(self, master, **kwargs): self.master=master pad=3 self._geom='200x200+0+0' master.geometry("{0}x{1}+0+0".format( master.winfo_screenwidth()-pad, master.winfo_screenheight()-pad)) master.bind('<Escape>',self.toggle_geom) def toggle_geom(self,event): geom=self.master.winfo_geometry() print(geom,self._geom) self.master.geometry(self._geom) self._geom=geom root=tk.Tk() app=FullScreenApp(root) root.mainloop()
I think if you are looking for fullscreen only, no need to set geometry or maxsize etc. You just need to do this: -If you are working on ubuntu: root=tk.Tk() root.attributes('-zoomed', True) -and if you are working on windows: root.state('zoomed') Now for toggling between fullscreen, for minimising it to taskbar you can use: Root.iconify()
Here's a simple solution with lambdas: root = Tk() root.attributes("-fullscreen", True) root.bind("<F11>", lambda event: root.attributes("-fullscreen", not root.attributes("-fullscreen"))) root.bind("<Escape>", lambda event: root.attributes("-fullscreen", False)) root.mainloop() This will make the screen exit fullscreen when escape is pressed, and toggle fullscreen when F11 is pressed.
This will create a completely fullscreen window on mac (with no visible menubar) without messing up keybindings import tkinter as tk root = tk.Tk() root.overrideredirect(True) root.overrideredirect(False) root.attributes('-fullscreen',True) root.mainloop()
Yeah mate i was trying to do the same in windows, and what helped me was a bit of lambdas with the root.state() method. root = Tk() root.bind('<Escape>', lambda event: root.state('normal')) root.bind('<F11>', lambda event: root.state('zoomed'))
Just use: # importing tkinter for gui import tkinter as tk # creating window window = tk.Tk() # setting attribute window.state('zoomed') window.title("Full window") # creating text label to display on window screen label = tk.Label(window, text="Hello world!") label.pack() window.mainloop() If you want to hide everything except the window, you can also use: import tkinter as tk root = tk.Tk() root.overrideredirect(True) root.overrideredirect(False) root.attributes('-fullscreen',True) root.title("FullScreen") label = tk.Label(root, text="Hello! Press 🪟 logo on the keypad > select the python logo > Close window to close") labela = tk.Label(root, text="🎉🎉🎉") label.pack() labela.pack() root.mainloop()
root = Tk() root.geomentry('1599x1499')
Python Tkinter Toplevel not the active window
I have a Python Program that opens a Toplevel window which is working I just wanted to know if there is an option to set the Toplevel window window to be active once it has been opened because at the moment it is still showing the parent window as the active window after opening it. The python code (Python 3.4.1) from tkinter import * class cl_gui: def __init__(self, master): master.title("DataBox") menu = Menu(master) master.config(menu=menu) menu_users = Menu(menu, tearoff=0) menu.add_cascade(label="Users", menu=menu_users) menu_users.add_command(label="View", command=self.f_openUsers) def f_openUsers(self): top = Toplevel() top.title("Users") root = Tk() app = cl_gui(root) root.mainloop()
You can set focus onto the new Toplevel widget as follows: def f_openUsers(self): top = Toplevel() top.title("Users") top.focus_set() # <- add this line See e.g. this handy tkinter guide.