10

I created a window:

root = Tk()

and removed the titlebar:

root.overrideredirect(True)

Now the window is not on the task bar in windows. How can i show it in the task bar? (I only want to bring my window to the front if other windows are on top of mine)

Faminator
  • 888
  • 1
  • 10
  • 16
  • I don't know exactly why, but my guess is that `overrideredirect` set to true removes all the possibilities from managing the window, such as resizing or things like that... Check out the [documentation](http://effbot.org/tkinterbook/wm.htm#Tkinter.Wm.overrideredirect-method) and other places around and I hope somebody comes up with a exhaustive answer... – nbro Jun 11 '15 at 21:09
  • It is possible to do but requires calling Win32 API functions as this is not something provided by Tk. You should explain why you want this though as you might be searching for a solution to the wrong problem. – patthoyts Jun 13 '15 at 11:11
  • Related answer which doesn't use any Win32 API functions: https://stackoverflow.com/a/6662135/10742758 – Maximouse Feb 09 '20 at 11:15

2 Answers2

14

Tk does not provide a way to have a toplevel window that has overrideredirect set to appear on the taskbar. To do this the window needs to have the WS_EX_APPWINDOW extended style applied and this type of Tk window has WS_EX_TOOLWINDOW set instead. We can use the python ctypes extension to reset this but we need to note that Tk toplevel windows on Windows are not directly managed by the window manager. We have therefore to apply this new style to the parent of the windows returned by the winfo_id method.

The following example shows such a 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()
TheLizzard
  • 7,248
  • 2
  • 11
  • 31
patthoyts
  • 32,320
  • 3
  • 62
  • 93
  • 1
    i saw that the methos `GetWindowLongPtrW` and `SetWindowLongPtrW` are only for win64bit and in win32bit you have to use others, here is a way to this for 32 and 64 bit toghether? – Mat.C Apr 13 '19 at 22:56
  • Why does it not work without `root.after(10, lambda: set_appwindow(root))` ? Like a simple call like `set_appwindow(root)` does not work. – Delrius Euphoria Jul 07 '21 at 19:06
  • @CoolCloud The window needs to be mapped on-screen. This delay is sufficient but it should probably be using the `` event. eg: `bind $root [list set_appwindow $root]`. But then in the set_appwindow function you need to remove the Map binding to avoid this being called every time the window is mapped on-screen. – patthoyts Jul 10 '21 at 10:13
  • Essentially until some event processing occurs (by running mainloop) your window does't yet exist properly. Once it is mapped, it is fully created and initialized and we can set the additional window manager properties. – patthoyts Jul 10 '21 at 10:14
  • Oh, I see, so `root.update()` might also do it? – Delrius Euphoria Jul 10 '21 at 12:01
  • @patthoyts Why didn't you use `style &= ~(WS_CAPTION | WS_THICKFRAME)`? That will remove the need for the `root.overrideredirect(True)`. The problem is that `tkinter` doesn't allow a lot of operations if the window is `overrideredirect`. – TheLizzard Sep 06 '21 at 11:47
  • This is really awesome! I have no idea how it works and I'm way to lazy to read and try to understand this, it seems really complicated. But thanks so much! this is perfect – Arjuna Nov 17 '22 at 15:27
1

A simplification of @patthoyts's answer:

# 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)

The style &= ~(WS_CAPTION | WS_THICKFRAME) just removes the title bar of the window. For more info read Microsoft's documentation.


Not really needed but to make it safer use this to define the functions:

# 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

I know the question specifically says it's about Windows but for Linux, this should work.

TheLizzard
  • 7,248
  • 2
  • 11
  • 31