Keeping Toplevel above root after calling filedialog.askdirectory() - python

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!

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

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

How do I make a button that closes one tkinter window and opens another?

When I make a button that closes the current window and opens another, the current window doesn't close.
from tkinter import *
root = Tk()
def new_window():
root.quit()
new_window = Tk()
new_window.mainloop()
Button(root, text="Create new window", command=new_window).pack()
root.mainloop()
(This isn't my program, it's just an example)
You should be able to do it like this:
import tkinter as tk
root = tk.Tk()
def new_window():
root = tk.Tk()
test = tk.Button(root, text="Create new window", command= lambda:[root.destroy(), new_window()]).pack()
root.mainloop()
test = tk.Button(root, text="Create new window", command= lambda:[root.destroy(), new_window()]).pack()
root.mainloop()
This will literally keep opening the exact same window with a button. The lambda allows you to call multiple functions. By calling .destroy() on your root window, it destroys your window, but doesn’t stop the program. Then you create a new root window with your function.
You can use this technique on your actual script.

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.

Categories