【问题标题】:How to detect if a window is in front of other windows如何检测一个窗口是否在其他窗口前面
【发布时间】:2021-11-28 16:39:15
【问题描述】:

当父级获得焦点时,有没有办法将子窗口放在前面?我要解决的是:我有一个父窗口(根)和一个名为“菜单”的子窗口(TopLevel)。子窗口是一个浮动菜单,上面有几个按钮,并且没有标题栏。

如果我设置 menu.wm_attributes('-topmost', 1) 比名为“menu”的子窗口始终保持在顶部,即使我打开另一个应用程序,菜单也会保持在所有窗口之上,这不是很实用.

如果我重置 menu.wm_attributes('-topmost', 0) 并且我专注于父窗口,则子窗口会停留在所有其他窗口之后,并且我看不到它。如果我正在运行我的应用程序,然后必须打开另一个应用程序,例如 Dbeaver 或 Firefox,就会发生这种情况。然后我将我的应用程序放在前面,但孩子留在 Dbeaver 或 Firefox 后面。

我要做的是检测主窗口何时聚焦,这样我就可以将子窗口放在前面,这样根和顶层都在前面。

我在网上做了一些广泛的搜索。发现很多关于检测窗口是打开还是关闭,但没有关于检测窗口是否打开。

我使用 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)

【问题讨论】:

  • 将窗口绑定到&lt;FocusIn&gt; 事件,例如root.bind('&lt;FocusIn&gt;', lambda e: print(e)),你也应该提供一个minimal reproducible example
  • 谢谢马蒂斯。我会试试这个建议。很难提供一个最小的可重现示例,因为我有数千行代码,我不确定哪个部分有用,但无论如何我都会尝试。l
  • 你能看一下代码,看看是否有办法找到调整它并让它工作 100% 的时间。
  • 你有没有尝试让子窗口成为主窗口的transient窗口?
  • transient 窗口是一个子窗口,它始终位于其父窗口的顶部(z 顺序)。你不需要把注意力集中在外面的东西上。

标签: python-3.x tkinter focus


【解决方案1】:

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

这个没有边框的浮动窗口看起来很酷。它允许我使用完整的父窗口来显示数据库信息,并将按钮(功能)放在浮动菜单上。

【讨论】:

  • 仅供参考。以上代码仅适用于 Linux。上面的 Matiiss 代码在 Windows 上运行良好。他们都做同样的事情,但在不同的平台上。
  • 您可能想在您的答案中添加该信息以便更明显,您可以edit您的答案
  • 是的,我已经修改了我的,还展示了我如何包含两个版本,以便它们可以在 Windows 或 Linux 上运行。
  • 太好了,您不妨将自己的答案标记为已接受,因为它比我的更全面(包括两个系统以及如何自动区分它们)
  • 您还可以从 if 语句中删除两个绑定,您需要定义 platform 是什么
【解决方案2】:

所以@acw1668 提到了这个更简单的解决方案,即使用.transient(我今天发现的):

from tkinter import Tk, Toplevel

root = Tk()
root.title('master')

top = Toplevel(root)
top.title('top')
top.transient(root)

root.mainloop()

.transient docs (more of a definiton)

【讨论】:

  • 我不知道它存在。在我的情况下,它更简单并且在 linux 上运行良好。但是,如果孩子有 top.overrideredirect(1),它在 Windows 上不起作用。使用 overrideredirect(1) 根本不会出现 chid。我使用 overrideredirect 来删除孩子周围的边框。因此,如果您不使用 top.overrideredirect(1) 删除边框,这在 Windows 和 Linux 上都可以正常工作。
  • 我试图发布菜单/工具栏的屏幕截图,上面有所有按钮,但这个功能在 stackoverflow 中似乎无法正常工作。
【解决方案3】:

所以我想出了一种完全不同的方法(至少在 Windows 上有效),如果 Toplevel 窗口将 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()

【讨论】:

  • 非常有趣。它可以在 Windows 上运行——而不是在 Linux 上。在 Linux 上,孩子始终处于领先地位。令人惊讶的是,这两个操作系统之间存在如此大的差异。对于只使用 Microsoft Windows 的用户来说,这绝对是另一种解决方案。
【解决方案4】:

我最终同时使用了 Matiiss 和 acw1668 解决方案,以使此菜单/工具栏与 Microsoft Windows 和 Linux 兼容。在下面的第二个版本中,我添加了更多代码来显示菜单/工具栏的外观。在顶部打开另一个应用程序时,父母和孩子都会移到后面,再次选择父母时都会回到前面。 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()

【讨论】:

    猜你喜欢
    • 2015-04-03
    • 2010-10-25
    • 1970-01-01
    • 1970-01-01
    • 2011-09-12
    • 1970-01-01
    • 1970-01-01
    • 2023-01-27
    • 1970-01-01
    相关资源
    最近更新 更多