试图用Tkinter在一个帧中嵌套多个帧。如何以面向对象的方式实现这一点?

2024-04-24 21:29:55 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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

我显然没有把事情做好。我重写程序的全部原因是为了更好地理解/信任面向对象代码。我希望我能得到更好的洞察力与此,所以任何帮助将是惊人的


Tags: selfinitmaintkinterargscolumnframekwargs
1条回答
网友
1楼 · 发布于 2024-04-24 21:29:55

你的代码有几个问题

可能最大的问题是Encodingtk.Tk继承,MainApplicationtk.FrameEncoding继承(使其既是根窗口又是框架),然后MainApplication创建Encoding的实例。另外,显式创建另一个tk.Tk()实例,给您两个根窗口。所有这些都需要解开

继承创建一个“是一个”关系。通过让MainApplicationEncoding继承,可以说MainApplication是一个Encoding对象。在代码中不是这样的Encoding对象只表示应用程序的一小部分。因此,您需要的是合成,而不是继承,即:MainApplication有一个Encoding对象

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

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

最后一点是,由于MainApplication创建了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%完成,但这是一个很好的开始。根据您的图片,我猜您将有其他类用于主应用程序的其他部分消息小部件、关键小部件和转录窗口

对于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实例的代码块需要负责调用packgrid。由于MainApplication是直接位于根窗口内部的唯一小部件,pack是最佳选择,因为您不必记住配置行和列权重,以便在调整窗口大小时获得正确的行为

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

下面的代码块应该如下所示:

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

相关问题 更多 >