【问题标题】:Inheriting from Frame or not in a Tkinter application在 Tkinter 应用程序中是否从 Frame 继承
【发布时间】:2011-11-10 03:12:55
【问题描述】:

我见过两种设置 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()   

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    Frame 通常用作geometry master for other widgets。 由于应用程序通常有许多小部件,您通常希望将它们全部包含在一个 Frame 中,或者至少使用 Frame 添加一些 borderwidth、填充或其他细节。

    您可能在网络上找到的许多示例 sn-ps 不使用框架,因为 他们只是想用最少的代码展示一些功能。

    所以,如果需要,请使用 Frame,否则不要。

    编辑:我认为组织 GUI 的最佳方式是在 Tkinter tutorial:

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

    这主要类似于您的 first 示例,因为 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 可以是一个独立的脚本,也可以是一个可导入的模块。 如果您尝试使用继承自 tk.TkSimpleApp 进行此操作,您最终会得到额外的不需要的窗口。

    【讨论】:

    • 我可以在两个版本中使用网格进行复杂的布局。区别是我可以用 Frame 做一个更漂亮的边框吗?
    • @foosion:Frame docs 显示了 Frame 具有的一些配置选项(是的,主要与边框有关),这些选项不能用网格完成。如果您需要其中之一,请使用框架,否则不要。
    • 的结论是,我们还是普遍使用Frame吧,因为没有真正的额外成本,它提供了更多的选择,不值得为每个程序考虑走哪条路?
    • @foosion:我认为这是合理的。
    • borderwidth=10,relief=SUNKEN 相当丑陋,IMO
    【解决方案2】:

    将顶级对象设置为从Tk 继承而不是Frame 可能会有好处。当您的 GUI 具有动态元素时,优势就会出现,例如一个Label,你想用textvariable=foo而不是text= 'Label text'来设置它的内容。

    在这种情况下,使用Tkinter.DoubleVarTkinter.IntVarTkinter.StringVar 对象来保存数据非常有帮助,因为只要设置了这些对象,GUI 就会自动更新。但是,要使用这些对象,您必须将它们的主控指定为根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() 
    

    【讨论】:

    • 您的答案并不完全正确。您可以将 any 小部件指定为主窗口,而无需为其提供根窗口。从 Frame 继承时,您可以很好地使用 tkinter 变量。
    【解决方案3】:

    我更喜欢的选项*是从类 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 程序的更多信息,请参阅my answer 问题Python Tkinter program structure

    【讨论】:

    • 从 Tk 继承而不是像上面的第一个示例中的 root = tk() 有什么好处?继承自 Tk 是否为您提供与继承自 Frame 相同的“美化”灵活性?
    • from module import * 总是让我紧张,但对于 Tkinter 来说这很常见,我已经克服了它。有真正的风险吗?我不太担心可读性,而是担心一些名称冲突。
    • @foosion:没有优势也没有劣势。您仍然拥有完全相同的“美化”灵活性。如果您想获得特定的边框效果,您仍然可以选择嵌入框架。我只是不明白让您的主应用程序对象成为其他对象的子对象的原因。从逻辑的角度来看,应用程序对象应该是层次结构中最顶层的对象。
    • @foosion:“from tkinter import *”的风险与任何其他模块相同。鉴于 tkinter 类具有非常通用的名称(按钮、标签等),很容易意外创建同名的类并导致名称冲突。
    • 在使用 Tix 小部件(例如,ScrolledListBox)并从 Tk(或 root = Tk())继承时,将 import Tkinter 更改为 import Tix 似乎可以正常工作,但从 Frame 继承时则不行。如果是这样,这将是从 Tk 继承的优势
    猜你喜欢
    • 2020-03-01
    • 2021-04-20
    • 2018-04-28
    • 1970-01-01
    • 2010-11-09
    • 1970-01-01
    • 2020-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多