尝试使用 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.Tk
,MainApplication
继承自tk.Frame
和Encoding
(使其成为根 window 和框架),然后 MainApplication
创建 Encoding
的实例。另外,您显式创建了另一个 tk.Tk()
实例,为您提供了两个根 windows。这一切都需要理清头绪。
继承创建 "is a" 关系。通过让 MainApplication
从 Encoding
继承,你说 MainApplication
是一个 Encoding
对象。在您的代码中情况并非如此 - Encoding
对象仅代表应用程序的一小部分。为此,你想要组合,而不是继承,即:MainApplication
有一个 Encoding
对象。
因此,第一步是从 MainApplication
继承的 class 列表中删除 Encoding
。
另一个可能被删除的东西是self.encoding_frame
。我认为没有理由拥有它,因为 MainApplication
本身就是一个框架。相反,让 MainApplication
继承自 ttk.LabelFrame
而不是 tk.Frame
.
最后的事情是,由于MainApplication
创建了Encoding
,它应该负责在Encoding
的实例上调用grid
或pack
。
总而言之,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 实例的代码块=]需要负责调用pack
或grid
。由于 MainApplication
是唯一直接位于根 window 内部的小部件,因此 pack
是最佳选择,因为您不必记住配置行和列权重以获得正确的行为window 已调整大小。
此外,我建议在同一个块中创建 root
,而不是在程序的最开始。
您的底部代码块应如下所示:
if __name__ == "__main__":
root = tk.Tk()
app = MainApplication(root)
app.pack(fill="both", expand=True)
root.mainloop()
我的代码基本上是这样做的:
这显然不是我想尝试的。为了进一步说明,我希望我的 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.Tk
,MainApplication
继承自tk.Frame
和Encoding
(使其成为根 window 和框架),然后 MainApplication
创建 Encoding
的实例。另外,您显式创建了另一个 tk.Tk()
实例,为您提供了两个根 windows。这一切都需要理清头绪。
继承创建 "is a" 关系。通过让 MainApplication
从 Encoding
继承,你说 MainApplication
是一个 Encoding
对象。在您的代码中情况并非如此 - Encoding
对象仅代表应用程序的一小部分。为此,你想要组合,而不是继承,即:MainApplication
有一个 Encoding
对象。
因此,第一步是从 MainApplication
继承的 class 列表中删除 Encoding
。
另一个可能被删除的东西是self.encoding_frame
。我认为没有理由拥有它,因为 MainApplication
本身就是一个框架。相反,让 MainApplication
继承自 ttk.LabelFrame
而不是 tk.Frame
.
最后的事情是,由于MainApplication
创建了Encoding
,它应该负责在Encoding
的实例上调用grid
或pack
。
总而言之,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 实例的代码块=]需要负责调用pack
或grid
。由于 MainApplication
是唯一直接位于根 window 内部的小部件,因此 pack
是最佳选择,因为您不必记住配置行和列权重以获得正确的行为window 已调整大小。
此外,我建议在同一个块中创建 root
,而不是在程序的最开始。
您的底部代码块应如下所示:
if __name__ == "__main__":
root = tk.Tk()
app = MainApplication(root)
app.pack(fill="both", expand=True)
root.mainloop()