【问题标题】:Attempting to nest several frames within a single frame with Tkinter. How do I accomplish this in an object oriented fashion?尝试使用 Tkinter 在单个帧中嵌套多个帧。我如何以面向对象的方式实现这一点?
【发布时间】:2019-10-24 22:28:59
【问题描述】:

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

这显然不是我想尝试的。为了进一步澄清,我希望我的窗口看起来类似于:

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

我显然没有做对。我重写程序的全部原因是为了让我对面向对象的代码有更好的理解/信心。我希望我能对此有更好的了解,所以任何帮助都会很棒。

【问题讨论】:

  • "#继承令人困惑。":避免multiple inheritance,你没有Encoding.__init__(...。阅读Why are multiple instances of Tk discouraged?
  • 为什么要避免多重继承?另外,我不确定我是否创建了多个 Tk 实例?恐怕你的评论有点混乱。你能说得更具体一点吗?
  • 不应该避免多重继承。相反,tkinter 的小部件已经继承自公共基类 Widget,它也继承自多个类,例如 BaseWidgetPack……如果您尝试进一步混合,它会使您的代码不必要地复杂化。跨度>
  • 另外关于多个实例,在您的代码root = tk.Tk() 中创建Tk 的一个实例。然后,您的Encoding 类继承自tk.Tk,当您调用它MainApplication 时,它还会创建Tk 的另一个实例。

标签: python python-3.x tkinter tkinter-layout


【解决方案1】:

您的代码存在几个问题。

也许最大的问题是Encoding继承自tk.TkMainApplication继承自tk.FrameEncoding(使其既是根窗口又是框架),并且然后MainApplication 创建一个Encoding 的实例。另外,您显式地创建了tk.Tk() 的另一个实例,为您提供了两个根窗口。这一切都需要解开。

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

因此,第一步是从MainApplication 继承的类列表中删除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% 完成的,但它是一个很好的起点。根据您的图片,我猜您将为主应用程序的其他部分提供其他类——消息小部件、键小部件和转录窗口。

对于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 现在是一个框架——而不是从继承自tk.TkEncoding 继承——创建MainApplication 实例的代码块需要负责调用@987654363 @ 或 grid。由于MainApplication 是唯一直接位于根窗口内的小部件,因此pack 是最佳选择,因为您无需记住配置行和列的权重即可在调整窗口大小时获得正确的行为。

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

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

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

【讨论】:

  • 这太棒了,非常感谢!我实际上是在尝试在另一个名为“构建 tkinter 应用程序的最佳方式?”的线程上遵循您的建议。但显然,我做得并不好。没想到直接从你那里得到答案! :D 我可能想多了,我应该像你一样把它与基础知识配对。感谢您的帮助。
猜你喜欢
  • 2016-03-17
  • 2015-03-04
  • 1970-01-01
  • 1970-01-01
  • 2020-09-25
  • 1970-01-01
  • 2021-10-02
  • 2021-10-25
  • 2020-11-22
相关资源
最近更新 更多