尝试使用 Tkinter 在单个框架内嵌套多个框架。我如何以面向对象的方式完成此操作?

Attempting to nest several frames within a single frame with Tkinter. How do I accomplish this in an object oriented fashion?

我的代码基本上是这样做的:

这显然不是我想尝试的。为了进一步说明,我希望我的 window 看起来与此类似:

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()

我显然没有做对。我重写程序的全部原因是我可以通过面向对象的代码获得更大的 understanding/confidence。我希望我能对此有更好的了解,所以任何帮助都会很棒。

您的代码存在几个问题。

可能最大的问题是Encoding继承自tk.TkMainApplication继承自tk.FrameEncoding(使其成为根 window 和框架),然后 MainApplication 创建 Encoding 的实例。另外,您显式创建了另一个 tk.Tk() 实例,为您提供了两个根 windows。这一切都需要理清头绪。

继承创建 "is a" 关系。通过让 MainApplicationEncoding 继承,你说 MainApplication 是一个 Encoding 对象。在您的代码中情况并非如此 - Encoding 对象仅代表应用程序的一小部分。为此,你想要组合,而不是继承,即:MainApplication 有一个 Encoding 对象。

因此,第一步是从 MainApplication 继承的 class 列表中删除 Encoding

另一个可能被删除的东西是self.encoding_frame。我认为没有理由拥有它,因为 MainApplication 本身就是一个框架。相反,让 MainApplication 继承自 ttk.LabelFrame 而不是 tk.Frame.

最后的事情是,由于MainApplication创建了Encoding,它应该负责在Encoding的实例上调用gridpack

总而言之,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% 完成,但这是一个很好的起点。根据您的图像,我猜您会有其他 class 用于主应用程序的其他部分——消息小部件、密钥小部件和转录 window。

对于 Encoding,大部分相同的建议都适用。由于它只是应用程序的一部分,因此不应继承自 tk.Tk。相反,您可以从 ttk.Frame 继承,然后删除 self.encoding_frame,因为 Encoding 对象本身已经是一个框架。

进行这些更改后,Encoding 应如下所示。请注意单选按钮如何将 self 作为其父项。如果您要创建适当的对象,class 中的所有小部件都需要是 class 本身或其后代之一的子项。像这样的 class 不应该在 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 —— 创建 [=15 实例的代码块=]需要负责调用packgrid。由于 MainApplication 是唯一直接位于根 window 内部的小部件,因此 pack 是最佳选择,因为您不必记住配置行和列权重以获得正确的行为window 已调整大小。

此外,我建议在同一个块中创建 root,而不是在程序的最开始。

您的底部代码块应如下所示:

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