Python 和 Tkinter:面向对象编程查询

Python and Tkinter: object oriented programming query

我正在努力学习 python、Tkinter 和 oop。下面是我按照 effbot.org

上的教程编写的代码
    from Tkinter import Tk, Frame, Label

class Nexus(object):
    """Top level object which represents entire app"""
    def __init__(self, main_window):
        self.nexus_frame = Frame(main_window)
        self.nexus_frame.pack()

        self.label = Label(main_window, text="Tkinter")
        self.label.pack()





def main():
    main_window = Tk()
    nexus_app = Nexus(main_window)
    main_window.wm_title("Hello World Window")
    width = main_window.winfo_screenwidth()
    height = main_window.winfo_screenheight()
    main_window.wm_minsize(width=width-100, height=height-100)
    main_window.mainloop()

if __name__ == "__main__":
    main()

这里首先创建一个顶层 window 并将其作为参数传递给 Nexus class,我在其中添加一个框架和一个标签到框架。然后我在主函数中设置顶层 window 相对于当前屏幕尺寸的尺寸。

我的问题是为什么顶层 window 在 main 函数中创建? 它不能在 Nexus class 本身的 __init__ 内部创建吗? 如果 main_window 是在 Nexus class 的 __init__ 内部创建的,并且 mainloop() 是在其中启动的,那会有什么不同?

一旦输入Tk.mainloop,将不会再执行任何代码。相反,Tk 事件循环将接管(因此得名)。

这意味着如果你,例如,做了这样的事情:

def main():
    ...
    main_window.mainloop()
    print 'Hello world!'

那么该打印语句将永远不会被执行(或者,至少,当 GUI 是 运行 时)。


那么,考虑到这一点,为什么在构造函数(__init__ 语句)中创建根 window 和执行主循环会出现问题?嗯,两个基本原因:

  • 这意味着构造函数永远不会returns,这是意想不到的。如果程序员看到这个:

    def main():
        Nexus()
        print 'Hello world!'
    

    那么他或她将期望执行打印语句。通常,您 不会 期望创建 class 的实例会导致无限循环(就像事件循环一样)。

  • 与此相关的是第二个原因:不可能创建多个 Nexus 实例,因为一旦创建一个实例,Tk.mainloop 就会接管。同样,这是出乎意料的:class 是一种对象类型的描述,您通常希望能够实例化多个这样的对象。

    此刻,如果你写:

    def main():
        ...
        Nexus(main_window)
        Nexus(main_window)
    

    然后您会在屏幕上看到您的 Nexus window 的两份副本。这是意料之中的,也是明智的。替代方案不会。


那么要点是什么?

当您处理 GUI 程序时,进入事件循环是您最不想做的事情。您的设置可能涉及创建一个对象(如现在),也可能涉及创建多个对象(例如,一个复杂的 GUI 应用程序可能有两个或三个 windows)。

因为我们希望能够在两种情况下编写相似的代码,所以通常的做法是创建根window(Tk对象)一次,然后将其作为参考任何需要了解它的 classes。