您的当前位置:首页正文

编程实践TCL-TK

来源:筏尚旅游网
第3部分 TK基础

23

TK基本原理

23.1 TK中的Hello,World!

我们的第一个Tk脚本非常简单。它创建了一个按钮,当你点击它时,就向标准输出设备打印“Hello,World!”。在按钮控件上方是一个由窗口管理器提供的标题栏,本例中在X视窗系统中是twm。

23.1.1 例23-1 “Hello,World!”Tk程序

#!/usr/local/bin/wish button .hello -text Hello \\ -command {puts stdout \"Hello, World!\ pack .hello -padx 20 -pady 10 第一行标识了脚本的解释器: #!/usr/local/bin/wish

如果此脚本象其它NUIX命令文件那样被调用,则这个特殊的行是必要的,第2节描述了如何设置脚本于不同的平台上。

在这个脚本中有两个TCL命令:一个用来创建按钮,一个用来使其在显示上可见。bu

tton命令创建一个按钮的实例:

button .hello -text Hello \\ -command {puts stdout \"Hello, World!\ => .hello 按钮的名字是.hello。按钮上的标签是Hello,与此按钮相关联的命令是: puts stdout \"Hello, World!\" pack命令将按钮映射到屏幕上。并提供了一些堆叠参数,所以在按钮周围有空间。 pack .hello -padx 20 -pady 10 当你将这两个命令键入wish中时,当button命令给出时,你不会看到任何东西。然而,在pack命令之后,你将会看到空的主窗口将会收缩到足以容纳按钮和其添充空隙(padding)。包装器的行为在第24和第25章进行深入的讨论。

Tk使用一种基本对象的系统来创建和命令控件。与每一种小控件(如按钮)相关联的

是一个用于创建此种类别控件实例的命令。当小控件被创建时,一个作用在这个控件实例上的一个新的TCL命令就定义了。例23-1创建了一个名为.hello的按钮,并且我们可以使用其名字作为一个TCL命令来操纵此按钮。例如,我们可以使用此按钮高亮几次: .hello flash 或者,我们可以运行与此按钮相关联的命令: .hello invoke => Hello, World! Tk有控件类和实例,但是它并不是完全面向对象的。不可能去对一个控件类派生子类并继承。相反,Tk,提供了非常灵活的控件,可以通过多种方式调节其外观。资源数据库可以存储被许多控件共享的配置信息,并且新的类也可以被引入到组资源中。控件行为通过使用分缓绑定的绑定标签得到共享。Tk使用部件(composition)来组装共享行为和属性的控件。

23.2 Tk小控件的命名

按钮实例名字.hello中的句点是必需的。Tk使用了一种可以反映出控件所在层次的命名系统。层次的根就是应用程序的主窗口,其名字就是一个简单的点(也就是.)。这与UNIX系统中的目录命名习惯很相似,其跟目录名为/,/也用来分隔文件名的组成部分。Tk

以同样的方式使用句点。主窗口的每一个子窗口控件以象.foo这样来命名。.foo的一个子控件将是.foo.bar,等等。正如文件系统中目录可以作为其它文件和目录的容器一样,Tk窗口层次使用框架控件作为其它控件和框架的容器。

构成Tk路径的每一部分必须以小写字母或数字开头。显然也不能包含句点。小写字母是为了避免与以大写字母开头的资源类名冲突。一个资源名可以包含Tk路径组成部分和Tk控件类别,并使用大小写来区分它们。第31章详细描述了资源。

 用变量存储控件名。

Tk命名系统有一个缺点。如果你的界面变化足够多以至导致某些控件改变了他们在控件层次中的位置。在这种情况下,它们可能需要改变其名字。你可以通过使用变量来保存重要控件的名字这种方式与这个程序麻烦相隔离。使用一个变量的引用而不是控件的路径名以便你需要改变什么,或者你想不其它的界面是得用你的代码。控件创建命令返回控件的名字: set b [button .hello -text \"Hello\" -command {puts \"Hello!\ 你可以将$b当作命令来操作按钮: $b configure -background green

23.3 Tk小控件的配置

例23-1展示了Tk命令中普遍使用的一种命名参数的传递方式。成对的参数指定了控件的属性。属性名以-开始,如-text,下一个参数就是这个属性的值。即使是简单的Tk控件也可能有一打或更多的可以通过这种方式指定的属性,复杂控件可能有30或更多属性。然而,Tk的魅力在于你仅需要指定默认值不太好的属性。这个可以通过Hello,World例子的简易性得到证明。

最后,每一个控件实例都支持一个configure操作,可以简写为config,它可以查询和改变这些属性。Config的语法,使用相同的创建控件时使用的命名参数对。例如,我们可以改变这个按钮的背景与红色,即使它已经被创建并已经被映射到屏幕上: .hello config -background red 控件属性可以在任何时候重新定义,即使是按钮在创建时的text和command。下面的命令将.hello变成一个goodbye按钮:

.hello config -text Goodbye! -command exit 控件有一个cget操作来查询某个属性的当前值: .hello cget -background => red 你可以用不带参数的configure来得到关于一个控件属性的更多细节: .hello config -background => -background background Background #ffe4c4 red 返回的信息包括命令行开关,资源名称,类名,默认值和当前值。类名和资源名与31章中描述的资源机制有关。如果你只指定configure不指定属性,则将返回控件所有属性配置信息的一个列表。例23-2运用这个来打印出关于一个控件的所有信息:

23.3.1 例23-2查看控件的所有属性

proc Widget_Attributes {w {out stdout}} { puts $out [format \"%-20s %-10s %s\" Attribute Default Value] foreach item [$w configure] { puts $out [format \"%-20s %-10s %s\" \\ [lindex $item 0] [lindex $item 3] \\ [lindex $item 4]] } }

23.4 Tk小控件的属性和资源数据库

一个控件属性以三种不同的方式命名:通过命令行选项,通过资源名、通过其资源类。命令行选项就是TCL脚本中使用的形式。此种形式都是小写字母并以连字符(例如,-offvalue)作前缀。属性的资源名没有前导连字符,内部单词边界有大小字母(如,offValue)。资源类以大写字母开头且内部单词边界有大写字母(如,OffValue)。

 本书中的表格使用资源名列出控件的属性。

如果你想通过资源机制来指定控件的属性,你需要知道这些命名习惯。命令行选项可以通过将资源名映射为全部小写字母衍生得到。使用资源来指定属性的优点是,你不必更改代码中的属性值。仅是通过少数资源数据库项,你就可以为你的所有控件指定属性。另外,如果属性是通过资源指定的,用户可以提供交互式的资源值(或规格?)来由应用程序提供的值。对于像颜色和字体的属性,对于用户来说是重要的。资源规格在31章是详述。

23.4.1 Tk手册页

本书提供了所有Tk命令的总结,控件的属性,默认绑定。然而,为了绝对正确,你可能需要读随Tk的联机手册页。它提供了Tk命令的完全参考。你应该使用UNIX的man程序去读它们: % man button UNIX手册页中的tkman程序提供了非常友好的图形用户界面。在Macintosh平台上,手册页格式化为HTML文档,可以在Tcl/Tk发行版本中的HTML Docs目录中找到。在Windows中,手册格式化成帮助文档。你可以在这个WEB上找到手册页:

http://www.tcl.tk/man/

有许多属性对于大多数Tk控件是通用的。这些属性在一个单独的名为options的手册页中进行描述。每一个手册页以标准选项(STANDARD OPTIONS)部分开始,它列出了这些标准属性所适用的标准选项。但是,你得看一下options手册页的描述。作为对比,本书中的表格列出所有控件的属性。

23.5 Tk命令总结

下面的表格列出了由Tk所加的TCL命令。表格中的页码是命令的主要参考,同时在索引中也有其它参考。

23.5.1 控件命令

表23-1列出了创建控件的命令。在Tk中有18种不同的控件,虽然其中的4种是按钮的变体,5种专用于不同风格的文本显示。

表 23-1. Tk 控件创建命令 命令 页码. 描述 button canvas 454 Create a command button. 557 Create a canvas, which supports lines, boxes, bitmaps, images, arcs, text, polygons, and embedded widgets. checkbutton entry frame label labelframe listbox menu menubutton message 458 创建一个与一个TCL变量相关联的toggle button 507 创建一个单行文本条控件. 485 创建一个使用布局管理器的容器控件. 490 创建一个只读多行文本标签. 485 创建一个与布器管理器一起使用的有额外标签属性的容器控件. (Tk 8.4) 519 创建一个面向单行的可滚动文本控件. 462 创建一个菜单. 462 创建一个可以弹出一个菜单的按钮. 493 创建一个只读多行文本消息. 表 23-1. Tk 控件创建命令 命令 页码. 描述 panedwindow radiobutton scale scrollbar spinbox text toplevel

429 创建一个容器控件,以窗格风格控制其控件. (Tk 8.4) 458 创建与一个变量相关联的一组单选按钮. 495 创建一个调节一个变量值的缩放按钮. 499 创建一个可以链接到另一个控件的滚动条. 511 创建一个由按钮控制ENTRY控件值的组合成的旋钮控件. (Tk 8.4) 531 创建一个通用可纹的文本控件. 485 创建一个新的顶层窗口的框架. 23.5.2 控件操纵命令

表23-2列出了操纵控件并提供像输入焦点、事件绑定、布局管理相关联的功能的命令。

表 23-2. Tk 控件操纵命令 命令 页 描述 表 23-2. Tk 控件操纵命令 命令 bell bind bindtags clipboard destroy event focus font grab grid image lower option pack place raise selection send 页 497 使终端响铃设备响铃. 描述 435 绑定一个TCL命令到某事件上. 437 创建绑定类和控制绑定继承. 594 操作剪贴板. 605 删除一个控件. 446 定义并产生虚拟事件. 603 操作输入焦点. 641 设置查询字体属性的尺寸. 604 从其它操作窃取输入控制权. 419 以约束条件排列控件到栅格中. 626 创建、操纵图像. 409 降低一个窗口的堆叠次序. 477 设置、查询资源数据库. 409 经约束条件Pack一个控件在显示区域中. 427 按位置放置控件在显示区域中. 409 升高一个窗口的堆叠次序. 593 操纵选择操作. 648 发送一个TCL命令到另一个Tk应用程序中. 表 23-2. Tk 控件操纵命令 命令 tk tkerror tkwait update winfo wm

页 描述 669 查询或设置应用程序名字或全局符号. 202 后台错误的处理者. 605 等待一个事件. 608 通过经历(穿越going through)事件循环来更新显示. 663 查询窗口状态. 657 与窗口管理器交互. 23.5.3 支撑过程

表23-3列出了一些实现标准对话框、可选菜单和其它功能的支撑过程。

Table 23-3. Tk 支撑过程support procedures 命令 tk_bisque tk_chooseColor tk_chooseDirectory tk_dialog tk_focusFollowsMouse tk_focusNext 页 描述 621 安装桔黄颜色族. 602 颜色选择对话框. (Tk 4.2) 600 目录选择对话框. (Tk 8.2) 599 创建简单对话框. 603 安装鼠标跟踪焦点模式. 604 按tab顺序聚焦文本控件. Table 23-3. Tk 支撑过程support procedures 命令 tk_focusPrev tk_getOpenFile tk_getSaveFile tk_messageBox tk_optionMenu tk_popup tk_setPalette

23.6 其它控件集

本书描述了由核心Tk发布版本所提供的控件集。也有些其它Tk的控件集。一些由TCL过程实现,它将基本控件组成有用的组合(如BWidgets)。其它是由基于C的控件(如Tix和BLT)组成的。一些更流行的控件集列在这里:

页 描述 604 按tab顺序聚焦前一个控件. 600 打开已存在文件对话框. (Tk 4.2) 600 打开新文件对话框. (Tk 4.2) 600 消息对话框. (Tk 4.2) 465 创建一个选择菜单. 465 创建一个弹出菜单. 621 设置标准调色板. (Tk 4.2) 23.6.1 BLT

George Howlett创建了BLT。它包括一个可以有效支持大型数据集的主要图表控件。也包括标签笔记本和树形视图控件。其忙录控件用一个透明的仅显示一个监视光标的控件覆盖应用程序,当应用程序忙于做某事且你不想接受鼠标点击时,这个是非常方便的。这是一个基于C的工具包。

http://www.sourceforge.net/projects/blt/

23.6.2 Tix

Tix由Ioi Lam所创建,现在由一些志愿者组成的小组所支持。它包括几个控件和一个在TCL中创建控件的基础组织。特色包括气球提示、标签窗口、窗格窗口和一个层次游览器。虽然它包含一些由TCL组合成的控件,但还是一个基于C的工具箱。

http://tix.sourceforge.net/

23.6.3 [incr Tk] and [incr Widgets]

[incr Tk]是一个使用[incr Tcl]对象系统来创建组合控件的基于C的框架。它包括很多控件,从简单的标签项控件到HTML显示控件。这些工作在Chad Smith的书[incr Tcl] from the Ground Up (Osborne-McGraw Hill, 1999).中有描述。

http://incrtcl.sourceforge.net

23.6.4 BWidgets

BWidgets是一个基于TCL控件集。包括多种组合控件,包括标签记事本、组合框、层次游览器。位于标准TCL库的WEB站点:

http://www.sourceforge.net/projects/tcllib

23.6.5 TkTable

TkTable是一个网格几何管理器和几个基于文本控件的组合。它使一些像电子数据表这样的表列数据很容易的显示出来,同时也提供了很多对单元格和其数据格式化的控制。

http://www.sourceforge.net/projects/tktable

24 TK

例子

本章通过一系列较短的子来介绍Tk。ExecLog在后台运行一个程序并显示其输出。浏览器的例子从书中显示TCL例子。Tcl Shell例

子让你键入TCL命令并在一从此从解释器中执行。

Tk提供了一种快速有效的方式产生来用户界面。在本章我们通过一系列小例子让你对你能做什么有一个感性的认识。这里掩盖了一些细节并在后面进行详细的描述。特别地,pack布器管理器在第25间涵盖,事件绑定在第29间讨论。Tk控件会在后面的章节中进行详细的讨论。

24.1 ExecLog

我们的第一个例子提供了一个用exec命令运行另一个程序的简单用户界面。此界面包括两个按钮(Run it和Quit)一个用于输入命令的entry控件,一个用于记录程序运行结果的文本控件。此脚本在一个管道中运行这个程序,并使用fileevent命令来等待输出。此结构使程序在执行时用户界面仍然保持响应。比如你可以运行make,并使结果保存在log中。完整的例子先给出,其中的命令在后面详细的讨论。

24.1.1例24-1使用exec记录一个程序的输出

#!/usr/local/bin/wish

# execlog - run a program with exec and log the output # Set window title wm title . ExecLog # Create a frame for buttons and entry. frame .top -borderwidth 10 pack .top -side top -fill x # Create the command buttons. button .top.quit -text Quit -command exit set but [button .top.run -text \"Run it\" -command Run] pack .top.quit .top.run -side right # Create a labeled entry for the command label .top.l -text Command: -padx 0 entry .top.cmd -width 20 -relief sunken \\ -textvariable command pack .top.l -side left pack .top.cmd -side left -fill x -expand true # Set up key binding equivalents to the buttons bind .top.cmd Run bind .top.cmd Stop focus .top.cmd # Create a text widget to log the output frame .t set log [text .t.log -width 80 -height 10 \\ -borderwidth 2 -relief raised -setgrid true \\ -yscrollcommand {.t.scroll set}] scrollbar .t.scroll -command {.t.log yview} pack .t.scroll -side right -fill y pack .t.log -side left -fill both -expand true pack .t -side top -fill both -expand true # Run the program and arrange to read its input proc Run {} { global command input log but if [catch {open \"|$command |& cat\ $log insert end $input\\n } else { fileevent $input readable Log $log insert end $command\\n $but config -text Stop -command Stop } } # Read and log output from the program proc Log {} { global input log if [eof $input] { Stop } else { gets $input line $log insert end $line\\n $log see end } } # Stop the program and fix up the button proc Stop {} { global input but catch {close $input} $but config -text \"Run it\" -command Run }

24.1.2窗口标题

第一个命令设置出现在由窗口管理器实现的标题栏。记住那个点(也就是.)就是主窗口的名字: wm title . ExecLog wm命令与窗口管理器交互。窗口管理器就是让你打开、关闭、调节

窗口大小的程序。它为窗口实现标题栏,可能也有一些用来关闭或调节大小的小按钮。不同的窗口管理器有完全不同的外表;此图显示了来自X窗口管理器twm的标题栏。

24.1.3按钮的框架

创建一个框架用来装出现在界面顶部的控件。此框架有一个为控件周围提供一些空间的边界:

frame .top -borderwidth 10 框架位于主窗口的顶部。默认的堆叠边是顶部,所以这里-side top是多余的,但是使用是为了清晰化。-fill x堆叠选项使框架将主窗口整个宽填满:

pack .top -side top -fill x

24.1.4命令按钮

创建了两个按钮:一个用来运行命令,另一个用来退出程序。它们的名字.top.quit和.top.run暗示它们是.top框架的子控件。这个影响pack命令,它默认将控件放置在父控件中: button .top.quit -text Quit -command exit set but [button .top.run -text \"Run it\" \\ -command Run] pack .top.quit .top.run -side right

24.1.5标签与Entry

标签和entry也作为.top框架的子控件创建。标签创建时在X方向没有填充空间,以便可以放置在后面的entry控件。Entry控件的大小根据字符来确定。Relief属性给予entry控件一些外观使它在显示上分开来。Entry控件的内容关联到TCL变量command上: label .top.l -text Command: -padx 0 entry .top.cmd -width 20 -relief sunken \\ -textvariable command 标签和entry控件停靠在.top框架内部的左边。Entry控件的另外停靠参数允许它扩展其停靠空间并在显示上填充其额外的空间。停靠空间和显示空间的区别在339页第25章中讨论: pack .top.l -side left pack .top.cmd -side left -fill x -expand true

24.1.6按键绑定与焦点

Entry控件的按键绑定提供了激活应用程序功能的另外一种方式。Bind命令将TCL命令与一个指定控件的事件关联起来。当用户在键盘上按回车键时,事件会被触发。当Control键已经按下时,字母c被键入时事件被产生。为使事件到达entry控件.top.cmd,必须将输入焦点给这个控件。默认情况下,当用鼠标左键点击它时就得到焦点。对于焦点跟随鼠标模式的用户来说显示使用focus命令是非常有益的。当鼠标在主窗口上时,用户可以向entry中打

字:

bind .top.cmd Run bind .top.cmd Stop focus .top.cmd

24.1.7可调大小文本和滚动条

创建一个文本控件并连同一个滚动条停靠到一个主框架中。文本控件的宽和高分别用字符和行数来指定。文本的setgrid属性是打开的。这限制其大小调节以便使整数行、平均大小的字符能被显示。

在Tk中,滚动条是一个单独的控件,它可以使用和这里相同的设置连到不同的控件上。当文本控件被修改时,文本的yscrollcommand更新滚动条的显示,且当用户操纵滚动行时,滚动条的command滚动与其关联的控件: frame .t set log [text .t.log -width 80 -height 10 \\ -borderwidth 2 -relief raised -setgrid true\\ -yscrollcommand {.t.scroll set}] scrollbar .t.scroll -command {.t.log yview} pack .t.scroll -side right -fill y pack .t.log -side left -fill both -expand true pack .t -side top -fill both -expand true 创建一个Tk控件的副作用是创建了一个操作此控件的新的TCL命令。此TCL命令与控件的路径名相同。在此脚本中,文本控件命令,也就是.t.log,在许多地方需要。可是,将一个重要控件的路径名存到一个变量中是一个好主意,因为如果你重新组织用户界面,路径名可

以改变。这样做的缺点是你必须在过程内部要用global声明此变量。为了证明这种方式此例中的变量log就是用于这个目的。

24.1.8Run过程

Run过程开始在命令entry中指定的程序。因为entry的textvariable属性,此值在全局command命令中可以得到,命令在管道中运行以便它在后台运行。Open命令参数的前导符|表示创建一个管道。Catch命令作为非法命令输入的保护。变量input设置成错误消息或正常的open返回的文件描述符。程序像这样开始:

if [catch {open \"|$command |& cat\ 从管道中捕获错误

管道通过cat程序从命令转移错误输出。如果你不像这里使用cat,当管道关闭时管道中的错误会输出弹出一个错误消息。在本例中,令人尴尬的是如何区分程序产生的错误和由于Stop过程实现的方式所产生的错误。不但如此某些程序间隔正常和错误输出,而且你可能想按顺序查看错误输出而不是在最后一下子查看。

如果管道成功打开,则使用fileevent命令设置一个回调函数。当管道产生输出时,脚本可以从中读出数据。Log注册为当管道可读时被调用的过程:

fileevent $input readable Log Command(或错误消息)被插入到log中。这个通过使用文本控件的名字来实现,名字以一个TCL命令存储在log变量中。命令的值被追加到log,并加了一个新行以便其输出可以出现在下一行。

$log insert end $command\\n 文本控件的insert函数带有两个参数:一个标记和一个在此标记上要插入的字串。符号标记end代表文本控件内容的末尾。

运行按钮在程序开始后变成停止按钮。这个避免界面的混乱,也证明了Tk界面的动态特性。再次,因为这个按钮在脚本中几个不同的地方使用,其路径名已经被存储在变量中but: but config -text Stop -command Stop

24.1.9Log过程

当管道中有数据可读或达到文件末尾时,Log过程就被激活。此条件首先被检查,且Stop过程被调用来进行清理。否则,一行数据被读取并插入到log中。文本控件的see操作用来将文本的视图定位以使新行对用户可见: if [eof $input] { Stop } else { gets $input line $log insert end $line\\n $log see end }

24.1.10 Stop过程

Stop过程通过关闭管道来终止程序。Close用一个catch进行包装。这个可以制止当进程上的管道过早地关闭时产生的错误。最后,按钮被恢复到其运行状态以便用户可以运行另一个命令: catch {close $input} $but config -text \"Run it\" -command Run 在多数情况下,关闭管道等同于杀死任务。在UNIX中,这个将导致下一次向其标准输出写操作时发送一个SIGPIPE信号到程序。没有一个杀死进程的内嵌方式,但是你可以exec UNIX的kill程序。Pid命令返回管道中进程的ID: foreach pid [pid $input] { catch {exec kill $pid} } 如果你需要对另一个进程更复杂的控制,你应该看一下在Explring Expect(Don Libes, O'Reilly & Associates, Inc., 1995)中描述的TCL的expect扩展。Expect提供了对交互式程序的强有力的控制。你可以写发送输入到交互式程序并对其输出进行模式匹配的TCL脚本。Expect被设计为自动使用交互式应用为目的的程序。

24.1.11 跨平台讨论

这个脚本可以在UNIX和Windows上运行,但是不能运行在Macintosh上因为那没有exec命令。另一个问题是取消任务的的绑定。这是类UNIX的,但Windows用户指望来取消一项任务,Macontosh用户指望。Platform_

CancelEvent定义了一个虚拟事件、<>,Stop绑定其上:

24.1.12 例24-2一个指定平台的取消事件 proc Platform_CancelEvent {} { global tcl_platform switch $tcl_platform(platform) { unix { event add <> } windows { event add <> } macintosh { event add <> } } } bind .top.entry <> Stop Tk也已经也定义了虚拟事件。event命令和虚拟事件在446页中描述。

因篇幅问题不能全部显示,请点此查看更多更全内容