Programming 版 (精华区)

发信人: Gaffe (时飞), 信区: Programming
标  题: tcl&tk
发信站: 哈工大紫丁香 (2002年01月02日12:05:26 星期三), 站内信件

◇ tcl & tk
----------------------------------------------------------------------------
----
From: syc@cc.ntu.edu.tw (Shiau Yong-Ching)
Newsgroups: csie.bbs.xwindow,csie.bbs.linux,tw.comp.unix
Subject: TCL & TK
Date: 3 Mar 1994 11:27:02 GMT
Organization: National Taiwan University
大家好,
        一提到直译语言,令人印象最深刻的,想必是BASIC了。没错,
    BASIC 陪伴很多人进入PC的奇妙世界,甚至到今天,Visual
        Basic 在Windows 下依然魅力十足,把Interpretor 的优点发挥
        的淋淋尽致—程式发展快、方便易学。在UNIX的世界里,C几乎
        是程式语言的代名词,BASIC 早已被人们所淡忘。但是,UNIX下
        是否有像BASIC 一般简单易学,功能强大的解译语言呢?答案是
        肯定的,而且功能皆有过之而无不及,举凡lisp、prolog等人工
        智慧语言,或者像Perl、tcl/tk 皆是,而且都有提供方便的图
        型界面,使得在X window下设计程式方便不少。以下文章就是在
        下寒假研究tcl/tk的心得,提供大家参考;并且强力推销tcl。
******
一、什麽是tcl?什麽是tk?
        tcl 是Tool Command Language 的缩写,而tk是一个X window的
        Tool Kits,是tcl在X Window System 的应用。tcl 是一种解译
        语言,也是一套C的函式库。为什麽这样说呢?因为tcl 的解译
        器被设计成一个C的函式库,提供基本的命令与控制结构,并且
        使用tcl 的任何程式皆可以根据tcl 的规格撰写C程式与之链结
        增加新的命令,以提高关键程式的效率、或增加新的特色。如tk
        就是这样子的示□。废话少说,以下先来个示□:
    ┌———————————————————————————————┐
    │  tk的解译器叫wish,是WIndowing SHell 的简称。只要在提示号    │
    │  下(xterm 下)输入wish就可以了。接下来你可以看到一个空白    │
    │  的视窗出现,xterm 下的提示号也变成了wish的提示号。此时,    │
    │  在提示号输入以下两行指令,就可以见到最简单,最让人惊奇的    │
    │  tk程式了:                                                  │
    │          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,再执行之。( 当然要先chmod +x hello)              │
    │    hello的内容:(第一行的内容可由`which wish`指令得知)     │
    │           #!/usr/local/bin/wish -f                           │
    │           button .b -text "Hello,World!" -command exit       │
    │           pack .b                                            │
    └———————————————————————————————┘
  *注1:请查询系统管理者详细的路径。
    注2:tcl/tk原始码可以在NCTUCCCA:/X/contrib/下找到。
    注3:cc.ntu.edu.tw的使用者其wish的路径为/remote/bin/wish
    注4:交谈式的wish的命令行编辑非常原始,使用者可以用fep 或ile
          两个front end 程式达到类似tcsh/bash/ksh 的行编辑。
    注5:Linux/386BSD的使用者(应该说XFree86的使用者)应该安装时就有
          tcl/tk了。
二、关於tcl
        因为tk是基於tcl 语言而来的,因此我们有必要先了解tcl 。
        与tk相同,tcl 也附了一个交谈式解译器tclsh ,可供线上学习tcl之用。
        tcl 的语法非常简单,基本上就是与Shell的语法类似。学过一点点shell
        programing的人非常容易进入状况:
                A.一个tcl 程式是由好几个tcl 叙述组成的。
                B.一个tcl 叙述与平常在shell 下面的命令一模一样,如前面
                  hello,world 的例子一样,第一个字是命令,剩下的全部是
                  该命令的参数。
                C.tcl 除了命令外就只有变数。变数与shell 变数一样,只有
                  一种型别:字串。钱号可以取出变数的值。
                D.tcl 对每一个叙述最多只做一次变数代换。而被大括号括住
                  的部份不做任何处理。
                E.tcl 会优先执行被方括号括住的叙述,并将其结果当成原来
                  命令的一部份。这与shell 的重音符号相同(mkdir `echo Hello`

       以下就是一些□例:
                unix% tclsh
                tcl% set x 100     输出     100
                tcl% set y 200              200
                tcl% expr $x + $y           300
                tcl% set z [expr x+y]       300, 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}  (引号会做变数代换

           [以下省略tcl的提示号]
                tcl的procedure:
                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 的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"
            相关的命令:
               set  var value
               append var value [value2 vaule3 ...]
               incr var [increament]       /* default = 1 */
               unset var [var2 var3 ...]
            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}            3
                   llength {}                      0
                   llength a                       1
                   linsert $x 2 a b c              Sun Mon a b c Tue ...
                   linsert $x 0 a                  a Sun Mon ...
                   lreplace $x 0 a                 a Mon Tue ...
                   lrange $x 0 1                   Sun Mon
                   lappend $x a b c
                   lsearch $x Sat
                   lsearch -glob $x S*  /* Wild Cards */
                   lsearch -regexp      /* regular expression */
                   lsort [-decreasing|-integer] $x
             Strings & Lists
                   set x a/b/c
                   set y /usr/local/bin/wish
                   split $x /             输出  a b c
                   split y                     {} usr local bin wish
                   反函数为join
             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是"tcl tk"
                   则Command变成set x tcl tk,引述个数不对了。
                   如果改成:
                       button .b -text "Reset" -command {set x $InitValue}
                   则x 值取决於按钮时的InitValue,而非真正的InitValue
                   所以可用下列方法解决:
                     button .b -text "Reset" -command [list set x $initValue
]
         控制结构:
                 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也都有效。
                 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}
                      }
         副程式:
               同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变数了。
       Errors& exceptions:
            catch {
                 tcl 程式码
            } messages
        如果程式码有错,catch return 1,否则为0,messages为实际的错误讯息。
以下没力气打中文,写不下去了,抄两个玩具给大家欣赏 :-b
#!/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 suken -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 <Return> {set result [power $base $power]}
bind .power <Return> {set result [power $base $power]}
# End of File
注: -relief sunken的意思是凹陷的轮廓。
      本程式产生一个视窗:
     ┌——————————————————————————┐
     │〔   A   〕to the power 〔   B  〕 is 〔        〕│
     └——————————————————————————┘
      只要输入A,B就可以得到A的B次方。
#!/usr/local/bin/wish -f
set id 0
entry .entry -width 30 -relief sunken -textvariable cmd
pack .entry -padx 1m -pady 1m   <- 显示输入行
bind .entry <Return> {          <- 当.entry 收到<Return>这个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个命令记录下来,
       用按钮就可以执行。
***
看到这里,您是否同意tcl/tk是UNIX世界的BASIC呢?
本篇文章是我阅读一本书:tcl and tk toolkit的部份心得。这本书已於1994年由
Addison-Wesley Publishing Company,Inc.出版(ISBN 0-201-63337-X)。此书
网路上有postscript档案,但是我不认为大家有□情逸致印个500页左右的
书吧---用10ppm的雷射印表机也要50几分钟、实际上用Postscript输出更慢,
还会卡纸哩:)最好是找找看有没有进口。
tcl/tk在USENET上有自己的讨论群:comp.lang.tcl各位可以参考其FAQ。FAQ可在
NCTUCCCA:/USENET/FAQ/comp/lang/tcl拿到。
UNIX下的Interpretor种类繁多,功能复杂,但愿这篇文章能收到抛砖引玉之效
使有人愿意写些中文文件来介绍与X Window 整合的其他Interpretor,如tkperl
、Prolog等。甚至是一些Windows的程式设计工具,如SUIT,xvwindow等library
使得 X Windows Programing对入门者不再是梦靥。
以上所提及的软体更是网路上可以免费取得的合法软体,特别是Linux下全部都有。
ps: 上学期末在天龙书局看到Larry Wall的Programing Perl,对perl有兴趣者可
    以去买,保证不会後悔!
pps: tk目前的版本无法处理16bits的字集,即Botton..无法看到中文,可能要
     等到tk 4.0。
        ======================================================
        Shiau Yong-Ching (萧永庆)
        E-mail: syc@cc.ntu.edu.tw          Phone: 7358499 - 717
        Department of Electrical Engineering, Taiwan University

--
※ 来源:·哈工大紫丁香 bbs.hit.edu.cn·[FROM: riee3.hit.edu.cn]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:412.967毫秒