python – App’s icon is not showing in taskbar because of Custom title bar?

Disclaimer, this may not be the best approach to achieve OP`s goal, but I’m sticking to this example because:

  • There are several question with this example on StackOverflow
  • It shows a basic knowledge that is important for deeper digging.

The improvement of this code to the original is that the style applies again after you iconify it and bring that windows back up.

See the twitch of this example is how the taskbar keeps track of the application. There is a good article of Raymond Chen (Ms-Developer) where he quotes this:

“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.”

Anyway, the basic idea is the following:

  1. Get the handle of the window. (Parent window of the root in this case for tkinter related reasons)
  2. get the current style in a hexadecimal code
  3. alter the hexadecimal code to your need with a bitwise operation
  4. apply the altered style to the window

I added the following code at the beginning of your script:

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

This code at the end of your script:

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

and added the line set_appwindow() in def frame_map(event=None):. Als I had to implement another additional two lines in def minieme1_(event=None): to update the hasstylevariable.

So the overall implementation of this approach was possible to have your window ready and withdrawn for the described reason. An additional variable to avoid an infinite loop while you alter the style of your window that is either True while it is shown or False while it is iconyfied, alongside your overrideredirect method.

Full Code

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

Leave a Comment