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

问题描述 投票:0回答:11

我使用 Tkinter 作为我的程序的 GUI,但正如我所见,许多程序没有像 Tkinter 那样的标准外观。我所说的标准外观是指标准标题栏、边框等。

例如Tkinter的标题栏:

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

vs GitHub 的标题栏:

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

看看他们如何拥有自己的自定义退出、调整大小和最小化按钮?使用 Tkinter 可以实现这种外观吗?

提前致谢! :)

python tkinter
11个回答
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()

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)

9
投票

我找到了一种使用

ctypes
使标题栏变黑的方法:(仅限win11)

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

我寻找了近一年的解决方案!


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

说明:
我们使用bd(边框厚度)=0来删除按钮的边框
然后我们将

<Enter>
事件绑定到一个函数,该函数 改变前景色。
为了返回到其原始状态,我们将
<Leave>
事件绑定到另一个函数


初始状态
将鼠标光标悬停在其上后状态发生变化

注意:光标不可见,因为我的屏幕捕获软件以某种方式删除了它


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

1
投票

enter image description here这里更新到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()

1
投票

This is how it looks我认为这是最简单的。我在我的一个项目中使用了它,由于客户方面的一些原因,客户无法为该项目支付任何费用,并且浪费了很多时间。

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: [email protected]")


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

0
投票

好吧,如果有人需要的话,我留下了旧的答案。 它是一个从头开始重新创建的自定义任务栏,组装 Windows 10 栏

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

0
投票

这是我的

  from tkinter import *
  import windll

  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, 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=self._win.destroy)
            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

0
投票

扩展该线程中的第二个答案,注意到拖动标题栏时窗口移动的错误,该答案提供了更好的移动,但在 y 轴上仍然很敏捷。我们可以使用全局变量以更简单的方式解决这个问题。这是我的实现:

def build():
  window.overrideredirect(True) #Turns off the title bar of the window
  window.geometry("800x600+100+100")

  # Create a new title bar
  title_bar = Frame(window, bg="dark slate blue", relief="raised", bd=2)
  close_button = Button(title_bar, text="X", command=window.destroy)
  title_bar.pack(expand=1, fill=X)
  close_button.pack(side=RIGHT)
  title_bar.bind('<Button-1>', get_title_click)
  title_bar.bind('<B1-Motion>', move_window)

def get_title_click(event):
  global title_click_x, title_click_y
  title_click_x = event.x_root
  title_click_y = event.y_root

def move_window(event):
  global title_click_x, title_click_y

  new_pos_x = window.winfo_x() + (event.x_root - title_click_x)
  new_pos_y = window.winfo_y() + (event.y_root - title_click_y)
  window.geometry(f'+{new_pos_x}+{new_pos_y}')
  title_click_x = event.x_root
  title_click_y = event.y_root

def main():
  global window

  window = Tk()
  build()
  mainloop()

main()

-1
投票

在Python 3.7中我可以使用 de 函数更改标题

root.title()
。来源:来源:https://www.geeksforgeeks.org/turtle-title-function-in-python/

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

© www.soinside.com 2019 - 2024. All rights reserved.