Make Toplevel window pop up to the top when root window clicked - python

I have a UI root window where two other Toplevel windows get created on separate button clicks. These Toplevel windows are anchored to the root window and drag along the screen with the root window.
My problem is if i have another window open and my UI is hiding behind it, if i click on my UI from the taskbar or the little i can see on the screen, only the root Tk window pops up and the other Toplevel windows are still hiding behind the other window.
I tried toplevel.lift() and toplevel.wm_attributes("-topmost", 1) but neither give me what i want.
How can I tie the Toplevel windows so that if they are open and I click on the root window the Toplevel window also pops to the top?

Here is a simple example that will open 2 windows and disable everything on the root window while also binding any interaction with that root window to lift all the top windows above it.
I have also bound the top level close event to first remove the root binding and then destroy the top levels then re-enable all the widgets in the root window. this should serve to be an antiquity example of what you are trying to do.
Let me know if you have any questions.
import tkinter as tk
class ExampleApp(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
self.master.geometry("400x150")
self.main_frame = tk.Frame(self.master)
self.main_frame.pack(expand=tk.YES, fill=tk.BOTH)
self.master.protocol('<WM_LBUTTONDBLCLK>', self.motion)
tk.Label(self.main_frame, text = "This is the main window").pack()
tk.Button(self.main_frame, text = "Open 2 top level windows!", command = self.open_windows).pack()
def motion(self, event):
x, y = event.x, event.y
print('{}, {}'.format(x, y))
def open_windows(self):
self.top1 = tk.Toplevel(self.master)
self.top2 = tk.Toplevel(self.master)
self.top1.geometry("100x100")
self.top2.geometry("100x100")
# ties the window close event to our customer close method for toplevel
self.top1.protocol("WM_DELETE_WINDOW", self.close_toplevels)
self.top2.protocol("WM_DELETE_WINDOW", self.close_toplevels)
self.master.bind("<Unmap>", self.icon_all)
self.top1.bind("<Unmap>", self.icon_all)
self.top2.bind("<Unmap>", self.icon_all)
self.master.bind("<Map>", self.de_icon_all)
self.top1.bind("<Map>", self.de_icon_all)
self.top2.bind("<Map>", self.de_icon_all)
for child in self.main_frame.winfo_children():
child.configure(state='disable')
tk.Label(self.top1, text ="Topwindow 1").pack()
tk.Label(self.top2, text ="Topwindow 2").pack()
# sets the top windows to their initial locations
self.lock_top_to_root()
#keeps the top windows in the specified locations compared to root window
self.master.bind("<Configure>", self.lock_top_to_root)
def withdraw_tops(self, event=None):
self.top1.withdraw()
self.top2.withdraw()
def de_icon_tops(self, event=None):
self.top1.deiconify()
self.top2.deiconify()
def icon_all(self, event=None):
self.withdraw_tops()
self.master.iconify()
def de_icon_all(self, event=None):
self.de_icon_tops()
self.master.deiconify()
self.lock_top_to_root()
def lock_top_to_root(self, event=None):
self.top1.lift() # lift both toplevel windows about root
self.top2.lift()
# places each top level at each side
# this is not set up to compensate for the root being resized but can be if you need it to.
self.top1.geometry('+{}+{}'.format(self.master.winfo_x()+10, self.master.winfo_y()+30))
self.top2.geometry('+{}+{}'.format(self.master.winfo_x()+275, self.master.winfo_y()+30))
def close_toplevels(self):
# customer close method to reset everything
self.master.unbind('<Configure>')
self.master.unbind("<Unmap>")
self.master.unbind("<Map>")
self.top1.destroy()
self.top2.destroy()
for child in self.main_frame.winfo_children():
child.configure(state='active')
root = tk.Tk()
my_example = ExampleApp(root)
root.mainloop()

Related

The state of a Tkinter window is not tracked correctly when maximized

I'm currently trying to resize a Tkinter window to a specific size. It is working great except when the window is maximized using the maximize button in the top toolbar. Here is a simple code that I've done for example.
When the window is maximized using the button, the print output is "normal" while it should be "zoomed". The window's size is then locked until the maximize button is pressed again.
I am working on Red Hat Enterprise Linux if it can help. Has anyone already had this problem before?
from tkinter import *
class Main:
def __init__(self, root):
self.root = root
Button(self.root, command=self.resize, text="Resize").pack()
def resize(self):
print(self.root.state())
self.root.attributes("-zoomed", False)
self.root.geometry("800x300")
root = Tk()
x = Main(root)
root.mainloop()

How to make pop-up window with force attention in Tkinter

I want to create a window which doesn't allow the user to access other windows until you give an input.
I tried win.attribute("ontop", True) but it allows the user to access other windows.
or is there any function like a force_focus_lock() in Tkinter python 3.8 which doesn't allow other window to
get focus until you give a input or the close present window.
I believe the below is what you are trying to do. Explanation is given in comments.
method #1: (PopOut1)
you can still move the main window
the new window assumes focus if there is a mouse release on main window
method #2: (PopOut2)
the main window is locked in place
the new window will assume focus, blink and "ding" if there is a mouse release on main window
import tkinter as tk
#first method
class PopOut1(tk.Toplevel):
def __init__(self, master, **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
self.geometry('400x300')
#set focus to this window
self.focus_set()
#releasing on any other tkinter window, within this process, forces focus back to this window
self.grab_set()
#second method
class PopOut2(tk.Toplevel):
def __init__(self, master, **kwargs):
tk.Toplevel.__init__(self, master, **kwargs)
self.geometry('400x300')
#set focus to this window
self.focus_set()
#disable the main window
master.attributes('-disabled', True)
#so this window can't end up behind the disabled window
#only necessary if this window is not transient
#self.attributes('-topmost', True)
#capture close event
self.protocol("WM_DELETE_WINDOW", self.close)
#event=None ~ in case you also want to bind this to something
def close(self, event=None):
#re-enable the main window
self.master.attributes('-disabled', False)
#destroy this window
self.destroy()
class App(tk.Tk):
TITLE = 'Application'
WIDTH, HEIGHT, X, Y = 800, 600, 50, 50
def __init__(self):
tk.Tk.__init__(self)
tk.Button(self, text="open popout 1", command=self.open1).grid()
tk.Button(self, text="open popout 2", command=self.open2).grid()
def open1(self):
PopOut1(self)
def open2(self):
#.transient(self) ~
# flash PopOut if focus is attempted on main
# automatically drawn above parent
# will not appear in taskbar
PopOut2(self).transient(self)
if __name__ == '__main__':
app = App()
app.title(App.TITLE)
app.geometry(f'{App.WIDTH}x{App.HEIGHT}+{App.X}+{App.Y}')
#app.resizable(width=False, height=False)
app.mainloop()

How to close parent window when child window is closed in tkinter

i am making a GUI and in it I am keeping the root hidden. So when i close the window which is showing using the x arrow, i wont the whole program to end not just the window that can be seen. Because other wise the user will have problems when closing the program.my root is hidden,
login can be seen,
when login is closed using the red x at the top i want to close the root as well how to do that?
You can bind to the <Destroy> event on your window. Within the bound function, you can do whatever you want including destroying the root window.
In the following example, killing the Toplevel windows will destroy the root window.
import tkinter as tk
class Window(tk.Toplevel):
def __init__(self, root, label):
super().__init__(root)
self.root = root
label = tk.Label(self, text=label)
label.pack(padx=20, pady=20)
self.bind("<Destroy>", self.kill_root)
def kill_root(self, event):
if event.widget == self and self.root.winfo_exists():
self.root.destroy()
root = tk.Tk()
label = tk.Label(root, text="Root window")
label.pack(padx=20, pady=20)
w1 = Window(root, "This is a toplevel window")
root.mainloop()
The reason for checking that event.widget is self is due to the fact that functions bound to the root or toplevel window are automatically inherited by all children within that window You only want to destroy the root window when the actual toplevel window is destroyed.

button open a duplicate window of the main page in a new window using python and tkinter in pycharm

How do you make a button open a duplicate window of the main page in a new window?
What would the python code be to do this with Tkinter?
For clarification, in notepad++ when you right click on the tab that you are in, it shows you a whole menu.
Then, there is a dropdown menu with an option that says, open in a new instance or move to a new instance.
This would then open the tab you selected into a new tab/window.
I haven't tried anything yet but I did look up about how to do this type of thing and there were no results on what exactly I wanted to do.
Can you try to show me an example of how what I need to do can be done?
Code:
Found Here
The simplest solution is to make your window be a subclass of Toplevel. Then, each time you need a new window, create a new instance of the class.
You can then leave the root window blank and hide it so that you only see your custom windows. It's important to make sure that you destroy the root window after the user has deleted the last application window, or else you'll end up with an invisible window that will be hard to kill.
Here is a bit of a contrived example:
import tkinter as tk
class AppWindow(tk.Toplevel):
def __init__(self, root):
tk.Toplevel.__init__(self, root)
self.root = root
menubar = tk.Menu(self)
windowMenu = tk.Menu(menubar)
windowMenu.add_command(label="New Window", command=self.new_window)
windowMenu.add_command(label="Quit", command=root.destroy)
menubar.add_cascade(label="Window", menu=windowMenu)
self.configure(menu=menubar)
self.text = tk.Text(self)
self.vsb = tk.Scrollbar(self, orient="vertical", command=self.text.yview)
self.text.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.text.pack(side="left", fill="both", expand=True)
# call a function to destroy the root window when the last
# instance of AppWindow is destroyed
self.wm_protocol("WM_DELETE_WINDOW", self.exit_on_last_window)
def new_window(self):
AppWindow(self.root)
def exit_on_last_window(self):
"""Destroy the root window when no other windows exist"""
self.destroy()
if not any([window.winfo_exists() for window in self.root.winfo_children()]):
self.root.destroy()
def quit(self):
self.root.destroy()
# create the root, then hide it. The app will create its
# own windows as Toplevels
root = tk.Tk()
root.withdraw()
# create the first window, then start the event loop
AppWindow(root)
tk.mainloop()

In tkinter, how to make custom child widget receive mouse scroll event?

I can't sort out how to make a custom widget receive mouse scroll events. If I bind to the root window, then notifications occur. And if I bind to a child of root window other than my widget (here a simple Listbox), the notifications also occur (evidenced by watching the list move when I move the wheel). What am I overlooking?
Example code where roll() never gets called:
#!/usr/bin/python3
from tkinter import *
from tkinter.ttk import *
class CustomWidget(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.width = 200
self.height = 200
self.canvas = Canvas(self, width=200, height=200)
self.canvas.config(background='red')
self.canvas.pack()
self.bind('<MouseWheel>', self.roll)
self.bind('<Button-4>', self.roll)
self.bind('<Button-5>', self.roll)
def roll(self, event):
print("detected mouse roll!");
if __name__ == "__main__":
root = Tk()
root.wm_title("TestRoot")
sb = Scrollbar(root, orient=VERTICAL)
lb = Listbox(root, yscrollcommand=sb.set)
sb.config(command=lb.yview)
cw = CustomWidget(root)
for char in list("abcdefghijklmnopqrstuvwxyz"):
lb.insert(END, char)
cw.pack()
lb.pack()
sb.pack()
root.update()
root.mainloop()
So in order for a frame to receive events, it needs to have focus. You can call frame.set_focus() on it, but as soon as you give another widget focus it won't work. To get around that we could bind <Button-1> to the frame and have that set the focus to the frame, but your canvas takes up the entire size of the frame, so You will need to bind the <Button-1> events to that instead.
Adding:
self.canvas.bind("<Button-1>", lambda _: self.focus_set())
after your other bindings in CustomWidget.__init__ will make your bindings work as long as the widget has focus, which it will when the user clicks it (similar how to the listbox works). If the canvas is never as large as it's frame, you may need to add another <Button-1> binding to the frame.

Categories

Resources