【问题标题】:Subclassing Tkinter.Text to create a custom widget子类化 Tkinter.Text 以创建自定义小部件
【发布时间】:2013-10-05 22:05:34
【问题描述】:

我正在尝试创建一个带有垂直滚动条的文本小部件,同时保留 Tkinter.Text 的所有方法/功能。

到目前为止,我有以下代码:

class ScrollableTextWidget(Tkinter.Text):
    def __init__(self, parent):
        self.parent = parent
        self.Frame = ttk.Frame(self.parent)
        Tkinter.Text.__init__(self, self.Frame, width=1, height=1)
        self.__initWidget()

    def __initWidget(self):
        self.Frame.grid(sticky="NSEW")
        self.ScrollbarY = ttk.Scrollbar(self.Frame, orient="vertical", 
                                        command=self.yview)
        self.configure(yscrollcommand=self.ScrollbarY.set)
        self.grid(column=0, row=0, sticky="NSEW")
        self.ScrollbarY.grid(column=1, row=0, sticky="NS")
        self.Frame.columnconfigure(0, weight=1)
        self.Frame.rowconfigure(0, weight=1)

可以像这样创建我的自定义小部件,还是应该将它放在Tkinter.frame 中并编写我自己的方法?

【问题讨论】:

    标签: python class widget tkinter inheritance


    【解决方案1】:

    我会为中型、大型项目或未来有支持的项目使用子类,因为子类可以很容易地替换或更新。无论如何,子类化的成本很小。 但是,如果您的任务是一次性的,那么做起来又脏又快。

    在这种情况下,我将从 Frame 子类化,因为它是最通用的小部件,不需要覆盖 packgrid 和其他方法。

    【讨论】:

    • 我想问的是,子类化 Tkinter.Text(或任何其他小部件以保留其方法和功能)以创建自定义小部件是一种好习惯。在这种情况下,我将 Tkinter.Text 子类化,将它与滚动条一起放在我班级的框架上。还是我应该继承 Frame 并将 Text Widget 和滚动条直接放在它上面并创建自己的方法和函数?
    【解决方案2】:

    将小部件子类化以创建自定义小部件是很正常的。但是,如果此自定义小部件由多个小部件组成,您通常会继承 Frame。例如,要创建一个带有滚动条的文本小部件,我会这样做:

    import Tkinter as tk
    
    class ScrolledText(tk.Frame):
        def __init__(self, parent, *args, **kwargs):
            tk.Frame.__init__(self, parent)
            self.text = tk.Text(self, *args, **kwargs)
            self.vsb = tk.Scrollbar(self, orient="vertical", command=self.text.yview)
            self.text.configure(yscrollcommand=self.vsb.set)
            self.vsb.pack(side="right", fill="y")
            self.text.pack(side="left", fill="both", expand=True)
    
    class Example(tk.Frame):
        def __init__(self, parent):
            tk.Frame.__init__(self, parent)
            self.scrolled_text = ScrolledText(self)
            self.scrolled_text.pack(side="top", fill="both", expand=True)
            with open(__file__, "r") as f:
                self.scrolled_text.text.insert("1.0", f.read())
    
    root = tk.Tk()
    Example(root).pack(side="top", fill="both", expand=True)
    root.mainloop()
    

    使用这种方法,请注意在插入文本时需要如何引用内部文本小部件。如果您希望这个小部件看起来更像一个真正的文本小部件,您可以创建到部分或全部文本小部件功能的映射。例如:

    import Tkinter as tk
    
    class ScrolledText(tk.Frame):
        def __init__(self, parent, *args, **kwargs):
            tk.Frame.__init__(self, parent)
            self.text = tk.Text(self, *args, **kwargs)
            self.vsb = tk.Scrollbar(self, orient="vertical", command=self.text.yview)
            self.text.configure(yscrollcommand=self.vsb.set)
            self.vsb.pack(side="right", fill="y")
            self.text.pack(side="left", fill="both", expand=True)
    
            # expose some text methods as methods on this object
            self.insert = self.text.insert
            self.delete = self.text.delete
            self.mark_set = self.text.mark_set
            self.get = self.text.get
            self.index = self.text.index
            self.search = self.text.search
    
    class Example(tk.Frame):
        def __init__(self, parent):
            tk.Frame.__init__(self, parent)
            self.scrolled_text = ScrolledText(self)
            self.scrolled_text.pack(side="top", fill="both", expand=True)
            with open(__file__, "r") as f:
                self.scrolled_text.insert("1.0", f.read())
    
    root = tk.Tk()
    Example(root).pack(side="top", fill="both", expand=True)
    root.mainloop()
    

    【讨论】:

    • 谢谢,这真的很有帮助。我确实尝试过投票/接受您的回答,但不幸的是我的声誉太低了。另一件事是,我将如何以相同的方式创建 ttk.Treeview 并能够将事件绑定到它?我应该在框架内公开和使用 treeview 实例吗?
    • 您的解决方案的问题正是您必须手动公开文本成员方法,而 OP 希望免费获得这个。因此,我猜测他最初将 Text 子类化的想法是正确的方法。我的 Tkinter 掌握太浅,无法回答这个问题,但如果有人能这样做,我会很高兴。
    • @user2830098 你不需要任何声望点来接受答案
    • @quickbug:我不明白公开文本小部件方法的问题。如果您只想这样做,则子类化文本小部件很好,但您不想将滚动条放在文本小部件内,因此您需要一个容纳两个小部件的容器。
    • 以这种方式定义自定义小部件需要考虑的一点是,使它们与通用小部件config() 方法一起工作。将需要一个自定义方法,根据其名称将各种选项值路由到适当的子小部件。虽然当然可以做到,但这只是设计、实施和维护的另一个细节。同样的事情也适用于使setitem() 方法做正确的事情。我相信如果现有问题被子类化,这将不是问题,或者至少不是问题。
    猜你喜欢
    • 2016-11-05
    • 1970-01-01
    • 1970-01-01
    • 2013-01-08
    • 2023-03-20
    • 2019-11-20
    • 1970-01-01
    • 2013-06-11
    • 2021-08-16
    相关资源
    最近更新 更多