force Toplevel Widget on top of root widget - python

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

Related

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!

Disable window controls when a messagebox is created in tkinter

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()

Python Tk Askstring Dialog Window Focus

I have an application with a GUI which ask's a user for input via Tk Askstring like below...
a = askstring('ABC', 'Please enter something!')
The problem I have when the dialog box is opened if the user clicks out of the dialog box it brings focus to the root GUI and sends the dialog box behind.
This is the GUI setup...
# GUI Attributes
root = tk.Tk()
root.geometry('800x480')
root.resizable(0, 0)
root.config(cursor='none')
root.title('')
root.wm_attributes('-type', 'splash')
Is there a way to set the focus for the askstring dialog box so this cannot happen?
Try this:
root.lift()
If you want the window to stay above all other windows, use:
root.attributes("-topmost", True)
Where root is your Toplevel or Tk.

How to disable window controls when a modal dialog box is active in TkInter?

I am writing a Python application with TkInter. At some point the application (root) displays a dialog box (dlg, which is a Toplevel). In order to make the dialog modal I use the following code:
dlg.focus_set()
dlg.grab_set()
dlg.transient(root)
root.wait_window(dlg)
This indeed cancels "custom" events outside the dialog box (like the widgets in the main application window), but it does NOT cancel the window manager events, so that for example clicking on the main application window has it regain focus and it can be moved, resized - and even closed! - while the "modal" dialog is still open.
How can I make my dialog truly modal, so that window manager events for the main application window are also suspended while the dialog box is active?
I am using Python 3.4.3 on Ubuntu 15.04.
you can use root.grab_set_global() as in this exemple:
import Tkinter
class Application(Tkinter.Frame):
def mygrab(self):
print "grab is ok"
root.grab_set_global()
def createWidgets(self):
self.QUIT = Tkinter.Button(self)
self.QUIT["text"] = "QUIT"
self.QUIT["command"] = self.quit
self.QUIT.pack({"side": "left"})
self.grab = Tkinter.Button(self)
self.grab["text"] = "Grab",
self.grab["command"] = self.mygrab
self.grab.pack({"side": "left"})
def __init__(self, master=None):
Tkinter.Frame.__init__(self, master)
self.pack()
self.createWidgets()
root = Tkinter.Tk()
app = Application(master=root)
app.mainloop()
root.destroy()`
Try this way:
dlg.focus_set()
dlg.grab_set()
dlg.transient(root)
dlg.wait_window(dlg)

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.

Categories