Tkinter,Windows:如何在没有标题栏的 Windows 任务栏中查看窗口?

Posted

技术标签:

【中文标题】Tkinter,Windows:如何在没有标题栏的 Windows 任务栏中查看窗口?【英文标题】:Tkinter, Windows: How to view window in windows task bar which has no title bar? 【发布时间】:2015-08-27 11:25:27 【问题描述】:

我创建了一个窗口:

root = Tk()

并删除了标题栏:

root.overrideredirect(True)

现在窗口不在窗口的任务栏上。如何在任务栏中显示它? (如果我的上面有其他窗户,我只想把我的窗户放在前面)

【问题讨论】:

我不知道确切原因,但我猜overrideredirect 设置为 true 会消除管理窗口的所有可能性,例如调整大小或类似的事情...查看@ 987654321@ 和其他地方,我希望有人想出一个详尽的答案...... 可以这样做,但需要调用 Win32 API 函数,因为这不是 Tk 提供的。您应该解释为什么要这样做,因为您可能正在寻找错误问题的解决方案。 不使用任何 Win32 API 函数的相关答案:***.com/a/6662135/10742758 【参考方案1】:

Tk 没有提供一种方法来让具有 overrideredirect 设置的***窗口出现在任务栏上。为此,窗口需要应用WS_EX_APPWINDOW 扩展样式,并且这种类型的Tk 窗口设置了WS_EX_TOOLWINDOW。我们可以使用 python ctypes 扩展来重置它,但我们需要注意 Windows 上的 Tk ***窗口不直接由窗口管理器管理。因此,我们必须将此新样式应用于winfo_id 方法返回的窗口的父级。

以下示例显示了这样一个窗口。

import tkinter as tk
import tkinter.ttk as ttk
from ctypes import windll

GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080

def set_appwindow(root):
    hwnd = windll.user32.GetParent(root.winfo_id())
    style = windll.user32.GetWindowLongPtrW(hwnd, GWL_EXSTYLE)
    style = style & ~WS_EX_TOOLWINDOW
    style = style | WS_EX_APPWINDOW
    res = windll.user32.SetWindowLongPtrW(hwnd, GWL_EXSTYLE, style)
    # re-assert the new window style
    root.withdraw()
    root.after(10, root.deiconify)

def main():
    root = tk.Tk()
    root.wm_title("AppWindow Test")
    button = ttk.Button(root, text='Exit', command=root.destroy)
    button.place(x=10, y=10)
    root.overrideredirect(True)
    root.after(10, set_appwindow, root)
    root.mainloop()

if __name__ == '__main__':
    main()

【讨论】:

我看到GetWindowLongPtrWSetWindowLongPtrW 方法只适用于win64bit,而在win32bit 中你必须使用其他方法,这里有一种方法可以同时用于32 位和64 位? 为什么没有 root.after(10, lambda: set_appwindow(root)) 就不行?像set_appwindow(root) 这样的简单调用不起作用。 @CoolCloud 窗口需要在屏幕上映射。这个延迟是足够的,但它可能应该使用<Map> 事件。例如:bind $root <Map> [list set_appwindow $root]。但是在 set_appwindow 函数中,您需要删除 Map 绑定以避免每次在屏幕上映射窗口时都会调用它。 基本上直到某些事件处理发生(通过运行主循环)你的窗口还没有正确存在。一旦它被映射,它就被完全创建和初始化,我们可以设置额外的窗口管理器属性。 哦,我明白了,所以root.update() 也可以这样做?【参考方案2】:

@patthoyts 回答的简化:

# Partially taken from: https://***.com/a/2400467/11106801
from ctypes.wintypes import BOOL, HWND, LONG
import tkinter as tk
import ctypes

# Defining functions
GetWindowLongPtrW = ctypes.windll.user32.GetWindowLongPtrW
SetWindowLongPtrW = ctypes.windll.user32.SetWindowLongPtrW

def get_handle(root) -> int:
    root.update_idletasks()
    # This gets the window's parent same as `ctypes.windll.user32.GetParent`
    return GetWindowLongPtrW(root.winfo_id(), GWLP_HWNDPARENT)

# Constants
GWL_STYLE = -16
GWLP_HWNDPARENT = -8
WS_CAPTION = 0x00C00000
WS_THICKFRAME = 0x00040000


if __name__ == "__main__":
    root = tk.Tk()

    hwnd:int = get_handle(root)
    style:int = GetWindowLongPtrW(hwnd, GWL_STYLE)
    style &= ~(WS_CAPTION | WS_THICKFRAME)
    SetWindowLongPtrW(hwnd, GWL_STYLE, style)

style &= ~(WS_CAPTION | WS_THICKFRAME) 只是删除了窗口的标题栏。更多信息请阅读Microsoft's documentation。


不是真的需要,但为了更安全,使用它来定义函数:

# Defining types
INT = ctypes.c_int
LONG_PTR = ctypes.c_long

def _errcheck_not_zero(value, func, args):
    if value == 0:
        raise ctypes.WinError()
    return args

# Defining functions
GetWindowLongPtrW = ctypes.windll.user32.GetWindowLongPtrW
GetWindowLongPtrW.argtypes = (HWND, INT)
GetWindowLongPtrW.restype = LONG_PTR
GetWindowLongPtrW.errcheck = _errcheck_not_zero

SetWindowLongPtrW = ctypes.windll.user32.SetWindowLongPtrW
SetWindowLongPtrW.argtypes = (HWND, INT, LONG_PTR)
SetWindowLongPtrW.restype = LONG_PTR
SetWindowLongPtrW.errcheck = _errcheck_not_zero

我知道这个问题专门说它与 Windows 有关,但对于 Linux,this 应该可以工作。

【讨论】:

以上是关于Tkinter,Windows:如何在没有标题栏的 Windows 任务栏中查看窗口?的主要内容,如果未能解决你的问题,请参考以下文章

如何创建一个有边框但没有标题栏的表单? (如 Windows 7 上的音量控制)

设置 tkinter ttk 框架的背景颜色

如何使用 python Tkinter 隐藏 Windows 控制台?

Tkinter 菜单没有选项卡 [仅限 Windows]

Tkinter overrideredirect 不再接收事件绑定

如何在 Windows 下更改 Tkinter 中菜单的颜色?