Попытка вложить несколько кадров в один кадр с помощью Tkinter. Как мне сделать это объектно-ориентированным способом? - PullRequest
1 голос
/ 25 октября 2019

Мой код в основном делает это:

my problem

Это явно не то, что я хочу попробовать. Для дальнейшего разъяснения я бы хотел, чтобы мое окно выглядело примерно так:

an ideal solution

from tkinter import *
import tkinter as tk
from tkinter import ttk

root = tk.Tk()

class Encoding(tk.Tk):
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.mode = StringVar()
##      If I remove the next line it breaks it entirely.
        self.encoding_frame = ttk.Frame(parent)
        self.encrypt = ttk.Radiobutton(self.encoding_frame, text='Encrypt', variable=self.mode, value='encrypt')
        self.decrypt = ttk.Radiobutton(self.encoding_frame, text='Decrypt', variable=self.mode, value='decrypt')
        self.encrypt.grid(column=0, row=0, ipadx=2, sticky=W)
        self.decrypt.grid(column=0, row=1, ipadx=2, sticky=W)
        self.encoding_frame.grid(column=0, columnspan=3, row=2, sticky=S)


class MainApplication(tk.Frame, Encoding):
    # Create a main frame here.
    # Would like frames to be nested within this frame. This seems redundant but nesting with a main
    # frame allows for consistent themes, and gives additional control of layout between subframes.
    # Inheritance is confusing.
    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)
        self.main_frame = ttk.LabelFrame(parent, text="Main Window", width=500, height=500)
        self.main_frame['borderwidth'] = 3
        self.main_frame['relief'] = 'raised'
        self.main_frame.grid(column=0, row=0)
        self.encoding = Encoding(self)
##      I wrote the following line hoping that I could have main_frame become the parent frame.
        self.encoding.encoding_frame = ttk.LabelFrame(self.main_frame)


if __name__ == "__main__":
    app = MainApplication(root)
    root.mainloop()

Я явно не понимаю, что-то правильно. Единственная причина, по которой я переписал программу, заключается в том, чтобы я мог лучше понять ее с помощью объектно-ориентированного кода. Я надеюсь, что смогу лучше понять это, поэтому любая помощь будет потрясающей.

1 Ответ

1 голос
/ 25 октября 2019

Есть несколько проблем с вашим кодом.

Возможно, самая большая проблема заключается в том, что Encoding наследуется от tk.Tk, MainApplication наследуется от tk.Frame и Encoding (делая его одновременно корневым окном и фреймом)и затем MainApplication создает экземпляр Encoding. Кроме того, вы явно создаете еще один экземпляр tk.Tk(), предоставляя вам два корневых окна. Это все должно быть распутано.

Наследование создает "это" отношение. Имея MainApplication наследование от Encoding, вы говорите, что MainApplication является Encoding объектом. Это не так в вашем коде - объект Encoding представляет только небольшую часть приложения. Для этого вам нужна композиция , а не наследование , то есть: MainApplication имеет Encoding объект.

Итак, первым шагом является удаление Encoding из списка классов, от которых наследуется MainApplication.

Другая вещь, которая может быть удалена, это self.encoding_frame. Я не вижу причин иметь его, так как MainApplication сам по себе является рамкой. Вместо этого MainApplication наследуется от ttk.LabelFrame, а не tk.Frame.

И, наконец, поскольку MainApplication создает Encoding, он должен отвечать за вызов grid или packв случае Encoding.

В целом, MainApplication можно сократить до этого:

class MainApplication(ttk.LabelFrame):
    def __init__(self, parent, *args, **kwargs):
        ttk.LabelFrame.__init__(self, parent, *args, **kwargs)

        self.configure(text="Main Window")
        self['borderwidth'] = 3
        self['relief'] = 'raised'

        self.encoding = Encoding(self)
        self.encoding.grid(row=0, column=0, sticky="ew")

Это не на 100% завершено, но это хорошее место для начала. Основываясь на вашем изображении, я предполагаю, что у вас будут другие классы для других частей основного приложения - виджет сообщений, виджеты ключей и окно транскрипции.

Для Encoding, большая частьприменяется тот же совет. Поскольку это только часть приложения, оно не должно наследоваться от tk.Tk. Вместо этого вы можете наследовать от ttk.Frame, а затем удалить self.encoding_frame, поскольку сам объект Encoding уже является фреймом.

С этими изменениями Encoding должен выглядеть примерно следующим образом. Обратите внимание, что радиокнопки имеют self в качестве родителя. Если вы создаете надлежащие объекты, все виджеты внутри класса должны быть дочерними по отношению к самому классу или одному из его потомков. Класс, подобный этому, никогда не должен помещать ничего в parent, кроме себя.

class Encoding(ttk.Frame):
    def __init__(self, parent, *args, **kwargs):
        ttk.Frame.__init__(self, parent, *args, **kwargs)

        self.mode = StringVar()
        self.encrypt = ttk.Radiobutton(self, text='Encrypt', variable=self.mode, value='encrypt')
        self.decrypt = ttk.Radiobutton(self, text='Decrypt', variable=self.mode, value='decrypt')

        self.encrypt.grid(column=0, row=0, ipadx=2, sticky=W)
        self.decrypt.grid(column=0, row=1, ipadx=2, sticky=W)

Наконец, поскольку MainApplication теперь является фреймом - вместо того, чтобы наследовать от Encoding, который наследуется от tk.Tk -- блок кода, который создает экземпляр MainApplication, должен отвечать за вызов pack или grid. Поскольку MainApplication является единственным виджетом, находящимся непосредственно внутри корневого окна, pack - лучший выбор, поскольку вам не нужно настраивать вес строк и столбцов, чтобы получить правильное поведение при изменении размера окна.

Кроме того, я рекомендую создавать root в том же блоке, а не в самом начале программы.

Ваш нижний блок кода должен выглядеть следующим образом:

if __name__ == "__main__":
    root = tk.Tk()
    app = MainApplication(root)
    app.pack(fill="both", expand=True)
    root.mainloop()
...