Tkinter,Windows:如何在没有标题栏的windows任务栏中查看window?
Tkinter, Windows: How to view window in windows task bar which has no title bar?
我创建了一个 window:
root = Tk()
并删除了标题栏:
root.overrideredirect(True)
现在 window 不在 windows 的任务栏上。我怎样才能在任务栏中显示它? (如果其他 windows 在我的上面,我只想把我的 window 放在前面)
Tk 不提供一种方法来使顶层 window 具有 overrideredirect 设置为出现在任务栏上。为此,window 需要应用 WS_EX_APPWINDOW 扩展样式,而这种类型的 Tk window 设置了 WS_EX_TOOLWINDOW。我们可以使用 python ctypes 扩展来重置它,但我们需要注意 Windows 上的 Tk 顶层 windows 不直接由 window 管理器管理。因此,我们必须将这种新样式应用于 winfo_id
方法返回的 windows 的父级。
下面的例子展示了这样一个window。
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()
@patthoyts 回答的简化:
# Partially taken from: https://whosebug.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)
只是删除了window的标题栏。有关详细信息,请阅读 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 应该有效。
我创建了一个 window:
root = Tk()
并删除了标题栏:
root.overrideredirect(True)
现在 window 不在 windows 的任务栏上。我怎样才能在任务栏中显示它? (如果其他 windows 在我的上面,我只想把我的 window 放在前面)
Tk 不提供一种方法来使顶层 window 具有 overrideredirect 设置为出现在任务栏上。为此,window 需要应用 WS_EX_APPWINDOW 扩展样式,而这种类型的 Tk window 设置了 WS_EX_TOOLWINDOW。我们可以使用 python ctypes 扩展来重置它,但我们需要注意 Windows 上的 Tk 顶层 windows 不直接由 window 管理器管理。因此,我们必须将这种新样式应用于 winfo_id
方法返回的 windows 的父级。
下面的例子展示了这样一个window。
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()
@patthoyts 回答的简化:
# Partially taken from: https://whosebug.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)
只是删除了window的标题栏。有关详细信息,请阅读 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 应该有效。