o tcl.0 注意事项
o tcl.01 tcl?什么是tk?
o tcl.02 wish (WIndowing SHell的简称)
§ tcl.02.01 交谈式示范
§ tcl.02.02 script file 示范
§ tcl.02.03 指令格式及范例
§ tcl.02.03.01 范例:一般
§ tcl.02.03.02 范例:procedure/while/eval/{}/exec/eval
§ tcl.02.03.03 范例:数组
§ tcl.02.03.04 范例:list
§ tcl.02.03.05 范例: 控制结构:
§ tcl.02.03.06 范例: 子程序:
§ tcl.02.03.07 范例: 错误&特别状况
§ tcl.02.03.08 范例:字符串
§ tcl.02.03.09 范例:其它expr之用法
§ tcl.02.03.10 范例:离开
§ tcl.02.03.11 一些特殊键
o tcl.03 Patch for Chinese Tk
o tcl.98 示范程序
§ tcl.98.01 power
§ tcl.98.02 window/history
tcl.0 注意事项
TCL/TK 速成教材
# 所有以#开始的行都可以不读, tcl/tk 的批注行亦以此#开始
# 为了统一,以ENSYS FAQ格式写.
# 目前只有ev004有tcl/tk
所以用 grep -v '#' 此檔,将得到一个简单的说明,尤其是你用于复习时
tcl/tk原始码可以在NCTUCCCA:/X/contrib/下找到。
wish的命令行编辑非常原始,使用者可以用fep 或ile
两个front end 程序达到类似tcsh/bash/ksh 的行编辑。
可用 which wish 找wish放那裹
#Linux的使用者(应该说XFree86的使用者)应该安装时就有tcl/tk了。
tcl/tk 指令的连续符号为 \, 同一般shell之用法
tcl/tk在USENET上有自己的讨论群:comp.lang.tcl各位可以参考其FAQ。FAQ可在
NCTUCCCA:/USENET/FAQ/comp/lang/tcl拿到。
#index前有*者,为改自
# Shiau Yong-Ching (萧永庆) 的原稿 syc@cc.ntu.edu.tw 3 Mar 1994 11:27
tcl.01 tcl?什么是tk?
1.tcl 是Tool Command Language 的缩写,
2.而tk是一个X window的Tool Kits,是tcl在X Window System 的应用。
3.tcl 是一种解译语言(shell),
也是一套C的函式库
#tcl的解译器同时被设计成一个C的函式库
#提供基本的命令与控制结构,并且
#使用tcl 的任何程序皆可以根据tcl 的规格撰写C程序与之链接
#增加新的命令,以提高关键程序的效率、或增加新的特色。如tk
#就是这样子的示范。
4.tclsh 为tcl 的shell,而
wish 为tcl + tk 的shell, 语法同tclsh
#此处直接介绍wish,学会wish,即等于学会tclsh
tcl.02 wish (WIndowing SHell的简称)
tcl.02.01 交谈式示范
请键入
wish
#这时您会看到一个wish开了一个窗口
button .b1 -text "Hello,World!" -command exit
pack .b1
#下完第二个指令后,原本空白的窗口就变成一个印有Hello,World
#的立体按钮,而且鼠标移近时会变成高亮度。但是别急,且慢按键
#在输入两个命令看看:
button .b2 -text "Hello,TCL/TK" -command "destroy .b2"
pack .b2
#此时,屏幕上会出现第二个按钮。以下两个命令可以更改颜色:
.b1 configure -background red
.b2 configure -foreground green
#按下第二个按钮会使第二个按钮消失,而第一个按钮会结束程序。
#每次这样写很麻烦,你亦也可以照UNIX的规矩把程序写成一个文件 │
#案hello,再执行之。如2.2
tcl.02.02 script file 示范
vi hello
#键入
#!/usr/local/bin/wish -f
button .b -text "Hello,World!" -command exit
pack .b
#存盘
chmod +x hello
hello
#执行
tcl.02.03 指令格式及范例
1.一个tcl/tk 程序是由好几个tcl/tk 指令组成的。
2.一个tcl/tk 指令与平常在shell 下面的命令一模一样,
#如前面hello,world 的例子一样,第一个字是命令,剩下的全部是
# 该命令的参数。
3. 除了命令外就只有变量。变量与shell 变量一样,只有一种型别:字符串。
用钱号($)可以取出变量的值。
4. 对每一个指令通常只做一次变量代换。而被大括号括住的部份不做任何处理。
若要变量代换二之以上需结合eval指令.
5. 会优先执行被方括号([])括住的叙述,并将其结果当成原来命令的一部份。
# 这与shell 的backquote之功用相同(如mkdir `echo Hello`)
tcl.02.03.01 范例:一般
#改自 Shiau Yong-Ching (萧永庆) 的原稿 syc@cc.ntu.edu.tw 3 Mar 1994 11:27
unix% tclsh
tcl% set x 100
#输出 100, 设定x变量(这时仍是字符串)
tcl% set y 200
tcl% expr $x + $y
#将$x $y当成数值, 执行 $x + $y,输出 300(但仍为字符串)
tcl% set z [expr $x+$y]
#设定z变量为300
tcl% set a [set b 100]
#设定a = b = 100
tcl% expr (3>4)||(6<=7)
#执行逻辑指令,输出 1
tcl% expr 14.1*sin($x)
tcl% set organization "Taiwan University"
tcl% set organization {Taiwan University}
#以上两指令在此同意,
#但若为 set organization "ENSYS:$jjkao", 则会
# 先做变量代换
相关的命令:
set 变量 变量值
append 变量 变量值 [变量值2 变量值3 ...]
incr 变量 [increament]
# 预设为= 1
unset var [var2 var3 ...]
NOTE: unset $var 不WORK
=> eval unset $var
tcl.02.03.02 范例:procedure/while/eval/{}/exec/eval
proc power {base p} {
set result 1
while {$p > 0} {
set result [expr $result*$base]
set p [expr $p-1]
}
return $result
}
power 2 6
# 可得 64
power 1.15 5
# 可得 2.01136
仔细观察procedure的demo,其实tcl 并没有procedure结构的语法。
proc只是一个命令,接受4个自变量:
proc 新命令名字 参数 一段tcl程序代码
参数与tcl程序代码用大括号括起来的原因是我们不希望tcl 现在
就执行这些程序代码,而是当procedure被呼叫时才执行。while结构也是
如此:
while 判断 程序代码
因为我们希望每次while执行时$result,$p的值都会变。如果不用大括号
括起来,则所有的值在tcl解译的时候就固定了,while循环永远也不会
结束。
与大括号相反,eval命令可以把一个字符串当成tcl 命令执行:
eval {set x 123} 等于 set x 123
eval "set x 123" 同上
eval可以造成tcl 对同一叙述parse两次,解决一些难缠的问题:
exec rm [glob *.o]
会告诉你:
"a.o b.o c.o" not found
正确的解法是叫tcl 再parse一遍命令行:
eval "exec rm [glob *.o]"
tcl.02.03.03 范例:数组
tcl 的array:不须宣告,直接用即可,但是只有一维数组而已。
set days_of_a_month(Jan) 31
set days_of_a_month(Fab) 28
多维数组可用单维数组仿真:
set matrix(1,1) 100
set matrix(3,9) 50
set matrix($x,$y) 66
set z $matrix(6,6) 77
数组的index其实为 "1,1" 、 "3,9" 与 "$x,$y"
tcl.02.03.04 范例:list
#tcl 还有一种数据结构叫list
set x {Sun Mon Tue Wed Thu Fri Sat}
lindex $x 1
#输出 Mon
lindex {a b {c d e} f g} 2
#输出 "c d e"
concat {a b} {c d} e
#输出 "a b c d e"
list {a b} {c d} e
#输出 "{a b} {c d} e"
llength { {a b} e f}
#输出list的长度,此处为3
llength {}
#输出 0
llength a
#输出 1
linsert $x 2 a b c
#将 a b c 插入 第二个list单元之后 Sun Mon a b c Tue ...
linsert $x 0 a
#a Sun Mon ...
lreplace $x 0 a
#取代 a Mon Tue ...
lrange $x 0 1
#输出list的一区域值 Sun Mon
lappend $x a b c
#加 a b c 在list最后
lsearch $x Sat
#找Sat在list的那一个位置
lsearch -glob $x S*
#用Wild Cards找
lsearch -regexp ...
#用regular expression的型式找(若您不知regexp,请看UNIX的书)
lsort [-decreasing|-integer] $x
#重排list元素的顺序
字符串与list之关系
set x a/b/c
set y /usr/local/bin/wish
split $x /
# spilt 将一个list 所有单元输出
# 第二个argument设定单元的分隔符, 如此处的 /
# a b c
split $y /
# {} usr local bin wish
# 注意第一个为空单元
反函数为join,可将一些单元组成一个list
Lists & Commands:
其实tcl 语言本身就是一个list,瞧,最后一个是command或是
list:
button .b -text "Reset" -command {set x 0}
list可以解决一些难以构成的命令:
假设有一个情况,我们写了下列命令:
button .b -text "Reset" -command "set x $InitValue"
此命令的情况是我们希望Reset button按下后把 x设回
InitValue,可是天不从人愿,如果$InitValue设定为"10 20"
则Command变成set x 10 20,引述个数不对了,因多了一个数。
如果改成:
button .b -text "Reset" -command {set x $InitValue}
则x 值取决于按钮时的InitValue,而非真正的InitValue
所以可用下列方法解决:
button .b -text "Reset" -command [list set x $initValue]
tcl.02.03.05 范例: 控制结构:
if:
if 判断 [then] 叙述 elseif 叙述 elseif 叙述 [else] 叙述
# then 与else 皆可省略。
while: (make b the reverse of a)
set b ""
set i [expr [llength $a] -1]
while {$i >= 0} {
lappend b [lindex $a $i]
incr i -1
}
for:
set b ""
for {set i [expr [llength $a] -1]} {$i >=0} {incr i -1} {
lappend b [lindex $a $i]
}
foreach:
set b ""
foreach i $a {
set b [linset $b 0 $i]
}
注意,受限于tcl 语法,大括号不能独立一行:
while {}
{
}
break与continue同C的用法
switch:
switch $x {
Mon {incr days(Mon)}
Tue {incr days(Tue)}
default {...}
}
亦可写成:
switch $x Mon {...} Tue {...} default {...}
或
switch $x \
Mon {...} \
Tue {...} \
default {...}
如果数个情状的动作相同可用 - 代表。
switch $x {
1 -
3 -
5 -
7 -
9 {incr odd}
default {incr even}
}
tcl.02.03.06 范例: 子程序:
同csh,tcl也有source 命令:
source tclInit.tcl
procedure:
proc name ArgList Body
定义一个叫做name 的procedure,
如果ArgList的最后一个为args,则此procedure
为不定自变量函数,而args为一list。
global name1 name2 ...
使用global中的name1 name2变量,而非自定local变量
return value
uplevel [level] script1 script2...
类似inline函式,把stript1 script2 ...串起来
然后在上一层中执行,而非在procedure自己的stack内执行
(可以更改上一层的变量)。
upvar [level] name localname [name1 localname1] ...
引用上一层的变量name,但是在本procedure内用
localname存取之。(call by reference)
uplevel例:
proc do {varName first last body} {
upvar $varName v
for {set v $first} {$v <= $last} {incr v}
uplevel $body
}
}
set a {}
do i 1 5 {
lappend a [expr $i*$i]
}
set a
# 显示 1 4 9 16 25
# 如果不用uplevel,则$body就不可能存取到 a变量了。
tcl.02.03.07 范例: 错误&特别状况
Errors& exceptions:
catch { tcl 程序代码 } messages
#如果程序代码有错,catch return 1,否则为0,
tcl.02.03.08 范例:字符串
string length
QUOTE: { }
#但用此法定义字符串,一些特殊键不能用,如跳行直接打RETURN即可,不能用\n
.canvas configure -width ${size}m 以避免使用变量 sizem
tcl.02.03.09 范例:其它expr之用法
expr 3 << 2
# 0011 -> 1100
expr (3 > 4) || ( 6 <= 7)
# 1
tcl.02.03.10 范例:离开
exit
tcl.02.03.11 一些特殊键
\a, \b, \f, \n, \r, \t, \v, \ddd (octal value), \xhh (hex value),
\空白
tcl.03 Patch for Chinese Tk
If you want to uudecode the file, you must delete the leading blank first.
-- jjkao
From ypyau@sunmp.csd.hku.hk Thu Mar 17 09:38:49 1994
Subject: Re: (fwd) Re: chinese version of Tcl/Tk
Date: Tue, 15 Mar 1994 11:49:32 +0800 (WST)
From: "Carl Y.P. Yau"
Hi,
Here's the patch for tkFont.c and the demo for showing Chinese characters.
Remember to have the Chinese Font ready on local Xserver before running
the demo program. It doesn't, in any way, a perfect solution right now. It
is just an ad hoc trial. I am planning to do a more extensive widget support
on Chinese Text which is now only available on Text widget. You're welcome
to comment and join.
Cheers,
Carl Y.P. Yau
Systems Research Group
CS Dept, HKU.
------->>----HERE'S the PATCH for tkFont.c ----------------------------
66d65
< #define CHIN_CHAR(c) ((char) c & 0x80)
532,534d530
<
< if (CHIN_CHAR(c)) {p++; maxChars--; } /* */
<
674d669
< if (CHIN_CHAR(c)) { p++; numChars--; } /* */
680d674
<
682,686c676
< if (CHIN_CHAR(start[0]))
< XDrawString16(display, drawable, gc, startX, y,
< start, (p - start)/2 );
< else XDrawString(display, drawable, gc, startX, y,
< start, p - start);
---
> XDrawString(display, drawable, gc, startX, y, start, p - start);
722,727c712
< if (CHIN_CHAR(start[0]))
< XDrawString16(display, drawable, gc, startX, y,
< start, (p - start)/2);
< else
< XDrawString(display, drawable, gc, startX, y,
< start, p - start);
---
> XDrawString(display, drawable, gc, startX, y, start, p - start);
------->>-----------Surprisingly short, isn't it-----------------------
------<<--- Here's the uuencoded tcl demo program-------------------------
begin 666 ChinText.tcl
M(R *(R!4:&ES('!R;V=R86T@:7,@86X@861A<'1A=&EO;B!F<F]M(&UK4W1Y M97)T5VET:%1A9W,@ M+3$@?B,@(&ES(?="E;&P@:6YS=&%L;&5D(&QO8V%L;'DN"B,*"G!R;V,@:6YS" M9&EU;2UR+6YO<FUA;?TM,38M,38P+3<R+3<F]GW<@=&5X="!A2!3='EL97,B"B @("!W;2!I
M8V]N;F%M92 N(")497AT(%-T>6QE'0@+G0@+7)E;&EE9B!R86ES960@
M+6)D(#(@+7ES8W)O;&QC;VUM86YD("(N<R!S970B("US971G<FEE<FUA;?TM,38M,38P+3<R+3<W!L87D@<W1Y;&5S?@H@(? M(? H* @(?!A8VL@+G0@+65X<&%N9?!Y97,@+69I;&P@8F]T: M(?UF:6QL(?D*(? )I9VAT M(?US:61E(&)O='1O;2 M9FEL;"!X"B @("!P86-K("YS("US:61E(' @(?!A8VL@+F]K M+7)E;&EE9B!F;&%T(?UC;VUM86YD(?(N="!Y=FEE=R(*(" @(?!S8W)O;&QB87(@+G,@ M:&5I9VAT(#(X?B W,? @(?UW:61T:? H)(? M7>6)I9R M9F]N=" M061O8F4M2&5L=F5T:6-A+4)O
M;&0M4BU.;W)M86PM*BTR-# M*@H@(" @:68@>UMT:R!C;VQO<FUO9&5L("Y= M @(?!](&5L<F5L:65F(?)A:7-E9?!<?@D)+6)O<F1EPH)+G0@=&%G(&-O;F9I9W5R92!C;VQO<C$@+6)A8VMG<F]U;F0@8FQA8VL@+6)O<F1E<?@D)+6)O<F1E3(U"B @(" N="!T86<@8V]N9FEG=7)E(&9G<W1I<'!L92 H@(? M @+G0@="&%G(&-O;F9I9W5R92!U;F1E6]U('1O(&1I6QE<RX@($1I<W!L87D@<G17:71H5&%G<F]L;&5D(?5S:6YG(&$@;65C:&%N:7-M?F-A;&QE9?!]>6QE6QE<R!A[.O
MM,>PU[7;LLK4QKSDHZS'I\#OO:W!ZM*[R-6[N:&C?2!C:&EN"B @("!I;G-E
M<G17:71H5&%G<G17:71H5&%G<G17:71H5&%GPHR+B!#;VQO<BY](&)I @:6YS97)T5VET:%1A9W,@+G0@ M9PH@(?>R @66]U(&-A;B!C:&%N9V4@96ET
M:&5R('1H92!]"B @("!I;G-E<G17:71H5&%GR!OPIC;VQO<G17:71H5&%G<G17:71H5&%GR @66]U(&-A;B!C875S92!E:71H97(@=&AE
M('T*(" @(&EN<&QE" M<G17:71H5&%G M86='S("YT(&9O<F5G<&QE"B'>R!R
M86YG97,@;V8@=&5X="X*?0H@(" @:6YS97)T5VET:%1A9W,@+G0@>PHU+B S
M+40@969F96-T<RY](&)I9PH@(" @:6YS97)T5VET:%1A9W,@+G0@>R @66]U
M(&-A;B!A<B!T:&4@8F%C:V=R;W5N9"!T;R!B92!DRX*?0H@
M(" @:6YS97)T5VET:%1A9W,@+G0@>PHV+B!9970@=&\@8V]M92Y](&)I9PH@
M(" @:6YS97)T5VET:%1A9W,@+G0@>R @36]R92!D:7-P;&%Y(&5F9F5C=',@
M=VEL;"!B92!C;VUI;F<@<V]O;BP@2!T;R!C
M:&%N9V4@;&EN92!J=7-T:69I8V%T:6]N(&%N9"!P97)H87!S(&QI;F4@< end @H* ;(#Q!;GDM16YT97(^(?)F;V-U
tcl.98 示范程序
tcl.98.01 power
#!/usr/local/bin/wish -f
proc power {base p} {
set result 1
while {$p > 0} {
set result [expr $result*$base]
incr p -1
}
return $result
}
entry .base -width 6 -relief sunken -textvariable base
label .label1 -text "to the power"
entry .power -width 6 -relief sunken -textvariable power
label .label2 -text "is"
label .result -textvariable result
pack .base .label1 .power .label2 .result -side left -padx 1m -pady 2m
bind .base {set result [power $base $power]}
bind .power {set result [power $base $power]}
# 注: -relief sunken的意思是凹陷的轮廓。
# 本程序产生一个窗口:
# ┌──────────────────────────┐
# │〔 A 〕to the power 〔 B 〕 is 〔 〕│
# └──────────────────────────┘
# 只要输入A,B就可以得到A的B次方。
tcl.98.02 window/history
#!/usr/local/bin/wish -f
set id 0
entry .entry -width 30 -relief sunken -textvariable cmd
pack .entry -padx 1m -pady 1m
# 显示输入行
bind .entry {
# 当.entry 收到这个event时
set id [incr id]
if {$id > 5} {
destroy .b[expr $id -5]
# <- tcl的变量名也可以用凑的, 删除第5次前的命令
}
button .b$id -command "exec <@stdin >@stdout $cmd" -text $cmd
pack .b$id -fill x
# <- 显示按钮,且水平(x)方向填满。
.b$id invoke
# <- 仿真按钮被按下
.entry delete 0 end
# <- 清除输入行
}
#end of file
# 本程序产生一个输入行,可以下命令,并且把过去的5个命令记录下来,
# 用按钮就可以执行。
If you have any question for this FAQ, please contact
environ@ev004.ev.nctu.edu.tw.
|