由于自定义标题栏,应用程序的图标未显示在任务栏中?

App's icon is not show in taskbar because of Custom title bar?

我正在创建一个代码编辑器,我想在其中自定义标题栏以匹配我的应用程序主题,我已经创建了一个自定义标题栏,但我的应用程序没有显示在任务栏中

如果有任何外部用于此,请告诉我

什么我要学习解决我的问题请告诉我

如何在任务栏上显示应用程序图标,其实我不知道 如果你能解决它 请帮助我解决问题

这是我的完整代码(不是完整代码,而是真实代码的简短版本):-

from tkinter import*
def move(e):
        xwin = root.winfo_x()
        ywin = root.winfo_y()
        startx = e.x_root
        starty = e.y_root
        ywin -= starty
        xwin -= startx
        def move_(e):
            root.geometry(f"+{e.x_root + xwin}+{e.y_root + ywin}")
        startx = e.x_root
        starty = e.y_root
        frame.bind("<B1-Motion>",move_)
def minieme1_(event=None):
        root.update_idletasks()
        root.overrideredirect(False)
        root.state("iconic")
def frame_map(event=None):
        root.update_idletasks()
        root.overrideredirect(True)
        root.state("normal")
        root.call()
def minimefunction(event=None):
        global size
        if size:
            root.geometry(f"{screen_width}x{screen_height-40}+0+0")
            minimsi.config(text=" \u2752 ")
            size = False
        else:
            root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
            minimsi.config(text=" \u25a0 ")
            size = True
            
def quitApp():
    root.destroy()
def close_blink(event=None):
    close_button.config(bg="red")
def close_blink1(event=None):
    close_button.config(bg="gray19")
def minimsi_blink(event=None):
    minimsi.config(bg="gray29")
def minimsi_blink1(event=None):
    minimsi.config(bg="gray19")
def minimsi1_blink(event=None):
    minimsi1.config(bg="gray29")
def minimsi1_blink1(event=None):
    minimsi1.config(bg="gray19")
root = Tk()
size = True
app_width = 600
app_height = 500
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
print(screen_width,screen_height)
x = (screen_width/2) - (app_width/2)
y = (screen_height/2) - (app_height/2)
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
root.overrideredirect(True)
frame = Frame(root,bg="gray29")

Label(frame,text="My App",font="Consolas 15",bg="gray29",fg="white").pack(side=LEFT,padx=10)
close_button = Button(frame,text=" X ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=quitApp)
close_button.pack(side=RIGHT)
minimsi = Button(frame,text=" \u25a0 ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minimefunction)
minimsi.pack(side=RIGHT)
minimsi1 = Button(frame,text=" - ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minieme1_)
minimsi1.pack(side=RIGHT)
frame.pack(fill=X)

yscroll = Scrollbar(orient=VERTICAL)
yscroll.pack(side=RIGHT,fill=Y)
editor = Text(font="Consolas 15",bg="gray19",fg="white",insertbackground="white",borderwidth=0,yscrollcommand=yscroll.set)
yscroll.config(command=editor.yview)
editor.pack(expand=True,fill=BOTH)
root.config(bg="gray19")

frame.bind("<Button-1>",move)
frame.bind("<B1-Motion>",move)
# minimsi1.bind("<Button-1>",minieme1_)
frame.bind("<Map>",frame_map)
close_button.bind("<Enter>",close_blink)
close_button.bind("<Leave>",close_blink1)
minimsi.bind("<Enter>",minimsi_blink)
minimsi.bind("<Leave>",minimsi_blink1)
minimsi1.bind("<Enter>",minimsi1_blink)
minimsi1.bind("<Leave>",minimsi1_blink1)


root.mainloop()


您可以在这张图片中看到问题:-

简短的回答是:您不能仅使用 Tkinter 来做到这一点。您 可以 通过 self.iconbitmap(path_to_icon) 设置 window 的图标,但为了拥有独特的任务栏图标,您需要将您的应用程序编译成 Windows可执行文件。

here

编辑:不相关,但作为实践,最好避免明星导入,例如from tkinter import * - 最好使用 import tkinter as tk 之类的东西,然后在 Tkinter 对象前加上 tk. 前缀以避免命名空间污染!

免责声明,这可能不是实现 OP 目标的最佳方法,但我坚持使用 因为:

  • Whosebug 上的这个例子有几个问题
  • 它展示了对深入挖掘很重要的基础知识。

此代码对原始代码的改进在于,在图标化后再次应用样式并将 windows 备份。

看到这个例子的抽搐是任务栏如何跟踪应用程序。有一个 good article 的 Raymond Chen (Ms-Developer) 他引用了这个:

“If you want to dynamically change a window’s style to one that doesn’t support visible taskbar buttons, you must hide the window first (by calling ShowWindow with SW_HIDE), change the window style, and then show the window.”

并且还指出了某些程序的弱点以及为什么它们会丢失或出现空白任务栏图标:

  • Window is taskbar-eligible.
  • Window becomes visible ? taskbar button created.
  • Window goes taskbar-ineligible.
  • Window becomes hidden ? since the window is not taskbar-eligible at this point, the taskbar ignores it.

总之,基本思路如下:

  1. 获得handle of the window。 (由于 tkinter 相关原因,本例中根的父级 window)
  2. get the current style in a hexadecimal code
  3. 通过按位运算根据需要更改十六进制代码
  4. apply the altered style 到 window

我在您的脚本开头添加了以下代码:

from ctypes import windll

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

def set_appwindow():
    global hasstyle
    if not hasstyle:
        hwnd = windll.user32.GetParent(root.winfo_id())
        style = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
        style = style & ~WS_EX_TOOLWINDOW
        style = style | WS_EX_APPWINDOW
        res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, style)
        root.withdraw()
        root.after(100, lambda:root.wm_deiconify())
        hasstyle=True

脚本末尾的代码:

hasstyle = False
root.update_idletasks()
root.withdraw()
set_appwindow()

并在 def frame_map(event=None): 中添加了行 set_appwindow()。此外,我还必须在 def minieme1_(event=None): 中实现另外两行来更新 hasstyle 变量。

因此,此方法的整体实施可能会让您的 window 准备就绪并出于所述原因撤回。一个附加变量,用于在您更改 window 的样式时避免无限循环,即在显示时为 True 或在图标化时为 False ,以及您的 overrideredirect 方法。

完整代码

from tkinter import*
from ctypes import windll

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

def set_appwindow():
    global hasstyle
    if not hasstyle:
        hwnd = windll.user32.GetParent(root.winfo_id())
        style = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
        style = style & ~WS_EX_TOOLWINDOW
        style = style | WS_EX_APPWINDOW
        res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, style)
        root.withdraw()
        root.after(100, lambda:root.wm_deiconify())
        hasstyle=True
    
def move(e):
    xwin = root.winfo_x()
    ywin = root.winfo_y()
    startx = e.x_root
    starty = e.y_root
    ywin -= starty
    xwin -= startx
    def move_(e):
        root.geometry(f"+{e.x_root + xwin}+{e.y_root + ywin}")
    startx = e.x_root
    starty = e.y_root
    frame.bind("<B1-Motion>",move_)
def minieme1_(event=None):
    global hasstyle
    root.update_idletasks()
    root.overrideredirect(False)
    root.state("iconic")
    hasstyle = False
def frame_map(event=None):
    root.overrideredirect(True)
    root.update_idletasks()
    set_appwindow()
    root.state("normal")
def minimefunction(event=None):
    global size
    if size:
        root.geometry(f"{screen_width}x{screen_height-40}+0+0")
        minimsi.config(text=" \u2752 ")
        size = False
    else:
        root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
        minimsi.config(text=" \u25a0 ")
        size = True
            
def quitApp():
    root.destroy()
def close_blink(event=None):
    close_button.config(bg="red")
def close_blink1(event=None):
    close_button.config(bg="gray19")
def minimsi_blink(event=None):
    minimsi.config(bg="gray29")
def minimsi_blink1(event=None):
    minimsi.config(bg="gray19")
def minimsi1_blink(event=None):
    minimsi1.config(bg="gray29")
def minimsi1_blink1(event=None):
    minimsi1.config(bg="gray19")
root = Tk()
size = True
app_width = 600
app_height = 500
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
print(screen_width,screen_height)
x = (screen_width/2) - (app_width/2)
y = (screen_height/2) - (app_height/2)
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
root.overrideredirect(True)
frame = Frame(root,bg="gray29")

Label(frame,text="My App",font="Consolas 15",bg="gray29",fg="white").pack(side=LEFT,padx=10)
close_button = Button(frame,text=" X ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=quitApp)
close_button.pack(side=RIGHT)
minimsi = Button(frame,text=" \u25a0 ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minimefunction)
minimsi.pack(side=RIGHT)
minimsi1 = Button(frame,text=" - ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minieme1_)
minimsi1.pack(side=RIGHT)
frame.pack(fill=X)

yscroll = Scrollbar(orient=VERTICAL)
yscroll.pack(side=RIGHT,fill=Y)
editor = Text(font="Consolas 15",bg="gray19",fg="white",insertbackground="white",borderwidth=0,yscrollcommand=yscroll.set)
yscroll.config(command=editor.yview)
editor.pack(expand=True,fill=BOTH)
root.config(bg="gray19")

frame.bind("<Button-1>",move)
frame.bind("<B1-Motion>",move)
# minimsi1.bind("<Button-1>",minieme1_)
frame.bind("<Map>",frame_map)
close_button.bind("<Enter>",close_blink)
close_button.bind("<Leave>",close_blink1)
minimsi.bind("<Enter>",minimsi_blink)
minimsi.bind("<Leave>",minimsi_blink1)
minimsi1.bind("<Enter>",minimsi1_blink)
minimsi1.bind("<Leave>",minimsi1_blink1)

hasstyle = False
root.update_idletasks()
root.withdraw()
set_appwindow()


root.mainloop()

Tested with python 3.10 and windows 11

您可以使用隐藏根目录 window 让系统 window 管理器在任务栏中显示一个图标,并将自定义 window 作为 隐藏根 window 的瞬态子 window 以模拟那些图标化和去图标化效果。

您需要在自定义 window 上绑定 <Button> 事件,以便在单击时将其置于最前面。还需要在隐藏根 window 上绑定 <FocusIn> 事件以将焦点移至自定义 window。

以下是所需的更改:

...
def minieme1_(event=None):
    # iconify hidden root window will iconify the custom window as well
    hidden_root.iconify()
...
def quitApp():
    # destroy the hidden root window will destroy the custom window as well
    hidden_root.destroy()
...

def on_focus(event):
    # bring custom window to front
    root.lift()

# create a hidden root window
hidden_root = Tk()
hidden_root.attributes('-alpha', 0)
hidden_root.title('My App')
# you can use hidden_root.iconbitmap(...) or hidden_root.iconphoto(...) to set the icon image

# use Toplevel instead of Tk for the custom window
root = Toplevel(hidden_root)
# make it a transient child window of hidden root window
root.transient(hidden_root)

root.bind('<Button>', on_focus)
hidden_root.bind('<FocusIn>', on_focus)
...
#frame.bind("<Map>",frame_map) # it is not necessary and frame_map() is removed as well
...