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

20 投票
11 回答
60511 浏览
提问于 2025-04-18 07:26

我在用Tkinter做程序的图形界面,但我发现很多程序的界面看起来和Tkinter的标准样式不一样。这里说的标准样式指的是那种常见的标题栏、边框等等。

比如,Tkinter的标题栏是这样的:

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

而GitHub的标题栏则是这样的:

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

你看,它们有自己定制的关闭、调整大小和最小化按钮,对吧?那么,使用Tkinter能做到这样的界面吗?

提前谢谢你们!:)

11 个回答

1

在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()
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()

解释:
我们把按钮的边框厚度设置为 0,这样就去掉了按钮的边框。
接着,我们把 <Enter> 事件绑定到一个函数,这个函数会改变按钮的前景颜色。
为了让按钮回到原来的状态,我们又把 <Leave> 事件绑定到另一个函数。

初始状态
初始状态 鼠标悬停后状态变化
鼠标悬停后状态变化

注意:光标不可见是因为我的屏幕录制软件不知怎么把它去掉了

9

我找到了一种方法,可以让标题栏变成黑色,这个方法只适用于Windows 11。

这是一个使用Tkinter的黑色标题栏示例:

import ctypes as ct


def dark_title_bar(window):
    """
    MORE INFO:
    https://learn.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))

我几乎花了一年的时间在寻找这个解决方案!

10

大多数人都知道在使用上面提到的 'move_window' 方法时会出现错误;我找到了一种解决办法,它是获取鼠标的确切位置,然后根据这个位置来移动窗口,而不是从窗口的角落开始移动:

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

是的,这是可能的。你可以在主窗口上使用 overrideredirect() 这个方法,这样就可以去掉标题栏和默认的窗口大小设置。之后,你需要从头开始重新设置这些功能,按照你想要的样子来调整窗口。下面是一个简单的示例,功能很基础:

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

撰写回答