Подклассификация Tkinter для создания пользовательского виджета

Я пытаюсь создать текстовый виджет с вертикальной полосой прокрутки, сохраняя при этом все методы/функции из 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 и написать свои собственные методы?

Ответ 1

Это очень нормально для подкласса виджета для создания пользовательского. Однако, если этот пользовательский виджет состоит из более чем одного виджета, вы обычно будете подклассом 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()

Ответ 2

Я буду использовать подкласс для среднего, большого проекта или для проекта с поддержкой в ​​будущем, потому что подкласс может легко заменить или обновить. В любом случае затраты на подклассификацию малы. Но если ваша задача одноразовая, сделайте это грязно и быстро.

В этом случае я бы выполнил подкласс из фрейма, потому что это самый общий виджет и не нужно переопределять pack, grid и другие методы.