Python GUI 窗口在没有焦点的情况下保持在顶部

Posted

技术标签:

【中文标题】Python GUI 窗口在没有焦点的情况下保持在顶部【英文标题】:Python GUI window stay on top without focus 【发布时间】:2017-12-01 04:04:33 【问题描述】:

我正在尝试在模仿messagease keyboard 的python 中编写我自己的屏幕键盘,它结合了滑动和点击来输入字符。 我打算在 Windows 上使用它,但如果可能的话,我想尝试让它跨平台。

目前我正在尝试找到一种方法让窗口保持在顶部,同时将光标/焦点保持在前一个窗口中,以便当我模拟按键(使用 pyautogui)时,它们会出现在正确的窗口中。

使用 tkinter,我可以使用这行代码将窗口保持在顶部,但我不知道如何保持前一个窗口处于焦点。

root.wm_attributes("-topmost", 1)

为了发送按键,我使用的是 pyautogui:

pyautogui.typewrite("characters to write")

我没有太多用 python 编写 gui 的经验,所以如果在不同的框架中更容易完成,我愿意切换。

所以我的问题是这样的:

如何制作一个可以与之交互(触摸、滑动等)但不会将键盘焦点从另一个窗口/应用程序移开的 gui 窗口?

【问题讨论】:

你的问题很宽泛,另外你评论你在GUI方面的一点经验,我建议你专注于单个库。 我提到尝试不同的库和我的一点经验表明我没有设置在 tkinter 上,如果它们更适合,我愿意使用其他库。 这听起来像是在推荐图书馆,对吗? 我不确定你的问题是什么。我只是想找到一种方法来在其他窗口之上显示一个窗口,同时将焦点和光标留在另一个窗口中。如果这可以在 tinker 中完成,那就太好了,但如果只能在其他库中完成,我也愿意在其中找到解决方案。 【参考方案1】:

这只是窗口,但要使用 python 设置窗口的样式以不获得焦点,您可以使用 ctypes:

import tkinter as tk
from ctypes import windll, wintypes

GWL_STYLE = -16
GWL_EXSTYLE = -20
WS_CHILD = 0x40000000
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080
WS_EX_NOACTIVATE = 0x08000000

SWP_FRAMECHANGED = 0x0020
SWP_NOACTIVATE = 0x0010
SWP_NOMOVE = 0x0002
SWP_NOSIZE = 0x0001

# write short names for functions and specify argument and return types
GetWindowLong = windll.user32.GetWindowLongW
GetWindowLong.restype = wintypes.ULONG
GetWindowLong.argtpes = (wintypes.HWND, wintypes.INT)

SetWindowLong = windll.user32.SetWindowLongW
SetWindowLong.restype = wintypes.ULONG
SetWindowLong.argtpes = (wintypes.HWND, wintypes.INT, wintypes.ULONG)

SetWindowPos = windll.user32.SetWindowPos

def find_root_window(win): # takes tkinter window ref
    w_id = win.winfo_id() # gets handle
    style = GetWindowLong(w_id, GWL_STYLE) # get existing style
    newstyle = style & ~WS_CHILD # remove child style
    res = SetWindowLong(w_id, GWL_STYLE, newstyle) # set new style
    res = SetWindowPos(w_id, 0, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)
    hwnd = int(root.wm_frame(), 16) # find handle of parent
    res = SetWindowLong(w_id, GWL_STYLE, style) # set back to old style
    res = SetWindowPos(w_id, 0, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)
    return hwnd # return parents handle

def set_no_focus(hwnd):
    style = GetWindowLong(hwnd, GWL_EXSTYLE) # get existing style
    style = style & ~WS_EX_TOOLWINDOW # remove toolwindow style
    style = style | WS_EX_NOACTIVATE | WS_EX_APPWINDOW
    res = SetWindowLong(hwnd, GWL_EXSTYLE, style)
    res = SetWindowPos(hwnd, 0, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)

def push_me():
    print('you pushed me!')

def focus_me(event):
    root.focus_force()

root = tk.Tk()
root.wm_attributes("-topmost", 1)
tk.Button(root, text="Push me", command=push_me).pack()
e = tk.Entry(root)
e.pack()
e.bind('<Button-1>', focus_me) # if we have a widget that must have focus it needs to be bound
root.update() # for some reason, window style is messed with after window creation, update to get past this
hwnd = find_root_window(root)
if hwnd:
    set_no_focus(hwnd)
root.mainloop()

您需要根据应用程序环境使用正确的 Get/Set 函数。对于 32 位 Windows,您似乎需要使用:GetWindowLongWSetWindowLongWGetWindowLongASetWindowLongA

但需要 64 位:GetWindowLongPtrWSetWindowLongPtrWGetWindowLongPtrASetWindowLongPtrA 见this

【讨论】:

以上是关于Python GUI 窗口在没有焦点的情况下保持在顶部的主要内容,如果未能解决你的问题,请参考以下文章

在没有窗口/GUI 的情况下运行 Pygame

在 Java 中没有焦点的情况下监听输入

在没有任何窗口的情况下保持和 NSDocument 打开

Python win32gui 获取窗口中虚拟光标的当前位置

如何在不从当前活动选项卡中获取焦点的情况下将子窗口添加到 QMdiArea(设置为 TAB 模式)?

在 Python GUI 中嵌入 C++ 程序