当你写一个较大的程序时,你最好将你的代码分布成一个或几个类,下面是一个出色的例子,它来自Matt Conway 的 A Tkinter Life Preserver .
范例 3-1. 我们的第二个Tkinter程序
# File: hello2.py from Tkinter import * class App: def __init__(self, master): frame = Frame(master) frame.pack() self.button = Button(frame, text="QUIT", fg="red", command=frame.quit) self.button.pack(side=LEFT) self.hi_there = Button(frame, text="Hello", command=self.say_hi) self.hi_there.pack(side=LEFT) def say_hi(self): print "hi there, everyone!" root = Tk() app = App(root) root.mainloop()
运行
当您运行上面的程序后, 下面的窗口将被显示出来.
图例3-1. 运行( Tk 8.0 版本 Windows 95 系统)
如果你点击右面的按钮,文字"hi there, everyone!"将被打印出来,如果点击左边的按钮将结束这个程序
代码描述
下面这个简单的应用程序被写为一个类,这个类的构建器( __init__ 方法) 被传入了一个父组件( master ),在类中它被添加了若干个子组件.构建器首先创建了一个Frame组件,frame是一个简单的容器,在这个例子里它被用来存放另外两个组件.
class App: def __init__(self, master): frame = Frame(master) frame.pack()
frame 实例被保存到一个叫做 frame的 内部变量(local variable)里.在创建这个 widget 之后, 我们立刻调用pack方法使这个 frame 显示出来.
我们创建了两个 Button widgets ,作为frame的一部分.
self.button = Button(frame, text="QUIT", fg="red", command=frame.quit) self.button.pack(side=LEFT) self.hi_there = Button(frame, text="Hello", command=self.say_hi) self.hi_there.pack(side=LEFT)
这一次,我们传递几个选项(options)给构建器 , 作为关键参数. 第一个button 的标签被指定为"QUIT", 并且被显示为红色( fg 是 foreground 的缩写 ). 第二个 button 的标签被指定为"Hello". 这两个 button 都有一个 command 选项.这个选项指定了一个函数或者一个绑定的方法在 其被点击时执行.
button的实例被存储在实例属性中,他们都被packed, 但是这次与第一个例子不同的地方是,pack 带有一个 side=LEFT 参数. 它的意思是,它将被放置到 frame 剩余空间的最左方; 第一个 button 被放置到 frame的最左边, 第二个就放置在第一个button的右边 (就是frame中左边剩余的空间). 缺省的, 每个组件的side都是相对于它的父组件 (也就是说,button的side是在frame中的side,而跟root没有直接联系)如果side没有给出, 它将缺省为 TOP .
下面给出的是 "hello" button 的回调内容. 在任意时刻点击"hello" button ,在控制台将会打印出一个消息:
def say_hi(self): print "hi there, everyone!"
最后,我们提供了一些脚本级代码来创建一个 Tk root 组件, 和 App 类的实例使用root 组件作为它的父类:
root = Tk() app = App(root) root.mainloop()
最后调用 root 的 mainloop 方法 . 它进入 Tk 事件循环(event loop), 这样,应用程序将等待,直到 quit 方法被调用 (就是点击 QUIT button), 或者窗口被关闭.
更多组件参考
在这两个例子中,Frame组件被保存为一个名为frame本地变量(local variable) , 另外两个 button 被存贮到两个实例属性里. 这里隐藏着一个比较严肃的问题:当 __init__ 函数返回,frame变量超出作用域时会发生什么事呢?
请放心; 实际上你不需要保留一个组件实例的引用.Tkinter会自动维护一个组件树, 所有一个组件在它的最后一个引用失去之后仍不会消失; 你必须明确地使用destroy方法消除一个组件.但是如果你创建一个组件后仍需要对它进行操作, 那么你最好为你保留这个组件的引用.
注意,你可以不保留一个组件的引用,用下面的方法创建组件有时会更方便:
Button(frame, text="Hello", command=self.hello).pack(side=LEFT)
用这种方法将返回None(实际上是pack方法的返回值);如果你象下面这样操作,你将不会得到你想要的结果,为了安全起见,你最好使用一个临时变量分两步来创建一个组件.
w = Button(frame, text="Hello", command=self.hello) w.pack(side=LEFT)
更多组件名称
另外容易引起混淆的地方,特别是对于有用Tck进行Tk编程经验的人, 是关于Tk的 widget 名称概念.在Tcl中,你必须明确的为每一个组件命名.例如,下面的例子是一个创建名为"ok"的Button的Tcl命令,
button .dialog.ok
相应的Tkinter命令是:
ok = Button(dialog)
然而,在Tkinter里, ok 和 dialog 是组件实例的引用, 不是组件的实际名称. 由于Tk需要属于它自己的名字, Tkinter会自动为每一个新组件起一个唯一的名字. 在上面的情况中, dialog 的名字也许会象".142723",button也会是".142723.143326"这样的名称.如果你希望得到一个Tkinter组件的全名,你可以简单的对一个组件使用 str 函数:
>>> print str(ok)
.1428748.1432920
(如果你打印什么东西, Python会自动使用 str 函数找出实际打印的内容,但是,显然,一个类似"name=ok"的操作不会如此,所以如果你需要得到一个名字,请明确的使用 str ).
如果你真的需要指定一个组件的名字,你可以在创建组件时使用 name 选项. 一个 (或者说是唯一) 这样作的理由,是你需要用Tcl代码写接口,而不会有其它什么好处.
在下面的例子里, 创建的组件被命名 " .dialog.ok " (或者, 如果你忘记 dialog 的名字, 它也会是这样 " .1428748.ok "):
ok = Button(dialog, name="ok")
为了避免Tkinter的命名冲突, 请不要使用仅有数字的名字. 同样要注意, name 是一个 "只可创建" 选项; 你不能在创建组件之后改变它的名字.