当前位置:文档之家› tcl_tk编程,tcl_tk编程教程

tcl_tk编程,tcl_tk编程教程

tcl_tk编程,tcl_tk编程教程
tcl_tk编程,tcl_tk编程教程

Tcl / Tk 大全 (1)

一. Tcl / Tk简介 (1)

1.1 背景 (1)

1.2 定义 (2)

二. Tcl / Tk基础 (2)

2.1 交互方式 (3)

2.2 非交互方式 (3)

三. TCL总体结构图 (4)

四. 与其它语言的比较 (4)

五. TCL语法 (5)

5.1 t c l 命令结构. (5)

5.2 TCL 的注释 (5)

5.3 数据类型 (5)

5.4 变量 (6)

5.5 字符串的操作 (8)

5.6 引用和置换 (10)

5.7 流的控制 IF 和 SWITCH (12)

5.8文件的输入输出和文件的信息 (14)

5.9 过程 (16)

六.工具箱 (17)

6.1 构件的介绍 (17)

6.2 创建构件 (18)

6.3 构件的选项 (18)

6.4 一个TCL./TK 构件的编程示例 (19)

七. 小结 (20)

Tcl / Tk 大全

摘要: Tcl/Tk 是一种简明,高效,可移植性好的编程语言。在信息产业领域具有广泛的应用。本文描述了TCL/TK成长历史,特点,优势及应用范围,阐述了TCL/TK的总体结构图,比较了TCL/TK与当今流行的C++,Java 的性能比较,详细阐述了TCL/TK的语法,并介绍了TK的工具箱.

一. Tcl / Tk简介

1.1 背景

Tcl/Tk 的发明人 John Ousterhout 教授在八十年代初,是伯克利大学的教授。在其教学过程中,他发现在集成电路 CAD 设计中,很多时间是花在编程建立测试环境上。并且,环境一旦发生了变化,就要重新修改代码以适应。这种费力而又低效的方法,迫使Ousterhout 教授力图寻找一种新的编程语言,它即要有好的代码可重用性,又要简单易学,这样就促成了 Tcl (Tool Command Language) 语言的产生。

Tcl 最初的构想的是希望把编程按照基于组件的方法 (component approach),即与其为单个的应用程序编写成百上千行的程序代码,不如寻找一个种方法将程序分割成一个个小

的, 具备一定“完整”功能的,可重复使用的组件。这些小的组件小到可以基本满足一些独立的应用程序的需求,其它部分可由这些小的组件功能基础上生成。不同的组件有不同的功能,用于不同的目的。并可为其它的应用程序所利用。当然, 这种语言还要有良好的扩展性, 以便用户为其增添新的功能模块。最后,需要用一种强的,灵活的“胶水”把这些组件“粘”合在一起, 使各个组件之间可互相“通信”,协同工作。程序设计有如拼图游戏一样,这种设计思想与后来的 Java 不谋而合。终于在 1988 年的春天, 这种强大灵活的胶水 - Tcl 语言被发明出来了。

1.2 定义

按照 Ousterhout 教授的定义, Tcl 是一种可嵌入的命令脚本化语言 (Command Script Language)。“可嵌入”是指把很多应用有效,无缝地集成在一起。“命令”是指每一条 Tcl 语句都可以理解成命令加参数的形式:

命令 [参数 1] [参数 2] [参数 3] [参数 4] ...... [参数 N]

脚本化是指 Tcl 为特殊的,特定的任务所设计。但从现在角度看,可以说 Tcl 是一种集 C 语言灵活强大的功能与 BASIC 语言易学高效的风格于一身的通用程序设计语言。

按照 Ousterhout 教授的定义, Tcl 是一种可嵌入的命令脚本化语言 (Command Script Language)。“可嵌入”是指把很多应用有效,无缝地集成在一起。“命令”是指每一条 Tcl 语句都可以理解成命令加参数的形式:

命令 [参数 1] [参数 2] [参数 3] [参数 4] ...... [参数 N]

脚本化是指 Tcl 为特殊的,特定的任务所设计。但从现在角度看,可以说 Tcl 是一种集 C 语言灵活强大的功能与 BASIC 语言易学高效的风格于一身的通用程序设计语言。

t c l (读作“t i c k l e ”)脚本语言和t k 工具箱是为X Wi n d o w 系统创建图形用户界面的编程环境。t c l 和t k 非常容易学习和使用,并且利用它们构造用户界面的速度要比利用传统的X Wi n d o w 编程方法快得多。它最初设计用来为交互式工具提供一种可复用的命令语言,但它的实际发展却远非如此,并且在众多的软件产品中被广泛应用。t c l / t k 的真正功能在于,利用t c l 脚本语言几乎完全可以编写复杂的图形应用程序,因而避开了利用C 语言编写界面时所遇到的界面编程的许多复杂性。

t c l / t k 的正式We b 站点是h t t p : / / w w w. s c r i p t i c s . c o m /。

在这里t c l 平台是S c r i t p i c s 公司的产品。S c r i t p i c s 公司要把t c l 脚本语言推广到主流的团体中去。S c r i t p i c s 公司提供了开发工具,技术支持和在开发t c l 和t k 公开代码的软件包是对t c l 实行商业支持服务。该站点还具有下载和安装t c l / t k 最新版本的连接。目前,可得到的最新t c l / t k 的版本是8 .4a 。

t c l 中的可执行程序t c l ,t c l s h ,w i s h 和t c l h e l p 被Red Hat 的r p m 安装在目录/ u s r / b i n 中。可以使用t c l h e l p 调出关于t c l 和t k 的帮助信息。系统中也提供了t c l s h 和w i s h 的手册页。

二. Tcl / Tk基础

t c l 是类似于UNIX shell 的一种解释性的语言,这就是说,t c l 命令首先被读取,接着就被执行。也是一个用来设置查看的工具箱,它可以利用t c l 语法来创建按钮、滚动杆、对话框以及窗口等G U I 组件。为了运行t c l 。t c l s h 和w i s h 与s h 或c s h 这样的标准UNIX shell 类似,它们都允许命令被交互地执行或从某个文件中读入。在实际情况中,人们很少交互地使用这些s h e l l ,因为它们的交互能力很有限。t c l 和w i s

h 之间的主要差别是:t c l s h 只理解t c l 命令,而w i s h 理解t c l 和t k 两种命令。

2.1 交互方式

本节将简单介绍tcl shell 的交互使用方式,从而来说明它的一个问题。要想开始交互使用t c l ,只须在UNIX shell 的提示符下输入t c l s h (或w i s h ),这时将出现如下提示符:

在本章中,交互的命令以百分号( % )开始。在这一提示符后,输入如下命令:

% echo “hello world ”

在输入这一命令之后,hello world 将会显示在新的提示符之后。接下来执行如下命令:% puts “hello world ”

此时,同样的输出结果将显示出来,不过这两个命令之间具有很大的差别。为了显示字符串“hello world ”,第一个命令运行的是e c h o 二进制文件,而第二个命令使用的是t c l 命令puts(put string)。e c h o 形式的”hello world ”,仅当交互运行t c l s h 时才起作用,这正是交互使用t c l s h 和w i s h 的问题之一。例如,如果把命令:echo “hello world ”

放入文件h e l l o w o r l d . t c l ,然后从t c l s h 中执行如下s o u r c e 命令:

% source helloworld.tcl

那么将会得到如下的错误信息:

invalid command name “echo ”

此行代码将在一个UNIX shell 中执行这一具有变化的命令。这只是在tcl shell 的交互方式中工作方式不同的事例之一。

2.2 非交互方式

在通常情况下,t c l s h 和w i s h -般是以非交互的方式来使用的,这就是说,它们在U N I X 的提示符( $ )下被调用并执行脚本,例如:

$ tclsh myprog.tcl

$ wish myprog.tcl

或者从一个脚本内来调用它们,这个脚本的第一行通常与如下内容类似:

#!/usr/bin/tclsh

在通常情况下,对脚本的每次安装都必须修改第一行,因为w i s h 或t c h s h 可能位于不同的位置。为了避免在每次安装时都必须对脚本进行编辑,t c l s h 的手册页推荐利用如下三行代码作为所有t c l / t k 脚本的头三行:

#!/bin/sh

# the next line restarts using tclsh \

exec wish “$0 ”“$@”

这意味着,用户只须在自己的路径中具有w i s h 就可以使用脚本。利用这种方法产生的各自的结果可能会根据系统上s h 版本的不同而不同。

t c l 非交互使用方式的实际优点是与UNIX shell 的非交互使用方式的优点相同。非交互使用方式允许把多个命令组合在一起,并且只要输入脚本的名字就可以执行脚本中的所有命令,同时这种方式可以加速对大型程序的开发和调试。

三. TCL总体结构图

四. 与其它语言的比较

下面就 Tcl 与经典的的程序设计语言 C++,及现在时髦的 Java 做一些比较:

C++,Tcl/Tk 和 Java 的性能比较表

五. TCL语法

本节将介绍t c l 的语法及其在脚本中的使用方式。下面几小节中的代码既可以交互地运行,也可以从脚本运行。在交互方式下,输出内容之间的间距会略有不同。

5.1 t c l 命令结构.

TCL命令的基本结构是:

commandname arguments

这里的c o m m a n d n a m e 是t c l 所要执行的命令,a rg u m e n t s 是提供给此命令的可选变元,整个行(c o m m a n d n a m e 和a rg u m e n t s )称为一个命令。命令之间通过换行( \ n )或者由分号( ; )来分隔。如果在一行上只有一个命令,那么分号可以省略。作为说明,看下面两个命令:

set foo 0

set bar 1

这两个命令可以写成两行,每行写一个;另外,它们也可以写在同一行上,例如:set foo 0; set bar 1;

有时需要在表达式中使用另一个表达式的值。使用方括号来实现:

puts [expr 1000/4];

输出将是2 5 0 。在需要时方括号也可以嵌套。

5.2 TCL 的注释

除命令外,t c l 脚本中另一种类型的代码行是注释。如同在UNIX shell 和P e r l 中那样,注释行是以#号开头的行,例如:

#this is a comment

但是与s h e l l 中不同的是,下面一行内容并不是注释

set foo 0 # initialize foo

而且它将会产生一个错误,这是因为t c l 解析器总是认为一条命令应该以换行或分号结束,因此如果想要在命令所在的同一行上包括注释内容,这个命令必须以分号结束,就像下面这样:

set foo 0;#initialize foo

因此,用分号结束所有的命令通常是一种很好的做法,尽管有时分号并不是必需的。

5.3 数据类型

t c l 不支持诸如i n t 、f l o a t 、d o u b l e 或c h a r 之类的变量类型。这意味着,在同一程序中,一个变量可以在不同的时刻分别被设置为数值、字符或字符串。

但是在内部,t c l 把所有的变量都当作字符串来看待。当需要操作变量时,t c l 允许以A N S IC 所能识别的任何一种方式来提供数字(实数或整数),下面列出的是可以提供给变量的有效数字值的例子:

7 4 整数

0 11 2 八进制,以0 开头

O x 4 a 十六进制,以O x 开头

7 4 .实数

7 4 . 0 实数

7 . 4 e 1 实数

7 . 4 e +1 实数

除此之外的其他值都被当作是字符串,如果把它们应用于数学表达式,则会产生错误。

5.4 变量

t c l 可以定义两种类型的变量:标量和数组。要创建标量变量并对它赋值,可利用s e t 命令,例如:

set banana 1 ;

这一命令将创建变量b a n a n a 并把它的值赋为1 。要想把变量b a n a n a 设置为另一个值,只须再次利用s e t ,例如:

set banana "Fresh from Brazil";

此时变量b a n a n a 具有的值为“Fresh from Brazil ”,命令中的双引号用来通知t c l 变量的值由引号中包括空格在内的所有字符组成(在本章后续章节中将讨论引用和置换)。

要输出b a n a n a 的值,可利用p u t s 命令:

puts $banana;

这一命令将把变量b a n a n a 的值显示到标准输出(也称为S T D O U T )。在变量名前放置$用来通知t c l 访问的是这一变量的值,这个规定称为变量置换,它与UNIX shell 中所使用的规定类似。

注意要使用变量内容时应使用$符,而在设置或改变变量时不必使用$.

要想创建一个一维数组,可以像下面这样进行:

set fruit(O)banana;

set fruit(1)orange;

这两个命令将创建一个数组变量f r u i t ,并把它的两个元素。0 和1 分别赋值为b a n a n a 和o r a n g e 。

注意计算机记数是从0 ,而不是从1 开始。

对数组的赋值不必按照索引的顺序,例如,下面命令在数组f r u i t 中创建了三个元素。

set fruit {100 }peach;

set fruit {2 }kiwi;

set fruit {87 }pear;

t c l 中的数组非常类似于把“关键字”与值联系在一起的关联数组。t c l 中的数到把某一给定的字符串与另一个字符串联系起来,这使得这种数组可能具有非数字的数组下标,例如:

fruit to 100:

set fruit {banana }100

这一命令把数组f r u i t 中元素b a n a n a 的值设置为1 0 0 ,被赋的值也可以不是数字,例如:

set food(koala) eucalyptus;

set food(chipmunk) acorn;

如果想访问存放在一维数组中的值,可利用$,例如:

puts $food(koala);

这一命令将显示出存放在数组f o o d 中下标为k o a l a 的值,数组的下标也可以是一个变量,例如:

set animal chipmunk;

puts $food($animal);

如果在前面进行了赋值,这两个命令将输出a c o r n 。

多维数组是一维数组的简单扩充,它们的设置方式如下:set myarray(1,1) 0;

这一命令把数组m y a r r a y 中位于1 ,1 处的元素的值设置为0 。通过利用逗号分隔下标,你可以设置三维、四维或更多维的数组,例如:

set array(1,1,1,1,1,1) “foo ”

除了设置数组的值外,t c l 还提供用来获取有关数组信息的命令a r r a y ,以及用来输出有关数组信息的命令p a r r a y 。首先,我们来考察p a r r a y 命令。如果已经提供了如下命令:

set food(koala) eucalyptus;

set food(chipmunk) acorn;

set food(panda) bamboo;

那么命令

parray food

将产生如下的输出结果:

food(chipmunk)=acorn

food(koala)=eucalyptus

food(panda)=bamboo

接下来我们来考察a r r a y 命令及其变元,这个命令用来获取有关数组及其元素的信息。

a r r a y 命令的基本语法如下:

array option arrayname

在本节后面将讨论它所支持的选项。

有关数组最常用的信息之一是数组的大小。如果已经提供了如下说明:

set fruit(0) banana;

set fruit(1) peach;

set fruit(2) pear;

set fruit(3) apple;

那么命令

array size fruit;

将返回4 ,这一数值通常在循环中非常有用。

由于数组可以具有非顺序的或非数字的下标,因此a r r a y 命令提供一个用来从数组中获取元素的选项。假设数组f o o d 已经像前面介绍的那样进行了定义,那么开始获取元素所须做的第一件事是利用s t a r t s e a r c h 遍历数组。这是通过首先获取数组的一个搜索I D 来完成的:

set food_sid [array startsearch food]

在方括号中的命令array startsearch food 返回一个字符串,这一字符串是搜索标志的名字

(请参看“引用和替换”)。这个名字将在以后的引用中用到,因此需要把它赋给某个变量。在本例中,这一变量为f o o d _ s i d 。

要获取f o o d 数组的第一个元素(以及其后的每个元素),可以利用如下命令:

array nextelement food $food_sid;

当完成对数组的搜索时,可利用如下命令终止搜索:

array donesearch food $food_sid;

a r r a y 命令的另一个选项是在遍历数组时经常用到的a n y m o r e 。当在搜索中还有元素时,它将返回t r u e (也就是1 ),这一命令当与前面所说明的数组f o o d 一起使用时,在前两次将返回1 。

array anymore food $food_sid;

要清除变量(标量或数组),可以利用u n s e t 命令,例如:

unset banana;

这个命令将取消b a n a n a 变量。如果你利用unset $banana (假如b a n a n a 已被设置为前面所显示

的值)来取代刚才的b a n a n a ,那么你会得到如下一条错误信息:

can't unset "0 ": no such variable

发生错误的原因是:当把$放在变量名的前面时,在执行命令之前变量的值将被替换进去。5.5 字符串的操作

对字符串进行操作的最简单的形式是利用a p p e n d 命令把多个字符和变量连接在一起。作为说明,考察如下几条命令

这些命令的输出结果为:

Begin a String with some text and add even more text to it.

你也可以利用如下命令来达到相同的结果:

但是这种做法与利用a p p e n d 相比,执行起来要慢一些,这是因为a p p e n d 不像s e t 那样要执行字符拷贝。

为了能够执行更多高级的字符操作,t c l 提供了s t r i n g 命令,这一命令能够识别许多选项。

s t i n g 命令的基本语法是:

string option string1 string2

这里的s t r i n g 1 和s t r i n g 2 可以是文字串(例如,“this is a string ”),也可以是变量,o p t i o n 是下列选项之一:

c o m p a r e 按照词典的排序方式进行比较,根据s t r i n g 1 小于、等于或大于s t r

i n g 2 ,分别返回-1 、0 或

1 (类似于C 库函数s t r c m p )f i r s t 返回在s t r i n g

2 中第一次出现s t r i n

g 1 的位置如果s t r i n g 1 没有出现在s t r i n g 2 中,则返回-1

l a s t 返回在s t r i n g 2 中最后一次出现s t r i n g 1 的位置。如果s t r i n g 1 没有出现在s t r i n g 2 中,则返回-1

s t r i n g 命令的如下选项将把s t r i n g 2 解释为要从s t r i n g l 中删除的一列字符:

t r i m 从s t r i n g 1 中删除开头和结尾的出现在s t r i n g 2 中的字符

t r i m l e f t 从s t r i n g 1 中删除开头的出现在s t r i n g 2 中的字符

t r i m r i g h t 从s t r i n g 1 中删除结尾的出现在s t r i n g 2 中的字符

s t r i n g 命令的如下选项只利用s t r i n g 1 作为变元:

l e n g t h 返回s t r i n g 1 包含的字符数

t o l o w e r 返回s t r i n g 1 中的所有字符被转换为小写字符后的新字符串

t o u p p e r 返回s t r i n g 1 中的所有字符被转换为大写字符后的新字符串现在让我们来考察几个例子。首先,创建一个字符串并获取其长度:

set str "Here Is A Test String";

string length $str;

这一命令给出的长度为2 3 (l e n g t h 选项对空格字符也计算在内)。现在,让我们来获取在$s t r 字符串中第一次和最后一次出现字符串“s t ”的位置:

string first "st" $str;

string last "st" $str

这两个命令给出第一次出现“t ”的值为1 3 (相应于在Te s t 中出现的位置),最后一次出现“s t ”的值也为1 3 (还是Te s t )。在S t r i n g 中出现的“t ”为什么不算数呢?这是因为大多数字符串比较函数是区分大小写和空格的,下面的代码将首先把$s t r 转换为小写字体,然后再执行查找:

string last "st" [string tolower $str];

此时,这一命令给出的值为1 6 ,它相应于S t r i n g 中的“s t ”。最后,让我们来删除$ s t r 字符串前后的空格,然后获取这一字符串的长度:

string length [string trim $str " "],

该命令的返回值为2 1 ,这意味着已经删除了第一个和最后一个空格。

33.2.6 数字的操作

t c l 提供两个操作数字变量和常数的命令:i n c r 和e x p r 。

t c l 中的i n c r 命令等价于C 语言中的操作符+ =、- =、+ +和- -,它的基本语法是: incr variable integer

这里的v a r i a b l e 必须是一个整数,i n c r 命令将把给定的i n t e g e r 添加到v a r i a b l e 上,因此减法可通过提供负整数来执行。现在让我们演示一下它的用法。首先,创建一个变量,并对这个变量执行i n c r :

set a 81;

incr a;

puts $a;

$ a 此时具有的值为8 2 。在缺省情况下,i n c r 与+ +相同;当没有提供i n t e g e r 变元时,i n c r 将把1 添加到所指定的变量上。现在,如果要从$ a 上减3 ,可利用如下命令:

incr a -3

puts $a

可以看到,$ a 具有的值为7 9 。最后一点需要说明的是,这里的i n t e rg e r 可以是某个变量的值,例如:

set a 6;

set b 9;

incr a $b;

puts $a;

此时$a 具有的值为1 5 。对于较复杂的数学操作,t c l 提供了e x p r 命令,这一命令对所有标准的ANSI C 操作符有效,操作符的优先级大部分与ANSI C 中的优先级相同。

当需要执行算术操作时,必须把e x p r 命令放在算术操作之前,例如:

set a 20;

set b 4;

set c $a/$b;

puts $c;

上述命令产生的输出结果为:

20/4

而不是所想要的结果5 。为了获得正确的答案,需要利用e x p r 命令,例如:

set c [expr $a/$b];

除了标准操作符+、-、*、/之外,还可以为e x p r 提供几个能够使它执行其他数学操作的选项。e x p r 命令的基本语法是:

expr function number

e x p r 能够识别的一些函数及其返回的值如下:

a b s ( x ) x 的绝对值

r o u n d ( x ) x 舍入后所得到的整数值

c o s ( x ) x 的余弦( x 为弧度)

c o s h ( x ) x 的双曲余弦

a c o s ( x ) x 的反余弦( 0 到 )

s i n ( x ) x 的正弦( x 为弧度)

s i n h ( x ) x 的双曲正弦( p/ 2 到p/ 2 )

a s i n ( x ) x 的反正弦( p/ 2 到p/ 2 )

t a n ( x ) x 的正切( x 为弧度)

t a n h ( x ) x 的双曲正切

a t a n ( x ) x 的反正切( p/ 2 到p/ 2 )

e x p ( x ) e 的x 次幕

l o g ( x ) x 的自然对数

l o g 1 0 ( x ) x 的底为1 0 的对数

s q r t ( x ) x 的平方根

下列数学函数采用两个数字变元:

p o w ( x , y ) x 的y 次幂

这个函数的用法如下:

set a 2;

set b [expr pow($a,3)];

puts $b;

这些命令产生的输出结果为8 . 0 ,即2 的3 次幕的值。

5.6 引用和置换

引用和置换被大量应用于与变量有关的操作。在本章的前面,我们曾提到过引用(利用双引号构成字符串)和置换的最基本形式。t c l 还支持另一种类型的引用,即花括号引用,以及另一种类型的置换,即命令的置换。回顾过去,我们知道双引号的主要用途是创建具有内嵌空格的字符串,例如:

set kiwi "Fresh from New Zealand";

双引号也可以用来创建多行的字符串,例如:

set kiwi "Fresh from

New Zealand 3 for a dollar";

除创建多行字符串外,在t c l 字符串中还可以使用ANSI C 语言的标准转义序列,例如:

set kiwi "Fresh from New Zealand\n\t3 for a dollar";

这一命令的输出结果如下:

Fresh from New Zealand

3 for a dollar";

除此以外,在双引号之间的字符串可以应用两种类型的置换。第一种类型的置换,也就是变量置换,在本章前面“变量”一节中已经做过说明。在利用双引号引起来的字符串中,你可以通过在变量名前添加$来访问该变量的值、因此下面的命令:

set fruit kiwi;

set place "New Zealand";

set how_many 3;

puts "$fruit,fresh from $place,$how_many for a dollar";

输出的结果为:

kiwi, fresh from New Zealand, 3 for a dollar

另一种类型的置换是命令置换。命令置换块以左括号( [ )开始,以右括号( ] )结束,例如:set len_in 2; puts "$len_in inches is [expr $len_in*2.54] cm";

这一行代码的输出结果为:

2 inches is 5.08 cm

5 . 0 8 是命令

expr $len_in*2.54

产生的结果。

由于这一命令位于括号中,因此它的返回值将被替换进去。在本例中,我们使用的是t c l 命令e x p r ,不过任何t c l 命令都可以放在括号之中。命令置换可用于大多数命令中,而不只限于由双引号括起来的命令,例如:

set len_in 2;

set len_cm [expr $len_in*2.54];

puts "$len_in inches is $len_cm cm";

这几个命令产生的输出结果与下面一行代码相同:

set len_in 2; puts "$len_in inches is [expr $len_in*2.54] cm";

可以在t c l 中使用的另一种类型的引用是花括号的引用,这种类型的引用类似于UNIX shell 中单引号的使用。花括号的引用将利用给定的字符来创建字符串集,其中不进行置换(命令置换或变量置换),并且也不对C 语言的转义序列进行解释,例如:

puts "This\nis a \nmulti-line\nstring"

这一命令产生的输出结果为:

This

is a

multi-line

string

而下面的命令

puts {This\nis a \nmulti-line\nstring}

产生的输出结果为:

This\nis a \nmulti-line\nstring

要想在花括号括起来的字符串中使用制表符、换行符以及其他的特殊字符,必须按原样实际地输入它们,例如:

puts{This

is a

multi-line

string}

这一命令将产生想要的输出结果。利用花括号括起来的字符串,其真正的用途在于把某些具有特殊意义的字符作为值提供给变量,例如:

set price 1.00 ;

puts "Pears,$$price per pound";

这两个命令的输出结果为:

Pears,$1.00 per pound

然而,$ $ p r i c e 有可能会引起混乱,因此最好的办法是使变量p r i c e 具有的值为$ 1 . 0 0 。你可以利用花括号像下面这样来达到目的:

set price {$1.00};

puts "Pears,$price per pound";

花括号引用的另一个用途是推迟求值,并用于控制结构和过程的定义。在这些情况下,当读取整个代码块后,变量的值将被置换进去。

5.7 流的控制 IF 和 SWITCH

t c l 提供几个用来控制流的命令,并且支持有关字符串和数字的所有ANSI C 的标准比较操作符。本节将从i f / e l s e i f / e l s e 命令开始讨论。下面显示的是一个最简单的i f 语句:

if {$x<0}{

set x 10;

}

警告T C L 对花括号和空格的使用是十分苛刻的。在i f ,e l s e i f 或e l s e 语句中的开始的花括号必须要和i f ,e l s e i f 或e l s e 在同一行中。而括号外必须是有一个空格。且e l s e 或e l s e i f语句必须要在前一个if 或elseif 的后括号的同一行中。这个例子在i f 语句体中只有一行代码,不过你可以添加任何数量的行和子语句块。如果需要执行其他条件的判断,那么每个判断可以像下面那样在括号中给定:

if {($x=="SJ") ($x=="LA")} {

puts "You Live in California!";

}

判断也可以像下面例子所示的那样进行嵌套:

if {(($arch=="ppc") ($arch=="intel"))&&($os!="Linux")} {

puts "Get Linux!";

}

要把else 语句体添加到if 语句,可以像下面这样来完成:

if {$x <= 0} {

set x 10;

} else {

set x 0;

}

你还可以根据需要添加任意多的elseif 语句:

if {$x == 0} {

set x 10;

} elseif {$x == 10} {

incr x -1;

} elseif {$x == 100} {

set x 50;

} else {

set x 0;

}

在许多情况下,添加过多的elseif 语句会使程序变得累赘并难以理解。为了提供表示相同逻辑关系的一种较紧凑的方法,可以利用t c l 的s w i t c h 命令。s w i t c h 的功能是把某一值(字符串或数字)与相应的块对应起来。当利用s w i t c h 语句编写代码时,上述i f 语句将变为:

switch $x {

0 {set x 10;}

10 {incr x -1;}

100 {set x 50;}

}

在缺省情况下,只有对应于匹配值的代码被执行,但如果代码块被指定为一个减号( - ),那么s w i t c h 语句将进行“下放”,从而执行后面的代码块,例如:

switch $x {

0 -

10 -100

{incr x -1};

}

这一s w i t c h 语句等价于下面的i f 语句:

if {($x == 0) ($x == 10) ($x=100)} {

incr x -1;

}

33.2.9 循环

t c l 提供三个循环命令,它们分别是:

? for

? foreach

? while

此外,t c l 还提供两个循环控制命令:

? break

? continue

当测试条件为t r u e 时,w h i l e 循环将执行它的循环体。w h i l e 循环的结构是:while {条件} {程序块}

下面的代码是一个简单的w h i l e 循环,它共计要循环1 0 次:

set x 0;

while {$x<10} {

incr x;

puts $x;

}

警告在for ,foreach 或while 语句中的开始的花括号必须要和for ,foreach 或while 在同一行中。而括号外必须是有一个空格。

f o r e a c h 将在一组变元中进行循环,并且每次都将执行它的循环体。f o r e a c h 循环具有如下结构:foreach variable {items} {block}

这里v a r i a b l e 是变量的名字,位于i t e m s 集合中的每个元素将依次赋给这个变量。下面是f o r e a c h 循环的一个例子:

foreach element {o c n p li} {

switch $element {

o

-n {puts gas;}

c-p-li

{puts solid;}

}

}

在这一个例子中,i t e m 集合是利用要查看的元素列表来指定的,其实这里也可以利用变量,例如:

set elements "o c n p li";

foreach element $elements {

switch $element {

o

-n {puts gas;}

c-p-li

{puts solid;}

}

}

如果给定的是一个变量而不是元素列表,则不应该使用花括号,因为花括号将被作为用于

引用的花括号来对待。f o r 循环是最常用到的循环,它的结构是:

for {initialization} {condition} {increment} {body}

下面是一个计数到1 0 的简单f o r 循环的例子:

for {set i 0} {$i<=10} {incr i} {puts $i;}

这里所看到的初始化语句是一个十分简单的语句,事实上,f o r 循环的初始化部分和增量部分可以根据需要而复杂化。现在,我们来考察循环控制命令b r e a k 和c o n t i n u e 。

b r e a k 命令用来中断循环并执行循环代码块之后的下一行代码,而

c o n t i n u e 命令用来跳到循环的下一次执行。c o n t i n u e 命令对于读入允许使用注释行的初始化文件非常有用,如果下面的语句包含在一

个读入文件的循环中,那么所有以#开头的行都将被跳过。

if {[regexp {^#} [string trim $line]]} {continue;}

5.8文件的输入输出和文件的信息

t c l 提供一个关于文件输入输出的简单而有效的方法,这一方法类似于C 的标准I / O 库中的方

法。对文件的输入输出而言,第一步是打开文件并获取文件句柄或文件I D ,这一命令将以r 方式打开文件/ e t c / p a s s w d 并返回一个文件句柄,这一句柄将被赋值给变量f 。

set f [open /etc/passwd r];

t c l 支持如下几种文件打开方式:

r 以只读方式打开文件;文件必须存在。

r +以可读可写的方式打开文件;文件必须存在。

w 以可写的方式打开文件;如果文件不存在,它将被创建;如果存在,它将被修改。

w +同w 一样;此外,还强调对文件可读。

a 以追加文本的方式打开文件;文件不存在时将被创建。

a +同a 一样;此外,还强调对文件可读。

O p e n 命令也可以被重载,它能够以强于e x e c 命令的控制能力来运行子进程。要想利用o p e n打开一个进程而不是一个文件,需要把文件名替换为以一个花括号括起来的字符串,这个字符串将以管道符( )开头并包含所要执行的命令。例如,下面的命令将以只读方式打开一个p s 进程:

set f [open { ps } r ];

对于以这种方式打开的进程,可利用t c l 的p i d 命令来返回与进程相关联的文件句柄的进程I D 。对于上面的例子来说,将返回归的进程I D ,$ f 是被打开的p s 命令的文件句柄。

pid $f;

如果文件(或进程)以可读的方式打开,那么就可以利用g e t s 命令从这个文件来读取信息。为了处理文件的所有行,经常需要用到下面的w h l i e 命令.

while {[gets $f line] >= 0}

这一命令能够正确运行的原因是:当到达文件结束时,g e t s 命令将返回- 1 。在这一例子中,g e t s 命令将从文件句柄$ f 中读入一行,并把这一值赋给变量l i n e 。在循环体中,可以对$ l i n e 进行访问和操作。

如果以可写的方式打开了文件,那么就可以利用p u t s 命令把输出写到这个文件中。如果文件句柄$ f 对应于一个以可写方式打开的文件,那么命令将把字符串“This is a line of text ”写到这个打开的文件中。

puts $f "This is a line of text";

另一个文件输入输出命令是c l o s e ,这一命令利用文件句柄作为它的参数。要关闭前面打开的文件,只须使用如下命令:

close $f;

在程序的末尾关闭所有打开的文件句柄通常是一个良好的做法;另外,当在程序中多次使用同一个文件句柄变量时,在下个o p e n 命令之前关闭它也是一种值得提倡的做法。除了对文件进行读和写外,有时也需要获取有关文件的信息。t c l 提供有一个f i l e 命令,该

命令可用来完成这一任务,f i l e 命令的语法是:

file option filename

这里的f i l e m a m e 是所要进行探测的文件名,o p t i o n 是下列选项之一。下列选项将根据文件的信息返回t r u e (1 )或f a l s e (O )。

e x e c u t a b l e 如果当前用户对该文件可执行,则返回t r u e

e x i s t s 如果文件存在,则返回t r u e

i s d i r e c t o r y 如果文件是一个目录,则返回t r u e

i s f i l e 如果文件是一个普通文件,则返回t r u e

o w n e d 如果当前用户拥有该文件,则返回t r u e

r e a d a b l e 如果当前用户对该文件可读,则返回t r u e

w r i t a b l e 如果当前用户对该文件可写,则返回t r u e

下列选项返回文件的附加信息:

a t i m e 返回自1 9 7 0 年1 月1 日以来最后一次访问文件的时间(以秒表示)

m t i m e 返回自1 9 7 0 年1 月1 日以来最后一次修改文件的时间(以秒表示}

s i z e 返回文件的大小(以字节表示)

r e a d l i n k 当给出的文件是符号链接时,返回符号链接的值

t y p e 返回给出文件类型的字符串

5.9 过程

t c l 中的过程等价于C 语言中的函数。要创建过程,可利用p r o c 命令,这一命令的语法是:

proc procedure_name {arguments} {body}

其中变元的数目是可变的,空的变元表由{ }来表示;b o d y 可以包含任何有效的t c l 语句并且长度不限。

下面所示是一个无变元的简单过程:

proc test_proc{} {puts "procedure test";}

要调用这一过程,只须给出它的名字,例如:

procedure test;

将得到输出结果。

下面是一个较实际的例子,它是一个文件输出过程,这个过程以文件名作为自己的一个变元:proc cat {filename} {

set f [open $filename r];

while {[gets $f line]>=0} {

puts $line;

}

close $f;

}

警告在过程的开始的花括号必须要和proc 的名称在同一行中。而括号外必须是有一个

空格。要想以/ e t c / p a s s w d 为参数调用这一过程,可利用如下命令:

cat/etc/passwd

这一命令将显示出/ e t c / p a s s w d 的内容。

在过程中可以使用的三个重要命令是r e t u r n 、g l o b a l 和c a t c h 。g l o b a l 命令用来为过程提供对全程变量的访问权;r e t u r n 命令用来从过程返回值;c a t c h 命令用来探测错误并返回失败值。你可以通过执行如下操作重写c a t 过程,从而使它变得较为健壮:

proc cat {filename} {

set ret_code 0;

catch {

set f [open $filename r];

while {[gets $f line] >= 0 } {

puts $line;

}

close f;

set ret_code 1;

}

return $ret_code;

}

你可以通过执行如下操作重写c a t 过程,从而使它变得较为健壮:

这一程序显示了命令c a t c h 和r e t u r n 的用法。当该过程的任何部分失败时,它将返回0 (f a l s e ),而当c a t 执行成功时,它将返回1 (t r u e )。当利用要执行的进程作为变元来调用c a t 时,这一信息是很有用的。

六.工具箱

t k 工具箱是一个利用t c l 脚本语言来编写X Wi n d o w 图形用户界面( G U I )的工具箱。t k 工具箱为t c l 语言添加了创建G U I 组件的功能,这种G U I 组件通常称为构件( w i d g e t )。本节主要讨论可用的t k 构件并说明如何创建它们。

6.1 构件的介绍

创建构件的基本方法是:

widget_type path option

这里的w i d g e t _t y p e 是下面列出的构件类型之一,p a t h 是窗口的路径名(通常以一个圆点开始,它是根窗口的名字),o p t i o n 是构件能够识别的任一选项。t k 工具箱定义的构件类型包括:

c a n v a s 用于绘制对象

e n t r y 用于单行文本的输入

f r a m e 用来包含其他构件

l i s t b o x 显示一组字符串并允许对其中的一个或多个字符串进行选择

m e n u 显示菜单条和菜单项

t e x t 显示多行文本

l a b e l 显示单行静态文本

b u t t o n 显示一个可点击按钮的构件

c h e c k b u t t o n 显示一个可选的方框

r a d i o b u t t o n 显示多个互相排斥只能单选的方框

s c a l e 类似滚动条,它用来设置一个值

要想创建和操作构件,必须使用wish(windowing shell)。当需要交互地调用w i s h 时,在

U N I X 提示符下输入w i s h ,此时将会出现如下w i s h 提示符:

%

与此同时,在屏幕上将弹出一个空的窗口。这一窗口是w i s h 的根窗口(称为.),以后创建的所有构件将随此窗口一起显示。

6.2 创建构件

本节说明如何创建和操作构件。首先,我们来创建一个按钮:

button .button;

这一行代码用来干什么呢?现在,我们就来解释一下。由于在这一行中指定的构件类型为b u t t o n ,因此也将创建一个按钮;这里设定的路径为. b u t t o n ,所以t k 将在根窗口(.是t k 的根窗口)中创建按钮,并把它命名为b u t t o n 。

现在按钮在哪儿呢?此时也只是创建了按钮,按钮并没有被立即显示。为了显示按钮,需要告诉t k 如何来显示这一构件。为此,利用p a c k 命令并给出所要显示的构件的路径:pack .button;

当运行这一命令后,按钮将被显示出来,但它却是空的(参见图3 3 - 1 ),这正是构件的选项所要派上用场的地方。

6.3 构件的选项

所有构件都可以使用标准的选项来控制它们的外观和功能。大多数构件可以识别如下选项:-background color ,-bg color 构件的背景色。有效值的格式为# R R G G B B 和# R R R G G G B B B ,或者是/ u s r / l i b / X 11 / rg b . t x t 所定义的名字之一

-foreground color ,-fg color 构件的前景色。有效值的格式为# R R G G B B 和# R R R G G G B B B ,或者是/ u s r / l i b / X 11 / rg b . t x t 所定义的名字之一

-height pixels :构件的高度(以像素为单位)

-width pixels 构件的宽度(以像素为单位)

-borderwidth pixels ,-db pixels 构件的边缘宽度(以像素为单位)

-padx pixels 构件在x 方向上所要求的附加空距

-pady pixels 构件在y 方向上所要求的附加空距

-relief type 构件的3 D 效果,这里的t y p e 是下列字符串之一:f l a t 、r a i s e d 、g r o v e 、

r i d g e 、s u n k e n

-text string 在构件中显示的字符串

-font font 在构件中显示文本所使用的字体。有效的字体定义由命令xlsfonts

给出

-command command 当利用构件时所执行的t c l 命令,它通常是一个过程名或e x e c 语句

除了上面的选项外,p a c k 命令还可以识别它自己的一些选项:

-side type 控制构件排放的位置。它的有效排放类型为l e f t 、r i g h t 、t o p 或b o t t o m 。例

如,l e f t 表示新构件应排放在现有构件的左边

-fill type 控制构件是否充满窗口所打开的空间。它的有效值为n o n e 、x 、y 或b o t h 。例

如,b o t h 表示构件应充满被打开的整个空间

-expand value 控制构件是否随着窗口大小的增加而扩大。这里的v a l u e 或者是0 或者是1 ,

表示构件的大小随窗口的变化而变化

6.4 一个TCL./TK 构件的编程示例

目前,你已经知道了有关构件和p a c k 的选项,因此可以着手使用它们。构件具有的一个有趣的特性是r e l i e f ,即构件的三维效果。为了了解每种r e l i e f 类型的外观表现,可以利用如下命令制作一些标签:

foreach I { raised sunken flat groove ridge} {

label .$I –relief $I –text $I;

pack .$I

}

这一例子将遍历r e l i e f 的所有类型,对于每种类型创建一个标签,并把每个标签的t e x t 选项设置为相应r e l i e f 类型的名字。在这里,有两点需要注意。第一,标签的大小不一;第二,标签的排放方式是一个放在另一个之上。这种情况是p a c k 命令缺省表现形式的一个例子,p a c k 命令将自动确定每个构件的大小然后把每个构件放置在前一个构件的下面。

现在,让我们来把所有的标签设置为同样的大小,并把它们并排放置,而不是一个标签压在另一个标签之上。为达到这一目的,可以有两种方法。第一种方法是把循环改写为:foreach I { raised sunken flat groove ridge} {

label .$I –relief $I –text $I –height 10 –width 10;

pack .$I –side left

}

第二种方法是利用c o n f i g u r e 选项重新配置标签,c o n f i g u r e 选项的语法是:

widget configure option

在这里,你可以利用下面的循环(在创建标签之后):

foreach I { raised sunken flat groove ridge} {

.$I configure –height 10 –width 10;

pack .$I –side left

}

为什么要利用c o n f i g u r e 呢?

这是因为当交互运行w i s h 时,如果已经提供了循环的一个版本,那么当修改并再次运行它时,将会产生下面的错误信息:

window name "raised" already exists in parent

通过这句话,w i s h 告诉程序员,他的程序试图重新创建现存的构件(在这里是标签r a i s e d )。由于这一原因,所以需要使用c o n f i g u r e 。事实上,任何需要修改现有构件的情况都要求使用c o n f i g u r e 。

在本例中,想要使用循环新版本的唯一方法是利用d e s t r o y 命令破坏现有的标签,就像下面这样:

foreach i {raised sunken flat groove ridge} {destroy .$i}

现在让我们回到这个例子。可以看出,

有两方面的问题应该解决。一方面的问题是,标签之间相距太近,因此难以区分,另一方面的问题是:窗口的大部分是空的。通过在排放标签时填充空白并通过增加它们的边框宽度,可以使它们很容易地被区分开来。为了使标签占用全部可用的空间,可以把p a c k 的f i l l 选项设置为b o t h ,从而使标签在x 和y 方向上都进行延伸,并且把e x p a n d

选项设置为t r u e :

注意为了查看f i l l 和e x p a n d 选项的效果,你需要重设窗口的大小,并使得标签适应窗口。通过把l a b e l 替换为不同类型的构件,可以很容易地对这个例子进行修改,从而使它显示出各种效果,f i l l 选项设置为b o t h ,从而使标签在x 和y 方向上都进行延伸,并且把e x p a n d 选项设置为t r u e :

注意为了查看f i l l 和e x p a n d 选项的效果,你需要重设窗口的大小,并使得标签适应窗口。通过把l a b e l 替换为不同类型的构件,可以很容易地对这个例子进行修改,从而使它显示出各种效果的标签。

七. 小结

本文对利用t c l / t k 编程进行了简单介绍。这里的例子显示了t c l / t k 的强大功能,利用它可以在很短的时间内以少量的代码创建出用户界面。虽然本章讨论了t c l / t k 的许多特性,但是还有更多的特性未讨论。我希望各位读者能以本章做为进身之阶,加入到开发t c 1 / t k 应用程序的行列中去。

相关主题
文本预览
相关文档 最新文档