Tools

TCL / TK tutorial 본문

Linux

TCL / TK tutorial

감자돌 2021. 5. 3. 09:24

 

 

1.  TCL 기본

○ Tcl은 간단한 구조와 문법을 가지고 있어 배우고 사용하기 쉬운 스크립트 언어이다.

○ Tcl의 모델은 다른 언어들과 다른 면이 많으므로 기본 개념을 이해할 필요가 있다.

 

1.1.  Tcl 시작하기

○ Tcl을 설치하면 tclsh과 wish의 두 가지 shell 프로그램을 사용할 수 있다.

○ tclsh는 csh나 sh 등의 대용으로 사용할 수 있는 명령어 해석기이다.

○ wish는 Tk 명령어들을 해석할 수 있도록 확장된 Tcl 해석기이다.

○ GUI를 가진 응용 프로그램 개발을 위해 wish를 사용할 수 있다.

○ tclsh나 wish를 실행시키면 % 모양의 프롬프트가 나타난다.

○ 프롬프트가 나타나면 명령을 입력해 원하는 작업을 할 수 있다.

○ 여러 명령을 파일에 넣어 두고 실행시킬 수도 있다.

○ 파일의 명령을 실행시키는 방법에는 여러 가지가 있다.

○ 먼저 Tcl의 source 명령을 사용해 실행시킬 수 있다.

source filename

○ 쉘 스크립트와 같이 독립적인 스크립트로 작성할 수도 있다.

○ 스크립트로 작성하기 위해서는 소스 코드의 맨 첫 줄에 다음을 추가하여야 한다.

#!/usr/local/bin/tcl

또는

#!/usr/lcoal/bin/wish

○ Tcl/Tk가 설치된 디렉토리가 /usr/local/bin이 아닌 경우는 다른 경로를 지정해야 한다.

○ Tcl의 라이브러리 기능을 사용할 수 있다.

○ Tcl의 라이브러리 기능은 나중에 자세히 설명.

 

1.2.  Tcl 명령

○ Tcl 명령의 기본적인 형태는 다음과 같다.

command arg1 arg2 arg3 ...

○ 'command'는 내부 명령이나 Tcl 프로시져의 이름이다.

○ 명령어와 인자를 구분하기 위해 스페이스나 텝 문자를 사용한다.

○ 한 명령줄의 마지막을 나타내기 위해 newline이나 ';'을 사용한다.

○ 위에서 설명한 기본적인 문법은 하나의 인자에 여러 단어를 허용하는 grouping, 그리고 변수나 중첩 명령 호출과 함께 사용되는 치환을 이용해 확장시킬 수 있다.

○ 인자는 모두 문자열이다.

 

1.3.  Hello World

puts stdout {Hello, World!}

○ 이 예제에서 puts가 command이고 인자는 stdout가 {Hello, World!}의 두 개다.

○ puts 명령어는 I/O 스트림에 스트링을 출력하고 newline 문자를 출력한다.

○ stdout은 출력될 I/O 장치의 이름이다.

○ Hello, World!는 {}에 의해 Grouping되었다.

 

1.4.  변수

○ 변수에 값을 대입하기 위해서는 set 명령을 사용한다.

○ set 명령은 두 개의 인자를 받는다.

○ 첫 번째 인자는 변수의 이름이며 두 번째 인자는 값.

○ 변수 이름은 길이에 제한이 없으며 대소문자를 구별.

○ 변수 사용 이전에 미리 변수를 정의할 필요는 없다.

○ 변수로부터 값을 얻어내기 위해서는 변수 이름 앞에 $를 붙인다.

set var 5

=> 5

set b $var

=> 5

○ 변수 이름 앞에 $를 붙이는 것을 '변수 치환'이라고 한다.

○ 명령어 해석기는 $var를 5로 대체(substitute)하게 된다.

 

1.5.  명령 치환

○ 중첩된 명령은 ‘[ ]’로 둘러싸게 되는데 이를 '명령 치환'이라고 한다.

○ ‘[ ]’ 부분은 ‘[ ]’ 안에 포함된 명령의 실행 결과로 대체된다.

○ 일반 shell에서의 ‘`’와 비슷한 의미이다.

set len [string length inclab]

=> 6

○ 위의 예에서 string 명령은 스트링에 대한 여러 작업을 하는 명령이다.

○ 여기서는 inclab이라는 스트링의 길이를 얻어낸다.

○ 해석기는 [string length inclab]을 'string length inclab' 명령의 실행 결과인 6으로 대체하게 된다.

 

1.6.  수식

○ 수식을 계산하기 위해서는 expr 명령을 사용한다.

expr 4.8 / 3

=> 1.6

○ expr은 정수, 실수, boolean 값에 대한 연산을 처리한다.

○ boolean 값에 대한 연산은 0이나 1을 돌려준다.

○ 정수는 필요한 경우 실수로 변환된다.

○ 8진수는 수 앞에 0을 붙여 표시한다.

○ 16진수는 수 앞에 0x를 붙여 표시한다.

set len [expr [string length inclab] + 3]

=> 10

○ 위의 예에서처럼 eval문은 명령 치환과 함께 사용되는 경우가 많다.

○ eval 명령은 기본 내장 수학 함수들을 지원한다.

set pi [expr 2*asin(1.0)]

=> 3.14159

○ 유효 자릿수는 tcl_precision 변수 값에 원하는 유효 자릿수를 대입해 바꿀 수 있으며 기본 값은 6이다.

expr 1 / 3

=> 0

expr 1 / 3.0

=> 0.333333

set tcl_precision 17

=> 17

expr 1/3.0

=> 0.33333333333333331

○ Tcl에서 사용할 수 있는 연산자와 함수는 다음과 같다.

- -, ~, !: Unary minus, bitwise NOT, logical NOT

- * / % + -

- <<, >> : Left or Right shift

- < > <= >=

- == !=

- &, ^, |, &&, ||

- x?y:z

- acos(x): Arc-cosin of x

- asin(x): Arc-sine of x

- atan(x): Arc-tangent of x

- atan2(y, x): Rectangular (x, y) to polar (r, th). atan 2 gives th.

- ceil(x): Ceiling of x

- cos(x): Cosine of x

- cosh(x): Hyperbolic cosine of x

- exp(x): Exponential

- floor(x): Floor of x

- fmod(x, y): Floating point remainder of x/y

- hypot(x, y): sqrt(x*x + y*y)

- log(x): Natural log of x

- log10(x): Log base 10 of x

- pow(x, y): x to the y power

- sin(x): Sine of x

- sinh(x): Hyperbolic sine of x

- sqrt(x): Square root of x

- tan(x): Tangent of x

- tanh(x): Hyperbolic tangent of x

- abs(x): Absolute value of x

- double(x): x의 실수형 값

- int(x): x의 정수형 값

- round(x): x를 내림한 정수값

 

1.7.  역슬래쉬 치환

○ 지금까지 변수 치환과 명령 치환의 두 가지 치환에 대해 알아 보았다.

○ 마지막 형태의 치환은 역슬래쉬 치환이다.

○ 해석기에게 특별한 의미를 가지는 문자를 표현하기 위해 사용된다.

○ 예를 들어 $는 변수 치환을 위해 사용되므로 명령어 상에 $를 사용하면 해석기는 이를 변수 치환으로 해석하여 할 것이다.

○ 이러한 문제를 해결하기 위해 역슬래쉬 치환을 사용한다.

set dollar \$

=> $

set x $dollar

=> $

○ 문자의 ASCII 코드를 사용하고 싶을 때에는 \ 이후에 16진수나 8진수로 ASCII 코드를 넣는다.

○ 역슬래쉬 치환의 또다른 사용 예는 한 명령 줄이 너무 긴 경우 다음 줄과 연결됨을 표시하기 위해 한 줄의 마지막에 \을 쓰는 것이다.

set totalLength [expr [string length $one] + \

      [string length $two]]

○ 역슬래쉬 치환의 예는 다음과 같다.

- \a: Bell(0x7)

- \b: Backspace(0x8)

- \f: Form feed(0xc)

- \n: Newline(0xa)

- \r: Carriage return(0xd)

- \t: Tab(0x9)

- \v: Vertical tab(0xb)

- \\: \

- \0XX: 8진수

- \xhh: 16진수

- \c: c 문자

 

1.8.  Grouping

○ 여러 단어를 하나로 Grouping하기 위해서는 '{ }' 와 ‘“’가 사용된다.

○ 인용 부호를 사용하는 경우는 치환을 허용한다.

○ { }를 사용하는 경우는 치환이 일어나지 않는다.

 

set s Hello

=> Hello

 

puts stdout "The length of $s is [string length $s]."

=> The length of Hello is 5.

 

puts stdout {The length of $s is [string length $s].}

=> The length of $s is [string length $s].

 

○ 인용부호가 자주 사용되는 예는 format 문이다.

○ format 명령은 C의 printf 함수와 유사하다.

○ format 명령의 첫 번째 인자는 format specifier이다.

puts [format "Item: %s\t%5.3f" $name $value]

 

1.9.  프로시져

○ 프로시져를 정의하기 위해서는 proc 명령을 사용한다.

proc name arglist body

○ 첫 번째 인자는 새로 정의할 프로시져의 이름이다.

○ 프로시져의 이름은 대소문자를 구분한다.

○ 두 번째 인자는 프로시져에게 전달할 파라미터의 리스트이다.

○ 세 번째 인자는 프로시져의 몸체 부분이다.

○ 정의된 프로시져는 다른 내장 명령과 똑같은 방법으로 사용한다.

proc diag {a b } {

      set c [expr sqrt ($a * $a + $b * $b)]

      return $c

}

○ diag 프로시져는 직각 삼각형의 두 변을 주었을 때 빗변의 길이를 구하는 프로시져이다.

○ 변수 c는 이 프로시져에서만 사용되는 변수이다.

○ 변수 c를 이용하지 않고 바로 값을 계산해 리턴할 수도 있다.

return [expr sqrt ($a * $a + $b * $b)]

○ Tcl에서는 프로시져 가장 마지막 명령의 결과를 리턴하므로 사실 마지막 줄의 return 명령은 불필요하다.

proc diag {a b} {

      expr sqrt ($a * $a + $b * $b)

}

 

1.10.  While 루프

set a 1; set product 1

while {$a <= 10} {

      set product [expr $product * $i]

      incr a

}

set product

=> 3628800

○ ';'을 사용해 두 개 이상의 명령을 한 줄에 쓸 수 있다.

○ while 명령의 첫 번째 인자는 boolean 수식이다.

○ boolean 수식이 참인동안 두 번째 인자에서 정의한 몸체 부분을 수행하게 된다.

○ while의 첫 번째 인자에 expr은 쓰지 않아도 무방하다.

○ incr 명령은 변수의 값을 1 증가시킨다.

○ 예에서 while의 첫 번째 인자를 { }를 사용해 grouping한 것을 알 수 있다.

○ { }를 써서 grouping함으로써 치환으로 인한 문제를 방지한다.

set a 1; while $a<=10 {incr a}

○ 위의 예는 무한 루프를 돌게 된다.

○ 이 명령의 수행 이전에 치환이 일어나 위의 while 문장은 while 1<=10 {incr a} 와 같은 의미가 되어 버린다.

 

while(n)                     Tcl Built-In Commands                    while(n)

NAME
       while - Execute script repeatedly as long as a condition is met

SYNOPSIS
       while test body

DESCRIPTION
       The while command evaluates test as an expression (in the same way that
       expr evaluates its argument).  The  value  of  the  expression  must  a
       proper boolean value;  if it is a true value then body is executed by
       passing it to the Tcl interpreter.  Once body has  been  executed  then
       test  is evaluated again, and the process repeats until eventually test
       evaluates to a false boolean value.  Continue commands may be  executed
       inside  body  to terminate the current iteration of the loop, and break
       commands may be executed inside body to cause immediate termination  of
       the while command.  The while command always returns an empty string.

       Note:  test  should almost always be enclosed in braces.  If not, vari-
       able substitutions will be made before the while command starts execut-
       ing,  which  means that variable changes made by the loop body will not
       be considered in the expression.  This is likely to result in an  infi-
       nite  loop.   If test is enclosed in braces, variable substitutions are
       delayed until the expression is evaluated (before each loop iteration),
       so  changes  in the variables will be visible.  For an example, try the
       following script with and without the braces around $x<10:

       set x 0 while {$x<10} {
           puts "x is $x"
           incr x }

EXAMPLE
       Read lines from a channel until we get to the end of  the  stream,  and
       print them out with a line-number prepended:

       set lineCount 0 while {[gets $chan line] >= 0} {
           puts "[incr lineCount]: $line" }

SEE ALSO
       break(n), continue(n), for(n), foreach(n)

KEYWORDS
       boolean, loop, test, while

 

1.11.  주석

○ Tcl에서 주석을 포함하는 줄은 #으로 시작한다.

○ 한 줄의 뒷부분에 주석을 달기 위해서는 ;으로 한 줄을 마치고 #을 달 수 있다.

set lab inclab       ;# comment

○ 주석문은 \에 의해 다음줄에 계속될 수 있다.

○ 주석문 안의 ;은 아무런 의미가 없다.

#this is the start of a Tcl comment \

and some more of it; still in the comment

 

1.12.  Command line argument

○ Tcl 쉘은 명령행에서 준 인자를 argv 변수를 통해 스크립트에게 전달한다.

○ argc 변수에는 인자의 개수가 들어간다.

○ 스크립트의 이름은 argv0 변수에 들어간다.

set progname $argv0

set first [lindex $argv 0]

set seconf [lindex $argv 1]

 

2.  스트링과 패턴 매칭

○ 스트링은 Tcl에서 사용하는 기본 자료 구조이다.

○ Tcl은 스트링을 다루는 많은 명령들을 가지고 있다.

○ 스트링에서 특정 패턴을 찾아내는 패턴 매칭을 쉽게 할 수 있다.

○ Tcl은 Glob 매칭과 Regular Expression 매칭의 두 가지 패턴 매칭 방법을 제공한다.

 

2.1.  string 명령

○ string 명령의 기본 문법은 다음과 같다.

string operation stringvalue ?otherargs?

○ operation 인자는 string 명령으로 무엇을 할 것인지를 나타낸다.

○ stringvalue 인자는 스트링 값이다.

○ 그 외에 명령에 따라 인자가 더 올 수도 있다.

○ string 명령의 여러 형태는 다음과 같다.

- string compare str1 str2: 두 스트링을 비교한다. 같으면 0을 돌려 주고 str1이 사전식 순서로 str2보다 앞이면 -1을, 그리고 그 외의 경우는 1을 돌려 준다.

- string first str1 str2: str2에서 str1이 처음으로 나타나는 위치를 돌려 준다. str1이 str2에 포함되어 있지 않으면 -1을 돌려 준다.

- string index string index_num: index_num번째 문자를 돌려 준다.

- string last str1 str2: str2에서 str1이 마지막으로 나타나는 위치를 돌려 준다. str1이 str2에 포함되어 있지 않으면 -1을 돌려 준다.

- string length str: str에 포함된 문자수를 돌려 준다.

- string match pattern str: str에 pattern이 포함되어 있으면 1을 돌려 주고 그렇지 않으면 0을 돌려 준다.

- string range str i j: str의 i번째부터 j번째까지의 스트링을 돌려 준다.

- string tolower str: str을 모두 소문자로 바꾼 스트링을 돌려 준다.

- string toupper str: str을 모두 대문자로 바꾼 스트링을 돌려 준다.

- string trim str ?chars?: str의 양 끝에서 chars에 포함된 문자를 모두 지운다. chars는 기본값이 whitespace 문자들(space, tab 등)이다.

- string trimleft str ?chars?: trim의 경우와 동일하지만 str의 시작 부분에 있는 문자만 지운다.

- string trimright str ?chars?: trim의 경우와 동일하지만 str의 마지막 부분에 있는 문자만 지운다.

- string wordend str ix: ix번째 문자가 포함된 단어가 끝난 곳의 index를 돌려 준다.

- string wordstart str ix: ix번째 문자가 포함된 단어가 시작된 곳의 index를 돌려 준다.

 

2.2.  append 명령

○ 여러 스트링을 합친다.

set foo z

append foo a b c

=> zabc

 

2.3.  format 명령

○ format 명령은 C의 printf나 FORTRAN의 format 명령과 유사하다.

○ format 명령의 형식은 다음과 같다.

format spec value1 value2 ...

○ spec은 포맷 지정자이다.

○ spec은 화면에 찍힐 문자들과 키워드들로 구성된다.

○ 키워드는 %로 시작한다.

○ spec에는 보통 whitespace를 포함하므로 인용부호를 사용하여 grouping하는 것이 일반적이다.

○ spec에서 % 뒤에 붙여 사용할 수 있는 포맷 지정자로는 다음과 같은 것들이 있다.

- d: 정수

- u: 부호없는 정수

- i: 정수. 16진수나 8진수를 쓸 수 있다.

- o: 부호없는 8진수

- x 또는 X: 부호없는 16진수. x를 쓰면 결과가 소문자로 찍히고 X를 쓰면 대문자로 찍힌다.

- c: ASCII 문자

- s: 스트링

- f: 실수

- e 또는 E: 지수형 표현. a.bE+-c와 같은 형태

- g 또는 G: f나 e중 더 짧게 표현할 수 있는 형태로 표현

 

○ 위치 지정자는 a$의 형식으로 %와 포맷 지정자의 사이에 쓴다.

set lang 2

format "%${lang}\$s" one un uno

=> un

 

2.4.  scan 명령

○ scan 명령은 C의 sscanf 함수와 유사하다.

○ scan 명령의 문법은 다음과 같다.

○ scan string format var1 var2 ...

○ scanf는 [ ]를 사용해 입력 문자의 필터링이 가능하다.

scan abcABC {%[a-z]} result

=> 1

set result

=> abc

 

2.5.  스트링 매칭

○ string match 명령은 glob 형태의 패턴 매칭을 수행한다.

○ * 기호는 어떤 문자열과 대응된다.

○ ? 기호는 하나의 문자와 대응된다.

○ [ ]으로 문자를 묶으면 [ ] 안에 포함된 임의의 한 문자와 대응된다.

○ [ ]은 명령 치환을 위해 사용되는 기호이기도 하므로 치환을 방지하기 위해서는 { }를 이용해 grouping을 해 줘야 한다.

string match a* alpha

=> 1

string match ?? XY

=> 1

string match {[ab]*} cello

=> 0

 

2.6.  Regular Expression

○ Regular Expression을 사용하면 더 강력한 패턴 매칭이 가능하다.

○ Regular Expression은 패턴을 정의할 때 사용하는 구문이다.

○ Regular Expression에서 사용되는 문법은 다음과 같다.

- .: 어떤 임의의 한 문자와 대응된다.

- *: 이전 패턴 항목을 0 또는 1회 이상 반복

- +: 이전 패턴 항목을 1회 이상 반복

- ?: 이전 패턴 항목을 0 또는 1회 반복

- ( ): 패턴을 묶는다.

- |: 두 가지 패턴 중 임의의 하나와 대응

- [ ]: [ ] 안에 포함된 문자 중 임의의 하나아 대응. [ ] 안에 나오는 첫 번째 문자가 ^인 경우는 [ ] 안의 문자를 제외한 모든 다른 문자와 대응된다.

- ^: 문자열의 처음과 대응

- $: 문자열의 마지막과 대응

Regular Expression의 예를 들어 보자.

- ..: 임의의 두 글자

- [Hh]ello: Hello 또는 hello

- [^a-zA-Z]: 알파벳을 제외한 모든 문자

- ba*: b, ba, baa, baaa, baaaa, ...

- (ab)+: ab, abab, ababab, ...

- .*: 모든 스트링과 대응

- hello|Hello: Hello 또는 hello

 

2.7.  regexp 명령

○ regexp 명령은 regular expression을 이용한 패턴 매칭을 하는 명령이다.

○ regexp 명령의 문법은 다음과 같다.

regexp ?flags? pattern string ?match sub1 sub2 ... ?

○ string에서 pattern과 일치하는 부분이 발견되면 1을 돌려 주고 그렇지 않으면 0을 돌려 준다.

○ pattern 안에 [나 $를 포함하는 경우에는 치환이 발생하지 않도록 { }로 grouping을 하거나 [, $ 앞에 \을 붙여 역슬래쉬 치환을 이용하도록 한다.

○ match 이후의 인자는 옵션이다.

○ 발견된 패턴은 match 변수에 저장된다.

○ pattern에 포함되는 sub 패턴들은 match 다음에 오는 변수에 차례로 저장된다.

○ sub 패턴은 ( )로 구분된다.

○ flags는 옵션이다.

○ flags 인자에 올 수 있는 값은 다음과 같다.

- -nocase: 대소문자 구분을 하지 않는다.

- -indices: 이 플래그를 사용하면 match 이후의 변수에 스트링을 저장하는 대신 string 변수에서 해당 패턴이 시작하는 위치를 저장한다.

○ pattern이 -로 시작하는 경우는 플래그를 패턴과 구분하기 위해 --를 쓰면 된다.

set env(DISPLAY) brutus:0.1

regexp {([^:]*):} $env(DISPLAY) match host

=> 1

set match

=> corvina:

set host

=> corvina

○ 위의 예는 DISPLAY 환경 변수를 brutus:0.1로 저장한 후 그 중 호스트 이름 부분만을 추출하는 프로그램이다.

3.  Tcl의 자료 구조

○ Tcl의 기본적인 자료 구조는 스트링이다.

○ Tcl에는 스트링 이외에 리스트와 배열의 두 가지 자료 구조가 더 있다.

○ 리스트는 스트링으로 구현된다.

○ 리스트의 구조는 스트링의 문법에 의해 정의된다.

○ 문법은 일반 명령의 경우와 같으며, 사실은 명령도 리스트의 일종이다.

○ 배열은 index를 가지는 변수이다.

○ index도 역시 스트링 값이므로 배열은 하나의 스트링(index)으로부터 다른 스트링(배열 원소의 값)에의 대응이라고 생각할 수 있다.

○ 작은 자료의 경우에는 리스트가 적당하며 큰 자료의 경우에는 배열이 적당하다.

 

3.1.  변수의 사용

set var {the value of var}

=> the value of var

set name var

=> var

set name

=> var

set $name

=> the value of var

○ set 명령에서 값을 주지 않으며 변수의 값만을 출력한다.

○ 변수 이름 앞에 $를 붙이는 경우와 붙이지 않는 경우를 구분하여야 한다.

○ unset 명령을 사용해 변수를 없앨 수 있다.

unset varName varName2 ...

○ info exist 명령을 통해 변수가 존재하는지 확인할 수 있다.

if ![info exists foobar] {

      set foobar 0

} else {

      incr foobar

}

 

3.2.  리스트

○ 다른 언어에서의 리스트 자료 구조와 달리 Tcl에서의 리스트는 스트링의 다른 표현에 불과하다.

○ Tcl 리스트는 Tcl 명령어와 동일한 구조를 가지고 있다.

○ 리스트는 whitespace로 구분된 스트링이다.

○ { }나 ""가 grouping을 위해 사용될 수 있다.

○ 리스트는 스트링으로 표현되기 때문에 수행 속도가 느려질 수 있으므로 긴 리스트를 사용하는 것은 피해야 한다.

○ 자주 사용되는 긴 길이의 리스트는 배열로 바꾸는 것이 바람직하다.

○ 리스트와 관련된 명령어는 다음과 같다.

- list arg1 arg2 ...: 리스트를 생성한다.

- lindex list i: list의 i번째 원소를 얻어 낸다.

- llength list: list의 길이를 알아 낸다.

- lrange list i j: 리스트의 i번째부터 j번째 원소들을 돌려 준다.

- lappend listVar arg arg ...: listVar에 원소를 추가한다.

- linsert list index arg arg ...: list의 index번째 원소 앞에 새로운 원소를 추가한다.

- lreplace list i j arg ag ...: list의 i부터 j까지의 원소를 새로운 원소들로 바꾼다.

- lsearch mode list value: list에서 value와 일치하는 원소를 찾아 위치를 돌려 준다. mode는 -exact, -glob, -regexp 중의 하나이며 -glob가 기본값이다.

- lsort switches list: list를 소트한다. switches에는 소트 방법을 주게 되는데, -ascii, -integer, -real, -increasing, -decreasing, -command 중의 하나이다.

- concat arg arg arg: 여러 리스트를 하나로 합친다.

- join list joinString: list의 원소를 joinString으로 구분해 합친다.

- split string splitChars: splitChars를 원소간의 경계로 생각해 string을 리스트 원소로 쪼갠다.

 

3.3.  리스트 만들기: list, lappend, concat

○ list 명령은 인자들을 원소로 가지는 새로운 리스트를 생성한다.

굳이 앞에 list라는 키워드를 사용하지 않아도 whitespace로 구분된 스트링은 리스트를 생성하므로 list 명령은 필요없는 기능으로 여겨질 수 있지만 list 명령은 만들어진 리스트를 돌려 주므로 리스트가 제대로 생성되었는지를 확인하는 데 사용할 수 있다.

set x {1 2}

=> 1 2

set x

=> 1 2

list $x \$ foo

=> {1 2} {$} foo

○ 위의 예제에서처럼 리스트 원소를 구분하기 위해 { }를 써서 나타내 주므로 만들어진 리스트를 확인할 수 있다.

○ lappend 명령은 리스트의 마지막에 원소를 추가하기 위해 사용한다.

lappend new 1 2

=> 1 2

lappend new 3 "4 5"

=> 1 2 3 {4 5}

set new

=> 1 2 3 {4 5}

○ concat 명령은 리스트들을 합치는 데 사용된다.

set x {4 5 6}

set y {2 3}

set z 1

concat $z $y $x

=> 1 2 3 4 5 6

○ list 명령과 concat 명령을 구분할 필요가 있다.

○ list나 lappend 명령은 리스트 구조를 그대로 유지하는 반면 concat 명령은 list 구조를 한 겹 벗겨 낸 후 합치게 된다.

 

3.4.  리스트의 원소 얻기: llength, lindex, lrange

○ llength 명령은 리스트의 원소 개수를 돌려 준다.

llength {a b {c d} "e f g" h}

=> 5

○ lindex 명령은 리스트의 특정 원소를 돌려 준다.

○ lindex 명령은 첫 번째 인자로 index를 받는다.

○ index는 0에서부터 시작한다.

○ index 대신에 end라는 키워드를 사용해 리스트의 마지막 원소를 지칭할 수 있다.

○ end 키워드는 lindex, linsert, lrange, lreplace 명령에 유효하다.

lindex {1 2 3} 0

=> 1

lindex {1 2 3} end

=> 3

○ lrange 명령은 리스트의 특정 범위의 원소를 돌려 준다.

lrange {1 2 3 {4 5}} 2 end

==> 3 {4 5}

 

3.5.  리스트의 수정: linsert, lreplace

○ linsert 명령은 리스트의 특정 위치에 원소를 삽입한다.

○ index가 0 이하이면 리스트의 맨 처음에 삽입한다.

○ index가 리스트의 크기보다 크면 리스트의 마지막에 추가된다.

○ index가 0보다 크고 리스트의 크기보다 작으면 index번째 원소의 앞에 삽입된다.

○ lreplace는 리스트의 특정 범위에 있는 원소들을 새로운 원소들로 변경한다.

○ 새로운 원소를 인자로 주지 않으면 특정 범위의 원소가 삭제된다.

○ linsert나 lreplace는 기존의 리스트를 변경하지는 않고 변경된 리스트를 돌려 주기만 한다.

linsert {1 2} 0 new stuff

=> new stuff 1 2

set x [list a {b c} e d]

=> a {b c} e d

lreplace $x 1 2 B C

=> a B C d

lreplace $x 0 0

=> {b c} e d

 

3.6.  리스트에서 원소 찾기

○ lsearch는 리스트에서 특정 원소의 index를 돌려 준다.

○ 존재하지 않는 경우는 -1을 돌려 준다.

○ glob 모드의 패턴 매칭이 기본 값이다.

○ 패턴 매칭 방법을 바꾸기 위해서는 다른 패턴 매칭을 나타내는 -exact나 -regexp 등의 옵션을 사용하면 된다.

lsearch {here is a list} l*

=> 3

○ lreplace 명령은 새로 넣고 싶은 원소가 이미 리스트에 포함되어 있는지를 확인하기 위해 보통 lsearch 명령과 같이 쓰인다.

○ 다음 프로시져는 리스트에서 특정 원소를 삭제한다.

proc ldelete { list value } {

      set ix [lsearch -exact $list $value]

      if {$ix >= 0} {

              return [lreplace $list $ix $ix]

      } else {

              return $list

      }

}

 

3.7.  리스트의 소트

○ lsort 명령을 사용하면 다양한 방법으로 리스트를 소팅할 수 있다.

○ 세 가지 기본적인 소팅 방법은 -ascii, -integer, -real이다.

○ -increasing이나 -decreasing은 오름차순으로 소트할 것인지 또는 내림차순으로 소트할 것인지를 나타낸다.

○ 기본 옵션은 -ascii -increasing이다.

○ 특별한 목적의 소팅을 위해 자신만의 소팅 함수를 제작할 수도 있다.

○ 예를 들어 이름의 리스트를 갖고 있고 이름은 세 글자로 이루어진 리스트라고 가정하자.

○ 기본적인 소트는 이름의 첫 번째 글자로 소팅할 것이다.

○ 다음 예제는 이름의 마지막 글자로 소팅한다.

proc mycompare {a b} {

      set alast [lindex $a end]

      set blast [lindex $b end]

      set res [string compare $alias $blast]

      if ($res != 0} {

              return $res

      } else {

              return [string compare $a $b]

      }

}

set list {{Eung Do Kim} {Hyo Jun Im} {Woo Joo Lee}}

=> {Eung Do Kim} {Hyo Jun Im}  {Woo Joo Lee}

lsort -command mycompare $list

=> {Hyo Jun Im} {Eung Do Kim} {Woo Joo Lee}

 

3.8.  split와 join 명령

○ split 명령은 스트링을 입력받아 특정 문자를 구분자로 생각하여 리스트를 만들어 낸다.

○ split 명령은 사용자 입력을 Tcl이 처리하기 쉬운 형태로 바꾸는 데 사용할 수 있다.

set line {welch:*:3116:100:Brent Welch:/usr/welch:/bin/csh}

split $line :

=> welch * 3116 100 {Brent Welch} /usr/welch /bin/csh

set line {this is "not a tcl list}

index $line 1

=> is

index $line 2

=> unmatched open quote in list

lindex [split $line] 2

=> "not

○ split 명령의 기본 구분자는 공백 문자이다.

○ 구분 문자가 두 개 이상 연달아 오면 그 사이는 빈 리스트로 표시하게 된다.

set line "\tHello, world."

split $line \ ,.\t

=> {} Hello {} world {}

○ join 명령은 split 명령의 반대이다.

○ join 명령은 리스트를 스트링으로 변환한다.

join {1 {2 3} {4 5 6}} :

=> 1:2 3:4 5 6

 

3.9.  배열

○ 배열은 Tcl에서 사용하는 중요한 자료 구조이다.

○ 배열의 인덱스는 스트링이다.

○ 배열은 내부적으로 해쉬 테이블을 사용해 구현되었으므로 탐색 시간이 짧다.

○ 배열의 인덱스 부분은 괄호로 구분된다.

○ 인덱스는 명령 substituion을 포함해 어떤 스트링 값도 가질 수 있다.

○ 배열의 원소값도 역시 변수 치환을 통해 얻어낼 수 있다.

set arr(0) 1

for (set i ) {$i <= 10} {incr i} {

      set arr($i) [expr $i * $arr([expr $i-1])]

}

○ 위의 예제는 arr(x)의 값을 x!로 만드는 프로그램이다.

○ 첫줄의 arr(0) 초기화는 arr을 배열 변수로 만든다.

○ 어떤 변수를 배열 변수로도 사용하고 일반 변수로도 사용해서는 안된다.

○ 예를 들어 다음은 오류이다.

set arr 3

==> can't set "arr": variable is array

○ 배열 변수의 한 원소는 일반 변수와 똑같이 사용할 수 있다.

○ 인덱스가 복잡한 경우는 인덱스의 다른 부분을 구분하기 위해 ','를 사용할 수 있다.

○ 괄호는 grouping 기능을 제공하지 않으므로 배열의 인덱스에 공백이 들어가서는 안된다.

○ 인덱스 부분에 공백을 넣기 위해서는 역슬래쉬 치환이나 grouping을 사용해야 한다.

set index {I'm asking for trouble}

set arr($index) {I told you so.}

○ 배열의 이름만을 다른 변수에 저장하고 사용할 수도 있다.

set name TheArray

=> TheArray

set ${name}(xyz) {some value}

=> some value

set x $TheArray(xyz)

=> some value

set x $(name}(xyz)

=> TheArray(xyz)

set x [set ${name}(xyz)]

=> some value

 

3.10.  array 명령

○ array 명령은 배열 변수에 대한 정보를 돌려 준다.

○ 배열의 원소들을 모두 검색하기 위해 array 명령을 사용할 수도 있다.

○ array names 명령을 foreach 루프와 함께 사용해 배열의 모든 원소에 대해 어떤 작업을 하게 할 수 있다.

foreach index [array names arr] {command body}

○ array names 명령에 의해 리턴되는 원소의 순서는 해쉬 테이블의 구현 방법에 따라 달라지므로 임의적이다.

○ array get과 array set 명령은 배열과 리스트간의 변환을 위해 사용된다.

○ array get 명령에 의해 리턴되는 리스트는 짝수개의 원소를 가지게 되는데, 먼저 배열의 인덱스 부분이 나오고 그 다음에 그 값이 나오는 순서로 모든 원소에 대해 반복하게 된다.

○ array set 명령에게 주는 인자도 array get에 의해 리턴되는 배열의 구조와 같아야 한다.

set fruit(best) kiwi

set fruit(worst) peach

set fruit(ok) banana

array get fruit

=> ok banana best kiwi worst peach

 

4.  순서제어 명령

○ Tcl에서의 실행 순서 제어도 역시 명령어를 통해 이루어진다.

○ 루프를 위한 명령으로는 while, foreach, for가 있다.

○ 조건문으로는 if와 switch가 있다.

○ 에러 처리 명령으로는 catch가 있다.

○ 순서를 잘 제어하기 위한 다른 명령으로 break, continue, return, error가 있다.

○ 순서제어 명령 뒤에는 보통 나중에 수행할 명령들을 모은 몸체 부분이 따르게 된다.

○ 몸체 부분은 치환을 방지하기 위해 { }로 묶어야 한다.

○ 순서제어 명령은 마지막에 수행한 명령의 리턴값을 돌려주게 된다.

○ { }의 또하나의 용도는 Newline 문자를 포함한 스트링을 묶을 수 있다는 것이다.

○ if, for, while 등의 명령은 참/거짓을 돌려주는 boolean 수식을 포함한다.

○ if, for, while 명령은 내부적으로 expr 명령을 자동으로 수행하기 때문에 expr을 명시적으로 적어주지 않아도 된다.

 

4.1.  If Then Else

○ if는 가장 기본적인 조건 명령이다.

○ 조건이 참이면 뒤에 따르는 첫 번째 몸체 부분을 실행하고 그렇지 않으면 두 번째 몸체 부분을 실행한다.

○ 이 명령의 문법은 다음과 같다.

if boolean then body1 else body2

○ then과 else 키워드는 옵션이므로 사용하지 않아도 된다.

if {$x == 0} {

      puts stderr "Divide by zero!"

} else {

      set slope [expr $y/$x]

}

○ Newline 문자는 한 명령의 끝을 나타내지만 grouping 되어 있는 경우는 예외이다.

○ if 명령의 첫 번째 인자는 boolean 수식이다.

○ 이후에 코드를 수정하게 되는 경우를 대비해 조건문과 몸체 부분은 아무리 간단해도 항상 { }로 묶어 주는 것이 좋다.

○ elseif 명령은 이어진 조건문을 구성하는 데 사용된다.

if {$key < 0} {

      incr range 1

} elseif {$key == 0} {

      return $range

} else {

      incr range -1

}

○ 그러나 이을 조건문의 내용이 길어지는 경우는 switch 명령을 쓰는 것이 좋다.

 

4.2.  Switch

○ switch 명령은 수식의 값에 따라 어떤 특정 몸체를 수행하게 하는 데 사용된다.

○ 조건은 간단한 비교문에서 패턴 매칭에 이르기까지 다양하게 사용할 수 있다.

○ switch문의 문법은 다음과 같다.

○ switch flags value pat1 body1 pat2 body2 ...

○ switch flags value { pat1 body1 pat2 body2 ... }

○ flags에는 다음과 같은 4가지 값 중의 하나가 들어갈 수 있다.

-exact: 정확하게 일치하는 패턴만을 찾는다.

-glob: glob 형태의 패턴 매칭을 수행한다.

-regexp: regular expression 패턴 매칭을 수행한다.

--: 플래그를 사용하지 않거나 플래그 부분의 마지막을 의미

○ 패턴과 몸체 부분을 grouping하는 방법에는 여러 가지가 있다.

○ 첫 번째 방법은 패턴과 몸체 부분을 하나의 인자로 묶는 것이다.

switch -exact -- $value {

      foo {doFoo; incr count(foo) }

      bar {doBar; return $count(foo) }

      default {incr count(other) }

}

○ 마지막 패턴인 default는 다른 일치되는 패턴이 없는 경우에 사용함을 의미한다.

○ default가 마지막 패턴이 아닌 경우는 default라는 문자열과 일치하는지를 검사하게 된다.

○ 두 번째 방법은 grouping을 하지 않는 것이다.

○ 이 경우 Newline 문자를 무시하게 하기 위해 역슬래쉬 치환을 사용해야 할 것이다.

○ { }로 grouping을 하지 않았으므로 치환이 이루어진다.

switch -regexp -- $value \

      ^$key {body1} \

      \t### {body2} \

      {[0-9]*} {body3}

○ 마지막 방법은 치환을 허용하면서 Newline 문자를 허용하기 위해 '"'를 쓰는 것이다.

switch -glob -- $value "

      $(key}* { puts stdout \"Key is $value\" }

      X* -

      Y* - {takeXorYaction $value }

"

○ 몸체 부분에 -를 쓰면 다음 패턴의 몸체 부분을 수행하게 된다.

○ 주석은 명령어가 나올 수 있는 위치에 써야 하므로 switch 문에서 주석을 달 때에는 유의해야 한다.

○ 다음은 주석을 잘못 단 예이다.

switch -- $value {

      # this comment confuses switch

      pattenr { # this comment is ok}

○ 위의 예에서 Tcl 해석기는 첫 번째 주석 부분을 패턴으로 인식하게 된다.

 

4.3.  Foreach

○ foreach 명령은 리스트에 있는 모든 값들을 어떤 변수에 대입해 가면서 명령 몸체 부분을 반복 수행하게 된다.

○ foreach 명령의 문법은 다음과 같다.

foreach loopVar valueList commandBody

○ valueList 부분에 직접 리스트를 넣을 수 있다.

set i 1

foreach value {1 3 5 7 11 13 17 19 23} {

      set i [expr $i*value]

}

set i

=> 111546435

○ 다음 예제에서는 리스트 값을 가지는 변수를 사용한다.

# argv is set by the Tcl shells

foreach arg $argv {

      switch -regexp -- $arg {

              -foo {set fooOption 1}

              -bar {barRelatedCommand}

              ([0-9]*) {scan -%d $arg intValue

      }

}

 

4.4.  While

○ while 명령은 테스트 부분과 몸체 부분의 두 인자를 가진다.

      while booleanExpr body

○ while 명령은 반복적으로 booleanExpr 부분을 검사해 참인 경우에 몸체 부분을 반복 수행한다.

○ while을 쓸 때는 치환이 잘못 일어나는 경우가 없도록 주의하여야 한다.

set i 0; while $i<10 {incr i}

○ 위의 예에서 $i는 변수 치환이 일어나 0<10 과 같은 조건문이 되어 항상 참이 되어 버린다.

○ 다음의 예는 { }를 사용해 변수 치환을 방지하였다.

set i 0; while {$i < 10} {incr i}

 

4.5.  For

○ for 명령은 C의 for 명령과 유사하다.

○ for 명령은 4개의 인자를 취한다.

for initial test final body

○ initial은 루프를 돌기 이전에 수행할 초기화 부분이다.

○ test는 루프의 반복 여부를 결정하기 위한 조건이다.

○ final은 루프의 수행이 끝난 후에 수행할 명령이다.

for {set i 0} {$i < 10} {incr i 3} {

      lappend aList $i

}

set aList

=> 0 3 6 9

 

4.6.  Break와 Continue

○ break 명령은 현재 수행 중인 루프에서 빠져 나간다.

○ continue는 다음 루프를 즉시 수행한다.

○ Tcl에는 goto 명령이 없다.

 

4.7.  Catch

○ catch 명령은 명령 수행 중 발생한 오류를 잡아 내기 위해 사용한다.

○ catch 명령의 문법은 다음과 같다.

catch command ?resultVar?

○ command는 수행할 몸체 부분이다.

○ 두 번째 인자는 명령 수행의 결과나 오류 메시지를 저장할 변수의 이름이다.

○ catch 명령은 오류없이 수행된 경우 0을 돌려 주고 오류가 발생한 경우는 1을 돌려 준다.

○ 다음은 catch 명령을 수행해 오류 처리를 한 예이다.

if [catch {

      command1

      command2

      command3

} result] {

      global errorInfo

      puts stderr $result

      puts stderr "*** Tcl TRACE ***"

      puts stderr $errorInfo

} else {

      #command body ok, result of last command is in result

}

 

4.8.  Error

○ error 명령은 catch로 잡아내지 않는 경우 스크립트를 종료시키는 오류를 임의로 발생시킨다.

○ error 명령의 문법은 다음과 같다.

error message ?info? ?code?

○ message는 catch 명령에게 전달될 에러 메시지이다.

○ info는 errorInfo 전역 변수에 넣을 값이다.

○ 보통 errorInfo는 보통 에러가 발생한 위치에서의 스택의 상태를 수집하기 위해 사용된다.

○ info 인자를 설정하지 않으면 error 명령 수행 상태가 errorInfo에 들어가게 된다.

proc foo {} {

      error bogus

}

foo

=> bogus

set errorInfo

=> bogus

      while executing

“error bogus"

      (procedure "foo" line 2)

      invoked from within

"foo"

○ code 인자는 오류의 종류에 대한 설명이 들어 간다.

○ code 인자는 errorCode라는 변수에 저장된다.

 

4.9.  Return

○ return 명령은 프로시져로부터 종료하기 위해 사용된다.

○ return 명령은 프로시져의 수행 중간에 프로시져 수행을 마치기 위해 사용된다.

5.  프로시져와 스코프

○ 프로시져는 자주 사용되는 일련의 명령들을 묶어 쉽게 사용할 수 있게 해 준다.

○ 각 프로시져는 변수에 대한 새로운 스코프를 가지게 된다.

○ 변수의 스코프란 그 변수가 정의되는 명령의 영역을 의미한다.

○ 본 절에서는 Tcl의 proc 명령과 변수의 스코프에 대해 살펴 본다.

 

5.1.  proc 명령

○ Tcl 프로시져는 proc 명령으로 정의한다.

○ proc 명령은 세 인자를 받는다.

proc name params body

○ name은 프로시져의 이름이다.

○ 프로시져의 이름은 대소문자를 구분하며 어떤 문자도 포함할 수 있다.

○ params는 파라미터 이름의 리스트이다.

○ body는 프로시져의 몸체 부분이다.

○ 정의된 프로시져는 다른 Tcl 명령과 똑같은 방식으로 사용한다.

○ 프로시져가 돌려주는 값은 프로시져의 마지막 명령이 수행한 명령의 결과값과 같다.

○ 특정 값을 돌려 주기 위해 return 명령을 사용할 수도 있다.

○ 프로시져의 파라미터 이름 리스트는 기본값을 가질 수 있다.

proc p2 {a {b 7} {c -2} } {

      expr $a / $b + $c

}

p2 6 3

=> 0

○ 프로시져 p2는 1개, 2개 또는 3개의 인자로 불릴 수 있다.

○ 만약 1개의 인자만을 주어 호출한다면 b와 c는 각각 기본값인 7과 -2를 가지게 된다.

○ 만약 2개의 인자만을 준다면 c만 기본값을 가지게 되고 나머지는 주어진 인자를 사용한다.

○ 프로시져는 args 키워드를 사용해 임의 개수의 파라미터를 사용하게 할 수도 있다.

proc argtest {a {b foo} args} {

      foreach param {a b args} {

              puts stdout "\t$param = [set $param]"

      }

}

argtest 1

=> a = 1

    b = foo

    args =

argtest 1 2

=> a = 1

    b = 2

    args =

argtest 1 2 3

=> a = 1

    b = 2

    args = 3

argtest 1 2 3 4

=> a = 1

    b = 2

    args = 3 4

 

5.2.  rename으로 명령어의 이름을 바꾸기

○ rename 명령은 명령어의 이름을 변경한다.

○ rename은 기존 프로시져에 기능을 추가하기 위해 사용할 수 있다.

rename foo foo.orig

○ 이렇게 하면 foo 프로시져를 새로 정의할 때 foo.orig 프로시져를 호출할 수 있게 된다.

○ rename의 두 번째 인자로 빈 스트링을 줌으로써 명령어를 사용할 수 없게 만들 수도 있다.

rename exec { }

 

5.3.  스코프

○ Pascal 등의 언어에서는 중첩된 프로시져의 경우 정의된 프로시져 내에서만 프로시져를 사용할 수 있지만, Tcl에서는 다른 프로시져에 중첩되어 정의된 프로시져도 어느 곳에서나 사용할 수 있다.

○ 변수 이름과 프로시져 이름은 다른 이름 공간을 사용하므로 프로시져와 변수에 같은이름을 사용하더라도 상관없다.

○ 각 프로시져 내에서 정의된 변수는 그 프로시져 안에서만 사용할 수 있다.

○ 프로시져가 리턴되면 그 변수는 더 이상 유효하지 않다.

○ 프로시져의 외부에서 정의된 변수는 upvar나 global 스코프 명령어를 사용하지 않으면 프로시져 내에서 사용불능.

○ 프로시져 외부의 이름과 일치하는 변수가 프로시져 내에서 다시 정의되면 외부 변수는 프로시져 내에서의 값 변경에 영향을 받지 않는다.

set a 5

set b -8

proc p1 {a} {

      set b 42

      set {$a < 0} {

              return $b

      } else {

              return $a

      }

}

p1 $b

=> 42

p1 [expr $a*2]

=> 10

○ 위의 예제에서 외부 변수 a는 프로시져 p1의 인자인 a와는 다르다.

○ 외부에서 정의된 변수 b도 내부에서 정의된 b 변수와는 다르다.

 

5.4.  global 명령

○ 프로시져 외부에서 정의된 변수를 프로시져 내부에서 사용하기 위해서는 global 키워드를 사용해 프로시져 내에서 사용할 수 있도록 하여야 한다.

○ global 명령의 문법은 다음과 같다.

global varName1 varName2 ...

○ global 명령은 외부 변수를 사용하고자 하는 프로시져의 내부에 포함되게 된다.

○ global 명령은 프로시져 내부에서만 유효하므로 여러 개의 프로시져에서 외부 변수를 사용하고자 하는 경우는 모든 프로시져에서 global 명령을 사용해야 한다.

○ 변수가 정의되기 이전에 global 명령을 사용해도 상관없지만, 이 경우는 변수가 정의되는 순간에 모든 프로시져에서 변수에 접근할 수 있게 된다.

○ 다음 예제는 랜덤하게 발생된 숫자의 상태를 기억하기 위해 global 변수를 사용한다.

proc randomInit { seed } {

      global rand

      set rand(ia) 9301;# Multiplier

      set rand(ic) 49297;# Constant

      set rand(im) 233280;# Divisor

      set rand(seed) $seed;# Last result

}

proc random {} {

      global rand

      set rand(seed) \

              [expr ($rand(seed)*$rand(ia) +

                      $rand(ic)) % $rand(im)]

      return [expr $rand(seed)/double($rand(im))]

}

proc randomRange { range } {

      expr int([random]*$range)

}

randomInit [pid]

=> 5049

random

=> 0.517687

random

=> 0.217177

randomRange 100

=> 17

 

5.5.  전역 상태 유지를 위해 배열 사용하기

○ Tcl 배열은 인덱스 값에 아무런 제한이 없기 때문에 무척 유연하게 사용할 수 있는 자료 구조이다.

○ 배열의 한 좋은 사용예는 다른 언어의 레코드처럼 관계있는 변수들을 한데 묶는 것이다.

○ 배열 이름에 global 명령을 사용하면 배열을 구성하는 모든 원소가 global 스코프를 가지게 되므로 무척 편리하다.

○ 예를 들어 Tk 응용 프로그램을 개발한다고 할 때 전역적으로 관리되어야 하는 변수를 하나의 배열에 넣어두고 프로시져에서는 그 배열에 대해서만 global 명령을 사용해 간단하게 전역 변수를 접근할 수 있게 된다.

○ 다음 예는 어떤 물체의 위치를 추적하기 위해 배열을 사용한다.

proc ObjInit { o x y } {

      global obj

      set obj($o, x) $x

      set obj($o, y) $y

      set obj($o, dist) [expr sqrt($x * $x + $y * $y)]

}

proc ObjMove { o dx dy } {

      global obj

      if ![info exists obj($o, x)] {

              error "Object $o not initialized"

      }

      incr obj($o, x) $dx

      incr obj($o, y) $dy

      set obj($o, dist) [expr sqrt($obj($o, x) * $obj($o, x) + \

              $obj($o, y) * $obj($o, y))]

}

○ 위의 예제는 전역 상태 변수로서 배열 obj를 사용하고 있다.

 

5.6.  upvar명령을 이용한 “Call by Name” 구현

○ 프로시져에게 변수의 값이 아닌 이름을 넘겨 주고 싶은 경우는 upvar 명령을 사용한다.

○ 보통 이 명령은 배열 변수에 사용된다.

○ upvar 명령의 문법은 다음과 같다.

upvar ?level? varName localVar

○ level 인자는 옵션으로서 기본값은 1로, Tcl 호출 스택의 한 단계 위를 의미한다.

○ level 인자에 숫자를 주면 그 숫자만큼 호출 스택의 단계를 넘어간 스코프를 의미하게 된다.

○ level 인자를 #number의 형태로 주게 되면 어떤 절대값을 가지는 특정 스코프를 의미한다.

○ #0은 global 스코프를 의미하므로 global foo 명령은 다음 명령과 동일한 의미가 된다.

upvar #0 foo foo

○ 상위 단계 스택의 변수는 일반 변수, 또는 배열 변수의 원소값, 또는 배열의 이름일 수가 있다.

○ 변수가 일반 변수이거나 배열 변수의 원소값인 경우 지역 변수는 일반 변수로 취급된다.

○ 변수가 배열의 이름인 경우는 지역 변수가 배열로 취급된다.

○ 다음 예제의 프로시져 PrintByName은 변수의 이름을 인자로 주었을 때 그 값을 출력해 준다.

proc PrintByName { varName } {

      upvar $varName var

      puts stdout "$varName = $var"

}

○ upvar 명령은 incr 명령을 개선하기 위해 사용할 수 있다.

○ incr 명령은 변수가 존재하지 않는 경우에 에러를 발생시킨다.

○ 다음 예제는 변수가 존재하지 않는 경우 변수를 새로 생성하는 새로운 incr 프로시져이다.

proc incr { varName {amount 1}} {

      upvar $varName var

      if [info exists var] {

              set var [expr $var + $amount]

      } else {

              set var $amount

      }

      return $var

}

○ upvar 명령은 배열에 대해 사용할 수도 있다.

○ 다음 예제는 upvar 명령을 사용해 스택을 구현하고 있다.

proc Push { stack value } {

      upvar $stack S

      if ![info exists S(top)] {

              set S(top) 0

      }

      set S($S(top)) $value

      incr S(top)

}

proc Pop { stack } {

      upvar $stack S

      if ![info exists S(top)] {

              return {}

      

      if {$S(top) == 0} {

              return {}

      } else {

              incr S(top) -1

              set x $S($S(top))

              unset S($S(top))

              return $x         }       }

6.  Eval

○ 어떤 변수나 명령을 해석하기 위해서는 치환을 필요로 한다.

○ 좀 더 고급 기능의 치환은 eval이나 subst, 그리고 after, uplevel, send 명령에 의해 수행된다.

 

6.1.  리스트로 명령어 조합하기

○ eval 명령은 Tcl 해석기에게 또다른 호출을 하게 만든다. / 명령어를 동적으로 조합하기 위해서는 eval 명령을 사용해야 한다.

○ 예를 들어 다음과 같은 명령을 조합해 두고 나중에 수행시키고 싶은 경우를 생각해 보자.

puts stdout "Hello, World!"

○ 이러한 경우에는 다음과 같이 하면 된다.

set cmd {puts stdout "Hello, World!"}

=> puts stdout "Hello, World!"

# sometime later...

eval $cmd

=> Hello, World!

○ 외부에 출력할 스트링이 string이라는 변수에 저장되어 있다고 가정해 보자.

○ eval 명령의 수행 시점에서 string 변수에 값이 없다고 하자.

set string "Hello, World!"

set cmd {puts stdout $string}

unset string

eval $cmd

=> cant't read "string": no such variable

○ 위와 같은 상황을 해결하기 위해서는 명령어를 list 명령을 사용해 조합하면 된다.

set string "Hello, World!"

set cmd [list puts stdout $string]

=> puts stdout {Hello, World!}

unset string

eval $cmd

=> Hello, World!

○ 다음은 잘못된 예이다.

set cmd "puts stdout $string"

=> puts stdout Hello, World!

eval $cmd

=> bad argument "World!": should be "nonewline"

○ concat는 리스트 구조를 유지하지 않으므로 문제가 된다.

set cmd [concat puts stdout $string]

 

6.2.  eva에서 수행하는 concat 기능 이용

○ eval 명령은 하나 이상의 인자를 받는 경우에 자동으로 concat 명령을 수행하게 된다.

eval list1 list2 list3 ...

○ concat 명령의 효과는 모든 리스트들을 하나의 리스트로 합치는 것이다.

○ 새로운 단계의 리스트 구조가 추가되지는 않는다.

○ 이러한 기능은 명령어가 여러 조각으로 분리되어 있는 경우에 유용하다.

○ 이러한 형태의 eval 명령은 프로시져의 args와 같이 사용하는 것이 일반덕이다.

○ 옵션 인자를 다른 명령에게 전달하기 위해 args 파라미터를 사용한다.

○ 다른 명령어를 호출할 때 eval을 사용하면 $args가 concat 기능에 의해 합쳐지므로 명령어에서 올바른 인자로 파악할 수 있게 되는 것이다.

○ 이러한 경우를 Tk의 예에서 살펴보도록 한다. / Tk에서 버튼을 만들기 위해서는 다음과 같은 형태의 명령을 수행한다.

button .foo -text Foo -command foo

○ 버튼이 생성된 후에는 pack 명령을 이용해 화면에 보이게 하여야 한다.

pack .foo -side left

○ 다음은 제대로 동작하지 않는다.

set args {-text Foo -command foo}

button .foo $args

=> unknown option "-text Foo -command foo"

○ 문제는 $args가 리스트 값으로서 button은 args를 구성하는 값 전체를 하나의 파라미터로 인식하게 된다는 것이다.

○ 문제를 해결하기 위해서는 args를 구성하는 원소 각각을 하나의 파라미터로 인식하게 만들어야 한다.

eval button .foo $args

=> .foo

○ 다음 예는 이러한 eval의 기능을 사용해 버튼을 만들고 보여주는 프로시져이다.

proc PackedButton {path txt cmd {pack {-side right}} args} {

      eval {button $path -text $txt -command $cmd } $args

      eval {pack $path} $pack

}

○ PackedButton에서 pack과 args는 모두 리스트 파라미터로서 명령어의 일부를 구성하게 된다.

 

6.3.  Uplevel 명령

○ uplevel 명령은 eval과 유사하지만 현재 프로시져와는 다른 스코프에서 명령어를 수행하게 된다.

○ 이것은 새로운 제어 구조를 정의하는 데에 유용하다.

○ uplevel 명령의 문법은 다음과 같다.

uplevel level command

○ level 파라미터의 의미는 upvar 명령의 경우와 동일하다.

 

7.  UNIX에서 작업하기

○ 여기서는 Tcl로 어떻게 프로그램을 수행시키며 파일 시스템에 접근하는지를 살펴 본다.

○ 여기서 소개하는 명령은 UNIX를 기초로 하여 만들어졌지만 DOS나 Macintosh 등에서도 Tcl 해석기가 구현되어 있으므로 플랫폼에 무관하게 사용할 수 있다.

 

7.1.  exec 명령으로 UNIX 프로그램 수행하기

○ exec 명령은 Tcl 스크립트에서 UNIX 명령을 수행한다.

○ 다음 예를 살펴 보자.

set d [exec date]

○ 이 경우 exec 명령의 수행 결과는 표준 출력으로 출력된 결과가 된다.

○ 만약 수행한 프로그램이 표준 오류 출력으로 결과를 찍거나 0이 아닌 상태 코드 값을 가지게 되는 경우는 에러를 유발시킨다.

○ exec 명령은 I/O redirection과 pipeline 구문을 완벽히 지원한다.

○ 다음은 exec 명령에서 사용할 수 있는 여러 문법을 나열하였다.

- keepnewline: 결과값의 마지막에 있는 newline 문자를 없애지 않는다.

- |: pipeline

- |&: 표준 에러 출력으로도 파이프라인을 한다.

- < fileName: fileName 파일로부터 입력을 받는다.

- <@ fileId: fileId의 id를 가지는 I/O 스트림으로부터 입력을 받는다.

- << value: 주어진 값으로부터 입력을 받는다.

- > fileName: fileName 파일에 결과를 쓴다.

- 2> fileName: fileName 파일애 표준 에러 출력 결과를 쓴다.

- >& fileName: 표준 출려과 표준 에러 출력 결과를 모두 fileName 파일에 쓴다.

- >> fileName: fileName 파일에 표준 출력 결과를 덧붙인다.

- 2>> fileName: fileName 파일에 표준 에러 출력 결과를 덧붙인다.

- >>& fileName: fileName 파일에 표준 출력과 표준 에러 출력 결과를 덧붙인다.

- >@ fileId: fileId id를 가지는 I/O 스트림에 표준 출력 결과를 쓴다.

- 2>@ fileId: fileId id를 가지는 I/O 스트림에 표준 에러 출력 결과를 쓴다.

- >&@ fileId: fileId id를 가지는 I/O 스트림에 표준 출력과 표준 에러 출력 결과를 쓴다.

- &: 백그라운드로 수행한다. 결과값은 프로세스 ID가 된다.

○ Tcl 쉘 프로그램은 기본적으로 Tcl 명령이 아닌 명령을 수행할 때 UNIX 프로그램으로 인식해 수행하도록 되어 있다.

○ 이러한 기능을 없애려면 다음과 같이 한다.

set auto_noexec anything

 

7.2.  파일 시스템

○ file 명령은 파일의 상태를 알 수 있는 여러 명령을 제공한다.

○ 다음은 file 명령의 여러 형태를 나열한 것이다.

- file atime name: 가장 최근에 파일에 접근한 시간을 10진수의 스트링으로 돌려 준다.

- file dirname name: name 파일이 위치하는 디렉토리 이름을 돌려 준다.

- file executable name: 실행 가능한 파일인 경우 1을 돌려 주고 그렇지 않은 경우 0을 돌려 준다.

- file exists name: 파일이 존재하는 경우 1을 돌려 주고 그렇지 않은 경우 0을 돌려 준다.

- file isdirectory name: name 파일이 디렉토리인 경우 1을 돌려 주고 그렇지 않은 경우 0을 돌려 준다.

- file isfile name: name 파일이 디렉토리, 심볼릭 링크, 장치가 아닌 순수 파일인 경우 1을 돌려주고 그렇지 않으면 0을 돌려 준다.

- file mtime name: 파일의 최근 수정 시간을 돌려 준다.

- file owned name: 현재 사용자가 파일의 소유자이면 1을 돌려 주고 그렇지 않으면 0을 돌려 준다.

- file readable name: 파일의 내용 읽기가 허가되어 있으면 1을 돌려 주고 그렇지 않으면 0을 돌려 준다.

- file readlink name: 심볼링 링크 name의 내용을 돌려 준다.

- file root name: 확장자를 제외한 부분을 돌려 준다.

- file extension name: 확장자 부분만을 돌려 준다.

- file size name: 파일의 크기를 바이트 단위로 계산해 돌려 준다.

- file stat name var: 파일의 상태를 var 배열에 넣는다. var 배열에 정의되는 원소들은 atime, ctime, dev, gid, ino, mode, mtime, nlink, size, type, uid이다.

- file tail name: 파일의 경로를 제외한 파일 이름 부분만 돌려 준다.

- file type name: 파일의 형태를 돌려 준다. 파일의 형태는 file, directorhy, characterSpecial, blockSpecial, fifo, link, socket 중의 하나이다.

- file writable name: 파일 쓰기 허가가 되어 있으면 1을 돌려 주고 그렇지 않으면 0을 돌려 준다.

○ 다음 예제는 두 파일의 수정 시간을 비교하기 위해 file mtime 명령을 사용한다.

proc newer { file1 file2 } {

      if ![file exists $file2] {

              return 1

      } else {

              # assume file1 exists

              expr [file mtime $file] > [file mtime $file2]

      }

}

○ 다음의 makedir 예제는 새로운 디렉토리를 생성할 필요가 있는지 검사하기 위해 file 명령을 사용한다.

○ makedir은 자신을 재귀적으로 호출한다.

proc makedir { pathname } {

      if {[file isdirectory $pathname]} {

              return $pathname

      } else if {[file exists $pathname]} {

              error "Non-directory $pathname already exists."

      } else {

              # Recurse to create intermediate directories

              makedir [file dirname $pathname]

              exec mkdir $pathname

              return $pathname

      }

}

○ 가장 일반적인 file 명령의 옵션은 stat와 lstat이다.

○ stat와 lstat는 세 번째 파라미터로 상태 정보를 받을 배열 변수를 가진다.

○ 다음의 fileeq 예제는 두 파일이 똑같은 file을 가리키는지를 찾는다.

proc fileeq { path1 path2 } {

      file stat $path1 stat1

      file stat $path2 stat2

      expr [$stat1(ino) == $stat2(ino) && \

              $stat1(dev) == $stat2(dev)]

}

 

7.3.  입출력 명령

○ 다음은 입출력을 위한 명령이다.

- open what ?access? ?permissions?: 파일이나 파이프라인에 대한 스트림 ID를 돌려 준다.

- puts ?-nonewline? ?stream? string: 스트링을 쓴다.

- gets stream ?varname?: 한 줄을 읽는다.

- read stream ?numBytes?: numBytes 만큼의 바이트를 읽는다.

- read -nonewline stream: 스트림으로부터 모든 데이터를 읽고 마지막의 newline 문자를 없앤다.

- tell stream: 현재 검색 중인 위치를 돌려 준다.

- seek stream offset ?origin?: 검색 위치를 offset으로 설정한다. origin은 start, current, end 중의 하나이다.

- eof stream: end-of-file 상태를 질의한다.

- flush stream: 스트림의 버퍼을 쓴다.

- close stream: I/O 스트림을 닫는다.

 

7.4.  파일 열기

○ open 명령은 입출력을 위해 파일을 연다.

○ open 명령의 리턴값은 I/O 스트림의 ID이다.

○ open의 결과를 저장해 두었다가 stdout이나 stdin, stderr를 사용했던 부분에 대신 사용하면 된다.

○ open 명령의 기본적인 문법은 다음과 같다.

open what ?access? ?permissions?

○ what 파라미터는 파일이나 파이프라인의 이름이다.

○ access 파라미터는 짧은 문자의 나열로 쓸 수도 있고 POSIX 접근 flag 형태로 쓸 수도 있다.

○ access 파라미터를 짧은 문자 나열 형태로 쓸 때 그 형식은 다음과 같다.

- r: 파일에서 내용을 읽을 수 있도록 파일을 연다. 파일은 존재하는 것이어야 한다.

- r+: 읽기와 쓰기를 위해 파일을 연다. 파일은 존재하는 것이어야 한다.

- w: 파일에 쓰기 위해 연다. 존재하는 파일인 경우는 덮어 쓰고 존재하지 않으면 생성한다.

- w+: 읽기와 쓰기를 위해 파일을 연다. 존재하는 파일인 경우는 덮어 쓰고 존재하지 않으면 생성한다.

- a: 쓰기를 위해 파일을 연다. 파일은 존재하는 것이어야 하며 쓴 데이터는 파일의 뒤에 덧붙여진다.

- a+: 읽기와 쓰기를 위해 파일을 연다. 파일은 존재하는 것이어야 하며 쓴 데이터는 뒤에 덧붙여진다.

○ access 파라미터를 POSIX 플래그 형태로 쓰는 경우 구문은 다음과 같다.

- RDONLY: 읽기를 위해 파일을 연다.

- WRONLY: 쓰기를 위해 파일을 연다.

- RDWR: 읽기와 쓰기를 위해 파일을 연다.

- APPEND: 파일에 덧붙이기 위해 연다.

- CREAT: 파일이 존재하지 않으면 새 파일을 생성한다.

- EXCL: CREAT와 같이 쓰면 파일이 이미 존재하는 것이어서는 안 된다.

- NOCTTY: 터미널 장치가 컨트롤 터미널이 되는 것을 막는다.

- NONBLOCK: 파일을 여는 중에 블록되지 않는다.

- TRUNC: 파일이 존재하면 끝을 자른다.

○ access 파라미터는 기본적으로 read 값을 가진다.

○ permission은 파일이 생성되는 경우에 기본적으로 가질 접근허가 비트를 의미하는 것으로 기본값은 0666이다.

○ permission 비트의 의미에 대해서는 UNIX의 chmod 매뉴얼을 참조하면 된다.

○ 다음 예는 POSIX 형태의 파라미터를 사용해 파일을 여는 예를 보여 준다.

set fileId [open /tmp/bar {RDWR CREAT} ]

○ 보통 파일을 열 때에는 에러 발생 유무를 확인해야 한다.

○ 다음 예제는 catch를 사용해 파일을 열 때에 발생한 오류를 확인한다.

if [catch {open /tmp/data r} fileId] {

      puts stderr "Cannot open /tmp/data: $fileId"

} else {

      # Read and process the file, then ...

      close $fileId

}

○ catch는 에러가 발생한 경우에 1을 돌려 주고 그렇지 않으면 0을 돌려 준다.

○ catch의 두 번째 인자는 결과값을 받을 변수의 이름이다.

○ 파이프라인 문자인 '|'를 첫 번째 인자로 사용해 프로세스 파이프라인을 열 수 있다.

○ 다음 예제는 UNIX의 sort 프로그램을 사용해 password 파일을 소트한 후에 split 명령을 사용해 결과 라인을 리스트 원소로 바꾼다.

set input [open "|sort /etc/passwd" r]

set contents [split [read $input] \n]

close $input

○ r+ 접근 모드를 사용하면 파일에 대한 읽기와 쓰기가 모두 가능하게 열 수 있다.

○ 파이프라인을 사용할 때에는 버퍼에 유의해야 한다.

○ puts 명령이 수행된 후라도 버퍼에는 아직 데이터가 남아 있을 수 있다.

○ flush 명령을 사용하면 버퍼에 남아있는 데이터를 모두 출력한다.

 

7.5.  파일 읽기와 쓰기

○ UNIX의 표준 I/O 스트림은 항상 열려져 있다.

○ 기본적으로 열려 있는 스트림의 이름은 stdin, stdout, stderr이다.

○ 다른 스트림은 open 명령을 사용해 연다.

○ puts 명령은 출력 스트림에 스트링과 newline 문자를 출력한다.

○ -nonewline 옵션을 사용하면 마지막에 newline 문자를 출력하지 않는다.

puts -nonewline "Enter value: "

set answer [gets stdin]

○ gets 명령은 한 줄의 입력을 받는다.

○ gets 명령은 두 가지 형태로 사용할 수 있다.

○ gets의 첫 번째 형태는 위의 예제에서처럼 I/O 스트림으로부터 한 줄을 읽어 그 값을 돌려 주는 것이다.

○ gets는 마지막의 newline 문자를 없애 주며 파일의 끝에 도달한 경우는 빈 스트링을 돌려주게 된다.

○ 빈 줄과 파일의 끝을 구분하기 위해서는 eof 명령을 사용해야 한다.

○ gets 명령에 두 번째 인자를 주게 되면 결과가 두 번째 인자에 쓰여진 변수에 들어가게 되며 리턴값은 스트링의 길이가 된다.

○ 파일의 끝에 다다른 경우는 -1을 돌려 준다.

○ 다음 예제는 파일의 처음부터 끝까지 읽는 루프의 예이다.

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

      #Process line

}

close $stream

○ read 명령은 스트림에서 데이터를 블록 단위로 읽는다.

○ 파일을 블록 단위로 읽으면 수행이 더 효율적이 된다.

○ read 명령도 두 형태로 사용할 수 있다.

○ numBytes 파라미터를 주지 않으며 파일 전체의 내용을 돌려 준다.

○ -nonewline 옵션을 주면 마지막 newline 문자가 지워진다.

○ numBytes 파라미터를 주면 numBytes 만큼의 바이트를 읽는다.

○ numBytes 파라미터와 -nonewline 옵션은 함께 사용할 수 없다.

○ 다음 예제는 read 명령을 사용해 파일의 내용을 읽는 예제이다.

foreach line [split [read $stream] \n] {

      # Process line

}

close $stream

○ 일반적인 크기의 파일에서 gets를 사용하는 것보다 위의 예에서처럼 read 명령을 사용하는 것이 10% 정도 더 빠르다.

○ 위의 예제에서 read는 파일 전체의 내용을 읽고 split 명령이 read를 줄 단위로 쪼개게 된다.

○ seek와 tell 명령을 사용하면 I/O 스트림의 랜덤한 위치를 접근할 수 있다.

○ 각 스트림은 seek offset이라고 불리는 스트림에서의 위치를 유지하고 있다.

○ 파일에서 읽거나 쓰면 읽거나 쓴 바이트만큼이 seek offset에서 증가된다.

○ tell 명령은 현재의 seek offset 값을 돌려 준다.

○ seek 명령은 seek offset 값을 설정한다.

○ close 명령은 I/O 스트림에 연결된 시스템 자원을 모두 해제하므로 중요한 명령이다.

○ 파일을 닫지 않으면 프로세스가 끝날 때 자동으로 닫힌다.

○ 오래동안 수행되는 프로세스에서 파일을 닫지 않으면 운영체제의 자원을 낭비하게 되므로 유의하여야 한다.

 

7.6.  현재 디렉토리- cd와 pwd

○ 모든 UNIX 프로세스는 파일을 찾을 때 기본위치로 삼는 현재 디렉토리라는 것을 가지고 있다.

○ pwd 명령은 현재 디렉토리의 위치를 돌려 준다.

○ cd 명령은 현재 디렉토리를 변경한다.

 

7.7.  glob를 사용해 파일 이름 찾기

○ glob 명령은 패턴을 이용해 파일 이름을 찾는다.

○ glob 명령의 문법은 다음과 같다.

glob ?flags? pattern ?pattern? ...

○ 패턴 파라미터의 문법은 string match 명령의 파라미터와 유사하다.

- *: 0 이상 개의 문자와 대응된다.

- ?: 하나의 문자와 대응된다.

- [abc]: abc 문자 집합과

- {a,b,c}: a,b,c 중의 하나와 대응된다.

- 기타 다른 문자는 같은 문자와 대응된다.

-nocomplain 옵션을 사용하면 일치하는 파일이 없는 경우 빈 스트링을 돌려 준다.

-nocomplain 옵션을 사용하지 않으면 일치하는 파일이 없는 경우 에러를 발생시킨다.

proc FindFile {startDir namePat} {

      set pwd [pwd]

      if [catch {cd $startDir} err] {

              puts stderr $err

              return

      }

      foreach match [glob -nocomplain -- $namePat]{

              puts stdout $startDir/$match

      }

      foreach file [glob -nocomplain *] {

              if [file isdirectory $file] {

                      FindFile $startDir/$file $namePat

              }

      }

      cd $pwd

}

○ 위의 예제에 나온 FindFile 프로시져는 파일 시스템의 하위 디렉토리를 순차적으로 검색해 원하는 파일을 찾는다.

 

7.8.  exit와 pid 명령

○ exit 명령은 스크립트를 종료한다.

○ exit 명령은 스크립트를 수행하던 모든 UNIX 프로세스도 종료시킨다.

○ exit 명령에 정수 인자를 주면 그 인자가 프로세스의 종료 상태가 된다.

○ pid 명령은 현재 프로세스의 프로세스 ID를 돌려 준다.

○ 이 값은 수행할 때마다 변하므로 난수 발생시 seed 값으로 활용할 수 있다.

 

7.9.  환경 변수

○ 환경 변수는 UNIX 프로세스와 연결된 스트링 값을 가지는 변수들이다.

○ 프로세스의 환경 변수는 env 배열을 사용해 접근할 수 있다.

○ 환경 변수의 이름은 배열의 인덱스가 되면 그 값이 환경 변수의 값과 같게 된다.

○ env 배열 값을 바꾸면 그 변경된 값은 환경 변수에도 반영된다.

○ 환경 변수는 자식 프로세스에게 상속된다.

○ 다음 프로그램은 환경 변수의 값을 화면에 출력해 준다.

proc printenv { args } {

      global env

      set maxl 0

      if {[llength $args] == 0} {

              set args [lsort [array names env]]

      }

      foreach x $args {

              if {[string length $x] > $maxl} {

                      set maxl [string length $x]

              }

      }

      incr maxl 2

      foreach x $args {

              puts stdout [format "%*s = %s" $maxl $x $env($x)]

      }

}

printenv USER SHELL TERM

=>

USER = welch

SHELL = /bin/csh

TERM = tx

8.  스크립트 라이브러리

○ 라이브러리는 유용한 프로시져들을 묶어서 다른 응용 프로그램에서 사용할 수 있게 하는 데 사용된다.

○ 커다란 스크립트 응용 프로그램을 만들 때에는 간단한 메인 스크립트를 작성하고 기타 함수는 라이브러리로 만드는 것이 좋다.

○ 이처럼 큰 응용 프로그램을 라이브러리 단위로 쪼개면 각 라이브러리는 필요할 때에만 메모리에 올라오므로 수행 효율을 향상시킬 수 있다.

○ 라이브러리를 사용하는 Tcl 프로그램을 작성할 때에는 코드 변환에 주의하여야 한다.

○ Tcl에는 형식화된 모듈 시스템이 없기 때문에 다른 패키지에서 프로시져와 전역 변수의 충돌을 막기 위해 코드 변환이 필요하다.

 

8.1.  unknown 명령

○ Tcl 라이브러리 기능은 unknown 명령에 의해 사용할 수 있다.

○ Tcl 해석기가 해석할 수 없는 명령을 만나게 되는 경우는 그 명령을 인다로 하여 unknown 명령을 호출하게 된다.

○ unknown 명령은 Tcl로 구현되어 있으므로 해석 불가능한 명령을 해석하는 새로운 unknown 명령을 만들 수 있다.

○ 여기서는 init.tcl 파일에 기본적으로 구현되어 있는 unknown 명령의 동작을 설명한다.

○ 라이브러리의 위치는 info library 명령을 사용해 알 수 있다.

○ 라이브러리 기능을 사용하기 위해 tclsh나 wish는 처음에 시작될 때 다음의 명령을 수행하게 된다.

source [info library]/init.tcl

 

8.2.  tclindex 파일

○ unknown 명령은 존재하지 않는 명령을 빨리 찾기 위해 인덱스를 사용한다.

○ 스크립트 라이브러리를 생성한 후에는 라이브러리에 어떤 프로시져가 등록되어 있는지를 기록한 인덱스를 생성하여야 한다.

○ auto_mkindex 프로시져는 인덱스를 생성하고 tclIndex라는 파일에 그 내용을 기록한다.

○ 만약 본 교재에 소개된 모든 예제가 /usr/local/tcl/examples/라는 디렉토리 밑에 있다면 다음 명령을 수행해 인덱스를 생성할 수 있다.

auto_mkindex /usr/local/tcl/examples *.tcl

○ 다음 예제는 라이브러리 인덱스를 안전하게 생성하는 프로시져이다.

○ 이 프로시져는 인덱스 파일을 처음부터 다시 생성한다.

proc Library_UpdateIndex {libdir} {

      if ![file exists $libdir/tclIndex] {

              set doit 1

      } else {

              set age [file mtime $libdir/tclIndex]

              set doit 0

              # Changes to directory may mean files were deleted

              if {[file mtime $libdir] > $age} {

                      set doit 1

              } else {

                      # Check each file for modification

                      foreach file [glob $libdir/*.tcl] {

                              if {[file mtime $file] > $age} {

                                      set doit 1

                                      break    } } } }

      if {$doit} {

              auto_mkindex $libdir *.tcl     }     }

 

8.3.  라이브러리 사용하기: auto_path

○ 스크립트 라이브러리를 사용하기 위해서는 unknown 명령에게 어디를 찾아 볼것인지를 알려 주어야 한다.

○ unknown 명령은 찾을 디렉토리를 auto_path라는 변수에 넣어 둔다.

○ auto_noloca 명령은 unknown 명령 메커니즘을 취소시킨다.

 

8.4.  자동 로드

○ tclIndex 파일을 살펴 보면 auto_index라는 배열이 정의된 것을 알 수 있다.

○ 각 배열의 한 원소는 스크립트 라이브러리를 정의한다.

○ tclIndex 파일의 줄을 하나 살펴 보면 다음과 같다.

set auto_index(Bind_Interface) "source $dir/bind_ui.tcl"

○ tclIndex 파일을 읽을 때 $dir은 tclIndex 파일을 포함하는 디렉토리 이름으로 대체된다.

9.  Tk 기초

○ Tk는 윈도우 프로그래밍을 위한 툴킷이다.

○ Tk는 X 윈도우 시스템을 위해 디자인되었지만 매킨토시와 MS-Windows에도 포팅되었다.

○ Tk는 widget을 생성하고 관리할 수 있는 Tcl 명령들을 제공한다.

○ widget은 GUI에서 어떤 특정한 모양과 동작을 가지는 윈도우를 지칭한다.

○ widget과 window라는 용어는 자주 혼용되어 사용된다.

○ widget 형태에는 버튼, 스크롤바, 메뉴, 텍스트 윈도우 등이 포함된다.

○ Tk는 또한 일반적으로 그림을 그리는 데 사용되는 캔버스라는 widget을 제공한다.

○ X 윈도우 시스템은 윈도우의 계층적인 구성을 지원하는데, Tk도 이러한 특성을 따른다.

○ 응용 프로그램에서 윈도우의 계층 구조라는 것은 하나의 메인 윈도우가 있으며 그 안에 여러 개의 자식 윈도우가 포함된다는 것을 의미한다.

○ 자식 윈도우도 또한 그 안에 여러 윈도우를 포함할 수 있다.

○ UNIX 파일 시스템에서 디렉토리가 다른 디렉토리와 파일을 포함하는 데 사용되는 것과 마찬가지로 윈도우의 계층 구조에서도 윈도우는 다른 윈도우를 포함하는 데 사용될 수 있다.

○ widget은 형상 관리자(geometry manager)에 의해 그 크기와 위치가 조절된다.

○ 형상 관리자가 widget에 대해 알게 되기 전에는 widget은 화면에 나타나지 않는다.

○ Tk에서는 여러 가지 형상 관리자를 사용할 수 있다.

○ 본 교재에서는 주로 packer 형상 관리자를 사용한다.

○ 형상 관리자에서 잘 사용되는 기법은 프레임 widget을 다른 widget을 포함하는 데 사용하는 것이다.

○ 하나 이상의 widget이 생성되고 프레임 widget에 형상 관리자에 의해 배치된다.

○ Tk를 사용해 만든 프로그램은 이벤트에 의해 동작하게 된다.

○ 이벤트는 bind명령을 통해 Tcl 명령과 연결된다.

○ X 프로토콜에 의해 정의된 이벤트에는 키보드와 마우스 입력을 포함한 여러 가지가 있다.

○ Tk widget은 기본적인 이벤트 처리기를 내장하고 있으므로 모든 사항을 프로그래밍할 필요는 없다.

○ 이벤트 바인딩은 전역 바인딩, 클래스 바인딩, 인스턴스 바인딩으로 나뉜다.

○ 클래스의 예로 모든 버튼을 나타내는 Button을 들 수 있다.

○ Tk 툴킷은 Button 클래스와의 바인딩을 통해 버튼에 대한 동작을 정의하는 기본 바인딩을 제공한다.

○ 프로그래머는 새로운 바인딩 그룹을 만들고 여러 바인딩을 묶을 수 있다. bindtags 명령은 바인딩 그룹을 관리하고 우선 순위를 정한다.

○ 바인딩과 관련된 개념으로 포커스(focus)를 들 수 있다.

○ 어떤 때에도 하나의 widget은 입력 포커스를 가지고 있으며 그 widget으로 키보드 입력이 전달된다.

○ 포커스를 주는 방법에는 두 가지가 있는데, 하나는 마우스 포인터 바로 밑에 있는 widget에게 포커스를 주는 것이며 또 하나는 직접 특정 widget에게 포커스를 주는 것이다.

○ Tk는 포커스를 바꿀 수 있는 명령을 제공한다.

○ Tk 스크립트는 widget을 생성하고 형상 관리자를 사용해 widget들을 배치하며 widget에 대한 action을 정의하는 구조로 이루어진다.

○ wish를 소스 프로그램의 이름 없이 그냥 실행시키면 기본적으로 빈 메인 윈도우를 하나 생성한 후 명령 라인 프롬프트를 보여 준다.

 

9.1.  Hello, World

○ 먼저 간단한 Tk 응용 프로그램을 짜 보도록 한다.

○ 버튼을 누르면 표준 출력에 "Hello, World!"라는 글자를 출력해 준다.

○ Hello, World 스크립트는 버튼을 생성하고 버튼을 배치하는 두 부분으로 구성된다.

○ 그림 10-1은 이 스크립트의 실행 화면을 보여 준다.

 

그림 10-1). Hello World 실행화면

#!/usr/local/bin/wish

button .hello -text Hello \

      -command {puts stdout "Hello, World!"}

pack .hello -padx 20 -pady 10

○ 첫 번째 줄은 다른 UNIX 명령과 똑같은 방식으로 수행하기 위해 필요한 줄이다.

○ 이 줄은 윈도우즈 등의 시스템에서 수행하는 경우는 필요없다.

○ button 명령은 button을 생성한다.

button .hello -text Hello \

      -command {puts stdout "Hello, World!"}

=>.hello

○ 버튼의 이름은 .hello이다.

○ 버튼에 쓰여질 글자는 Hello이다.

○ 버튼과 연결된 명령은 다음 명령이다.

puts stdout "Hello, World!"

○ pack 명령은 버튼을 화면에 보여 준다.

○ Tk는 widget을 생성하고 이름을 붙일 때 객체 기반 시스템을 사용한다.

○ 각 widget 클래스는 그 클래스의 widget의 한 인스턴스를 생성하는 명령과 연결되어 있다.

○ 새로운 widget이 생성되면 새로운 그 인스턴스와 연결될 새로운 Tcl 명령이 정의된다.

○ 위의 예제에서 만든 버튼은 .hello라는 이름을 사용해 접근할 수 있다.

○ .hello 버튼이 잠시동안 깜빡거리게 하고 싶은 경우는 다음 명령을 수행한다.

.hello flash

○ 버튼과 연결된 명령을 수행하기 위해서는 다음 명령을 수행한다.

.hello invoke

=> Hello, World!

 

9.2.  Tk widget에 이름 붙이기

○ 버튼의 이름 앞에 붙은 점 기호는 필요하다.

○ Tk는 widget의 이름이 widget 계층 구조에서의 위치를 반영할 수 있도록 하고 있다.

○ 계층 구조의 루트는 메인 윈도우이며 그 윈도우의 이름은 ‘.’이다.

○ 이것은 UNIX의 파일 시스템에서 ‘/’가 루트 디렉토리를 나타내는 동시에 디렉토리 이름을 구분하기 위해 사용되는 것과 유사하다.

○ Tk는 UNIX 파일 시스템에서의 '/'와 비슷한 용도로 '.'을 사용한다.

○ .hello라는 widget은 메인 윈도우의 자식 윈도우를 나타내는 이름이다.

○ .hello라는 widget의 자식 윈도우를 만들고 싶다면 그 윈도우의 이름은 .hello.hi의 형식으로 하면 될 것이다.

○ Tk 경로명은 항상 소문자나 숫자로 시작해야 한다.

○ 이러한 Tk의 이름 붙이는 방법에는 한 가지 큰 단점이 있다.

○ 단점은 어떤 widget을 widget 계층 구조의 다른 위치로 옮기고 싶은 경우에는 widget의 이름을 바꾸어야 한다는 것이다.

○ 이러한 문제를 해결하기 위해서는 직접 widget의 이름을 사용하지 말고 widget의 이름을 담는 변수를 사용하는 것이 좋다.

 

9.3.  Tk widget의 설정

○ Hello, World! 예제에는 Tk widget의 속성을 바꾸는 방법이 소개되어 있다.

○ widget의 속성 이름은 -로 시작한다.

○ -로 시작하는 속성 이름 다음에 오는 파라미터가 그 속성의 속성값이 된다.

○ 간단한 widget은 10개 내외의 속성을 가지며 복잡한 widget의 경우는 20개 이상의 속성을 가진다.

○ 이러한 속성값에는 기본값이 주어여 있으므로 그 중 필요한 일부의 속성만을 변경하면 된다.

○ 각 widget 인스턴스는 속성을 얻어 내고 바꿀 수 있는 configure(줄여서 config라고도 쓸 수 있음)라는 연산을 지원한다.

○ config 명령의 문법은 widget을 생성할 때 주는 속성과 똑같은 파라미터를 준다.

○ 예를 들어 .hello 버튼의 배경 색을 빨간색으로 바꾸고 싶은 경우는 다음 명령을 수행하면 된다.

.hello config -background red

○ config 연산은 현재의 속성 값을 얻어내는 데에도 사용할 수 있다.

.hello config -background

=> -background background Background #ffe4c4 red

○ 리턴값은 명령행 옵션, 자원의 이름, 클래스 이름, 기본값, 그리고 현재 값이 포함되어 있다.

○ 클래스와 리소스 이름은 X 윈도우의 자원 관리 메커니즘과 관계가 있다.

○ 보통은 현재 값만이 필요하다.

○ 현재의 값만을 얻어내기 위해서는 cget 연산을 수행하면 된다.

.hello cget -background

=> red

○ widget 속성은 언제라도 바꿀 수 있다.

○ 다음 명령은 .hello widget 버튼을 Goodbye! 버튼으로 바꾼다.

.hello config -text Goodbye! -command exit

 

9.4.  Tk widge 속성과 X 자원

○ widget의 속성에 이름을 붙이는 방법에는 명령행 옵션, 이름, 클래스의 세 가지가 있다.

○ 명령행 옵션은 Tcl에서 사용하는 방법이다.

○ 명령행 옵션은 소문자이며 - 기호로 시작한다.

○ 이름과 클래스로 widget 속성에 이름을 붙이는 것은 X 윈도우즈 시스템의 자원 명세서와 관련이 깊다.

○ 속성에 해당하는 자원의 이름은 앞에 - 기호가 붙지 않으며 이름의 중간중간에 대문자가 들어가기도 한다.

○ 자원 클래스는 대문자로 시작하며 중간에 대문자가 들어간다.

○ X에서는 이와 같은 이름 붙이는 방법들을 사용해 미리 정의된 widget 속성에 이름을 붙여 두었으므로 이름 붙이는 법을 잘 알아두는 것이 편리하다.

○ 보통 Tk와 함께 man page가 따라오는 것이 보통이므로 이러한 man page를 참조하는 것이 좋다.

% man button

 

9.5.  Tk 명령 종합

○ 다음은 Tk에서 widget을 만들기 위해 사용하는 명령들이다.

- button: 버튼을 생성한다.

- checkbutton: Tcl 변수와 연결된 토글할 수 있는 버튼을 만든다.

- radiobutton: 여러 개의 radio button 중에서 하나를 택할 수 있다.

- menubutton: 메뉴를 부르는 버튼을 만든다.

- menu: 메뉴를 생성한다.

- canvas: 캔버스를 만든다.

- label: 텍스트 레이블을 만든다.

- entry: 한 줄의 입력을 받을 수 있는 widget을 생성한다.

- message: 읽을 수만 있는 여러 줄의 텍스트 메시지를 생성한다.

- listbox: 리스트 박스를 만든다.

- text: 범용의 텍스트 widget을 만든다.

- scrollbar: 다른 widget과 연결되는 스크롤 바를 만든다.

- scale: 어떤 변수의 값을 설정할 때 사용되는 scale widget을 만든다.

- frame: 형상 관리자와 함께 사용되 다른 widget을 포함하는 데 사용되는 프레임 widget을 만든다.

- toplevel: X 윈도우의 새로운 top-level 프레임을 만든다.

다음은 widget들을 다루는 여러 명령들이다.

- after: 얼마간의 시간이 지난 후에 특정 명령을 수행한다.

- bell: X 벨 장치를 통해 벨을 울린다.

- bind: Tcl 명령을 X 이벤트에 연결한다.

- bindtags: 바인딩 클래스를 생성하고 바인딩 상속을 제어한다.

- clipboard: X 클립보드를 다룬다.

- destroy: widget을 없앤다.

- fileevent: Tcl 명령을 file descriptor와 연결시킨다.

- focus: 입력 포커스를 조절한다.

- grab: 다른 widget으로부터 입력 포커스를 빼앗아 온다.

- image: 이미지를 생성하고 다룬다.

- lower: 윈도우의 스택 순서에서 윈도우를 밑으로 보낸다.

- option: Xresources 데이터베이스에 접근한다.

- pack: 화면에 widget들을 배치해 보여 준다.

- place: 화면에 위치를 주어 widget을 배치한다.

- raise: 윈도우의 스택 순서에서 윈도우를 위로 올린다.

- selection: X PRIMARY selection을 다룬다.

- send: Tcl 명령을 다른 Tk 응용에게 전송한다.

- tk: 응용 프로그램의 이름을 묻거나 설정한다.

- tkerror: 에러를 처리한다.

- tkwait: 어떤 이벤트의 발생을 기다린다.

- update: 이벤트 루프를 통해 화면을 갱신한다.

- winfo: 윈도우의 상태에 대해 질의한다.

- wm: 윈도우 메니저와 상호 정보교환을 한다.

 

 

10.  예제로 배우는 Tk

○ Tk는 쉽고 재미있게 사용자 인터페이스를 만들 수 있게 해 준다.

○ 본 절에서는 Tk로 어떤 일들을 할 수 있는지를 살펴 보는 데 주안점을 둔다.

 

10.1.  ExecLog

○ 첫 번째로 살펴 볼 프로그램은 UNIX 프로그램을 수행할 수 있는 간단한 사용자 인터페이스를 제공한다.

○ 사용자 인터페이스는 'Run it'과 'Quit'의 두 버튼, 명령을 입력 받는 entry widget, 그리고 수행된 프로그램의 로그를 기록하는 텍스트 widget으로 구성된다.

○ 이 스크립트는 프로그램을 파이프라인으로 수행하며 출력을 기다리기 위해 fileevent 명령을 사용한다.

○ 이러한 구조는 프로그램이 수행 중인 경우라도 사용자 인터페이스가 동작하도록 해 준다

○ 그림 10-2와 다음 예제는 ExecLog 프로그램의 실행예와 소스 코드이다.

 

그림 10-2). ExecLog 프로그램의 실행예

#!/usr/local/bin/wish -f

# execlog - run a UNIX program 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 <Return> Run

bind .top.cmd <Control-c> 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"} input] {

              $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

}

 

○ 첫 번째 명령은 윈도우 관리자에 의해 구현되는 윈도우의 타이틀 바를 바꾼다.

wm title .ExecLog

○ wm 명령은 윈도우 관리자와 통신을 한다.

○ 윈도우 관리자는 윈도우를 열고 닫으며 크기를 바꿀 수 있게 해 주는 프로그램이다.

○ 사용자 인터페이스의 최상위 부분에 나타날 widget들을 담기 위해 프레임이 생성된다.

○ 프레임은 widget을 위한 공간을 마련하기 위해 border를 가지고 있다.

frame .top -borderwidth 10

○ 프레임은 메인 윈도우 안에 위치된다.

○ 기본적인 packing 면은 top이므로 -side top 옵션은 빼도 상관없다.

○ -fill x 옵션은 메인 윈도우의 크기만큼 프레임이 커지도록 한다.

○ 여기서는 두 개의 버튼이 생성된다.

○ 하나의 버튼은 프로그램을 수행하는 데 사용되고 또 하나의 버튼은 ExecLog 프로그램을 종료하는 데 사용된다.

○ 각 버튼의 이름은 .top.qui과 .top.run이다.

○ label과 entry도 .top 프레임의 자식으로 생성된다.

○ entry의 크기는 입력받을 수 있는 문자 개수로 정해진다.

○ relief 속성은 화면에 어떻게 보일 것인지를 정한다.

○ label과 entrysms .top 프레임의 왼쪽에 배치된다.

○ entry widget에 대해 지정된 키 바인딩은 Enter 키나 Ctrl-C 키를 눌렀을 때에도 어떤 동작을 취할 수 있게 해줌.○ bind 명령은 Tcl 명령을 어떤 widget의 X event와 연결시킨다.

○ <Return> 이벤트는 사용자가 Enter 키를 눌렀을 때 발생된다.

○ <Control-c> 이벤트는 사용자가 Ctrl-C 키를 눌렀을 때 발생된다.

○ 이벤트가 entry widget으로 가기 위해서는 입력 포커스가 widget에 가 있어야 하므로 focus 명령으로 입력 포커스를 entry widget에 보내 준다.

○ text widget이 생성되고 프레임에 스크롤바와 함께 들어가게 된다.

○ scrollbar은 Tk에서 별도의 widget이며 여기서 사용한 방법과 같이 다른 widget과 연결된다.

○ text widget의 yscrollcommand 옵션은 widget이 수정될 때 scrollbar의 화면 표시를 갱신하게 된다.

○ setgrid 속성을 켜면 메인 윈도우의 크기를 바꿀 수 있게 된다.

○ Run 프로시져와 Log 프로시져, Stop 프로시져는 프로그램을 수행하고 결과를 기록하며 수행을 중지시키는 일을 하는 프로시져이다.

 

10.2.  Tcl Shell

○ 다음은 Tcl Shell 프로그램을 구현해 본 것이다.

#!/usr/local/bin/wish

# Simple evaluator. It executes Tcl in its own interpreter

# and it uses up the following identifiers

# Tk widgets:

#   .eval - the frame around the text log

# Procedures:

#   _Eval - the main eval procedure

# Variables:

#   prompt - the command line prompt

#   _t - holds the ID of the text widget

 

# A frame, scrollbar, and text

frame .eval

set _t [text .eval.t -width 80 -height 20 \

      -yscrollcommand {.eval.s set}]

scrollbar .eval.s -command {.eval.t yview}

pack .eval.s -side left -fill y

pack .eval.t -side right -fill both -expand true

pack .eval -fill both -expand true

 

# Insert the prompt and initialize the limit mark

.eval.t insert insert "Tcl eval log\n"

set prompt "tcl> "

.eval.t insert insert $prompt

.eval.t mark set limit insert

.eval.t mark gravity limit left

focus .eval.t

 

# Key bindings that limit input and eval things

bind .eval.t <Return> { _Eval .eval.t ; break }

bind .eval.t <Any-Key> {

      if [%W compare insert < limit] {

              %W mark set insert end

      }

}

bindtags .eval.t {.eval.t Text all}

 

proc _Eval { t } {

      global prompt _debug

      set command [$t get limit end]

      if [info complete $command] {

              set err [catch {uplevel #0 $command} result]

              $t insert insert \n$result\n

              $t insert insert $prompt

              $t see insert

              $t mark set limit insert

              return

      }

}

○ 위의 프로그램을 실행시키면 그림 10-3과 같은 화면이 나타난다.

 

그림 10-3). Tcl Shell 프로그램의 실행예

11.  Pack 형상 관리자

○ 형상 관리자는 화면에 widget을 배치한다.

○ 형상 관리자에는 여러 가지 종류가 있으며 각 widget마다 다른 종류의 형상 관리자를 사용할 수 있다.

○ 여기서는 주로 pack 형상 관리자에 대해 알아 보도록 한다.

○ 형상 관리자는 부모 widget을 하나 사용하며 그 안에 여러 개의 자식 widget을 배치하게 된다.

○ 부모 widget은 보통 프레임이지만 반드시 그렇지는 않다.

○ 하나의 widget 한번에 하나의 형상 관리자에 의해서만 관리된다.

○ widget이 형상 관리자와 연결되지 않으며 화면에 나타나지 않는다.

○ packer는 강력한 형상 관리자이다.

○ 각 윈도우의 정확한 위치를 상세히 설명하는 대신, 윈도우가 어떤 방식으로 배치되어야 하는지만 알려 주면 packer가 알아서 위치를 찾아 배치하게 된다.

○ 화면에 자신이 원하는 대로 widget들을 배치하기 위해서는 packer가 사용하는 알고리즘을 잘 이해해야만 한다.

○ 본 절에서는 예제를 통해 packer 형상 관리자에 대해 살펴 본다.

○ 본 절에서 소개하는 예제에서 메인 윈도우의 배경은 항상 검은 색이다.

 

11.1.  모서리쪽에 붙이기

○ 다음 예제에서는 두 개의 프레임을 생성한 후 각각을 메인 윈도우의 위쪽에 붙인다.

○ 둘 중 더 위쪽에 배치될 윈도우가 .one이며 그 아래에 .two 윈도우가 배치된다.

○ 모서리 위치는 top, right, bottom, left 중 하나로 지정할 수 있다.

# Make the main window black

. config -bg black

# Create and pack two frames

frame .one -width 40 -height 40 -bg white

frame .two -width 100 -height 50 -bg grey50

pack .one .two -side top

○ 위의 예제를 실행하면 다음과 같은 화면이 만들어진다.

 

그림 10-4). 모서리에 붙이기

 

○ 위의 예제에서 메인 윈도우는 두 개의 자식 윈도우를 담을 만큼의 크기로 줄어들었다.

○ 이러한 기능을 없애려면 pack propagate 명령을 사용한다.

○ pack propagate 명령을 부모 프레임에 사용하면 부모 윈도우는 자식 윈도우의 크기에 맞추어 크기가 변경되지 않는다.

# Make the main window black

. config -bg black

# Create and pack two frames

frame .one -width 40 -height 40 -bg white

frame .two -width 100 -height 50 -bg grey50

pack propagate . false

pack .one .two -side top

○ 위의 예제를 실행하면 그림 10-5와 같은 화면이 만들어진다.

 

그림 10-5). pack propagate 명령의 사용예

 

11.2.  가로 세로 쌓기

○ 일반적으로 프레임 안에서는 가로나 세로로 widget을 쌓을 수 있다.

○ 만약 한 프레임 안의 자식 widget들에 대해 left와 top을 동시에 사용한다면 그 결과는 원하는 것과 다를 수 있다.

○ 이러한 경우는 서로 다르게 쌓고 싶은 widget들을 담을 자식 프레임을 만드는 것이 좋다.

○ 예를 들어 앞의 예제에서 위쪽 프레임에 버튼을 가로로 나열해 보자.

# Make the main window black

. config -bg black

# Create two frames

frame .one -bg white

frame .two -width 100 -height 50 -bg grey50

# Create a row of buttons

foreach b {alpha beta gamma} {

      button .one.$b -text $b

      pack .one.$b -side left

}

pack .one .two -side top

○ 위의 예제를 실행시키면 그림 10-6과 같은 화면이 나타난다.

 

그림 10-6). 버튼을 가로로 나열한 예

○ 이번에는 좀더 복잡한 배치를 위해 프레임을 중복해서 사용하도록 하자.

# Make the main window black

. config -bg black

# Create two frames

frame .one -bg white

frame .two -width 100 -height 50 -bg grey50

# Create a row of buttons

foreach b {alpha beta} {

      button .one.$b -text $b

      pack .one.$b -side left

}                

# Create a frame for two more buttons

frame .one.right

foreach b {delta epsilon} {

      button .one.right.$b -text $b

      pack .one.right.$b -side bottom

}

pack .one.right -side right

pack .one .two -side top

○ 위의 예제를 실행시키면 그림 10-7과 같은 화면이 나온다.

 

그림 10-7). 프레임을 중복사용한 예

 

11.3.  Cavity 모델

○ packing 알고리즘은 프레임 안의 남는 공간에 대해 cavity 모델을 사용한다.

○ 메인 윈도우가 생성될 때 메인 프레임은 비어 있으며 widget을 배치할 자리가 마련되어 있다.

○ widget은 남은 공간의 한 면을 전부 차지한다.

○ 이러한 규칙을 설명하기 위해 다음 예제를 살펴 보자.

# Make the main window black

. config -bg black

# pack two frames on the bottom.

frame .one -width 100 -height 50 -bg grey50

frame .two -width 40 -height 40 -bg white

pack .one .two -side bottom

# pack another frame to the right

frame .three -width 20 -height 20 -bg grey75

pack .three -side right

○ 위의 예제를 실행시키면 그림 10-8과 같은 화면이 나온다.

 

그림 10-8). Cavity 모델

○ 위의 예제에서 .three widget을 오른쪽에 배치하였으나 .two의 아래로는 내려가지 않았다.

○ 그것은 .two widget이 bottom 면에 배치되어 있으므로 가로쪽으로 한 면을 전부 차지하고 있기 때문이다.

 

11.4.  팩킹 공간과 디스플레이 공간

○ packer는 팩킹 공간과 디스플레이 공간을 구분한다.

○ 디스플레이 공간은 widget을 화면에 보여주기 위해 요청되는 구역이다.

○ 팩킹 공간은 widget의 배치를 위해 허용하는 구역이다.

○ 화면 구성상의 특징 때문에 팩킹 공간은 디스플레이 공간보다 더 클 수 있다.

○ -fill 옵션은 디스플레이 공간이 차지하는 공간을 다 채우도록 만든다.

# Make the main window black

. config -bg black

# pack two frames on the bottom.

frame .one -width 100 -height 50 -bg grey50

frame .two -width 40 -height 40 -bg white

# pack with fill enabled

pack .one .two -side bottom -fill x

frame .three -width 20 -height 20 -bg red

pack .three -side right -fill x

○ 위의 예제를 실행시키면 그림 10-9와 같은 화면이 나온다.

 

그림 10-9). fill 옵션의 사용예

○ -fill x 옵션이 사용되면 위와 같이 자신이 차지하는 구역 전부에 꽉 채워져 화면에 나타나게 된다.

○ 이와 같은 fill 옵션은 다음과 같이 메뉴바를 만드는 데 많이 사용된다.

frame .menubar -bg white

frame .body -width 150 -height 50 -bg grey50

# Create buttons at either end of the menubar

foreach b {alpha beta} {

      button .menubar.$b -text $b

}

pack .menubar.alpha -side left

pack .menubar.beta -side right

# Let the menu bar fill along the top

pack .menubar -side top -fill x

pack .body

○ 위의 예제를 실행시키면 그림 10-10과 같은 화면이 나타난다.

 

그림 10-10). 메뉴바에서의 fill 옵션 사용예

○ 좀더 많은 공간을 확보하고 싶을 때에는 x나 y 방향으로 더 많은 공간을 요구하는 -ipadx나 -ipady 옵션을 사용한다.

○ 다음 예제는 위의 예제에 내부 패딩을 추가하였다.

# Create and pack two frames

frame .menubar -bg white

frame .body -width 150 -height 50 -bg grey50

# Create buttons at either end of the menubar

foreach b {alpha beta} {

      button .menubar.$b -text $b

}

pack .menubar.alpha -side left -ipady 10

pack .menubar.beta -side right -ipadx 10

# Let the menu bar fill along the top

pack .menubar -side top -fill x -ipady 5

pack .body

○ 위의 예제를 실행시키면 그림 10-11과 같은 화면이 나타난다.

 

그림 10-11). 내부 패딩을 추가한 예

○ 내부 패딩의 크기를 다르게 했으므로 alpha와 beta 버튼의 크기는 다르다.

○ 버튼은 -padx와 -pady의 또다른 패딩 옵션을 제공한다.

○ -padx와 -pady 옵션은 버튼 안에 들어 가는 글자가 버튼의 경계보다 안 쪽에 들어가게 한다.

# Foo has internal padding from the packer

button .foo -text Foo -anchor e -padx 0 -pady 0

pack .foo -side right -ipadx 10 -ipady 10

# Bar has its own padding

button .bar -text Bar -anchor e -pady 10 -padx 10

pack .bar -side right -ipadx 0 -ipady 0

○ 위의 예제를 실행시키면 그림 10-12와 같은 화면이 나타난다.

 

그림 10-12). 버튼의 패딩옵션 사용예

○ 원래 -padx와 -pady 옵션은 외부 패딩을 정하기 위해 사용된다.

○ 이것은 widget의 바깥 부분에 들어가는 패딩이며 3차원 효과를 나타내는 부분이기도 하다.

. config -borderwidth 10

# OK is the default button

frame .ok -borderwidth 2 -relief sunken

button .ok.b -text OK

pack .ok.b -padx 5 -pady 5

# Cancel is not

button .cancel -text Cancel

pack .ok .cancel -side left -padx 5 -pady 5

○ 위의 예제를 실행시키면 그림 10-13과 같은 화면이 나타난다.

 

그림 10-13). 외부 패딩의 사용 예

 

11.5.  크기 확장과 크기 조절

○ -expand true 옵션은 widget이 자기 자리가 아니지만 비어 있는 곳까지 확장하도록 해 준다.

○ -expand 옵션이 사용되는 더 일반적인 예는 크기를 변경할 수 있는 윈도우에서이다.

○ 윈도우가 더 커지면 새로 만들어진 남는 공간을 사용하라고 widget들에게 말해 주어야 한다.

○ 윈도우의 크기는 사용자가 직접 변경할 수도 있고 프로그램상에서 wm geometry 명령을 사용해 바꿀 수도 있다.

○ 기본적으로 윈도우의 크기는 변경할 수 없게 되어 있다.

○ wm minisize나 wm maxsize 명령은 윈도우의 크기를 바꿀 수 있게 하는 부가적인 효과를 가지고 있다.

# Make the main window black

. config -bg black

# Create and pack two frames

frame .menubar -bg white

frame .body -width 150 -height 50 -bg grey50

# Create buttons at either end of the menubar

foreach b {alpha beta} {

      button .menubar.$b -text $b

}

pack .menubar.alpha -side left

pack .menubar.beta -side right

# Let the menu bar fill along the top

pack .menubar -side top -fill x

pack .body

# Resize the main window to be bigger

wm geometry . 200x100

# Allow interactive resizing

wm minsize . 100 50

○ 위의 예제를 실행시키면 그림 10-14와 같은 화면이 나온다.

 

그림 10-14). interactive resizing이 허용된 윈도우의 예

○ 위의 예제 실행 후에 다음 명령을 실행하면 윈도우는 그림 10-15와 같이 바뀐다.

pack .body -expand true -fill both

 

그림 10-15). expand 사용 예

○ 그러나 하나의 부모 widget 밑에 있는 두 개 이상의 widget이 모두 -expand 옵션을 사용하는 경우는 각 widget이 비례적으로 남은 공간을 나누어 가지게 된다.

○ 위의 pack 명령 대신 다음 명령을 실행하면 그림 10-16과 같이 화면이 바뀐다.

pack .menubar -expand true -fill x

pack .body -expand true -fill both

 

그림 10-16). fill 옵션의 사용예

 

11.6.  Anchoring

○ widget이 디스플레이 공간보다 더 많은 팩킹 공간이 있을 때에는 -anchor 옵션을 사용해 팩킹 공간 상에서의 위치를 지정할 수 있다.

○ 기본 위치는 center이다.

○ 가능한 위치는 n, ne, e, se, s, sw, w, nw이다.

# Make the main window black

. config -bg black

# Create two frames to hold open the cavity

frame .prop -bg white -height 80 -width 20

frame .base -width 120 -height 20 -bg grey50

pack .base -side bottom

# Float a label and the prop in the cavity

label .foo -text Foo

pack .prop .foo -side right -expand true

○ 위의 예제를 실행시키면 그림 10-17과 같은 화면이 나타난다.

 

그림 10-17). widget의 기본 위치

○ .base 프레임은 바닥에 배치된다.

○ .prob와 .foo 레이블은 오른쪽에 배치되지만 fill 옵션은 선택되지 않는다.

○ 그에 따라 .foo는 기본적으로 중앙에 배치되게 된다.

○ 다음 예는 -anchor 옵션을 사용하는 예이다.

# Make the main window black

. config -bg black

# Create two frames to hold open the cavity

frame .prop -bg white -height 80 -width 20

frame .base -width 120 -height 20 -bg grey50

pack .base -side bottom

# Float a label and the prop

# Change their position with anchors

label .foo -text Foo

pack .prop .foo -side right -expand true -anchor sw

pack .foo -side right -expand true -anchor ne

○ 위의 예제를 실행시키면 그림 10-18과 같은 화면이 나타난다.

 

그림 10-18). anchor 옵션의 사용예

 

11.7.  팩킹 순서

○ packer는 프레임에 들어가는 자식 widget들의 순서를 유지하고 있다.

○ 기본적으로 새로 추가된 widget은 가장 마지막 팩킹 순서 뒤에 추가되게 된다.

○ 팩킹 순서의 가장 큰 효과는 가장 높은 팩킹 순서를 가지는 widget이 가장 모서리쪽에 가깝게 배치된다는 것이다.

○ -before나 -after 팩킹 옵션을 사용하면 팩킹 순서를 바꿀 수 있다.

○ 이미 들어간 widget의 팩킹 순서도 바꿀 수 있다.

# Create five labels in order

foreach label {one two three four five} {

      label .$label -text $label

      pack .$label -side left -padx 5

}

# ShuffleUp moves a widget to the beginning of the order

proc ShuffleUp { parent child } {

      set first [lindex [pack slaves $parent] 0]

      pack $child -in $parent -before $first

}

# ShuffleDown moves a widget to the end of the order

proc ShuffleDown { parent child } {

      pack $child -in $parent

}

○ 위의 예제를 실행시키면 그림 10-19와 같은 화면이 나타난다.

 

그림 10-19). 기본 팩킹

○ 여기서 다음 두 명령을 실행시키면 화면이 그림과 같이 바뀐다.

ShuffleUp . .five

ShuffleDown . .three

 

그림 10-20). 팩킹 순서를 바꾼 예

○ pack slaves 명령은 팩킹 순서로 자식 widget의 리스트를 돌려 준다.

○ 위의 예제의 ShuffleUp 명령은 이 pack slaves 명령을 사용한다.

○ 지금까지의 모든 예제에서는 widget이 부모 frame에 들어갔다.

○ 일반적으로 모든 widget은 그 부모의 자손 window에는 어디에나 포함될 수 있다.

○ 예를 들어 .a.b widget은 .a, .a.c, .a.d.e.f 등의 window에 모두 포함될 수 있다.

○ -in 옵션은 다른 부모를 선택하게 하여 준다.

○ pack forget 명령은 포함된 widget을 끄집어 낸다.

12.  X 이벤트와 Tcl 명령의 연결

○ 바인딩은 Tcl 명령을 X 윈도우 시스템의 이벤트와 연결시키는 작업을 말한다.

○ 이벤트에는 다음과 같은 경우에 발생한다.

- 키가 눌려진 경우

- 키에서 손을 뗀 경우

- 마우스 버튼이 눌려진 경우

- 마우스 버튼을 놓은 경우

- 윈도우 안으로 마우스 포인터가 들어가는 경우

- 마우스 포인터가 윈도우 밖으로 빠져 나가는 경우

- 윈도우의 크기가 변경되는 경우

- 윈도우가 열리는 경우

- 윈도우가 닫히는 경우

- 입력 포커스를 얻은 경우

- widget이 없어지는 경우

○ 바인딩은 binding tag들로 정의되며 각 widget은 binding tag들의 집합과 연결된다.

○ 바인딩과 widget이 분리됨으로써 유연하고 강력한 시스템을 구성할 수 있다.

 

12.1.  bind 명령

○ bind 명령은 현재 바인딩에 대한 정보를 돌려 주고 새로운 바인딩을 정의하는 데 사용된다.

○ bind 명령의 문법은 다음과 같다.

bind bindingTag ?eventSequence? ?command?

○ bindingTag는 주로 widget 클래스 이름이거나 widget 인스턴스 이름이다.

○ binding tag는 뒤에서 자세히 설명한다.

○ 하나의 파라미터만 주는 경우 명령 바인딩이 설정된 이벤트들을 돌려 준다.

bind Menubutton

=> <Key-space> <ButtonRelease-1> <B1-Motion> <Motion> <Button-1> <Leave> <Enter>

○ <Button-1>은 왼쪽 마우스 버튼을 클릭하는 경우 발생하는 이벤트이다.

○ <B1-Motion>은 왼쪽 마우스 버튼을 클릭한 채로 마우스를 움직이는 경우 발생하는 이벤트이다.

○ 명령어의 바인딩은 이와 같은 이벤트의 나열에 대해 정의된다.

○ bind 명령에 eventSequence 파라미터를 주면 그 이벤트에 연결된 명령을 보여 준다.

○ 이벤트 바인딩을 위해 사용되는 Tcl 명령은 이벤트 키워드를 위한 몇 가지 추가적인 문법을 제공한다.

○ 이러한 키워드는 '%' 기호로 시작한다.

○ 이러한 키워드들은 이벤트와 관련된 데이터로 대체.

○ %W는 widget의 경로명으로 대치된다.

○ %X와 %Y 키워드는 이벤트 발생 좌표로 대치된다.

○ %는 인용 부호에 상관없이 항상 치환된다.

○ % 기호를 사용하기 위해서는 %%로 사용.

○ 이러한 이유로 바인딩 명령은 프로시져를 호출하는 방법으로 짧게 만드는 것이 좋다.

○ 새로운 바인딩은 연속된 일련의 이벤트와 명령을 줌으로써 정의된다.

bind Menubutton <B1-Motion> {tkMbMotion %W down %X %Y}

○ 바인딩 명령의 처음이 '+' 기호로 시작하면 그 명령은 바인딩 명령에 추가된다.

bind bindingTag event {+ command args}

○ 이벤트에 대한 바인딩을 삭제하기 위해서는 이벤트를 빈 스트링과 연결시킨다.

bind bindingTag event { }

○ 바인드 명령은 실행 전체에 걸쳐 영향을 미친다.

 

12.2.  bindtags 명령

○ 바인딩은 관련 바인딩을 묶는 binding tag와 연결.

○ 각 widget은 binding tag의 집합과 연결되어 있다.

○ bindtags 명령은 widget의 binding tag를 제어한다.

○ bindtags 명령의 문법은 다음과 같다.

bindtags widget ?tagList?

○ 다음 명령은 text widget에 대한 기본 binding tag를 돌려 준다.

bindtags .t     =>    .t Text . all

○ 모든 Tk widget은 기본적으로 다음과 같은 4개의 binding tag를 가지고 있다.

- widget의 Tk 경로명

- widget의 클래스. 클래스 이름은 그 widget을 생성하는 명령으로부터 도출해 낼 수 있다. 예를 들어 버튼 widget의 클래스 이름은 Button이고 캔버스의 클래스 이름은 Canvas이다.

- widget의 맨 꼭대기 윈도우의 Tk 경로명

- 전체 binding tag인 'all'

○ bindtags 명령을 사용하면 binding tag와 그 순서를 바꿀 수 있다.

○ tagList 파라미터는 Tcl 리스트이다.

○ 다음 명령은 $t에 대한 binding tag를 재배치하고 . binding tag를 삭제한다.

bindtags $t [list all Text $t]

○ tag 리스트의 순서는 이벤트가 집합의 둘 이상의 binding tag와 일치되는 경우에 수행할 명령의 순서를 결정한다.

○ Tk widget은 자신이 속한 클래스에 바인딩하는 것을 기본 동작으로 한다.

○ widget의 이름에 이벤트를 바인딩해서 추가적인 바인딩을 추가할 수 있다.

○ 가장 꼭대기의 윈도우에 대한 바인딩은 대화 상자에서 단축키를 처리하기 위해 사용할 수 있다.

○ all binding tag : 全 widget에 해당하는 바인딩을 의미.

○ all에 정의된 기본 바인딩은 widget 간에 입력 포커스를 변경하는 것이다.

frame .one -width 30 -height 30

frame .two -width 30 -height 30

bind Frame <Enter> {%W config -bg red}

bind Frame <Leave> {%W config -bg white}

bind .two <Any-Button> {puts "Button %b at %x %y"}

pack .one .two -side left

bind all <Control-c> {destroy %W}

bind all <Enter> {focus %W}

○ 위의 예제에서 Frame 클래스는 마우스가 들어오면 빨간색으로 변하고 나가면 하얀색으로 변하는 명령과 바인딩되었다.

○ .two widget은 마우스 버튼이 눌려진 경우 그 정보를 화면에 보여주게 하였다.

○ all에 대해 Control-c에 대한 바인딩을 정의하였으므로 모든 widget은 Ctrl-C 키를 누른 경우 사라지게 된다.

 

12.3.  이벤트 표기법

○ 이벤트는 다음과 같은 표기법에 의해 표현된다.

<modifier-modifir-type-detail>

○ 여기서 가장 중요한 부분은 Button이나 Motion 등의 단어가 사용되는 type 부분이다.

○ detail은 키 이름이나 버튼 종류를 지정하기 위해 사용한다. (예를 들어 Key-a, Button-1과 같이)

○ modifier는 이벤트가 발생할 때 이미 눌려져 있는 키를 의미한다.(Control-Key-a 또는 B2-Motion등과 같이)

○ < >로 둘러 싸서 하나의 이벤트임을 나타낸다

○ 다음은 모든 이벤트 type을 나타내고 있다.

- ButtonPress, Button: 마우스 버튼이 눌려졌다.

- ButtonRelease: 마우스 버튼이 떼어졌다.

- Circulate: 윈도우의 쌓이는 순서가 바뀌었다.

- Configure: 윈도우의 크기, 위치, border 또는 쌓이는 순서가 바뀌었다.

- Destroy: 윈도우가 사라졌다.

- Enter: 마우스가 윈도우 안으로 들어왔다.

- Expose: 윈도우가 아이콘 표시에서 원래 화면으로 바뀌었다.

- FocusIn: 윈도우가 입력 포커스를 받았다.

- FocusOut: 윈도우가 입력 포커스를 잃었다.

- Gravity: 부모 윈도우의 크기가 바뀌어 윈도우가 이동되었다.

- KeyPress, Key: 키가 눌려졌다.

- KeyReleas: 키가 떼어졌다.

- Motion: 마우스가 윈도우상에서 움직이고 있다.

- Leave: 마우스가 윈도우를 떠나고 있다.

- Map: 윈도우가 열려졌다.

- Property: 윈도우의 속성이 변경되거나 삭제되었다.

- Reparent: 윈도우가 reparent 되었다.

- Unmap: 윈도우가 아이콘 표시로 바뀌었다.

- Visibility: 윈도우의 보이는 모양이 바뀌었다.

 

12.4.  키보드 이벤트

○ KeyPress 이벤트와 KeyRelease 이벤트는 서로 다른 명령을 수행하게 할 수 있게 하기 위해 구분되어 있다.

○ KeyPress는 줄여서 Key라고 써도 되며 아예 Key도 생략할 수 있다.

○ KeyPress의 경우는 양쪽을 둘러싸는 < > 기호를 생략할 수 있다.

○ 다음은 모두 같은 의미이다.

- <KeyPress-a>

- <Key-a>

- <a>

- a

○ 이 외에도 다음과 같은 특수 키들이 정의되어 있다.

- Return

- Escape

- BackSpace

- Tab

- Up

- Down

- Left

- Right

- comma

- period

- dollar

- asciicircum

- numbersign

- exclam

 

12.5.  마우스 이벤트

○ 버튼 이벤트의 경우도 ButtonPress 이벤트와 ButtonRelease 이벤트가 분리되어 있다.

○ 다음은 모두 같은 의미이다.

- <ButtonPress-1>

- <Button-1>

- <1>

○ 숫자키 1이 눌린 이벤트인 경우는 <1>이라고 표기하면 안되고 <KeyPress-1>, <Key-1> 또는 그냥 1이라고 표기하여야 한다.

○ 마우스의 좌표는 %x와 %y 키워드로 얻음.

○ 이렇게 얻은 마우스좌표는 widget에서의 상대 좌표.

○ %X와 %Y는 화면상에서의 절대 좌표를 돌려 준다.

 

12.6.  지정자(modifier)

○ 지정자는 이벤트가 발생한 시점에 다른 키나 버튼이 눌려진 상태임을 나타낸다.

○ 전형적인 지정자로 Shift와 Control 키를 들수있다.

○ 예를 들어 Shift와 Control 키를 동시에 누르고 c 키를 눌렀을 때 발생하는 이벤트는 <Shift-Control-Key-c>로 표시할 수 있다.

○ 다음은 주로 사용되는 지정자들이다.

- Control: Control 키

- Shift: Shift 키

- Lock: caps-lock 키

- Meta: 메타 키

- Alt: Alt 키

- Button1(B1), Button2(B2), ... Button5(B5): 1(2,3...5)번째 마우스 버튼

- Double: 더블 클릭

- Triple: 세 번 연속으로 클릭

- Any: 지정자의 어떤 조합도 허용

 

12.7.  이벤트의 나열

○ bind 명령은 연속적으로 발생하는 둘 이상의 이벤트에 대해서도 명령을 지정할 수 있는 기능을 제공.

bind . a {puts stdout A}

bind . abc {puts stdout C}

bind .foo <Control-x><Control-s> {Save; break}

 

12.8.  이벤트 키워드

○ 다음은 바인딩에서 사용할 수 있는 키워드들이다.

- %%: % 기호

- %#: 이벤트의 일련 번호

- %a: 이벤트의 above 필드

- %b: 버튼 번호

- %c: count 필드

- %d: detail 필드

- %f: 포커스 필드

- %h: height 필드

- %k: keycode 필드

- %m: mode 필드

- %o: override_redirect 필드

- %p: place 필드

- %s: state 필드

- %t: time 필드

- %v: value_mask 필드

- %w: width 필드

- %x: x 상대 좌표

- %y: y 상대 좌표

- %A: 출력되는 문자

- %B: border 필드

- %E: send_event 필드

- %K: 이벤트의 keysim

- %N: 10진수로 표현된 keysim

- %R: 루트 윈도우 ID

- %S: sub 윈도우 ID

- %T: type 필드

- %W: widget의 경로명

- %X: x_root 필드

- %Y: y_root 필드

 

▶ 출처:  홍익대학교


▶ 원문: http://www.cs.hongik.ac.kr/~kimyj/research/gaia/tcltk.hwp