Python tkinter Notebook标签添加关闭按钮元素,及左侧添加存储状态提示图标案例,类似Notepad++页面

效果图展示


粉色框 是当前页面,橙色框 是鼠标经过,红色框 是按下按钮,灰色按钮 是其他页面的效果;
存储标识可以用来识别页面是否存储:例如当前页面已经保存用蓝色,未保存用红色,其他页面已经保存用灰色,未保存用淡红色。

首先导入模块

python 复制代码
import tkinter as tk
from tkinter import ttk

定义一个名为CustomNotebook的类,继承自ttk.Notebook

python 复制代码
class CustomNotebook(ttk.Notebook):
    """定义一个名为CustomNotebook的类,继承自ttk.Notebook"""

    __initialized = False  # 初始化一个私有变量,用于标记是否已经初始化

    def __init__(self, *args, **kwargs):
        # 如果尚未初始化,则调用自定义初始化方法,并设置已初始化标志
        if not self.__initialized:
            self.__initialize_custom_style()
            #CustomNotebook.__initialized = True
            self.__inititialized = True

        # 设置notebook的样式为"CustomNotebook"
        kwargs["style"] = "CustomNotebook"
        # 调用父类的初始化方法
        ttk.Notebook.__init__(self, *args, **kwargs)

        self._active = None # 初始化一个私有变量,用于存储当前活动的tab

        # 绑定鼠标左键按下事件到on_close_press方法
        self.bind("<ButtonPress-1>", self.on_close_press, True)
        # 绑定鼠标左键释放事件到on_close_release方法
        self.bind("<ButtonRelease-1>", self.on_close_release)

    def on_close_press(self, event):
        """当按钮被按下时触发,位于关闭按钮上方"""

        # 获取鼠标点击位置的元素
        element = self.identify(event.x, event.y)

        # 如果元素包含"close",则执行以下操作
        if "close" in element:
            # 获取鼠标点击位置的索引值
            index = self.index("@%d,%d" % (event.x, event.y))
            # 将按钮状态设置为按下
            self.state(['pressed'])
            # 将_active属性设置为点击的索引值
            self._active = index

    def on_close_release(self, event):
        """ 
        当鼠标在关闭按钮上释放时调用此方法。
        event:  包含鼠标事件信息的对象。
        """
        if not self.instate(['pressed']): # 如果按钮没有按下状态,直接返回
            return
        try:
            element =  self.identify(event.x, event.y) # 获取鼠标释放位置的元素
            index = self.index("@%d,%d" % (event.x, event.y)) # 获取元素在列表中的索引
            if "close" in element and self._active == index: # 如果元素是关闭按钮,并且当前激活的标签页与释放位置的标签页相同
                self.forget(index)  # 删除该标签页
                self.event_generate("<<NotebookTabClosed>>") # 生成一个表示标签页关闭的事件
        except: pass
        self.state(["!pressed"]) # 将按钮状态设置为非按下状态
        self._active = None # 将当前激活的标签页设置为None

    def __initialize_custom_style(self):
        # 创建一个ttk样式对象
        style = ttk.Style()
        # 定义四个图片对象,分别表示关闭按钮的不同状态
        self.images = (
            # 元素普通状态时图标
            tk.PhotoImage("img_closenormal", data='''
                R0lGODdhCwALAIMAAJKSkpeXl5ubm5+fn6CgoKampqqqqq2trbGxsba2tr29vcHBwdnZ2QAAAAAAAAAA
                ACwAAAAACwALAAAIXQAXJEBwwEDBAgUGHEigYOCBhwYMEBCAIAEDBgYQXhwg4ACCiwwKgOQIEeRGjgUK
                GgBJYABKgyYZuBRAQORFmwwEBBhgE6FNAQAEDCBAtCVHoAcC6AzANAAAAAYCAgA7
                '''),
            # 选中的选项卡的关闭图标
            tk.PhotoImage("img_closeselected", data='''
                R0lGODdhCwALAIVUAJ9dcptfe6NfcpthfZ5hfJ9ifp5nfaNjda9lc6Rmea1nfqRpe6Fqfq9ofK1ofqxr
                frFndbJqeLJufZ9rgKpngaBqgKtpg6tphK5uhKxvia5xhq91iq1xjK1zj692jLFyg7N1hrJ3i7B2jbF4
                ja51ka94kq97l698mq9+m7B8mbB/nLCDn6aEoayMprCForGMq7KNq7KPrrOXt7WfwP///wAAAAAAAAAA
                AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAACwALAAAIcgBnwFiBwoQJEh0yUGARQ8YLFwRPHLwQ
                wEUMGjQOcsB4YYAKFxhpbMRooYCJFSlCYlTgoAAJFSlMhHTgQEGBDilSqoyggACGEiFFhGxwAINQGiNC
                gMAIQcADDR48bAjxQUIEBABaGJgwoQKDBQkOCGAREAA7
                '''),
            # 鼠标经过时选项卡的关闭图标
            tk.PhotoImage("img_closeactive", data='''
                R0lGODdhCwALAIVSAN5IN+lRNeBQP+5bPvtSP+FSQuJXR+VbRu5dQetfRuVbSf9dS/xeTO5iR+hhTOxl
                TP1wTP1iUP1kUv9lVPxnVv9rVP1pWP9rWv9vXv5/WP54Xf9yYv11YP90ZP99YP94af97bP99bv6BW/6C
                Yv6FYP+BZf6JZv+Eb/6Jav+Oa+eJe/+Bcf+Edf+Hef+KfP+Rd/+WfP+bev+Pgu+Rhv+RhP+ViP+Zjf+h
                lv+lmv+1rP+/twAAAAAAAAAAAAAAACwAAAAACwALAAAIcwB13KDhgkWIEB8wRJiBIwcOGzRktFjx
                wUKBGjh27GABYoNGCQZc2NC4owNJkCsIktRYoYKCDywmkizhQYODDSFWrNyRYsSDCSY1wiBJosGCCxpP
                vIihMcMAAgwoXOBQAoUJERACzAAAoICAAwkaIAigIiAAOw==
                '''),
            # 按下关闭按钮时选项卡的关闭图标
            tk.PhotoImage("img_closepressed", data='''
                R0lGODdhCwALAIUAAGsiD20jEHIkEHYlEXsnEX4oEogrE4orFIwsFJEtFJUvFZoxFpwwFp4yF6EzF6Yz
                F642GbU3GbY4Grk5Grk6G7s7G7w6G7w8G8A+HcJAHcVDH8ZEH8dEIMhGIMtJIsxKIs1KIpdxZ8eAbtqI
                cuKdiQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAACwALAAAIcABDABBA4AACBQscNBARIAGDBxAkVKhw
                oeIABiRIRJhQIeMFDAUeZCTRMWOGCwYgRBhpMgMGBBEqWBipYcOGDAoolBz5wcOGBRdGchjps0FQEhk6
                eMgIgoODihgycOgAAoQHDiIsYMBwoeYGDhtGBAQAOw==
                '''),
            )

        # 使用element_create方法创建一个名为"close"的元素,类型为"image",图像文件名为"img_closenormal"
        style.element_create("close", "image", "img_closenormal",
                            # 下面设置各状态图标,激活顺序越往后的越要写在前面,比如按下要先鼠标经过激活,按下就要写在鼠标经过前
                            # 当元素处于激活、按下、未禁用状态时,显示"img_closepressed"图片
                            ("active", "pressed", "!disabled", "img_closepressed"),
                            # 当元素处于激活且未禁用状态时,显示"img_closeactive"图片
                            ("active", "!disabled", "img_closeactive"), 
                            # 当选项卡处于选中状态时,显示"img_closeselected"图片
                            ("selected", "img_closeselected"), 
                            # 设置元素的边框宽度为10像素,无边框;设置元素的粘性属性为空字符串,表示不粘附在其他元素上。
                            border=10, sticky='')
        '''
        notebook有如下状态可以设置
            disabled    禁用状态,该状态下的控件无法接收用户输入。
            normal      正常状态,该状态下的控件可以接收用户输入。
            active      激活状态(鼠标经过),该状态下的控件可以接收用户输入,并且会显示特殊效果(如闪烁)。
            selected    选中状态,该状态下的控件会显示特殊效果(如高亮)。
            insensitive 不敏感状态,该状态下的控件不会响应用户的键盘操作。
            focus       聚焦状态,该状态下的控件会显示特殊效果(如边框)。
        '''
        # 设置Notebook的样式为"CustomNotebook",并为"CustomNotebook.client"添加一个样式选项,设置其"sticky"属性为"nswe"
        style.layout("CustomNotebook", [("CustomNotebook.client", {"sticky": "nswe"})])
        # 设置CustomNotebook.Tab的布局样式
        style.layout("CustomNotebook.Tab", [
            ("CustomNotebook.tab", { # 设置 CustomNotebook.tab 的样式
                "sticky": "nswe", # 设置 tab 的粘性属性为 NSWE,表示在水平方向上可拉伸,垂直方向上可滚动
                "children": [     # 设置 tab 的子元素
                    ("CustomNotebook.padding", { # 设置 CustomNotebook.padding 的样式
                        "side": "top",      # 设置 padding 的侧边距在顶部
                        "sticky": "nswe",   # 设置 padding 的粘性属性为 NSWE
                        "children": [       # 设置 padding 的子元素
                            ("CustomNotebook.focus", { # 设置 CustomNotebook.focus 的样式
                                "side": "top",    # 设置 focus 的侧边距在顶部
                                "sticky": "nswe", # 设置 focus 的粘性属性为 NSWE
                                "children": [     # 设置 focus 的子元素
                                    # 设置 CustomNotebook.label 的样式
                                    ("CustomNotebook.label", {"side": "left", "sticky": ''}), # 设置 label 的侧边距在左侧,无粘性
                                    # 设置 CustomNotebook.close 的样式
                                    ("CustomNotebook.close", {"side": "left", "sticky": ''}), # 设置 close 的侧边距在左侧,无粘性
                                ]
                            })
                        ]
                    })
                ]
            })
        ])

将定义好的Notebook应用到tk窗口,并添加存储标识,不需要的删除即可

python 复制代码
if __name__ == "__main__":
    root = tk.Tk()
    notebook = CustomNotebook(width=400, height=400, padding=(2,5,2,2))
    notebook.pack(side="top", fill="both", expand=True)

    images = (
        # 原色
        tk.PhotoImage('save_original', data = '''
            R0lGODdhDgAOAIU9ADFjpTFjrTFjtTFrtTljrTlrrcDAwEJzvUpzvUp7vVJ7rVJ7vVqEvWOEvWOMzmuU
            zmuU1nOUxnOc1nOc3nuUxnucxnul53ut54Slzoylxoyl1oyt1ozGY5StxpS13py13qWtxqW9563G57XO
            773O78bW78bW987e9zlrvQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAADgAOAAAIjAAzUFhwIIXBgykYgDAQ4cQJFBAjQiTh
            oEOCExdQOJA4kcSBAycsaOSIwkKJASlCkoQ4IYSAFCYsOHDwoOZMBxI+vIxpoWfPCRKC6kxRwqdPoEE9
            BEhBwqiFoFA3LG26EgUEDUtHiOTAtSuHBxiybvXK1UEFAAhEiKhagUIBCg0ODBBAN4DdAAAIKAgIADs=
            '''),
        # 黑白
        tk.PhotoImage('save_gray', data = '''
            R0lGODdhDgAOAIU9AFxcXF1dXV9fX2JiYmRkZGVlZW1tbW9vb3R0dHZ2dn5+foGBgYeHh46Ojo+Pj5CQ
            kJKSkpaWlpeXl6CgoKGhoaOjo6WlpaioqKmpqa2trbCwsLKysru7u8DAwMLCwsrKys3NzdTU1NXV1dzc
            3P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAADgAOAAAIiwApQEhgoIDBgwUUZOjwYMQIEhAjQgTB
            4AKCERZIMJA4EYQBAyMmaORIYkKIAQVCkoQogUOAAiImMGDQoOZMBhE2vIw5oWdPCRGC6iwQwqdPoEE1
            ACgAwuiEoFAvLG26koSDCks/iMTAtSuGBhOybvXKlYEEAAc8eKgqAQIBCAsMDAhAF4BduwIQBAQAOw==
            '''),
        # 红色
        tk.PhotoImage('save_red', data = '''
            R0lGODdhDgAOAIU9AP/h4f++vv+3t/+2tv+vr/+srP+kpP+iov+dnf+UlP+Skv+Pj/+Li/+Kiv+Hh/+F
            hf+Dg/+Cgv95ef94eP90dP9ycv9xcf9wcP9paf9jY/9gYP9YWP9WVv9RUf9PT/tHR/pGRvhERPVBQfM/
            P/I+PgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAADgAOAAAIiwAhUNjg4YPBgx80LDhQIUAAABAjQiSA
            oQGHAA4AYJA4kYAHDwEiaOQIIMKAEB9CkoQoAcGIDwIiYMBwoeZMDBMSvIwZoWdPCROC6vwwwKdPoEEV
            kPhAwGiEoFAbLG26EoCFB0sLiGTAtSuDCxGybvXKFYMEEh0MGKgqgQIIChk8hBhBl4RduyI4BAQAOw==
            '''),
        # 淡红色
        tk.PhotoImage('save_lowred', data = '''
            R0lGODdhDgAOAIU9AP/w8P/p6f/o6P/h4f/e3v/W1v/U1P/Pz//Gxv/ExP/Bwf+9vf+8vP+5uf+3
            t/+1tf+0tP+rq/+qqv+mpv+kpP+jo/+iov+bm/+Vlf+Skv+Kiv+IiP+Dg/+Bgft5efp4ePh2dvVzc/Nx
            cfJwcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAADgAOAAAIiwAhUNjg4YPBgx80LDhQIUAAABAjQiSA
            oQGHAA4AYJA4kYAHDwEiaOQIIMKAEB9CkoQoAcGIDwIiYMBwoeZMDBMSvIwZoWdPCROC6vwwwKdPoEEV
            kPhAwGiEoFAbLG26EoCFB0sLiGTAtSuDCxGybvXKFYMEEh0MGKgqgQIIChk8hBhBl4RduyI4BAQAOw==
            '''),
        )

    colour = {"blue":None, "red":None, "violet":None, "orange":None, "green":None}
    for color in colour.keys():
        colour[color] = tk.Frame(notebook, background=color) # 新建Frame作为标签内容
        notebook.add(colour[color], text=color) # 将新建的Frame添加到notebook生成一个标签

    colour['white'] = tk.Frame(notebook) 
    notebook.add(colour['white'], text='white', image='save_gray' , compound='left') # 新建一个直接带image元素的标签,图标元素位于左侧
    notebook.insert(3, colour['white'])  #将'white'标签移动到3的位置
    notebook.select(colour['white'])

    # 修改标签的image元素
    notebook.tab(colour['blue']  , image='save_original', compound='left')
    notebook.tab(colour['red' ]  , image='save_red'     , compound='left')
    notebook.tab(colour['violet'], image='save_lowred'  , compound='left')

    root.mainloop()

上面代码中的base64编码的字符串,可以用下面代码获取,要用gif格式文件转换,传入文件路径即可。

python 复制代码
from PIL import Image
import base64, io
def bmp_to_base64(img_path):
    "将gif图片转为base64编码的字符串"
    # 打开图片
    img = Image.open(img_path)
    # 将图片数据转为字节流
    byte_arr = io.BytesIO()
    img.save(byte_arr, format='GIF')
    img_bytes = byte_arr.getvalue()
    # 将字节流转为base64编码的字符串
    base64_str = base64.b64encode(img_bytes).decode()
    return base64_str
print (bmp_to_base64('gif文件路径'))

#想要拖动标签切换位置的可以绑定鼠标事件,自己试试吧。

相关推荐
博观而约取28 分钟前
Django ORM 1. 创建模型(Model)
数据库·python·django
精灵vector2 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习2 小时前
Python入门Day2
开发语言·python
Vertira2 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉2 小时前
Python之 sorted() 函数的基本语法
python
项目題供诗2 小时前
黑马python(二十四)
开发语言·python
晓13133 小时前
OpenCV篇——项目(二)OCR文档扫描
人工智能·python·opencv·pycharm·ocr
是小王同学啊~3 小时前
(LangChain)RAG系统链路向量检索器之Retrievers(五)
python·算法·langchain
AIGC包拥它3 小时前
提示技术系列——链式提示
人工智能·python·langchain·prompt
孟陬3 小时前
Python matplotlib 如何**同时**展示正文和 emoji
python