19.3 Tkinter举例

19.3.1 标签组件

在例19.1中,我们展示了Tkinter版的“Hello World!”——tkhellol.py实际上,它利用组件向你展示了如何创建一个Tkinter应用程序

19.3 Tkinter举例 - 图1

图 19-1 Tkinter标签组件(tkhello.py)

第一行,我们先创建了一个顶层窗口。随后是写着那串举世闻名的字符的标签组件。我们指明用packer来管理和显示组件,并最终调用mainloop()来运行GUI程序。图19-1展示了运行该GUI程序后,你将会看到的效果。

19.3.2 按钮组件

第二个例子和第一个很相似。但我们这次将创建一个按钮而不只是显示一个简单的文字标签。例19.2是tkhello2.py的源码。

例19. 2

本例和tkhellol.py完全相同,除了我们创建的是按钮组件而非标签组件。

19.3 Tkinter举例 - 图2

前面几行是相同的,不同的只是我们创建的是按钮组件。我们的按钮有一个额外的参数,Tkinter. quit()方法。这将给我们的按钮安装一个回调函数,在按钮按下(并释放)后让整个程序退出。最后的两行是通常的pack()和进入mainloop()。这个简单的按钮应用程序展示在图19-2中。

19.3 Tkinter举例 - 图3

图 19-2 Tkinter标签组件(tkhellol.py)

19.3.3 标签和按钮组件

我们把tkhellol.py和tkhello2.py组合到tkhello3.py中,得到一个同时包含标签和按钮的脚本。另外,我们现在还使用了更多的参数,而不再满足于完全使用那些自动添入的缺省参数。例19. 3给出了tkhello3.py的源码。

除了对组件新加的参数,我们还看到对packer的一些参数。fill参数告诉packer让QUIT按钮填充水平方向的剩余空间,而expand参数则引导packer填充了水平方向的所有可视空间,并拉伸按钮到达窗口的左右边界。

例19.3

本例同时展示了标签和按钮组件。既然我们已经了解了按钮组件和如何配置它,我们就可以设置得更多一些,而不必像以前那样大都使用缺省参数。

19.3 Tkinter举例 - 图4

19.3 Tkinter举例 - 图5

正如你在图19-3中看到的,对packer没有其他指令时,组件是按垂直顺序放置的(依次放在其他组件的上面)。要水平放置则需要创建一个框架对象,再用它来添加按钮。作为父对象的唯一子对象,框架将占据父对象的空间(参见19.3.6小节例19.6中listdir.py模块对按钮的处理)。

19.3 Tkinter举例 - 图6

图 19-3 Tkinter标签和按钮控件(tkhello3.py)

19.3.4 标签、按钮和进度条组件

我们的最后一个例子tkhello4.py,增加了一个进度条组件。具体来说,这个进度条是用来和标签组件交互的。进度条的滑块被用作控制标签组件文本大小的工具。滑块的位置值越大字体就越大,反之亦然,越小的位置值意味着越小的字体。例19.4展示了tkhello4.py的源码。

例19.4

我们最后一个组件例子介绍了进度条组件,重点放在组件间通过回调函数的交互[诸如resize()]。你对进度条组件的动作将影响标签组件上的文字。

19.3 Tkinter举例 - 图7

19.3 Tkinter举例 - 图8

这段脚本新增加的resizing()回调函数(5〜7行)附加在进度条组件上。这段代码在进度条的滑块被移动时激活,调整标签里文字的大小。

我们还限定了顶层窗口的尺寸(250x150) (第10行)。这段脚本和前3段的最后一个不同点是用“from Tkinter import *”把Tkinter模块的属性引入我们的名称空间。虽然不建议这样做,因为这会“污染”你的名称空间,但这个程序涉及大量对Tkinter属性的引用,这正是我们这样做的主要原因。这种方式(译者注:指import Tkinter的方式)要求访问每个属性时都使用它们的全部限定性名称。而通过这种不被推荐的快捷方式,我们可以在访问属性时减少输入并且让代码易于理解,但同时也付出了一些代价。

正如你在图19-4所看到的,滑块装置及当前位置值都显示在窗口的显著位置。图19-4展示了用户把进度条/滑块移动到36时的GUI程序状态。

从代码中可以看出,进度条的初始值在程序启动时被设置为12(第18行)。

19.3 Tkinter举例 - 图9

图 19-4 Tkinter标签、按钮和滑块控件(tkhello4.py)

19.3.5 偏函数应用举例

在看更大的GUI程序之前,我们先回顾一下第11章11. 7. 3节介绍的偏函数应用(Partial FunctionApplication, PFA)。

Python2. 5新增了PFA等一系列新特性,它们显著提高了Python对函数编程的支持。

偏函数允许你“预存”一些函数变量并有效地“冻结”了这些预定参数,在运行时你获得了所需的其他变量后再把它们“解冻”出来,用这些最终确定的参数去调用函数。

最妙的是,PFA不仅仅局限于函数。它们对任何“可调用”的东西都有效,任何有函数接口的对象,比如类、方法、或可调用对象,只要是有括号的。对于有许多待调对象并且许多调用都反复使用相同参数的情况,用PFA是最合适不过的。

GUI编程有很好的操作环境,因为很有可能你需要GUI组件有某些一致的外观和体验,而这些一致性表现在可以使用相同的参数创建相似的对象。我们现在要展示的应用程序中,将有多个按钮有着相同的前景色和背景色。对这些仅有细小差别的按钮,每次调相同的构造器作初始化时都输入些相同的参数实在是一种浪费:前景和背景色都一样,只是文字有细小差别。

我们将用交通指示牌作为例子,程序中尝试创造一种文字型的交通指示牌,并且把它们分成如下几类:危急、警告、通知(正好和日志信息级别相类似)。指示牌的类型决定了它们在创建时的颜色格局。例如,危急指示牌使用亮红文字和白色背景,警告指示牌使用黑色文字和金色背景,通知也就是普通指示牌使用黑色文字和白色背景。我们约定“Do Not Enter”和“Wrong Way”标识为危急,“MergingTraffic”和“Railroad Crossinig”标识为警告,“Speed Limit”和“One Way”标识为通知。该程序创造“指示牌”,它们都只是些按钮。当用户点下按钮时,将简单地弹出一个Tk响应对话框,显示危急/错误、警告、通知。这的确不够好玩,但如何创建这些按钮却很有趣。你将在例19. 5看到这里所描述的程序。

例19.5 运用PFA的路灯指示牌GUI程序(pfaGUI2.py)

按照指示类型创建适当前景、背景色的路灯指示牌。使用PFA帮助“模板化”常用GUI参数。

19.3 Tkinter举例 - 图10

19.3 Tkinter举例 - 图11

当你执行这个程序时,会看到一个类似图19-5的GUI。

逐行解释

1 ~ 18行

作为开始,我们导入了functional. partial()、一些Tkinter属性以及Tk对话框(1〜5行)。然后,我们定义了一些标识及其相应类型。

20 ~ 28行

Tk对话框被关联到按钮回调函数,我们将在创建按钮时使用它们(20〜23行)。然后加载Tk,设置标题,并创建了一个QUIT按钮(25〜28行)。

30 ~ 33行

这些行展示了PFA的魔力。我们通过两个步骤实现PFA。第一步是模板化的按钮类及根窗口top。这样当每次我们调用MyButton时,它会转而调用Button(Tkinter. Button()创建了一个按钮)并使用top作为其第一个参数。我们把这一切“冻结”在了MyButton里。

PFA的第二步使用了第一步的结果——MyButton,并再次对它模板化。我们对每个不同的指示类型都创建了单独类型的按钮。当用户创建一个危急按钮CritButton时(通过调用CritButton()),它会转而调用MyButton并使用恰当的按钮回调和前景、背景色参数,这意味着用top、按钮回调、前景、后景去调用Button。你看出它是如何展开并逐步调用低层直到按钮组件了吗?如果没有PFA这个特性,它执行的那些调用本该由你自己执行。我们把同样的步骤应用到WarnButton和ReguButton上。

35 ~ 42行

按钮类创建过程结束后,我们遍历了指示列表并创建出指示牌。我们使用了一个Python求值字串,它由正确的按钮名字、作为按钮标签传入的text参数组成,然后再pack()一下。如果这是个危急指示牌,我们就把按钮文字全转成大写,否则的话就以标题形式显示。最后一步在第39行完成,同时也展示了Python2. 5引入的另一个特性,临时操作符。随后我们对每一个按钮创建字串施以eval(),每次创建一个按钮,最终形成了前面看到的图形。最后我们进入主事件循环,启动GUI。

这个应用程序使用了一些Python2.5的新特性,所以你不能在旧版上运行它。

19.3 Tkinter举例 - 图12

图 19-5 在使用 MacOSX的XDarwin服务器运用PFA的路灯指示牌GUI应用程序on XDarwin in MacOS X(pfaGUI2.py)

19.3.6 中级Tkinter范例

我们以一个比较大型的例子来总结本节,listdir.py。这个应用程序是一个目录树遍历工具。它从当前目录开始并提供文件列表功能。双击列表中的任意其他目录都会让该工具转向这个新的目录,同时用新目录中的文件列表替换原有的文件列表。源码作为例19.6给出。

例19. 6

这个稍高级一些的GUI程序扩大了组建的使用范围,演员名单里新增了列表框、文本框和滚动条。而且还有大量的回调函数,例如鼠标点击、键盘输入和滚动条操作。

19.3 Tkinter举例 - 图13

19.3 Tkinter举例 - 图14

19.3 Tkinter举例 - 图15

19.3 Tkinter举例 - 图16

在图19-6中,我们展示了Windows环境中的GUI外观。

这个程序的Unix版本在图19-7中展示。

19.3 Tkinter举例 - 图17

图 19-6 Windows下的表目录GUI应用程序(listdir.py)

19.3 Tkinter举例 - 图18

图 19-7 Unix下的表目录GUI应用程序(Listdir.py)

逐行解释

1 ~ 5行

开始的几行包括通常的Unix启动行和导入os模块、time.sleep函数及Tkinter模块的所有属性。

9 ~ 13行

这些行定义了DirList类的构造器,以及一个代表我们程序的对象。我们创建的第一个标签包含了应用程序的主标题和它的版本号。

15 ~ 19行

我们声明了一个名为cwd的Tk变量来保存当前所在目录的名字——我们马上就会看到这个值从哪来。还创建了另一个标签来显示当前目录的名字。

21 ~ 29行

这段代码定义了我们这个GUI程序的核心,dirs(列表框)包含了被列目录的文件列表。使用一个滚动条以便用户在文件数目超过列表框窗口尺寸时移动列表。这两个组件都包含在一个框架组件中。列表框用bind()方法把回调函数(setDirAndGo)和列表项绑定起来。

绑定意味着把一个回调函数连接在键盘输入、鼠标动作或其他什么事件上,当这个事件被用户触发时就会执行这个回调函数。当列表框中的任一项被双击时setDirAndGo()函数就会被调用。滚动条被Scrollbar. config()方法贴附在列表框上。

31 ~ 34行

随后我们创建了一个文本框让用户输入目录名,以便转到他/她想去的目录,并在列表框中显示该目录中的文件。我们为该文字输入区加入了一个RETURN或Enter键的绑定,这样用户就能用敲RETURN的方法代替按钮点击,同样的事也会发生在上面提到的列表框中。当用户双击列表项时,效果等同于用户在文本框中输入目录名然后点击“go”按钮。

36 ~ 53行

接下来我们定义了一个按钮框架(bfm)来保管这三个按钮:一个“clear”按钮(clr),一个“go”按钮(ls)和一个“quit”按钮(quite)。每一个按钮都有各自不同的配置和点击时的回调函数。

55 ~ 57行

构造器的最后一部分初始化了这个GUI程序,程序将从当前工作目录开始。

59 ~ 60行

clrDir()方法清空Tk字符串变量cwd,其中保存着当前的“活动”目录。这个变量用来跟踪我们当前所处的目录,更重要的是,在错误发生时协助返回上一个目录。你一定注意到了回调函数中的ev参数的缺省值是None。这样的任意值都可能由窗口系统传回,它们在你的回调函数里可以用也可以不用。

62 ~ 69行

setDirAndGo()方法设置了要到达的目录并产生一个对doLS()方法的调用,后者负责实现其余的一切。

71 ~ 108行

现在看来,doLS()是整个GUI程序的关键。它负责所有的安全性检查(目标是否是一个目录以及它是否存在?)如果有错误发生,最终目录会被设置为当前目录。如果一切正确,它调用os. listdir()来取得新的文件集合并替换列表框中的列表。当后台忙于获取新目录信息时,高亮的蓝色条会变成亮红色。当新目录设置完毕,它会恢复蓝色。

110 ~ 115行

listdir.py中的最后一段代码明显是代码的主体。main()函数只有在该脚本被直接调用时才会执行,并且当它执行时会创建GUI程序,后者随之掌控该程序。

我们把该程序的所有其他方面都留给读者作为练习,再次提醒,把整个程序看成是一系列组件和功能的组合,一切就都会简单起来。如果你清楚地知道每个单独程序段的意思,那么整个脚本就不会再显得可怕了。

但愿我们给了你一个够好的关于Python和Tkinter的GUI编程介绍。请记住熟悉Tkinter编程最好的方法就是实践和模仿一些例子!Python发行包附带了很多可供你学习的应用程序范例。

如果你下载了源码包.就会在Lib/lib-tk、Lib/idlelib和Demo/tkinter下发现Tkinter的演示代码。如果你把Win32版本的Python安装在C: \Python2. x,那么可以在Lib\lib-tk和Lib\idlelib下找到这些演示代码。最后那个目录包含了最出名的Tkinter例子程序:IDLEIDE本身。还有一些关于Tk编程的书籍供进一步参考,其中一本是专为Tkinter编写的。