简体   繁体   English

我可以更改 Tkinter 中的标题栏吗?

[英]Can I change the title bar in Tkinter?

I'm using Tkinter as GUI for my program, but as I see, many programs don't have standard look as Tkinter does.我使用 Tkinter 作为我程序的 GUI,但正如我所见,许多程序没有 Tkinter 那样的标准外观。 By standard look I mean standard title bar, borders, etc.标准外观是指标准的标题栏、边框等。

For example, Tkinter's title bar:比如Tkinter的标题栏:

http://pokit.org/get/img/1a343ad92cd8c8f19ce3ca9c27afecba.jpg

vs GitHub's title bar: vs GitHub 的标题栏:

http://pokit.org/get/img/cf5cef0eeae5dcdc02f450733fd87508.jpg

See how they have their own custom exit, resize and minimize buttons?看看他们如何拥有自己的自定义退出、调整大小和最小化按钮? Is it possible to achieve that look using Tkinter?是否可以使用 Tkinter 实现这种外观?

Thanks in advance: :)提前致谢: :)

Yes it's possible.是的,这是可能的。 You can use the overrideredirect() method on the root window to kill the title bar and the default geometry settings.您可以使用根窗口上的overrideredirect()方法来终止标题栏和默认几何设置。 After that, you need to rebuild all those methods from scratch to set it back up like you want.之后,您需要从头开始重建所有这些方法,以便按照您的意愿进行设置。 Here's a small working example with minimal functionality:这是一个功能最少的小型工作示例:

root = Tk()

def move_window(event):
    root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))

root.overrideredirect(True) # turns off title bar, geometry
root.geometry('400x100+200+200') # set new geometry

# make a frame for the title bar
title_bar = Frame(root, bg='white', relief='raised', bd=2)

# put a close button on the title bar
close_button = Button(title_bar, text='X', command=root.destroy)

# a canvas for the main area of the window
window = Canvas(root, bg='black')

# pack the widgets
title_bar.pack(expand=1, fill=X)
close_button.pack(side=RIGHT)
window.pack(expand=1, fill=BOTH)

# bind title bar motion to the move window function
title_bar.bind('<B1-Motion>', move_window)

root.mainloop()

Most will know there is an error when using the 'move_window' method used above;大多数人都会知道使用上面使用的“move_window”方法时会出错; I found a fix that gets the exact position of the mouse and moves with that rather than from the corner:我找到了一个修复程序,它可以获取鼠标的确切位置并随之移动,而不是从角落移动:

    def get_pos(event):
        xwin = app.winfo_x()
        ywin = app.winfo_y()
        startx = event.x_root
        starty = event.y_root

        ywin = ywin - starty
        xwin = xwin - startx


        def move_window(event):
            app.geometry("400x400" + '+{0}+{1}'.format(event.x_root + xwin, event.y_root + ywin))
        startx = event.x_root
        starty = event.y_root


        app.TopFrame.bind('<B1-Motion>', move_window)
    app.TopFrame.bind('<Button-1>', get_pos)

These are the modifications that I have made using python 3.7.2这些是我使用 python 3.7.2 所做的修改

from tkinter import *
root = Tk()
root.overrideredirect(True) # turns off title bar, geometry
root.geometry('400x100+200+200') # set new geometry

# make a frame for the title bar
title_bar = Frame(root, bg='#2e2e2e', relief='raised', bd=2,highlightthickness=0)

# put a close button on the title bar
close_button = Button(title_bar, text='X', command= root.destroy,bg = "#2e2e2e",padx = 2,pady = 2,activebackground='red',bd = 0,font="bold",fg='white',highlightthickness=0)

# a canvas for the main area of the window
window = Canvas(root, bg='#2e2e2e',highlightthickness=0)

# pack the widgets
title_bar.pack(expand=1, fill=X)
close_button.pack(side=RIGHT)
window.pack(expand=1, fill=BOTH)
xwin=None
ywin=None
# bind title bar motion to the move window function

def move_window(event):
    root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))
def change_on_hovering(event):
    global close_button
    close_button['bg']='red'
def return_to_normalstate(event):
    global close_button
    close_button['bg']='#2e2e2e'
    
    
title_bar.bind('<B1-Motion>', move_window)
close_button.bind('<Enter>',change_on_hovering)
close_button.bind('<Leave>',return_to_normalstate)
root.mainloop()

Explanation:解释:
We use bd(border thickness)=0 to remove the borders from the the button我们使用 bd(border thickness)=0 来移除按钮的边框
Then we bind the <Enter> event to a function which changes the foreground color.然后我们将<Enter>事件绑定到一个改变前景色的函数。
And to return to its original state we bind the <Leave> event to another function为了返回其原始状态,我们将<Leave>事件绑定到另一个函数

初始状态
Initial State初始状态将鼠标光标悬停在其上后状态更改
Change in state after hovering mouse cursor over it将鼠标光标悬停在其上后状态更改

Note : The cursor is not visible because my screen capture software somehow removed it注意:光标不可见,因为我的屏幕捕获软件以某种方式将其删除

I found a way of making the title bar black using ctypes : (win11 only)我找到了一种使用ctypes使标题栏变黑的方法:(仅限 win11)

Tkinter dark title bar example: Tkinter 深色标题栏示例:

import ctypes as ct


def dark_title_bar(window):
    """
    MORE INFO:
    https://docs.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
    """
    window.update()
    DWMWA_USE_IMMERSIVE_DARK_MODE = 20
    set_window_attribute = ct.windll.dwmapi.DwmSetWindowAttribute
    get_parent = ct.windll.user32.GetParent
    hwnd = get_parent(window.winfo_id())
    rendering_policy = DWMWA_USE_IMMERSIVE_DARK_MODE
    value = 2
    value = ct.c_int(value)
    set_window_attribute(hwnd, rendering_policy, ct.byref(value),
                         ct.sizeof(value))

I searched almost a year for a solution!我搜索了将近一年的解决方案!

In python3.5.2 I had to make some modifications to get this to work:在 python3.5.2 中,我必须进行一些修改才能使其正常工作:

#custom title bar for tkinter

from tkinter import Tk, Frame, Button, Canvas

root = Tk()

def move_window(event):
    root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))

root.overrideredirect(True) # turns off title bar, geometry
root.geometry('400x100+200+200') # set new geometry

# make a frame for the title bar
title_bar = Frame(root, bg='white', relief='raised', bd=2)

# put a close button on the title bar
close_button = Button(title_bar, text='Close this Window', command=root.destroy)

# a canvas for the main area of the window
window = Canvas(root, bg='black')

# pack the widgets
title_bar.pack(expand=1, fill="x")
close_button.pack(side="right")
window.pack(expand=1, fill="both")

# bind title bar motion to the move window function
title_bar.bind('<B1-Motion>', move_window)

root.mainloop()

enter image description here here you go updated to python 3.8 and new variable for title bar background and for main content background and title name add and new background + clear some indention errors在此处输入图像描述您将更新到 python 3.8 和标题栏背景和主要内容背景和标题名称的新变量添加和新背景 + 清除一些缩进错误

from tkinter import *

root = Tk()
# turns off title bar, geometry
root.overrideredirect(True)
# set new geometry
root.geometry('400x100+200+200')
# set background color of title bar
back_ground = "#2c2c2c"

# set background of window
content_color = "#ffffff"
# make a frame for the title bar
title_bar = Frame(root, bg=back_ground, relief='raised', bd=1, highlightcolor=back_ground,highlightthickness=0)

# put a close button on the title bar
close_button = Button(title_bar, text='x',  command=root.destroy,bg=back_ground, padx=5, pady=2, activebackground="red", bd=0,    font="bold", fg='white',        activeforeground="white", highlightthickness=0)
 # window title
title_window = "Title Name"
title_name = Label(title_bar, text=title_window, bg=back_ground, fg="white")
# a canvas for the main area of the window
window = Canvas(root, bg="white", highlightthickness=0)

# pack the widgets
title_bar.pack(expand=1, fill=X)
title_name.pack(side=LEFT)
close_button.pack(side=RIGHT)
window.pack(expand=1, fill=BOTH)
x_axis = None
y_axis = None
# bind title bar motion to the move window function


def move_window(event):
    root.geometry('+{0}+{1}'.format(event.x_root, event.y_root))


def change_on_hovering(event):
    global close_button
    close_button['bg'] = 'red'


def return_to_normal_state(event):
   global close_button
   close_button['bg'] = back_ground


title_bar.bind('<B1-Motion>', move_window)
close_button.bind('<Enter>', change_on_hovering)
close_button.bind('<Leave>', return_to_normal_state)
root.mainloop()

This is how it looks I think this is the easy one.这就是它的外观,我认为这是最简单的。 I used it on one of my project , due to some reasons from client side, client was not able to make any payment for that project and a lot of time got wasted.我在我的一个项目中使用了它,由于客户端的一些原因,客户无法为该项目支付任何款项,并且浪费了很多时间。

from cProfile import label
from tkinter import *
import ttkbootstrap as ttk
from ttkbootstrap import Style
from tkinter import messagebox as m_box



win = Tk()
win.overrideredirect(True)
win.attributes("-topmost", True)
win.geometry("600x300+300+300")
win.resizable(1, 1)
style = Style("cyborg")

# ============================================================= Title bar Header


def ext():
    exit()

def minim():
    win.overrideredirect(0)
    win.wm_state("iconic")
    win.overrideredirect(1)

def about():
    pass
    m_box.showinfo("About", "Developer: Vivek phogat\nemail: Chaudharyvivekphogat@outlook.com")


#------------------------------- Header section
Header_frame = ttk.Label(win)

info_label = ttk.Label(Header_frame,text="Customized title bar enjoy :-)",bootstyle="WARNING",font=("Comic Sans MS", 15))
info_label.pack(padx=5, pady=5,side = LEFT)

win_close_btn1 = ttk.Button(Header_frame, text=" X ", command=(win.destroy), bootstyle="danger")
win_close_btn1.pack( side = RIGHT,anchor= NE)
Min_but = ttk.Button(Header_frame, text=" _ ",  command=minim, bootstyle="light")
Min_but.pack( side = RIGHT,anchor= NE)
about_btn1 = ttk.Button(Header_frame, text=" a ", command=about, bootstyle="secondary")
about_btn1.pack(side = RIGHT,anchor= NE)


Header_frame.pack(fill=X)
#-------------------------------


#title bar get position.
def get_pos(event):
    global xwin
    global ywin
    xwin = event.x
    ywin = event.y

#title bar drag functon.
def drag(event):
    win.geometry(f"+{event.x_root - xwin}+{event.y_root - ywin}")


Header_frame.bind("<B1-Motion>", drag)
Header_frame.bind("<Button-1>", get_pos)
info_label.bind("<B1-Motion>", drag)
info_label.bind("<Button-1>", get_pos)


Footer_frame = ttk.Frame(win)

label = ttk.Label(Footer_frame, text= " Hope you all are doing good enjoy the code.",bootstyle="WARNING",font=("Comic Sans MS", 20)).pack()
Footer_frame.pack(expand= TRUE )
win.mainloop()

未悬停的窗口图像 悬停的窗口图像

ok i left the old answer if some need it.好的,如果有人需要,我会留下旧答案。 it is a recreated from scratch custom taskbar that assemble windows 10 bar它是一个从头开始重新创建的自定义任务栏,它组装了 windows 10 bar

    from tkinter import *
    window = Tk()
    bg = "#f5f6f7"
    title_window = "test app"


    class app:
        def __init__(self, main):
            self.main = main
            self.main.configure(bg=bg)
            self.main.overrideredirect(True)
            self.main.geometry('230x130')
            self.main.resizable(width=False, height=False)
            self.top_bar = Frame(main,bg=bg, cursor="sizing")
            self.top_bar.pack(fill=X)
            self.title_txt = Label(self.top_bar, text=title_window ,bg=bg)
            self.title_txt.pack(side="left", padx=3)
            close_btn = Button(self.top_bar,text="x", cursor="arrow", bg=bg,    fg="black", highlightthickness=0,activebackground="red", activeforeground="white",bd=0, command=self.main.quit)
            close_btn.pack(side="right")
            bottom_bar = Frame(main, bg=bg)
            bottom_bar.pack()
            label_scr = Label(bottom_bar, text="label 1", padx=100, pady=5, bg=bg)
            label_scr.grid(row=0, column=0, columnspan=3)
            button_scr = Button(bottom_bar, text="Button1", bg=bg, bd=0)
            button_scr.grid(row=2, column=0, columnspan=3, pady=3)
            button2_scr = Button(bottom_bar, text="Button2", bg=bg,bd=0)
            button2_scr.grid(row=3, column=0, columnspan=3, pady=3)



    def move_window(event):
        window.geometry(f"+{event.x_root}+{event.y_root}")


    execution = app(window)
    execution.top_bar.bind('<B1-Motion>', move_window)
    execution.title_txt.bind('<B1-Motion>', move_window)
    window.mainloop()

here is my one这是我的

  class CustomTitle():
        """
        Ex:
            root = Tk()
            titleBar = CustomTitle(root,title_text = 'Hello,World!' , bg = "#000000" , fg = '#ffffff')
            titleBar.resizeable = True
            titleBar.packBar()
            root.mainloop()
    
            Note:
                    Try to Give Color value in Hex and the 3rd car should be number
                        #7a4e7a
                           ↑ (this one)                  
        """
        resizeable = True
        font_style = ('Candara',13)
        
    
        def __init__(self,win,title_text='Custom Title Bar',bg='#ffffff',fg="#000000"):  
            # deactivating main title bar
            self._win = win
            win.title(title_text)
    
            # props
            self.bg = bg
            self._maximized = False
            self._win_width = win.winfo_width()
            self._win_height = win.winfo_height()
            self._scr_width = win.winfo_screenwidth()
            self._scr_height = win.winfo_screenheight()
            self._addWidget(title_text,bg,fg)
            
        def packBar(self):
            self._title_bar.pack(fill=X)
            self._checkAbility()
            self._win.overrideredirect(1)
            self._finilize()
    
        def _checkAbility(self):
            if not self.resizeable:
                self._maximize_btn.config(state=DISABLED)
            else:
                self._resizey_widget.pack(side=BOTTOM,ipadx=.1,fill=X)
                self._resizex_widget.pack(side=RIGHT,ipadx=.1,fill=Y)
    
        def _maximize_win(self):
            if not self._maximized:
                self._past_size = root.geometry()
                self._win.geometry(f"{self._scr_width}x{self._scr_height}+{0}+{0}")
                self._maximize_btn.config(text = '🗗')
            else:
                self._win.geometry(self._past_size)
                self._maximize_btn.config(text = '🗖')
            self._maximized = not self._maximized
    
    
        def _minimize(self):
            Minimize = win32gui.GetForegroundWindow()
            win32gui.ShowWindow(Minimize, win32con.SW_MINIMIZE)
    
        
        def _setIconToTaskBar(self,mainWindow):
            GWL_EXSTYLE = -20
            WS_EX_APPWINDOW = 0x00040000
            WS_EX_TOOLWINDOW = 0x00000080
            # Magic
            hwnd = windll.user32.GetParent(mainWindow.winfo_id())
            stylew = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
            stylew = stylew & ~WS_EX_TOOLWINDOW
            stylew = stylew | WS_EX_APPWINDOW
            res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, stylew)
        
            mainWindow.wm_withdraw()
            mainWindow.after(10, lambda: mainWindow.wm_deiconify())
    
        def _addWidget(self,title_text,bg,fg):
            self._title_bar = Frame(self._win,bd=1,bg=bg)
    
            self._title_text = Label(self._title_bar,text=title_text,bg=bg,fg=fg,font=self.font_style)
            self._title_text.pack(side=LEFT,padx=4,pady=3)
            self._title_text.bind("<B1-Motion>",self._drag)
    
            self._close_btn = Button(self._title_bar,text = '×',bd=0,bg=bg,fg=fg,width=3,font=self.font_style,command=lambda: quit())
            self._close_btn.pack(side=RIGHT,fill=Y)
            self._maximize_btn = Button(self._title_bar,text="🗖",bd=0,bg=bg,fg=fg,width=3,font=self.font_style,command=self._maximize_win)
            self._maximize_btn.pack(side=RIGHT,fill=Y)
            self._minimize_btn = Button(self._title_bar,text="_",bd=0,bg=bg,fg=fg,width=3,font=self.font_style,command=self._minimize)
            self._minimize_btn.pack(side=RIGHT,fill=Y)
            self._title_bar.bind('<Button-1>', self._drag)
    
            self._resizex_widget = Frame(self._win,cursor='sb_h_double_arrow')
            self._resizex_widget.bind("<B1-Motion>",self._resizex)
    
            self._resizey_widget = Frame(self._win,cursor='sb_v_double_arrow')
            self._resizey_widget.bind("<B1-Motion>",self._resizey)
    
            self._hover_effect()
    
        def _hover_effect(self):
            try:
                num = int(self.bg[3]) - 1
                newbg = self.bg.replace(self.bg[3],str(num))
            except:
                newbg = "#c7ebe8"
    
            def change_bg(which_one,bg = newbg):
                which_one.config(bg=bg)
            def restore_bg(which_one):
                which_one.config(bg=self.bg)
            self._maximize_btn.bind('<Enter>',lambda event: change_bg(self._maximize_btn))
            self._maximize_btn.bind('<Leave>',lambda event: restore_bg(self._maximize_btn))
            self._minimize_btn.bind('<Enter>',lambda event: change_bg(self._minimize_btn))
            self._minimize_btn.bind('<Leave>',lambda event: restore_bg(self._minimize_btn))
            self._close_btn.bind('<Enter>',lambda event: change_bg(self._close_btn,bg='#db2730'))
            self._close_btn.bind('<Leave>',lambda event: restore_bg(self._close_btn))
    
    
        def _finilize(self):
            self._win.after(10, lambda: self._setIconToTaskBar(self._win))
    
        def _drag(self,event):
            xwin = root.winfo_x()
            ywin = root.winfo_y()
            startx = event.x_root
            starty = event.y_root
    
            ywin = ywin - starty
            xwin = xwin - startx
    
            def _move_window(event): # runs when window is dragged
    
                root.geometry(f'+{event.x_root + xwin}+{event.y_root + ywin}')
    
    
            def _release_window(event): # runs when window is released
                root.config(cursor="arrow")
                
            self._title_bar.bind('<B1-Motion>', _move_window)
            self._title_bar.bind('<ButtonRelease-1>', _release_window)
            self._title_text.bind('<B1-Motion>', _move_window)
            self._title_text.bind('<ButtonRelease-1>', _release_window)
    
    
        def _resizex(self,event):
    
            xwin = root.winfo_x()
    
            difference = (event.x_root - xwin) - root.winfo_width()
    
            if root.winfo_width() > 150 : # 150 is the minimum width for the window
                try:
                    root.geometry(f"{ root.winfo_width() + difference }x{ root.winfo_height() }")
                except:
                    pass
            else:
                if difference > 0: # so the window can't be too small (150x150)
                    try:
                        root.geometry(f"{ root.winfo_width() + difference }x{ root.winfo_height() }")
                    except:
                        pass
    
    
        def _resizey(self,event):
    
            ywin = root.winfo_y()
    
            difference = (event.y_root - ywin) - root.winfo_height()
    
            if root.winfo_height() > 150: # 150 is the minimum height for the window
                try:
                    root.geometry(f"{ root.winfo_width()  }x{ root.winfo_height() + difference}")
                except:
                    pass
            else:
                if difference > 0: # so the window can't be too small (150x150)
                    try:
                        root.geometry(f"{ root.winfo_width()  }x{ root.winfo_height() + difference}")
                    except:
                        pass

This is for me the best form:这对我来说是最好的形式:

import tkinter as tk
from turtle import title, width

root= tk.Tk()

root.title('Civ IV select songs')
canvas1 = tk.Canvas(root, width = 300, height = 600)
canvas1.pack()

标题栏的名称已更改

We have to change the DWMWindowAttributes for the tkinter window with ctypes which is under windll.dwmapi.DwmSetWindowAttribute我们必须使用 windll.dwmapi.DwmSetWindowAttribute 下的 ctypes 更改tkinter window 的windll.dwmapi.DwmSetWindowAttribute

For changing the title bar color, we can use DWMWA_CAPTION_COLOR which is set as 35 (attribute number) for windows 11.要更改标题栏颜色,我们可以使用DWMWA_CAPTION_COLOR设置为35 (属性编号) windows 11。

Here is an example:这是一个例子:

import tkinter
from ctypes import windll, byref, sizeof, c_int

root = tkinter.Tk()
root.title("Tkinter Window")
root.update()

HWND = windll.user32.GetParent(root.winfo_id()) # the window we want to change

# These attributes are for windows 11
DWMWA_CAPTION_COLOR = 35
DWMWA_TITLE_COLOR = 36

COLOR_1 = 0x0000FFFF # color should be in hex order: 0x00bbggrr
COLOR_2 = 0x00008000 

windll.dwmapi.DwmSetWindowAttribute(HWND, DWMWA_CAPTION_COLOR, byref(c_int(COLOR_1)), sizeof(c_int))
windll.dwmapi.DwmSetWindowAttribute(HWND, DWMWA_TITLE_COLOR, byref(c_int(COLOR_2)), sizeof(c_int))

root.mainloop()

截屏

more info about the color api used here: https://learn.microsoft.com/en-us/windows/win32/gdi/colorref有关此处使用的颜色 api 的更多信息: https://learn.microsoft.com/en-us/windows/win32/gdi/colorref

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM