在Tkinter应用中是否继承Frame

41 投票
3 回答
41353 浏览
提问于 2025-04-17 01:24

我见过两种基本的方式来设置一个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 个回答

2

将你的顶层对象设置为继承自 Tk 而不是 Frame 是有好处的。这个好处在于,当你的图形界面(GUI)中有动态元素时,比如你想用 textvariable=foo 来设置一个 Label 的内容,而不是用 text= 'Label text' 的时候,这种做法就显得特别有用。

在这种情况下,使用 Tkinter.DoubleVarTkinter.IntVarTkinter.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() 
15

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

不过,让SimpleAppobject继承,相比从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可以是一个独立的脚本,也可以作为一个可导入的模块。如果你尝试让SimpleApptk.Tk继承,你会得到额外的不必要的窗口。

43

我比较喜欢的选择是从Tk这个类继承。我觉得这是更合理的选择,因为窗口实际上就是你的应用程序。从Frame继承对我来说没有比从ButtonCanvasLabel继承更有意义。因为你只能有一个主窗口,所以从这个类继承是合情合理的。

我还认为,如果你用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程序结构

撰写回答