我创建了一个窗口:
root = Tk()
并删除了标题栏:
root.overrideredirect(True)
现在窗口不在Windows 的任务栏上。如何在任务栏中显示它? (如果其他窗口在我的上面,我只想将我的窗口带到前面)
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()
@patthoyts 答案的简化:
# Partially taken from: https://stackoverflow.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 的文档。
并不是真正需要,但为了更安全,使用它来定义函数:
# 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 应该有效。
from tkinter import *
from ctypes import windll
import win32gui
import win32con
from win32api import GetMonitorInfo, MonitorFromPoint
def get_taskbar_height():
monitor_info = GetMonitorInfo(MonitorFromPoint((0, 0)))
monitor_area = monitor_info.get("Monitor")
work_area = monitor_info.get("Work")
taskbar_height = monitor_area[3] - work_area[3]
return taskbar_height
class CustomTitle():
"""
Ex:
win = Tk()
titleBar = CustomTitle(win,title_text = 'Hello,World!' , bg = "#000000" , fg = '#ffffff')
titleBar.resizeable = True
titleBar.packBar()
win.mainloop()
Note:
Try to Give Color value in Hex and the 3rd car should be number
#7a4e7a
"""
global win
def __init__(self,win,title_text='Custom Title Bar',
bg='black',fg="white",binding_bg="gray",
font_style = ('Candara',13) , resizable=True,width=3,height=5):
# deactivating main title bar
self._win = win
win.title(title_text)
self.binding_bg=binding_bg
self.font_style = font_style
self.resizeable = resizable
self.moveable = True
self.btnwidth = width
self.height = height
# props
self.bg = bg
self._maximized = False
self._win_width = win.winfo_width()
self._win_height = win.winfo_height()
self._scr_width = win.winfo_screenwidth()
self._scr_height = win.winfo_screenheight()
self._addWidget(title_text,bg,fg)
self.y = win.winfo_y()
def packBar(self):
self._title_bar.pack(fill=X)
self._checkAbility()
self._win.overrideredirect(1)
self._finilize()
def _checkAbility(self):
if not self.resizeable:
self._maximize_btn.config(state=DISABLED)
else:
self._resizey_widget.pack(side=BOTTOM,ipadx=.1,fill=X)
self._resizex_widget.pack(side=RIGHT,ipadx=.1,fill=Y)
def _maximize_win(self):
if not self._maximized or self.y<=0:
self._past_size = win.geometry()
self._win.geometry(f"{self._scr_width}x{self._scr_height-get_taskbar_height()//2}+{0}+{0}")
self._maximize_btn.config(text = '🗗')
self.moveable = False
else:
self._win.geometry(self._past_size)
self._maximize_btn.config(text = '🗖')
self.moveable = True
self._maximized = not self._maximized
def _minimize(self):
Minimize = win32gui.GetForegroundWindow()
win32gui.ShowWindow(Minimize, win32con.SW_MINIMIZE)
def _setIconToTaskBar(self,mainWindow):
GWL_EXSTYLE = -20
WS_EX_APPWINDOW = 0x00040000
WS_EX_TOOLWINDOW = 0x00000080
# Magic
hwnd = windll.user32.GetParent(mainWindow.winfo_id())
stylew = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
stylew = stylew & ~WS_EX_TOOLWINDOW
stylew = stylew | WS_EX_APPWINDOW
res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, stylew)
mainWindow.wm_withdraw()
mainWindow.after(1, mainWindow.wm_deiconify)
def _addWidget(self,title_text,bg,fg):
self._title_bar = Frame(self._win,bd=1,bg=bg,height=self.height,)
self._title_text = Label(self._title_bar,text=title_text,height=self.height,
bg=bg,fg=fg,font=self.font_style)
self._title_text.pack(side=LEFT,padx=4,pady=3)
self._title_text.bind("<B1-Motion>",self._drag)
self._close_btn = Button(self._title_bar,text = '❌',height=self.height,
bd=0,bg=bg,fg=fg,width=self.btnwidth,
font=self.font_style,command=self._win.destroy)
self._close_btn.pack(side=RIGHT,fill=Y)
self._maximize_btn = Button(self._title_bar,text="🗖",height=self.height,
bd=0,bg=bg,fg=fg,width=self.btnwidth,
font=self.font_style,command=self._maximize_win)
self._maximize_btn.pack(side=RIGHT,fill=Y)
self._minimize_btn = Button(self._title_bar,text="_",bd=0,height=self.height,
bg=bg,fg=fg,width=self.btnwidth,
font=self.font_style,command=self._minimize)
self._minimize_btn.pack(side=RIGHT,fill=Y)
self._title_bar.bind('<Button-1>', self._drag)
self._resizex_widget = Frame(self._win,cursor='sb_h_double_arrow')
self._resizex_widget.bind("<B1-Motion>",self._resizex)
self._resizey_widget = Frame(self._win,cursor='sb_v_double_arrow')
self._resizey_widget.bind("<B1-Motion>",self._resizey)
self._hover_effect()
def _hover_effect(self):
def change_bg(which_one,bg = self.binding_bg):
which_one.config(bg=bg)
def restore_bg(which_one):
which_one.config(bg=self.bg)
self._maximize_btn.bind('<Enter>',lambda event: change_bg(self._maximize_btn))
self._maximize_btn.bind('<Leave>',lambda event: restore_bg(self._maximize_btn))
self._minimize_btn.bind('<Enter>',lambda event: change_bg(self._minimize_btn))
self._minimize_btn.bind('<Leave>',lambda event: restore_bg(self._minimize_btn))
self._close_btn.bind('<Enter>',lambda event: change_bg(self._close_btn,bg='red'))
self._close_btn.bind('<Leave>',lambda event: restore_bg(self._close_btn))
def _finilize(self):
self._win.after(1, lambda: self._setIconToTaskBar(self._win))
def _drag(self,event):
if self.moveable:
xwin = win.winfo_x()
ywin = win.winfo_y()
print(ywin)
startx = event.x_root
starty = event.y_root
ywin = ywin - starty
xwin = xwin - startx
def _move_window(event): # runs when window is dragged
win.geometry(f'+{event.x_root + xwin}+{event.y_root + ywin}')
def _release_window(event): # runs when window is released
win.config(cursor="arrow")
self._title_bar.bind('<B1-Motion>', _move_window)
self._title_bar.bind('<ButtonRelease-1>', _release_window)
self._title_text.bind('<B1-Motion>', _move_window)
self._title_text.bind('<ButtonRelease-1>', _release_window)
else:
pass
def _resizex(self,event):
xwin = win.winfo_x()
difference = (event.x_root - xwin) - win.winfo_width()
if win.winfo_width() > 150 : # 150 is the minimum width for the window
try:
win.geometry(f"{ win.winfo_width() + difference }x{ win.winfo_height() }")
except:
pass
else:
if difference > 0: # so the window can't be too small (150x150)
try:
win.geometry(f"{ win.winfo_width() + difference }x{ win.winfo_height() }")
except:
pass
def _resizey(self,event):
ywin = win.winfo_y()
difference = (event.y_root - ywin) - win.winfo_height()
if win.winfo_height() > 150: # 150 is the minimum height for the window
try:
win.geometry(f"{ win.winfo_width() }x{ win.winfo_height() + difference}")
except:
pass
else:
if difference > 0: # so the window can't be too small (150x150)
try:
win.geometry(f"{ win.winfo_width() }x{ win.winfo_height() + difference}")
except:
pass
# Example usage
win = Tk()
win.geometry("300x200")
win.eval(f'tk::PlaceWindow {win.winfo_pathname(win.winfo_id())} center')
titleBar = CustomTitle(win,title_text = 'Hello,World!' , bg = "blue" ,
fg = 'white' ,binding_bg="lightblue",font_style=("Candress",13)
,resizable=True,width = 5,height=1)
titleBar.packBar()
win.mainloop()