如何检测 window 是否在其他 windows 前面
How to detect if a window is in front of other windows
有没有办法在 parent 聚焦时把 child window 放在前面?我要解决的问题是:我有一个 parent window (root) 和一个 child window (TopLevel) 称为“菜单”。 child window 是一个浮动菜单,上面有几个按钮,titlebar-less。
如果我设置 menu.wm_attributes('-topmost', 1) 而不是 child window 所谓的“菜单”始终保持在顶部,即使我打开另一个应用程序菜单停留在最上面windows,不太实用
如果我重置 menu.wm_attributes('-topmost', 0) 并且我关注 parent window,child window 保持不变在所有其他 windows 之后,我看不到它。如果我是 运行 我的应用程序,然后必须打开另一个应用程序(例如 Dbeaver 或 Firefox),就会发生这种情况。然后我将我的应用程序放在前面,但 child 留在 Dbeaver 或 Firefox 后面。
我想做的是检测主要 window 何时聚焦,这样我就可以将 child window 放在前面,这样根和顶层在前面。
我在网上做了一些广泛的搜索。发现了很多关于检测 window 是否打开或关闭的信息,但没有关于检测 windows 是否打开的信息。
我使用 python 3.8 和 tkinter。
这是我到目前为止在该部分代码中的内容。还不能很好地工作,但已经很接近了:
def push_menu_front(event):
focus = 0
focus += 1
if focus != 0:
print("focus is 1")
menu.wm_attributes('-topmost', 1)
def push_menu_back(event):
focus = 0
focus += 1
if focus != 0:
print("focus is 0")
menu.wm_attributes('-topmost', 0)
root.bind("<FocusIn>", bring_menu_front)
root.bind("<FocusOut>", bring_menu_back)
在 Matiiss 的建议下,我获得了以下代码以用于 Windows,而不是 Linux。所以我结合了我的 Linux 版本和他的 Windows 版本,以根据所使用的操作系统工作。还有一个新版本使用的代码更少,如已接受的答案所示。
from tkinter import Tk, Toplevel
from sys import platform
def push_menu_front_win(event=None):
menu.attributes('-topmost', True)
menu.attributes('-topmost', False)
def push_menu_front(event=None):
menu.attributes('-topmost', True)
def push_menu_back(event=None):
menu.attributes('-topmost', False)
root = Tk()
root.title('master')
root.geometry("300x300+300+100")
menu = Toplevel(root)
menu.title('top')
menu.geometry("120x300+610+100")
# --------------- Select the OS used with if statement ----------
# Microsoft Windows OS
if platform == "win32":
root.bind("<FocusIn>", push_menu_front_win)
# MAC OS
elif platform == "darwin":
root.bind("<FocusIn>", push_menu_front)
root.bind("<FocusOut>", push_menu_back)
# Linux OS
elif platform == "linux" or platform == "linux2":
root.bind("<FocusIn>", push_menu_front)
root.bind("<FocusOut>", push_menu_back)
root.mainloop()
这个没有边框的浮动 windows 看起来很酷。它允许我使用完整的父级 window 来显示数据库信息,并将按钮(功能)放在浮动菜单上。
@acw1668 提到了这个更简单的解决方案,即使用 .transient
(我今天发现):
from tkinter import Tk, Toplevel
root = Tk()
root.title('master')
top = Toplevel(root)
top.title('top')
top.transient(root)
root.mainloop()
所以我想出了一个完全不同的方法(它至少适用于 windows),如果 Toplevel
window 有 overrideredirect
标志设置为 True
:
from tkinter import Tk, Toplevel, Label, Entry
root = Tk()
root.title('master')
root.bind('<FocusIn>', lambda _: top.deiconify())
top = Toplevel(root)
top.title('top')
top.overrideredirect(True)
root.mainloop()
我最终使用了 Matiiss 和 acw1668 解决方案来使这个 menu/toolbar 与 Microsoft Windows 和 Linux 兼容。在下面的第二个版本中,我添加了更多代码来展示 menu/toolbar 的样子。在顶部打开另一个应用程序时,parent 和 child 都移到后面,再次选择 parent 时,return 都移到前面。 acw1668'方案减少了代码量
简单版:
from tkinter import Tk, Toplevel, Button
from sys import platform
def start_side_menu():
global menu
menu = Toplevel(root)
menu.geometry("90x300+620+123")
menu.title("top")
menu.resizable(False, False)
# Remove borders in Windows
if platform == "win32":
menu.overrideredirect(1)
# Remove borders in Linux + keep child with parent
else:
menu.wm_attributes('-type', 'splash')
# acw1998 solution to keep the child with the parent with Linux
menu.transient(root)
def push_menu_front_win(event=None):
# Matiiss solution to keep the child with the parent with Windows
menu.attributes('-topmost', True)
menu.attributes('-topmost', False)
root = Tk()
root.title('master')
root.geometry("300x300+300+100")
# Microsoft Windows OS call this function ----------
if platform == "win32":
root.bind("<FocusIn>", push_menu_front_win)
start_side_menu()
root.mainloop()
在浮动菜单上显示最终大小和按钮的较长版本:
from tkinter import Tk, Toplevel, Button
from sys import platform
def start_side_menu():
global menu
menu = Toplevel(root)
menu_width = 85
menu_height = 377
menu.title("")
menu.resizable(False, False)
# Remove borders in Windows
if platform == "win32":
r = (mon_width / 1) - (menu_width * 5.55)
t = (mon_height / 2) - (menu_height / 1.2)
menu.geometry(f'{menu_width}x{menu_height}+{int(r)}+{int(t)}')
menu.overrideredirect(1)
# Remove borders in Linux + keep child with parent
else:
r = (mon_width / 1) - (menu_width * 5.75)
t = (mon_height / 2) - (menu_height / 1.5)
menu.geometry(f'{menu_width}x{menu_height}+{int(r)}+{int(t)}')
# acw1998 solution to keep the child with the parent with Linux
menu.transient(root)
menu.wm_attributes('-type', 'splash')
# a couple of button just to show how to toolbar looks like
search_checklist_btn = Button(menu, text='SEARCH', font=('FreeSans', 11), width=11, height=2, bg="#729FCF")
search_checklist_btn.place(x=0, y=0, width=85)
save_checklist_btn = Button(menu, text='NEW', font=('FreeSans', 11), width=11, height=2, bg="#729FCF")
save_checklist_btn.place(x=0, y=47, width=85)
def push_menu_front_win(event=None):
# Matiiss solution to keep the child with the parent with Windows
menu.attributes('-topmost', True)
menu.attributes('-topmost', False)
root = Tk()
root.title('')
root.geometry("920x980+500+20")
mon_width = root.winfo_screenwidth()
mon_height = root.winfo_screenheight()
# For Microsoft Windows OS
if platform == "win32":
root.bind("<FocusIn>", push_menu_front_win)
# start the menu function
start_side_menu()
root.mainloop()
有没有办法在 parent 聚焦时把 child window 放在前面?我要解决的问题是:我有一个 parent window (root) 和一个 child window (TopLevel) 称为“菜单”。 child window 是一个浮动菜单,上面有几个按钮,titlebar-less。
如果我设置 menu.wm_attributes('-topmost', 1) 而不是 child window 所谓的“菜单”始终保持在顶部,即使我打开另一个应用程序菜单停留在最上面windows,不太实用
如果我重置 menu.wm_attributes('-topmost', 0) 并且我关注 parent window,child window 保持不变在所有其他 windows 之后,我看不到它。如果我是 运行 我的应用程序,然后必须打开另一个应用程序(例如 Dbeaver 或 Firefox),就会发生这种情况。然后我将我的应用程序放在前面,但 child 留在 Dbeaver 或 Firefox 后面。
我想做的是检测主要 window 何时聚焦,这样我就可以将 child window 放在前面,这样根和顶层在前面。
我在网上做了一些广泛的搜索。发现了很多关于检测 window 是否打开或关闭的信息,但没有关于检测 windows 是否打开的信息。
我使用 python 3.8 和 tkinter。
这是我到目前为止在该部分代码中的内容。还不能很好地工作,但已经很接近了:
def push_menu_front(event):
focus = 0
focus += 1
if focus != 0:
print("focus is 1")
menu.wm_attributes('-topmost', 1)
def push_menu_back(event):
focus = 0
focus += 1
if focus != 0:
print("focus is 0")
menu.wm_attributes('-topmost', 0)
root.bind("<FocusIn>", bring_menu_front)
root.bind("<FocusOut>", bring_menu_back)
在 Matiiss 的建议下,我获得了以下代码以用于 Windows,而不是 Linux。所以我结合了我的 Linux 版本和他的 Windows 版本,以根据所使用的操作系统工作。还有一个新版本使用的代码更少,如已接受的答案所示。
from tkinter import Tk, Toplevel
from sys import platform
def push_menu_front_win(event=None):
menu.attributes('-topmost', True)
menu.attributes('-topmost', False)
def push_menu_front(event=None):
menu.attributes('-topmost', True)
def push_menu_back(event=None):
menu.attributes('-topmost', False)
root = Tk()
root.title('master')
root.geometry("300x300+300+100")
menu = Toplevel(root)
menu.title('top')
menu.geometry("120x300+610+100")
# --------------- Select the OS used with if statement ----------
# Microsoft Windows OS
if platform == "win32":
root.bind("<FocusIn>", push_menu_front_win)
# MAC OS
elif platform == "darwin":
root.bind("<FocusIn>", push_menu_front)
root.bind("<FocusOut>", push_menu_back)
# Linux OS
elif platform == "linux" or platform == "linux2":
root.bind("<FocusIn>", push_menu_front)
root.bind("<FocusOut>", push_menu_back)
root.mainloop()
这个没有边框的浮动 windows 看起来很酷。它允许我使用完整的父级 window 来显示数据库信息,并将按钮(功能)放在浮动菜单上。
@acw1668 提到了这个更简单的解决方案,即使用 .transient
(我今天发现):
from tkinter import Tk, Toplevel
root = Tk()
root.title('master')
top = Toplevel(root)
top.title('top')
top.transient(root)
root.mainloop()
所以我想出了一个完全不同的方法(它至少适用于 windows),如果 Toplevel
window 有 overrideredirect
标志设置为 True
:
from tkinter import Tk, Toplevel, Label, Entry
root = Tk()
root.title('master')
root.bind('<FocusIn>', lambda _: top.deiconify())
top = Toplevel(root)
top.title('top')
top.overrideredirect(True)
root.mainloop()
我最终使用了 Matiiss 和 acw1668 解决方案来使这个 menu/toolbar 与 Microsoft Windows 和 Linux 兼容。在下面的第二个版本中,我添加了更多代码来展示 menu/toolbar 的样子。在顶部打开另一个应用程序时,parent 和 child 都移到后面,再次选择 parent 时,return 都移到前面。 acw1668'方案减少了代码量
简单版:
from tkinter import Tk, Toplevel, Button
from sys import platform
def start_side_menu():
global menu
menu = Toplevel(root)
menu.geometry("90x300+620+123")
menu.title("top")
menu.resizable(False, False)
# Remove borders in Windows
if platform == "win32":
menu.overrideredirect(1)
# Remove borders in Linux + keep child with parent
else:
menu.wm_attributes('-type', 'splash')
# acw1998 solution to keep the child with the parent with Linux
menu.transient(root)
def push_menu_front_win(event=None):
# Matiiss solution to keep the child with the parent with Windows
menu.attributes('-topmost', True)
menu.attributes('-topmost', False)
root = Tk()
root.title('master')
root.geometry("300x300+300+100")
# Microsoft Windows OS call this function ----------
if platform == "win32":
root.bind("<FocusIn>", push_menu_front_win)
start_side_menu()
root.mainloop()
在浮动菜单上显示最终大小和按钮的较长版本:
from tkinter import Tk, Toplevel, Button
from sys import platform
def start_side_menu():
global menu
menu = Toplevel(root)
menu_width = 85
menu_height = 377
menu.title("")
menu.resizable(False, False)
# Remove borders in Windows
if platform == "win32":
r = (mon_width / 1) - (menu_width * 5.55)
t = (mon_height / 2) - (menu_height / 1.2)
menu.geometry(f'{menu_width}x{menu_height}+{int(r)}+{int(t)}')
menu.overrideredirect(1)
# Remove borders in Linux + keep child with parent
else:
r = (mon_width / 1) - (menu_width * 5.75)
t = (mon_height / 2) - (menu_height / 1.5)
menu.geometry(f'{menu_width}x{menu_height}+{int(r)}+{int(t)}')
# acw1998 solution to keep the child with the parent with Linux
menu.transient(root)
menu.wm_attributes('-type', 'splash')
# a couple of button just to show how to toolbar looks like
search_checklist_btn = Button(menu, text='SEARCH', font=('FreeSans', 11), width=11, height=2, bg="#729FCF")
search_checklist_btn.place(x=0, y=0, width=85)
save_checklist_btn = Button(menu, text='NEW', font=('FreeSans', 11), width=11, height=2, bg="#729FCF")
save_checklist_btn.place(x=0, y=47, width=85)
def push_menu_front_win(event=None):
# Matiiss solution to keep the child with the parent with Windows
menu.attributes('-topmost', True)
menu.attributes('-topmost', False)
root = Tk()
root.title('')
root.geometry("920x980+500+20")
mon_width = root.winfo_screenwidth()
mon_height = root.winfo_screenheight()
# For Microsoft Windows OS
if platform == "win32":
root.bind("<FocusIn>", push_menu_front_win)
# start the menu function
start_side_menu()
root.mainloop()