【问题标题】:Is there a way to add close buttons to tabs in tkinter.ttk.Notebook?有没有办法在 tkinter.ttk.Notebook 的选项卡中添加关闭按钮?
【发布时间】:2016-09-12 20:29:27
【问题描述】:

我想为tkinter.ttk.Notebook 中的每个选项卡添加关闭按钮。我已经尝试添加图像并对点击事件做出反应,但不幸的是BitmapImage 没有bind() 方法。

如何修复此代码?

#!/usr/binenv python3

from tkinter import *
from tkinter.ttk import *


class Application(Tk):
    def __init__(self):
        super().__init__()
        notebook = Notebook(self)
        notebook.pack(fill=BOTH, expand=True)
        self.img = BitmapImage(master=self, file='./image.xbm')
        self.img.bind('<Button-1>', self._on_click)
        notebook.add(Label(notebook, text='tab content'), text='tab caption', image=self.img)

    def _on_click(self, event):
        print('it works')

app = Application()
app.mainloop()

图像.xbm

#define bullet_width 11
#define bullet_height 9
static char bullet_bits = {
    0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0xf8, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00
}

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    主题 (ttk) 小部件的一个优点是您可以使用单个小部件“元素”创建新的小部件。虽然不是很简单(也没有很好的记录),但您可以创建一个新的“关闭选项卡”元素并将其添加到“选项卡”元素。

    我将提出一种可能的解决方案。我承认这不是特别容易理解。在 tkdocs.com 上可以找到有关如何创建自定义小部件样式的最佳资源之一,从 Styles and Themes 部分开始。

    try:
        import Tkinter as tk
        import ttk
    except ImportError:  # Python 3
        import tkinter as tk
        from tkinter import ttk
    
    class CustomNotebook(ttk.Notebook):
        """A ttk Notebook with close buttons on each tab"""
    
        __initialized = False
    
        def __init__(self, *args, **kwargs):
            if not self.__initialized:
                self.__initialize_custom_style()
                self.__inititialized = True
    
            kwargs["style"] = "CustomNotebook"
            ttk.Notebook.__init__(self, *args, **kwargs)
    
            self._active = None
    
            self.bind("<ButtonPress-1>", self.on_close_press, True)
            self.bind("<ButtonRelease-1>", self.on_close_release)
    
        def on_close_press(self, event):
            """Called when the button is pressed over the close button"""
    
            element = self.identify(event.x, event.y)
    
            if "close" in element:
                index = self.index("@%d,%d" % (event.x, event.y))
                self.state(['pressed'])
                self._active = index
                return "break"
    
        def on_close_release(self, event):
            """Called when the button is released"""
            if not self.instate(['pressed']):
                return
    
            element =  self.identify(event.x, event.y)
            if "close" not in element:
                # user moved the mouse off of the close button
                return
    
            index = self.index("@%d,%d" % (event.x, event.y))
    
            if self._active == index:
                self.forget(index)
                self.event_generate("<<NotebookTabClosed>>")
    
            self.state(["!pressed"])
            self._active = None
    
        def __initialize_custom_style(self):
            style = ttk.Style()
            self.images = (
                tk.PhotoImage("img_close", data='''
                    R0lGODlhCAAIAMIBAAAAADs7O4+Pj9nZ2Ts7Ozs7Ozs7Ozs7OyH+EUNyZWF0ZWQg
                    d2l0aCBHSU1QACH5BAEKAAQALAAAAAAIAAgAAAMVGDBEA0qNJyGw7AmxmuaZhWEU
                    5kEJADs=
                    '''),
                tk.PhotoImage("img_closeactive", data='''
                    R0lGODlhCAAIAMIEAAAAAP/SAP/bNNnZ2cbGxsbGxsbGxsbGxiH5BAEKAAQALAAA
                    AAAIAAgAAAMVGDBEA0qNJyGw7AmxmuaZhWEU5kEJADs=
                    '''),
                tk.PhotoImage("img_closepressed", data='''
                    R0lGODlhCAAIAMIEAAAAAOUqKv9mZtnZ2Ts7Ozs7Ozs7Ozs7OyH+EUNyZWF0ZWQg
                    d2l0aCBHSU1QACH5BAEKAAQALAAAAAAIAAgAAAMVGDBEA0qNJyGw7AmxmuaZhWEU
                    5kEJADs=
                ''')
            )
    
            style.element_create("close", "image", "img_close",
                                ("active", "pressed", "!disabled", "img_closepressed"),
                                ("active", "!disabled", "img_closeactive"), border=8, sticky='')
            style.layout("CustomNotebook", [("CustomNotebook.client", {"sticky": "nswe"})])
            style.layout("CustomNotebook.Tab", [
                ("CustomNotebook.tab", {
                    "sticky": "nswe",
                    "children": [
                        ("CustomNotebook.padding", {
                            "side": "top",
                            "sticky": "nswe",
                            "children": [
                                ("CustomNotebook.focus", {
                                    "side": "top",
                                    "sticky": "nswe",
                                    "children": [
                                        ("CustomNotebook.label", {"side": "left", "sticky": ''}),
                                        ("CustomNotebook.close", {"side": "left", "sticky": ''}),
                                    ]
                            })
                        ]
                    })
                ]
            })
        ])
    
    if __name__ == "__main__":
        root = tk.Tk()
    
        notebook = CustomNotebook(width=200, height=200)
        notebook.pack(side="top", fill="both", expand=True)
    
        for color in ("red", "orange", "green", "blue", "violet"):
            frame = tk.Frame(notebook, background=color)
            notebook.add(frame, text=color)
    
        root.mainloop()
    

    这是它在 linux 系统上的样子:

    【讨论】:

    • 让每个标签的新元素独一无二有多难?在我的应用程序中,我想要一个“关闭”元素和一个“保存/修改”元素,当内容当前未保存或保存时,它会变为红色或灰色(就像在 Notepad++ 中一样)。我面临的问题是如何控制每个选项卡中的“保存”元素颜色。因此,对于您的答案,当您单击“关闭”元素时,如何使一个“关闭”元素更改颜色? (当然不是关闭选项卡/子项)
    • 好的,我刚找到stackoverflow.com/questions/23038356/…,它谈论的是主题代替样式。仍在尝试消化它...当然,该示例并未解决您的附加元素的额外复杂性...
    • 惊人的例子!您能否解释一下 tk.PhotoImage 中的“数据”来自哪里?我自己尝试过搜索,但所有其他代码示例似乎都在使用它们的本地图像(具有 .png、.gif 等扩展名)
    • 是否可以修改此代码,以便只有当鼠标光标悬停在选项卡中的“x”元素上时,选项卡内的“x”按钮才会将颜色更改为红色或黄色?这样用户就不会在意外选择和关闭选项卡之间混淆。
    • @amrsa:我已经为这个错误添加了一个解决方法。
    【解决方案2】:

    我非常喜欢使用这段代码,谢谢!!! 通过将构造函数修改为:修复了创建多个 Notebook 的错误:

        def __init__(self, *args, **kwargs):
            if not self.__initialized:
                self.__initialize_custom_style()
                CustomNotebook.__initialized = True
    

    希望其他人也可以利用:-)

    【讨论】:

    • 很高兴你喜欢它。
    猜你喜欢
    • 2013-08-18
    • 2015-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多