2

I want to fully customize my app so I created a title bar and figured out a way to drag it around but now my issue is that it's lacking features, like resizing and animations of sorts, I was wondering if it's possible to just get rid of the title bar with some library? or perhaps one that could restore some of the functionality and allow me to not have to use overrideredirect every time i want to hide the window?

the first option is better tho, I did use ctypes.windll to get the window back into the taskbar, but it also does not appear te be the most practical solution.

the code so far looks something like this:

import tkinter as tk
from ctypes import windll


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


class Application(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.overrideredirect(True)
        self.after(10, self.setup_window)

        self.geometry('1000x500+500+250')

        self.app_hidden = False
        self.bind('<Expose>', self.show_app)


    # for full customization im using different widgets as buttons
    # lets assume its on the custom title bar

        text = tk.Label(self, width=10, height=2,
                    bg='blue', text='close', foreground='white')
        text.pack()
        text.bind('<ButtonPress-1>', self.hide_app)

    # taken from another post
    def setup_window(self, t: int = 10):
        hwnd = windll.user32.GetParent(self.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)
        self.wm_withdraw()
        self.after(t, self.wm_deiconify)

    def hide_app(self):
        if not self.app_hidden:
            self.overrideredirect(False)
            self.iconify()
            self.app_hidden = True

    def show_app(self, event):
        if self.app_hidden:
            self.overrideredirect(True)
            self.setup_window()
            self.app_hidden = False


if __name__ == '__main__':

    app = Application()
    app.mainloop()

what I want is to be able to either get back the border, Iv'e tried doing it by adding

WS_BORDER = 0x00800000
...
def setup_window(self):
    ...
    style = style | WS_BORDER
    ...

But it didn't work, and i would also like to not have unnecessary bindings that already exist, so the main question is whether it's possible to overrideredirect without overrideredirect, if there's some library out there that is capeable of interacting with the default windows maneger on high level?

Tomer Poliakov
  • 349
  • 1
  • 3
  • 12

1 Answers1

3

As I stated in my comment, as far as I know, there is no extension to tkinter what does the job for you. You have to do your window managment for your own. Anyways you may want something like this:

import win32con
import win32api
import win32gui
import tkinter as tk

def override(event):
    hwnd = win32gui.GetParent(root.winfo_id())
    style= win32api.GetWindowLong(hwnd, win32con.GWL_STYLE)
    style&= ~win32con.WS_MINIMIZEBOX
    style&= ~win32con.WS_MAXIMIZEBOX
    style&= ~win32con.WS_SYSMENU
    style&= ~win32con.WS_CAPTION
    #style&= ~win32con.WS_SIZEBOX
    valid= win32api.SetWindowLong(hwnd, win32con.GWL_STYLE, style)
    root.bind('<Map>', None)
    

root = tk.Tk()
root.bind('<Map>', override)
root.mainloop()

If you play with these options you can get various window styles, like below.

WS_MINIMIZEBOX:

enter image description here

WS_MAXIMIZEBOX:

enter image description here

WS_MINIMIZEBOX & WS_MAXIMIZEBOX:

enter image description here

WS_SYSMENU:

enter image description here

WS_CAPTION:

enter image description here

WS_CAPTION & SIZEBOX:

enter image description here

Ctypes Solution

import tkinter as tk
from ctypes import windll, wintypes

GWL_STYLE = -16
WS_SYSMENU = 0x00080000

SWP_FRAMECHANGED = 0x0020
SWP_NOACTIVATE = 0x0010
SWP_NOMOVE = 0x0002
SWP_NOSIZE = 0x0001

GetWindowLong = windll.user32.GetWindowLongW
SetWindowLong = windll.user32.SetWindowLongW
SetWindowPos = windll.user32.SetWindowPos

class App(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.hwnd = int(self.wm_frame(), 16)
        tk.Button(self, text="Remove buttons", command=self.remove_buttons).pack()
        tk.Button(self, text="Add buttons", command=self.add_buttons).pack()

    def remove_buttons(self):
        old_style = GetWindowLong(self.hwnd, GWL_STYLE)
        new_style = old_style & ~WS_SYSMENU
        SetWindowLong(self.hwnd, GWL_STYLE, new_style)
        SetWindowPos(self.hwnd, 0, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)

    def add_buttons(self):
        old_style = GetWindowLong(self.hwnd, GWL_STYLE)
        new_style = old_style | WS_SYSMENU
        res = SetWindowLong(self.hwnd, GWL_STYLE, new_style)
        res = SetWindowPos(self.hwnd, 0, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE)

if __name__ == "__main__":
    app = App()
    app.mainloop()
Thingamabobs
  • 7,274
  • 5
  • 21
  • 54