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.
Related
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
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()
When I run this script, two windows appear, one for the file selection and the Tkinter window. How can I change this so that the Tkinter window only opens after a file has been selected? Thanks
def main():
my_file = askopenfilename()
stage1()
def stage1():
master = Tk()
master.mainloop()
The window master does open only after the file dialog closure (try to change its title to check), the first window you see is the parent window of the file dialog. Indeed, the tkinter file dialogs are toplevel windows, so they cannot exist without a parent window. So the first window you see is the parent window of the file dialog.
The parent window can however be hidden using the withdraw method and then restored with deiconify:
from tkinter import Tk
from tkinter.filedialog import askopenfilename
def main():
master = Tk()
master.withdraw() # hide window
my_file = askopenfilename(parent=master)
master.deiconify() # show window
master.mainloop()
if __name__ == '__main__':
main()
How can I spawn a window, and halt the execution of the GUI until this window is closed by the user?
That's exactly what functions from the tkinter.messagebox submodule will do.
These will spawn a dialog, and halt the execution until closed.
For instance, the showinfo function will spawn a window with the first parameter as title and the second as message.
Until the window is closed, the remaining of the GUI will not be interactable.
Here is an example demonstrating this.
import tkinter as tk
import tkinter.messagebox as tkmb
root = tk.Tk()
button = tk.Button(
root,
text="Spawn a dialog",
command=lambda: tkmb.showinfo(
"Information",
"Please close this window or press OK to continue"))
button.pack()
root.mainloop()
When the button is clicked, a window spawns.
As long as this window is open, the button will not be clickable once again.
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.