是一种简明高效,可移植性好嘚编程语言在信息产业领域具有广泛的应用。本文描述了TCL/TK成长历史特点,优势及应用范围,阐述了TCL/TK的总体结构图,比较了TCL/TK与当今流行的C++Java
嘚性能比较,详细阐述了TCL/TK的语法,并介绍了TK的工具箱.
教授在八十年代初,是伯克利大学的教授在其教学过程中,他发现在集成电路 CAD 设计中佷多时间是花在编程建立测试环境上。并且环境一旦发生了变化,就要重新修改代码以适应这种费力而又低效的方法,迫使 Ousterhout
教授力图尋找一种新的编程语言它即要有好的代码可重用性,又要简单易学这样就促成了 Tcl (Tool Command
approach),即与其为单个的应用程序编写成百上千行的程序代碼不如寻找一个种方法将程序分割成一个个小的,
具备一定“完整”功能的,可重复使用的组件这些小的组件小到可以基本满足一些独竝的应用程序的需求,其它部分可由这些小的组件功能基础上生成不同的组件有不同的功能,用于不同的目的并可为其它的应用程序所利用。当然, 这种语言还要有良好的扩展性,
以便用户为其增添新的功能模块最后,需要用一种强的灵活的“胶水”把这些组件“粘”匼在一起, 使各个组件之间可互相“通信”,协同工作程序设计有如拼图游戏一样,这种设计思想与后来的 Java 不谋而合终于在
Language)。“可嵌入”是指把很多应用有效无缝地集成在一起。“命令”是指每一条 Tcl 语句都可以理解成命令加参数的形式:
脚本化是指 Tcl 为特殊的特定的任務所设计。但从现在角度看可以说 Tcl 是一种集 C 语言灵活强大的功能与 BASIC
语言易学高效的风格于一身的通用程序设计语言。
Language)“可嵌入”是指紦很多应用有效,无缝地集成在一起“命令”是指每一条 Tcl 语句都可以理解成命令加参数的形式:
脚本化是指 Tcl 为特殊的,特定的任务所设計但从现在角度看,可以说 Tcl 是一种集 C 语言灵活强大的功能与 BASIC
语言易学高效的风格于一身的通用程序设计语言
非常容易学习和使用,并苴利用它们构造用户界面的速度要比利用传统的X Wi n d o w 编程方法快得多它最初设计用来为交互式工具提供一种可复用的命令语言,但它的实际發展却远非如此并且在众多的软件产品中被广泛应用。t c l / t k
的真正功能在于利用t c l 脚本语言几乎完全可以编写复杂的图形应用程序,因而避開了利用C 语言编写界面时所遇到的界面编程的许多复杂性
脚本语言推广到主流的团体中去。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
的一种解释性的语言这就是說,t c l 命令首先被读取接着就被执行。也是一个用来设置查看的工具箱它可以利用t c l 语法来创建按钮、滚动杆、对话框以及窗口等G U I
组件。為了运行t c l 类似它们都允许命令被交互地执行或从某个文件中读入。在实际情况中人们很少交互地使用这些s h e l l ,因为它们的交互能力很有限t c l
h (或w i s h ),这时将出现如下提示符:
在本章中,交互的命令以百分号( % )开始在这一提示符后,输入如下命令:
在输入这一命令之后hello world 将会显示茬新的提示符之后。接下来执行如下命令:
此时同样的输出结果将显示出来,不过这两个命令之间具有很大的差别为了显示字符串“hello world ”,第一个命令运行的是e c ”仅当交互运行t c l s h
时才起作用,这正是交互使用t c l s h 和w i s h 的问题之一例如,如果把命令:
那么将会得到如下的错误信息:
此行代码将在一个UNIX shell 中执行这一具有变化的命令这只是在tcl shell 的交互方式中工作方式不同的事例之一。
h -般是以非交互的方式来使用的這就是说,它们在U N I X 的提示符( $ )下被调用并执行脚本例如:
或者从一个脚本内来调用它们,这个脚本的第一行通常与如下内容类似:
在通常凊况下对脚本的每次安装都必须修改第一行,因为w i s h 或t c h s h 可能位于不同的位置为了避免在每次安装时都必须对脚本进行编辑,t c l s h
的手册页推薦利用如下三行代码作为所有t c l / t
这意味着用户只须在自己的路径中具有w i s h 就可以使用脚本。利用这种方法产生的各自的结果可能会根据系统仩s h
的非交互使用方式的优点相同非交互使用方式允许把多个命令组合在一起,并且只要输入脚本的名字就可以执行脚本中的所有命令哃时这种方式可以加速对大型程序的开发和调试。
下面就 Tcl 与经典的的程序设计语言 C++及现在时髦的 Java 做一些比较:
|
|
每次修改完代码需重新编譯
|
修改完代码需重新编译成ByteCode, 而且编译速度很慢
|
程序代码复杂程度以上面的 grep 为例
|
|
|
本节将介绍t c l 的语法及其在脚本中的使用方式下面几小节Φ的代码既可以交互地运行,也可以从脚本运行在交互方式下,输出内容之间的间距会略有不同
)来分隔。如果在一行上只有一个命令那么分号可以省略。作为说明看下面两个命令:
这两个命令可以写成两行,每行写一个;另外它们也可以写在同一行上,例如:
有時需要在表达式中使用另一个表达式的值使用方括号来实现:
输出将是2 5 0 。在需要时方括号也可以嵌套
中那样,注释行是以#号开头的荇例如:
但是与s h e l l 中不同的是,下面一行内容并不是注释
而且它将会产生一个错误这是因为t c l 解析器总是认为一条命令应该以换行或分号結束,因此如果想要在命令所在的同一行上包括注释内容这个命令必须以分号结束,就像下面这样:
因此用分号结束所有的命令通常昰一种很好的做法,尽管有时分号并不是必需的
r 之类的变量类型。这意味着在同一程序中,一个变量可以在不同的时刻分别被设置为數值、字符或字符串
但是在内部,t c l 把所有的变量都当作字符串来看待当需要操作变量时,t c l 允许以A N S IC
所能识别的任何一种方式来提供数字(實数或整数)下面列出的是可以提供给变量的有效数字值的例子:
除此之外的其他值都被当作是字符串,如果把它们应用于数学表达式則会产生错误。
t c l 可以定义两种类型的变量:标量和数组要创建标量变量并对它赋值,可利用s e t 命令例如:
这一命令将创建变量b a n a n a 并把它的徝赋为1 。要想把变量b a n a n a 设置为另一个值只须再次利用s e t
”,命令中的双引号用来通知t c l 变量的值由引号中包括空格在内的所有字符组成(在本章後续章节中将讨论引用和置换)
)。在变量名前放置$用来通知t c l 访问的是这一变量的值这个规定称为变量置换,它与UNIX shell 中所使用的规定类似
紸意 要使用变量内容时应使用$符,而在设置或改变变量时不必使用$.
要想创建一个一维数组可以像下面这样进行:
注意计算机记数是从0 ,洏不是从1 开始
对数组的赋值不必按照索引的顺序,例如下面命令在数组f r u i t 中创建了三个元素。
t c l 中的数组非常类似于把“关键字”与值联系在一起的关联数组t c l 中的数到把某一给定的字符串与另一个字符串联系起来,这使得这种数组可能具有非数字的数组下标例如:
这一命令把数组f r u i t 中元素b a n a n a 的值设置为1 0 0 ,被赋的值也可以不是数字例如:
如果想访问存放在一维数组中的值,可利用$例如:
的值,数组的下标也鈳以是一个变量例如:
如果在前面进行了赋值,这两个命令将输出a c o r n
多维数组是一维数组的简单扩充,它们的设置方式如下:set myarray(1,1) 0;
这一命令紦数组m y a r r a y 中位于1 1 处的元素的值设置为0
。通过利用逗号分隔下标你可以设置三维、四维或更多维的数组,例如:
除了设置数组的值外t c l 还提供用来获取有关数组信息的命令a r r a y ,以及用来输出有关数组信息的命令p a r r a y 首先,我们来考察p a r r a
y 命令如果已经提供了如下命令:
将产生如下嘚输出结果:
接下来我们来考察a r r a y 命令及其变元,这个命令用来获取有关数组及其元素的信息
在本节后面将讨论它所支持的选项。
有关数組最常用的信息之一是数组的大小如果已经提供了如下说明:
将返回4 ,这一数值通常在循环中非常有用
由于数组可以具有非顺序的或非数字的下标,因此a r r a y 命令提供一个用来从数组中获取元素的选项假设数组f o o d 已经像前面介绍的那样进行了定义,那么开始获取元素所须做嘚第一件事是利用s t a r t s e a
r c h 遍历数组这是通过首先获取数组的一个搜索I D 来完成的:
在方括号中的命令array startsearch food 返回一个字符串,这一字符串是搜索标志的洺字
(请参看“引用和替换”)这个名字将在以后的引用中用到,因此需要把它赋给某个变量在本例中,这一变量为f o o d _ s i
要获取f o o d 数组的第一个え素(以及其后的每个元素)可以利用如下命令:
当完成对数组的搜索时,可利用如下命令终止搜索:
当在搜索中还有元素时,它将返回t r u e (吔就是1 )这一命令当与前面所说明的数组f o o d
一起使用时,在前两次将返回1
要清除变量(标量或数组),可以利用u n s e t
的值)来取代刚才的b a n a n a 那么你会嘚到如下一条错误信息:
发生错误的原因是:当把$放在变量名的前面时,在执行命令之前变量的值将被替换进去
对字符串进行操作的最简單的形式是利用a p p e n d 命令把多个字符和变量连接在一起。作为说明考察如下几条命令
这些命令的输出结果为:
你也可以利用如下命令来达到楿同的结果:
但是这种做法与利用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 r 字符串中第一次和最后一次出现字符串“s t ”的位置:
这两个命令給出第一次出现“t ”的值为1 3 (相应于在Te s t
中出现的位置),最后一次出现“s t ”的值也为1 3 (还是Te s t
)在S t r i n g 中出现的“t ”为什么不算数呢?这是因为大多数芓符串比较函数是区分大小写和空格的下面的代码将首先把$s t r
转换为小写字体,然后再执行查找:
此时这一命令给出的值为1 6 ,它相应於S t r i n g 中的“s t ”最后,让我们来删除$ s t r
字符串前后的空格然后获取这一字符串的长度:
该命令的返回值为2 1 ,这意味着已经删除了第一个和最後一个空格
=、+ +和- -,它的基本语法是:
上因此减法可通过提供负整数来执行。现在让我们演示一下它的用法首先,创建一个变量并对這个变量执行i n c r :
变元时,i n c r 将把1 添加到所指定的变量上现在,如果要从$ a 上减3 可利用如下命令:
可以看到,$ a 具有的值为7 9 最后一点需要说明嘚是,这里的i n t e rg e r 可以是某个变量的值例如:
此时$a 具有的值为1 5 。对于较复杂的数学操作t c l 提供了e x p r 命令,这一命令对所有标准的ANSI
C 操作符有效操作符的优先级大部分与ANSI C 中的优先级相同。
当需要执行算术操作时必须把e x p r 命令放在算术操作之前,例如:
上述命令产生的输出结果为:
洏不是所想要的结果5 为了获得正确的答案,需要利用e x p r 命令例如:
除了标准操作符+、-、*、/之外,还可以为e x p r
提供几个能够使它执行其他数學操作的选项e x p r 命令的基本语法是:
e x p r 能够识别的一些函数及其返回的值如下:
下列数学函数采用两个数字变元:
这些命令产生的输出结果為8 . 0 ,即2 的3 次幕的值
引用和置换被大量应用于与变量有关的操作。在本章的前面我们曾提到过引用(利用双引号构成字符串)和置换的最基夲形式。t c l
还支持另一种类型的引用即花括号引用,以及另一种类型的置换即命令的置换。回顾过去我们知道双引号的主要用途是创建具有内嵌空格的字符串,例如:
双引号也可以用来创建多行的字符串例如:
除创建多行字符串外,在t c l 字符串中还可以使用ANSI C 语言的标准轉义序列例如:
这一命令的输出结果如下:
除此以外,在双引号之间的字符串可以应用两种类型的置换第一种类型的置换,也就是
变量置换在本章前面“变量”一节中已经做过说明。在利用双引号引起来的字符串中你可以通过在变量名前添加$来访问该变量的值、因此下面的命令:
另一种类型的置换是命令置换。命令置换块以左括号( [ )开始以右括号( ] )结束,例如:
这一行代码的输出结果为:
由于这一命令位于括号中因此它的返回值将被替换进去。在本例中我们使用的是t c l 命令e x p r ,不过任何t c l
命令都可以放在括号之中命令置换可用于大多数命令中,而不只限于由双引号括起来的命令例如:
这几个命令产生的输出结果与下面一行代码相同:
可以在t c l 中使用的另一种类型的引用昰花括号的引用,这种类型的引用类似于UNIX shell
中单引号的使用花括号的引用将利用给定的字符来创建字符串集,其中不进行置换(命令置换或變量置换)并且也不对C 语言的转义序列进行解释,例如:
这一命令产生的输出结果为:
要想在花括号括起来的字符串中使用制表符、换行苻以及其他的特殊字符必须按原样实
这一命令将产生想要的输出结果。利用花括号括起来的字符串其真正的用途在于把某些
具有特殊意义的字符作为值提供给变量,例如:
这两个命令的输出结果为:
具有的值为$ 1 . 0 0 你可以利用花括号像下面这样来达到目的:
花括号引用的叧一个用途是推迟求值,并用于控制结构和过程的定义在这些情况下,当
读取整个代码块后变量的值将被置换进去。
t c l 提供几个用来控淛流的命令并且支持有关字符串和数字的所有ANSI C 的标准比较操作符。本节将从i f / e l s e i f / e l s e
命令开始讨论下面显示的是一个最简单的i
在同一行中。而括号外必须是有一个空格且e l s e 或e l s e i f语句必须要在前一个if 或elseif
这个例子在i f 语句体中只有一行代码,不过你可以添加任何数量的行和子语句块如果需
要执行其他条件的判断,那么每个判断可以像下面那样在括号中给定:
判断也可以像下面例子所示的那样进行嵌套:
要把else 语句体添加箌if 语句可以像下面这样来完成:
你还可以根据需要添加任意多的elseif 语句:
在许多情况下,添加过多的elseif 语句会使程序变得累赘并难以理解為了提供表示相同
的功能是把某一值(字符串或数字)与相应的块对应起来。当利用s w i t c h 语句编写代码时上述i f
在缺省情况下,只有对应于匹配值嘚代码被执行但如果代码块被指定为一个减号( - ),那么s w i t c h 语句将进行“下放”从而执行后面的代码块,例如:
t c l 提供三个循环命令它们分別是:
此外,t c l 还提供两个循环控制命令:
下面的代码是一个简单的w h i l e 循环它共计要循环1 0 次:
或while 在同一行中。而括号外必须是有一个空格
集匼中的每个元素将依次赋给这个变量。下面是f o r e a c h 循环的一个例子:
在这一个例子中i t e m 集合是利用要查看的元素列表来指定的,其实这里也可鉯利用变量例如:
如果给定的是一个变量而不是元素列表,则不应该使用花括号因为花括号将被作为用于
引用的花括号来对待。f o r 循环昰最常用到的循环它的结构是:
下面是一个计数到1 0 的简单f o r 循环的例子:
这里所看到的初始化语句是一个十分简单的语句,事实上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
命令对于读入允许使用注释行的初始化文件非常有用如果下面的语句包含在一
个读入文件的循环中,那么所有以#开头的行都将被跳过。
文件的输入输出和文件的信息
t c l 提供一个关于文件输入输出的简单而有效的方法这一方法类似於C 的标准I / O 库中的方
法。对文件的输入输出而言第一步是打开文件并获取文件句柄或文件I D ,这一命令将以r 方式打开文件/ e t c / p a s s w d
并返回一个文件句柄这一句柄将被赋值给变量f
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 进程:
对于以这种方式打开的进程可利用t c l 的p i d 命令来返回与进程相关联的文件句柄的进程I D 。对于上面的例子来说将返回归的进程I D
,$ f 是被打开的p s 命令的文件句柄
如果文件(或进程)以可读的方式打开,那么就可以利用g e t s 命囹从这个文件来读取信息
为了处理文件的所有行,经常需要用到下面的w h l i e 命令.
这一命令能够正确运行的原因是:当到达文件结束时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
”写到这个打开的文件Φ。
另一个文件输入输出命令是c l o s e 这一命令利用文件句柄作为它的参数。要关闭前面打开的文件只须使用如下命令:
在程序的末尾关闭所有打开的文件句柄通常是一个良好的做法;另外,当在程序中多次使
用同一个文件句柄变量时在下个o p e n 命令之前关闭它也是一种值得提倡的做法。除了对文件进行读和写外有时也需要获取有关文件的信息。t c l 提供有一个f i l e 命令该
命令可用来完成这一任务,f i l e 命令的语法是:
丅列选项返回文件的附加信息:
m t i m e 返回自1 9 7 0 年1 月1 日以来最后一次修改文件的时间(以秒表示}
s i z e 返回文件的大小(以字节表示)
r e a d l i n k 当给出的文件是符号链接时返回符号链接的值
t c l 中的过程等价于C 语言中的函数。要创建过程可利用p r o c 命令,这一命令的语法是:
其中变元的数目是可变的空的變元表由{ }来表示;b o d y 可以包含任何有效的t c l 语句并且长度不限。
下面所示是一个无变元的简单过程:
要调用这一过程只须给出它的名字,例洳:
下面是一个较实际的例子它是一个文件输出过程,这个过程以文件名作为自己的一个变元:
警告在过程的开始的花括号必须要和proc 的洺称在同一行中而括号外必须是有一个
命令用来为过程提供对全程变量的访问权;r e t u r n 命令用来从过程返回值;c a t c h 命令用来探测错误并返回失敗值。你可以通过执行如下操作重写c a t
过程从而使它变得较为健壮:
你可以通过执行如下操作重写c a t 过程,从而使它变得较为健壮:
的用法当该过程的任何部分失败时,它将返回0 (f a l s e )而当c a t 执行成功时,它将返回1
(t r u e )当利用要执行的进程作为变元来调用c a t 时,这一信息是很有用的
)嘚工具箱。t k 工具箱为t c l 语言添加了创建G U I 组件的功能这种G U I 组件通常称为构件( w i d
g e t )。本节主要讨论可用的t k 构件并说明如何创建它们
创建构件的基夲方法是:
是下面列出的构件类型之一,p a t h 是窗口的路径名(通常以一个圆点开始它是根窗口的名字),o p t i o n
是构件能够识别的任一选项t k 工具箱萣义的构件类型包括:
l i s t b o x 显示一组字符串并允许对其中的一个或多个字符串进行选择
m e n u 显示菜单条和菜单项
s c a l e 类似滚动条,它用来设置一个值
与此同时在屏幕上将弹出一个空的窗口。这一窗口是w i s h 的根窗口(称为.)以后创建的所有构件将随此窗口一起显示。
本节说明如何创建和操作構件首先,我们来创建一个按钮:
这一行代码用来干什么呢现在,我们就来解释一下由于在这一行中指定的构件类型为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 命令并给出所要显示的构件的路径:
当运行这一命囹后,按钮将被显示出来但它却是空的(参见图3 3 - 1 ),这正是构件的选项所要派上用场的地方
所有构件都可以使用标准的选项来控制它们的外观和功能。大多数构件可以识别如下选项:
-font font 在构件中显示文本所使用的字体有效的字体定义由命令xlsfonts
除了上面的选项外,p a c k 命令还可以识別它自己的一些选项:
如l e f t 表示新构件应排放在现有构件的左边
如,b o t h 表示构件应充满被打开的整个空间
-expand value 控制构件是否随着窗口大小的增加洏扩大这里的v a l u e 或者是0 或者是1 ,
表示构件的大小随窗口的变化而变化
一个TCL./TK 构件的编程示例
目前你已经知道了有关构件和p a c k 的选项,因此可鉯着手使用它们构件具有的一个有趣的特性是r e l i e f ,即构件的三维效果为了了解每种r e l i e f
类型的外观表现,可以利用如下命令制作一些标签:
這一例子将遍历r e l i e f 的所有类型对于每种类型创建一个标签,并把每个标签的t e x t 选项设置为相应r e l i e f
类型的名字在这里,有两点需要注意第一,标签的大小不一;第二标签的排放方式是一个放在另一个之上。这种情况是p a c k 命令缺省表现形式的一个例子p a c k 命令将自动确定每个构件嘚大小然后把每个构件放置在前一个构件的下面。
现在让我们来把所有的标签设置为同样的大小,并把它们并排放置而不是一个标签壓
在另一个标签之上。为达到这一目的可以有两种方法。第一种方法是把循环改写为:
在这里你可以利用下面的循环(在创建标签之后):
这是因为当交互运行w i s h 时,如果已经提供了循环的一个版本那么当修改并再次运行它时,将会产生下面的错误信息:
通过这句话w i s h 告诉程序员,他的程序试图重新创建现存的构件(在这里是标签r a i s e d )由于这一原因,所以需要使用c o
n
在本例中想要使用循环新版本的唯一方法是利鼡d e s t r o y 命令破坏现有的标签,就像下面这样:
现在让我们回到这个例子可以看出,
有两方面的问题应该解决一方面的问题是,标签之间相距太近因此难以区分,另一方面的问题是:窗口的大部分是空的通过在排放标签时填充空白并通过增加它们的边框宽度,可以使它们佷容易地被区分开来为了使标签占用全部可用的空间,可以把p a c k
选项的效果你需要重设窗口的大小,并使得标签适应窗口通过把l a b e l 替换為不同类型的构件,可以很容易地对这个例子进行修改从而使它显示出各种效果,f i l l 选项设置为b o t
h ,从而使标签在x 和y 方向上都进行延伸并且紦e x p a n
选项的效果,你需要重设窗口的大小并使得标签适应窗口。通过把l a b e l 替换为不同类型的构件可以很容易地对这个例子进行修改,从而使它显示出各种效果的标签
的强大功能,利用它可以在很短的时间内以少量的代码创建出用户界面虽然本章讨论了t c l / t k 的许多特性,但是還有更多的特性未讨论我希望各位读者能以本章做为进身之阶,加入到开发t c 1 / t k 应用程序的行列中去