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。
我正在努力学习 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。