我使用 Tkinter 作为我的程序的 GUI,但正如我所见,许多程序没有像 Tkinter 那样的标准外观。我所说的标准外观是指标准标题栏、边框等。
例如Tkinter的标题栏:
vs GitHub 的标题栏:
看看他们如何拥有自己的自定义退出、调整大小和最小化按钮?使用 Tkinter 可以实现这种外观吗?
提前致谢! :)
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()
大多数人都会知道使用上面使用的“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)
我找到了一种使用
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))
我寻找了近一年的解决方案!
这些是我使用 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>
注意:光标不可见,因为我的屏幕捕获软件以某种方式删除了它
在 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()
这里更新到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()
我认为这是最简单的。我在我的一个项目中使用了它,由于客户方面的一些原因,客户无法为该项目支付任何费用,并且浪费了很多时间。
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()
好吧,如果有人需要的话,我留下了旧的答案。 它是一个从头开始重新创建的自定义任务栏,组装 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()
这是我的
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
扩展该线程中的第二个答案,注意到拖动标题栏时窗口移动的错误,该答案提供了更好的移动,但在 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()
在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()