How to display a custom Tkinter window (with overrideredirect
true) on top of other opened windows when clicking from the taskbar icon? My code below works (maximize not implemented yet) except when this Tkinter window is overlapped by other non-Tkinter windows. For instance, when this Tkinter window is situated below two windows (of other programs) and when called (by clicking the taskbar icon), it will take 2 clicks on that icon before this window will appear on top of those 2 windows. I want to bring my window automatically on top when its taskbar icon clicked.
I based my code from this answer and stitched this workaround for taskbar icon. I tried searching for any Tkinter code that deals on taskbar icon click event but found nothing.
import tkinter as tk
from tkinter import ttk
def get_pos(event):
global xwin
global ywin
xwin = event.x
ywin = event.y
def move_window(event):
root.geometry(f'+{event.x_root - xwin}+{event.y_root - ywin}')
def quit():
root.destroy()
#window contents
root = tk.Tk()
container = tk.Toplevel(root)
root.overrideredirect(True)
#default window dimension
root.geometry('350x150+200+200')
root.minsize(350, 150)
container.attributes("-alpha",0.0)
back_ground = "#2c2c2c"
#minimize btn binding
def onRootIconify(event): root.withdraw()
container.bind("<Unmap>", onRootIconify)
root.bind("<Unmap>", onRootIconify)
def onRootDeiconify(event): root.deiconify()
container.bind("<Map>", onRootDeiconify)
root.bind("<Map>", onRootDeiconify)
#title bar
title_bar = tk.Frame(root, bg=back_ground, bd=1,
highlightcolor=back_ground,
highlightthickness=0)
#minimize btn
minimize_btn = tk.Button(title_bar, text='🗕', bg=back_ground, padx=5, pady=2,
bd=0, font="bold", fg='white', width=2,
activebackground="red",
activeforeground="white",
highlightthickness=0,
command=lambda: container.wm_state('iconic'))
#maximize btn
maximize_btn = tk.Button(title_bar, text='🗖', bg=back_ground, padx=5, pady=2,
bd=0, font="bold", fg='white', width=2,
activebackground="red",
activeforeground="white",
highlightthickness=0,
command=None)
#close btn
close_button = tk.Button(title_bar, text='🗙', bg=back_ground, padx=5, pady=2,
bd=0, font="bold", fg='white', width=2,
activebackground="red",
activeforeground="white",
highlightthickness=0,
command= quit)
#window title
title_window = "Untitled window"
title_name = tk.Label(title_bar, text=title_window, font="Arial 12", bg=back_ground, fg="white")
#main area of the window
window = tk.Frame(root, bg="white", highlightthickness=1, highlightbackground=back_ground)
txt = tk.Label(window, bg='white', text="Prototype window").pack(anchor="center")
# pack the widgets
title_bar.pack(fill='x', side=tk.TOP)
title_name.pack(side='left', padx=5)
close_button.pack(side='right')
maximize_btn.pack(side=tk.RIGHT)
minimize_btn.pack(side=tk.RIGHT)
window.pack(fill='both', expand=True, side=tk.TOP)
# bind title bar motion to the move window function
title_bar.bind("<B1-Motion>", move_window)
title_bar.bind("<Button-1>", get_pos)
#workaround to enable window dragging on window title text
title_name.bind("<B1-Motion>", move_window)
title_name.bind("<Button-1>", get_pos)
minimize_btn.bind('<Enter>', lambda x: minimize_btn.configure(bg='#777777'))
minimize_btn.bind('<Leave>', lambda x: minimize_btn.configure(bg=back_ground))
maximize_btn.bind('<Enter>', lambda x: maximize_btn.configure(bg='#777777'))
maximize_btn.bind('<Leave>', lambda x: maximize_btn.configure(bg=back_ground))
close_button.bind('<Enter>', lambda x: close_button.configure(bg='red'))
close_button.bind('<Leave>',lambda x: close_button.configure(bg=back_ground))
root.mainloop()
The custom window:
After looking for related answers and recommendations of Coder, I finally solved my problem. In my solution, I used the technique from this answer. My finished code does not use any invisible Tkinter window and heavily utilizes ctypes.windll
(hence my code is only limited to Windows). The logic for minimize, maximize, and close window are finished as well.
I managed to solve the following:
#https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowpos
#https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow
import tkinter as tk
from tkinter import ttk
from ctypes import windll
def set_appwindow():
global hasstyle
GWL_EXSTYLE=-20
WS_EX_APPWINDOW=0x00040000
WS_EX_TOOLWINDOW=0x00000080
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(10, lambda:root.wm_deiconify())
hasstyle=True
def get_pos(event):
global xwin
global ywin
xwin = event.x
ywin = event.y
def move_window(event):
global previousPosition
root.geometry(f'+{event.x_root - xwin}+{event.y_root - ywin}')
previousPosition = [root.winfo_x(), root.winfo_y()]
def move_window_bindings(*args, status=True):
if status == True:
title_bar.bind("<B1-Motion>", move_window)
title_bar.bind("<Button-1>", get_pos)
title_name.bind("<B1-Motion>", move_window)
title_name.bind("<Button-1>", get_pos)
else:
title_bar.unbind("<B1-Motion>")
title_bar.unbind("<Button-1>")
title_name.unbind("<B1-Motion>")
title_name.unbind("<Button-1>")
def quit():
root.destroy()
#reference: https://programtalk.com/python-examples/ctypes.windll.user32.ShowWindow/
def minimize(hide=False):
hwnd = windll.user32.GetParent(root.winfo_id())
windll.user32.ShowWindow(hwnd, 0 if hide else 6)
def maximizeToggle():
global maximized
global previousPosition
if maximized == False:
#maximize current window
maximize_btn.config(text="❐")
hwnd = windll.user32.GetParent(root.winfo_id())
SWP_SHOWWINDOW = 0x40
windll.user32.SetWindowPos(hwnd, 0, 0, 0, int(root.winfo_screenwidth()), int(root.winfo_screenheight()-48),SWP_SHOWWINDOW)
maximized = True
move_window_bindings(status=False)
else:
#restore down window
maximize_btn.config(text="🗖")
hwnd = windll.user32.GetParent(root.winfo_id())
SWP_SHOWWINDOW = 0x40
windll.user32.SetWindowPos(hwnd, 0, previousPosition[0], previousPosition[1], int(root.minsize()[0]), int(root.minsize()[1]),SWP_SHOWWINDOW)
maximized = False
move_window_bindings(status=True)
#---------------------------------
root = tk.Tk()
root.overrideredirect(True)
#window details
maximized = False
back_ground = "#2c2c2c"
dimension = (300, 300)
#------------------------------
if len(dimension) == 0:
#default window dimension
x = (root.winfo_screenwidth()/2)-(350/2)
y = (root.winfo_screenheight()/2)-(250)
root.geometry(f'350x150+{int(x)}+{int(y)}')
root.minsize(350, 150)
dimension = (350, 150)
previousPosition = [int(x), int(y)]
else:
x = (root.winfo_screenwidth()/2)-(dimension[0]/2)
y = (root.winfo_screenheight()/2)-250
root.geometry(f'{dimension[0]}x{dimension[1]}+{int(x)}+{int(y)}')
root.minsize(dimension[0], dimension[1])
previousPosition = [int(x), int(y)]
#title bar
title_bar = tk.Frame(root, bg=back_ground, bd=1,
highlightcolor=back_ground,
highlightthickness=0)
#window title
title_window = "Untitled window"
title_name = tk.Label(title_bar, text=title_window,
font="Arial 12", bg=back_ground, fg="white")
#minimize btn
minimize_btn = tk.Button(title_bar, text='🗕', bg=back_ground, padx=5, pady=2,
bd=0, font="bold", fg='white', width=2,
activebackground="red",
activeforeground="white",
highlightthickness=0,
command=minimize)
#maximize btn
maximize_btn = tk.Button(title_bar, text='🗖', bg=back_ground, padx=5, pady=2,
bd=0, font="bold", fg='white', width=2,
activebackground="red",
activeforeground="white",
highlightthickness=0,
command=maximizeToggle)
#close btn
close_button = tk.Button(title_bar, text='🗙', bg=back_ground, padx=5, pady=2,
bd=0, font="bold", fg='white', width=2,
activebackground="red",
activeforeground="white",
highlightthickness=0,
command= quit)
#hover effect
minimize_btn.bind('<Enter>', lambda x: minimize_btn.configure(bg='#777777'))
minimize_btn.bind('<Leave>', lambda x: minimize_btn.configure(bg=back_ground))
maximize_btn.bind('<Enter>', lambda x: maximize_btn.configure(bg='#777777'))
maximize_btn.bind('<Leave>', lambda x: maximize_btn.configure(bg=back_ground))
close_button.bind('<Enter>', lambda x: close_button.configure(bg='red'))
close_button.bind('<Leave>',lambda x: close_button.configure(bg=back_ground))
#main area of the window
window = tk.Frame(root, bg="white", highlightthickness=1, highlightbackground=back_ground)
txt = tk.Label(window, bg='white', text="Prototype window").pack(anchor="center")
# pack the widgets
title_bar.pack(fill='x', side=tk.TOP)
title_name.pack(side='left', padx=5)
close_button.pack(side='right')
maximize_btn.pack(side=tk.RIGHT)
minimize_btn.pack(side=tk.RIGHT)
window.pack(fill='both', expand=True, side=tk.TOP)
move_window_bindings(status=True)
#ctype
hasstyle = False
root.update_idletasks()
root.withdraw()
set_appwindow()
root.mainloop()
Demo:
If someone can transform my code into a classful syntax, please comment since I have a hard time in doing so hehe. Code optimizations are very welcomed.