在Tkinter应用中是否继承Frame
我见过两种基本的方式来设置一个tkinter程序。有没有什么理由让我们更倾向于其中一种呢?
from Tkinter import *
class Application():
def __init__(self, root, title):
self.root = root
self.root.title(title)
self.label = Label(self.root, text='Hello')
self.label.grid(row=0, column=0)
root = Tk()
app = Application(root, 'Sample App')
root.mainloop()
还有
from Tkinter import *
class Application(Frame):
def __init__(self, title, master=None):
Frame.__init__(self, master)
self.grid()
self.master.title(title)
self.label = Label(self, text='Hello')
self.label.grid(row=0, column=0)
app = Application('Sample App')
app.mainloop()
3 个回答
将你的顶层对象设置为继承自 Tk
而不是 Frame
是有好处的。这个好处在于,当你的图形界面(GUI)中有动态元素时,比如你想用 textvariable=foo
来设置一个 Label
的内容,而不是用 text= 'Label text'
的时候,这种做法就显得特别有用。
在这种情况下,使用 Tkinter.DoubleVar
、Tkinter.IntVar
和 Tkinter.StringVar
这些对象来存储数据会非常方便,因为每当这些对象的值被设置时,图形界面会自动更新。不过,要使用这些对象,你需要把它们的“主控”设置为正在运行的根 Tkinter.Tk()
实例。如果你明确地让你的主对象成为 Tkinter.Tk
的子类,这样就更简单了。然后你可以生成框架和小部件,这样就能把 Tk
实例传递下去,并正确设置你的变量。
下面是一个简短的示例程序来说明这个想法。
import Tkinter as tk
class Tkclass(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
app=Application(self)
app.master.title("Animal to Meat")
app.mainloop()
class Application(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.grid(sticky=tk.N+tk.S+tk.E+tk.W)
self.meatvar = tk.StringVar(master=parent)
self.meatvar.set("Meat?")
self.createWidgets()
def createWidgets(self):
top=self.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.columnconfigure(1, weight=1)
self.columnconfigure(2, weight=1)
self.columnconfigure(3, weight=1)
self.cowButton = tk.Button(self, text='Cow', command=self.setBeef)
self.cowButton.grid(row=0,column=0)
self.pigButton = tk.Button(self, text='Pig',command=self.setPork)
self.pigButton.grid(row=0,column=1)
self.meatLabel = tk.Label(self)
self.meatLabel.configure(textvariable=self.meatvar)
self.meatLabel.grid(row=0,column=2)
self.quit = tk.Button(self, text='Quit',command=self.QuitApp)
self.quit.grid(row=0, column=3)
def setBeef(self):
self.meatvar.set("Beef")
def setPork(self):
self.meatvar.set("Pork")
def QuitApp(self):
top=self.winfo_toplevel()
top.quit()
main = Tkclass()
Frame通常被用作其他小部件的“几何主控”。因为一个应用程序通常会有很多小部件,所以你可能会想把它们都放在一个Frame里,或者至少用Frame来添加一些边框宽度、内边距或其他美观的效果。
你在网上找到的很多示例代码可能没有使用Frame,因为它们只是想用最少的代码展示某个功能。
所以,如果你需要Frame,就用它;如果不需要,就别用。
编辑:我认为组织图形用户界面(GUI)的最好方法可以在这个Tkinter教程中找到:
simpleApp.py:
import Tkinter as tk
class SimpleApp(object):
def __init__(self, master, **kwargs):
title=kwargs.pop('title')
frame=tk.Frame(master, **kwargs)
frame.pack()
self.label = tk.Label(frame, text=title)
self.label.pack(padx=10,pady=10)
if __name__=='__main__':
root = tk.Tk()
app = SimpleApp(root,title='Hello, world')
root.mainloop()
这主要和你的第一个示例相似,因为SimpleApp
是从object
继承的,而不是从Frame
。我觉得这样比从Frame
继承要好,因为我们并没有重写任何Frame
的方法。我更喜欢把SimpleApp
看作是拥有一个Frame
,而不是一个Frame
。
不过,让SimpleApp
从object
继承,相比从tk.Tk
继承,有一个显著的好处:这使得将SimpleApp
嵌入到更大的应用程序中变得简单:
import simpleApp
import Tkinter as tk
class BigApp(object):
def __init__(self, master, **kwargs):
title=kwargs.pop('title')
frame=tk.Frame(master, **kwargs)
frame.pack()
self.simple = simpleApp.SimpleApp(frame,title=title)
frame.pack(padx=10, pady=10)
self.simple2 = simpleApp.SimpleApp(frame,title=title)
frame.pack()
if __name__=='__main__':
root = tk.Tk()
app = BigApp(root,title='Hello, world')
root.mainloop()
因此,simpleApp.py可以是一个独立的脚本,也可以作为一个可导入的模块。如果你尝试让SimpleApp
从tk.Tk
继承,你会得到额外的不必要的窗口。
我比较喜欢的选择是从Tk这个类继承。我觉得这是更合理的选择,因为窗口实际上就是你的应用程序。从Frame
继承对我来说没有比从Button
、Canvas
或Label
继承更有意义。因为你只能有一个主窗口,所以从这个类继承是合情合理的。
我还认为,如果你用import Tkinter as tk
来导入,而不是from Tkinter import *
,代码会更容易阅读。这样你在调用的时候就会明确提到tk
这个模块。我不建议对所有模块都这样做,但在Tkinter中我觉得这样更合理。
举个例子:
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.label = tk.Label(text="Hello, world")
self.label.pack(padx=10, pady=10)
app = SampleApp()
app.mainloop()
* 注意:自从我最初写这个回答以来,我改变了看法。现在我更倾向于从Frame
继承,而不是Tk
。其实两者没有实质上的优劣之分,这更多是个哲学上的选择。不管怎样,我认为无论你是从Frame
还是Tk
继承,我觉得这两种选择都比最初那个不继承任何东西的例子要好。
从Frame
继承相对于Tk
的一个小优势是,如果你想让你的应用程序支持多个相同的窗口。在这种情况下,从Frame
继承可以让你把第一个窗口作为根窗口的子窗口创建,而额外的窗口则作为Toplevel
实例的子窗口。不过,我见过的程序中很少有需要这样做的。
想了解更多关于我认为Tkinter程序应该如何结构的信息,可以查看我对问题的回答:我的回答,问题是Python Tkinter程序结构。