Tcl/Tk 문법 - 2부 Tk

Development 2008. 6. 9. 09:39
출처 : http://terzeron.net/wp/?p=245

1999/07/16

1장 소개

1) X Window System과 Tk의 소개

X 윈도우 시스템은 MIT와 DEC가 공동으로 개발한, 하드웨어와 OS에 독립적인 윈도우 시스템이다. 하드웨어와 OS에 독립적일 수 있는 이유는, X 클라이언트와 X 서버로 각각의 기능이 분리되어 있기 때문이다. X 서버는 장치구동기(device driver)를 통해 하드웨어와 OS를 제어하고, X 클라이언트는 X 프로토콜을 이용해 X 서버에게 윈도우 작업을 요청하게 되는 것이다. X 프로토콜은 사람이 이해하기 어렵기 때문에, Xlib을 이용하여 C로 X 윈도우 프로그래밍을 할 수 있도록 지원하고 있다.

그러나 Xlib조차 프로그래밍하기가 쉽지 않다. 아주 간단한 윈도우를 하나 만드려고 해도 프로그래머가 지정해야 할 것들이 너무 많기 때문이다. 그래서 이미 Xlib 수준에서 구현된 위짓(widget)을 재사용하게 된다. 이것을 바로 툴킷(Toolkit)이라고 한다. 툴킷에도 여러 단계가 있어서 가장 하위 레벨은 X Toolkit Intrinsics이다. 이것은 여러 X 응용프로그램에서 자주 사용되는, 아주 핵심적인 위짓을 미리 만들어놓은-정의해놓은-라이브러리라고 할 수 있다. Xt라고 불리우는 X Toolkit Intrinsics 이외에, 좀 더 세련된 위짓들의 집합을 제공하는 것으로는 Motif와 Qt, Gtk 등이 있다.

Tk는 일반적인 뜻으로는 툴킷(Toolkit)을 의미한다. 그러나 여기서 말하는 Tcl/Tk는 Tcl에서 사용하는 툴킷으로 그 의미가 한정된다. 우리가 이제부터 살펴볼 것은 Tcl에서 윈도우를 어떻게 생성하고 다룰 것인가에 관한 것이다. Tk를 중심으로 Tcl/Tk를 익혀보기 위해서는 wish(windowing shell)가 필요하다. 1부에서 밝힌대로 Tcl/Tk를 설치했다면 wish가 존재할 것이다. /usr/X11R6/bin이나 /usr/openwin/bin 등의 X 응용프로그램 디렉토리에서 찾아서 실행해보자. 제대로 설치가 되어 있다면 다음 명령을 수행했을 때, 버튼 위에 쓰여진 환영 인사를 만날 수 있을 것이다.


button .b -text "Hello, world!"
pack .b

pack은 사용자가 만든 위짓 인스턴스를 화면에 디스플레이하는 명령이다.

2) X resource

X 윈도우 시스템에서 폰트나 색상, 사이즈와 같이 위짓의 속성을 바꿀 수 있는 변수들을 X resource라고 한다. 리소스(resource)는 X 클라이언트가 요청하면 X 서버가 할당해주는 형태로 되어 있다. X 윈도우 시스템에는 여러가지 위짓이 존재하고, 각각의 위짓은 또 다양한 리소스를 필요로 하게 되므로 상당히 많은 종류의 리소스가 존재한다. 그러나 여기서는 Tcl/Tk에서 중요하게 다루는 몇 가지 리소스에 대해 짧게 살펴보고 지나갈 것이다. X 윈도우 프로그래밍에 관심있는 독자라면 Xlib 프로그래밍이나 X 툴킷 프로그래밍에 관련된 책을 읽어보는 것이 좋겠다.

우선 가장 많이 사용되는 것은 색상이다. 아주 간단한 RGB방식으로 빨간색, 파란색, 녹색을 1 바이트(0에서 255까지)씩 빛을 혼합하는 것이다. 예를 들면 흰색은 #ffffff이고, 검정색은 #000000이다. 빨간색은 #ff0000이고, 녹색은 #0000ff이다. 보라색은 #ffff00이 되겠다. 그러나 프로그래머가 매번 광원의 색을 혼합하여 색상을 만들어내는 것은 번거로운 일이 될 것이다. X 윈도우 시스템에서는 showrgb라는 명령으로 이미 지정되어 있는 색상의 이름을 확인할 수 있다. k에서는 이렇게 정의되어 있는 색상의 이름을 그대로 사용할 수 있다.

다음으로 많이 사용하는 리소스는 폰트이다. 폰트는 XLFD(X Logical Font Description)을 따라서 그 이름을 지정하면 되는데, 복잡한 설명보다는 xlsfonts를 이용하여 폰트 리스트에서 마음에 드는 폰트명을 골라서 사용하면 된다. 그러나 폰트명만 가지고는 폰트의 생김새를 알 수 없으므로, xfontsel이라는 프로그램으로 각각의 폰트가 어떻게 생겼는지 확인하는 것이 좋겠다. 어떻게 사용하는지는 차차 살펴보기로 하자.

2장 Tk Widget

Tk 위짓은 Motif의 스타일을 제공하는 위짓이다. 각각의 위짓은 클래스(class)와 인스턴스(instance)로 그 개념을 나누어 볼 수 있다. 클래스는 특정 위짓의 종류를 지칭하고, 인스턴스는 그 위짓 클래스에 속하는 위짓을 하나 만들어 이름을 붙여준 것을 가리킨다. 위짓 인스턴스는 계층을 이루어서 하나의 윈도우를 구성하게 된다. Xterm을 예로 보면, Xterm은 왼쪽이나 오른쪽에 스크롤바(scrollbar)를 가지고 있고, 나머지 부분에 텍스트입력창이 존재한다. 이런 경우에는 최상위의 윈도우 아래에 스크롤바와 텍스트입력창이 존재하는 계층구조라고 할 수 있을 것이다. 메뉴(menu)를 가지고 있는 X 응용프로그램이라면 메뉴 안에 메뉴 아이템이 존재하고, 그 아이템이 다시 메시지창을 가진다든가 해서 아주 복잡한 계층구조를 형성할 것이다.

Tk에서 제공하는 위짓에는 프레임(frame), 레이블(label), 버튼(button), 체크버튼(checkbutton), 라디오버튼(radiobutton), 메시지(message), 리스트박스(listbox), 스크롤바(scrollbar), 스케일(scale), 엔트리(entry), 메뉴(menu), 메뉴버튼(menubutton) 등이 있다.

특별한 위짓으로는 top-level 위짓이 있는데, 이것은 “.”(dot)으로 표현된다. “.”을 쓰면 최상위의 위짓이고, “.a”라 하면 top-level 위짓 바로 아래 레벨에 위치하는 위짓 인스턴스 a가 되는 것이다.

1) 프레임(frame)

프레임은 위짓을 그룹으로 묶을 때 사용하는 위짓이다. 나중에 살펴 볼 pack 명령만으로는 위짓들을 관리하기가 어려워서 프레임을 이용하여 위짓을 그룹단위로 사용하게 된다. 프레임은 -relief 옵션을 주어 5가지의 형태로 만들 수 있는데, 다음의 예는 각각의 형태를 한 윈도우에 모아서 보여주는 것이다.


foreach re { raised sunken flat groove ridge } {
frame .$re -width 15m -height 10m -relief $re -bg gray40 -borderwidth 4
pack .$re -side left -padx 2m -pady 2m
}

[그림 1]

2) 버튼(button)

버튼은 마우스 버튼을 누르면 움푹하게 들어가고, 손가락을 떼면 다시 올라오는 위짓이다. 가장 흔히 만날 수 있는 위짓이 바로 버튼이다. 버튼도 프레임과 마찬가지로 -relief 옵션을 이용하여 5가지의 형태로 만들 수 있다.

다음은 버튼 2개를 하나의 프레임 안에 넣는 예이다. “.f.b1″ 또는 “.f.b2″는 f라는 프레임 인스턴스 아래에 두 개의 버튼 인스턴스가 존재한다는 의미이다. 물론 위에서 설명한 바와 같이, 프레임 f는 top-level 위짓 바로 아래 레벨에 위치하는 프레엠 위짓 클래스의 f라는 이름을 가진 인스턴스가 되는 것이다.


frame .f
button .f.b1 -text button1
button .f.b2 -text button2
pack .f.b1 .f.b2

아무 것도 나오지 않는다. 이상할 것은 없다. 바로 프레임 f를 pack하지 않았기 때문이다. pack에 대해서는 뒤에서 자세히 다루어 보도록 한다. 다음 명령을 추가하면 버튼 2개가 나타날 것이다.


pack .f

3) 체크버튼(checkbutton)과 라디오버튼(radiobutton), 리스트박스(listbox)

체크버튼은 여러 개의 버튼을 눌러서 동시에 여러가지를 선택할 수 있게 하는 버튼이다. 반면에 라디오버튼은 라디오에서 채널을 맞추듯 한 번에 단 하나만을 선택할 수 있게 하는 버튼이다. 체크버튼과 라디오버튼은 그 버튼이 체크되는 순간에 특정 변수에 특정 값을 대입할 수 있도록 -variable, -value 옵션을 제공한다.(-value 옵션은 라디오버튼에서만 사용된다.) 리스트박스는 체크버튼을 확장해 놓은 것으로 생각할 수 있는 위짓으로 하나 이상의 아이템을 선택할 수 있다.


checkbutton .red -text Red -variable color_red
checkbutton .green -text Green -variable color_green
checkbutton .blue -text Blue -variable color_blue
pack .red .green .blue -side top -fill x

radiobutton .red -text Red -variable color -value red
radiobutton .green -text Green -variable color -value green
radiobutton .blue -text Blue -variable color -value blue
pack .red .green .blue -side top -fill x<
[그림 2]
[그림 3]

위의 두 예제는 체크버튼과 라디오버튼이 어떻게 다른가를 알려주는 예이다. 체크버튼의 경우에, 몇 개를 선택하고서 puts $color_red $color_green $color_blue를 실행하여 각각의 값을 확인해 보자. 체크되어 있는 색상의 변수에는 이, 아닌 경우에는 0이 대입되어 있는 것을 알 수 있다. 라디오버튼의 예제는 색상을 선택한 후에, puts $color 해 보면 값이 설정되어 있는 것을 확인할 수 있다.


listbox .name -selectmode browse
pack .name
set f [open /etc/passwd]
while {[gets $f line] >= 0} {
set item [lindex [split $line :] 0]
.name insert end “$item”
}
close $f

[그림 4]

이 예제를 -selectmode 옵션의 값을 single, multiple, extended로 바꾸어가며 테스트해보자. 그러면 마우스의 바인딩(binding)이 어떻게 바뀌는지 알 수 있다. 예를 들면, extended로 바꾸어 놓고 실행하면 마우스의 첫번째 버튼을 누른 채로 드래그할 때 해당 아이템들 모두를 선택할 수 있다.

4) 메시지(message)

메시지는 길이가 긴 텍스트를 보여주는 메시지 창을 의미한다. 다음의 예는 오른쪽 정렬로 텍스트를 보여준다. 폰트를 지정하고, 배경색과 전경색을 지정하는 방법을 눈여겨 보아두자.


message .m -justify right -font "-*-helvetica-*-*-*--*-120-*" -bg white -fg black -text "Message widgets are similar to labels except that they display multiline strings."
pack .m

[그림 5]

5) 스크롤바(scrollbar) & 스케일(scale)

스크롤바는 대개 수직으로 세워져 있는 위짓인 반면에, 스케일은 수평으로 눕혀져 있는 위짓이다. 물론 각각의 위짓은 옵션을 이용하여 그 모양을 반대로 바꿀 수 있다. 스크롤바는 많은 아이템을 검색할 때 사용되고, 스케일은 슬라이더(slider)를 이용하여 정수값을 변화시킬 때 사용된다.


listbox .li -yscrollcommand ".sc set"
scrollbar .sc -command ".li yview"
pack .li -side left
pack .sc -side right -fill y
foreach it [lsort [glob *]] {
.li insert end $it
}

[그림 6]

스크롤바를 상하로 움직이면 리스트박스 인스턴스인 .li에 yview명령을 수행하게 되어 리스트박스가 상하로 움직이게 되는 것이다.

scale .percent -from 0 -to 100 -orient horizontal -command showvalue
pack .percent
proc showvalue parameter {
puts [.percent get]
}

[그림 7]

이 예는 슬라이더를 움직이면 그 해당 정수값을 출력하는 것이다. horizontal을 vertical로 바꾸면 슬라이더가 수직으로 나타날 것이다. 스케일에 -command 옵션으로 값에 변화가 있을 때마다 프러시져를 호출할 수 있다. 잘 살펴보면 프러시져 안에서 스케일의 현재 값을 얻기 위해 .percent get하고 있음을 알 수 있다.

6) 엔트리(entry)

엔트리는 텍스트 입력창이다. 엔트리에 텍스트를 입력한 후에 puts $var로 변수의 값을 확인해보자.


entry .e -relief groove -textvariable var
pack .e

7) 메뉴(menu)와 메뉴버튼(menubutton)

메뉴에는 체크버튼, 라디오버튼, 메뉴버튼, 명령(command), 구분선(separator) 등을 아이템으로 가질 수 있다. 체크버튼과 라디오버튼은 위에서 설명한 바와 같은 방식으로 작동하고, 명령은 프러시져를 호출하기 위해 사용하며, 구분선은 아이템을 시각적으로 그룹화하기 위해 사용된다.


menubutton .mb -text "Menu" -menu .mb.m
pack .mb
menu .mb.m
.mb.m add radiobutton -label Red -variable red
.mb.m add radiobutton -label Blue -variable blue
.mb.m add separator
.mb.m add command -label "Hello" -command hello
proc hello {} {
puts "Hello, world!"
}

다만 메뉴 자체는 pack 명령을 사용하지 않는다. 메뉴가 속해있는 다른 위짓이 그 작업을 대신 수행해준다. 여기서는 메뉴버튼을 pack해서 화면에 디스플레이하고, menu인 .m을 연결시킨다.

메뉴의 한 항목이 다시 메뉴를 부르는 것을 캐스케이딩 메뉴(cascading menu)라고 하는데, 이것은 add하는 항목에 cascade라고 쓰고 -menu option을 이용하여 새로운 메뉴를 연결시키면 가능하다.


.mb.m add cascade -labe "Another menu" -menu .mb.cm
menu .mb.cm
.mb.cm add radiobutton -label Green -variable green

메뉴바를 만들고 거기서 다시 서브메뉴를 만드는 경우가 있는데, 이런 경우에는 우선 메뉴바를 하나 만들고, 메뉴바에 메뉴버튼을 몇 개 붙인 후, 각각의 버튼에 메뉴를 연결하는 것이다. 메뉴바는 메뉴버튼을 그룹으로 만드는 것이므로 프레임으로 선언한다.


frame .menubar -relief raised
menubutton .menubar.file -text File -menu .menubar.file.menu
menubutton .menubar.edit -text Edit -menu .menubar.edit.menu
pack .menubar -fill x -expand true
pack .menubar.file .menubar.edit -side left

menu .menubar.file.menu
.menubar.file.menu add command -label “Open”
.menubar.file.menu add command -label “Save”
.menubar.file.menu add command -label “Exit”
File과 Edit 메뉴인 .menubar.file.menu와 .menubar.edit.menu는 위에서 설명했던 대로 사용자가 만들면 된다. 중요한 것은 메뉴는 pack하지 않고, 메뉴를 연결하는 위짓(여기서는 메뉴바와 메뉴버튼)은 반드시 pack해야 한다는 것이다. 메뉴의 단축키를 지정하거나 특정 문자에 밑줄을 긋기 위해서는 -accelerator와 -underline 옵션을 사용하면 된다.


.menubar.file.menu add command -label "Open" -underline 0 -accelerator "Ctrl+O" -command open
proc open {} {
puts "open"
}

이 예에서 -underline 옵션의 값이 0인데, 이것은 Open이라는 label의 첫글자 O에 밑줄을 긋겠다는 의미이다. 단축키는 Ctrl+O로 지정했지만, 실제로 Ctrl+O를 누른다고 해서 그 항목이 선택되지는 않는다. 단축키와 그 항목을 연결하기 위해서는 bind 명령을 사용해야 한다. 이것은 뒤에서 다루기로 한다.

3장 Pack

pack 명령을 이용하면 위짓들의 위치를 조정할 수 있다. pack 명령에서 사용되는 옵션에는 -after, -anchor, -before, -expand, -fill, -in, -ipadx, -ipady, -padx, -pady, -side 등이 있는데, 가장 일반적으로 쓰는 옵션은 -side라고 할 수 있다. -side 옵션의 값으로 줄 수 있는 것은 left, right, top, bottom이 있다.


button .a -text a
button .b -text b
button .c -text c

세 개의 버튼을 생성한 후에, 다음의 두 명령을 각각 실행하여 위짓들의 배치를 확인해보자.

pack .a .b .c -side left
pack .a .b .c -side top

[그림 8]
[그림 9]

위짓들을 배치하다보면 가끔 상하나 좌우로 조금씩 공간이 생기는데, 이것을 없애주려면 -fill 옵션을 사용하면 된다. -fill 옵션에는 x, y, both로 각각 값을 줄 수 있다.


button .a -text aaaaaaaa
button .b -text b
button .c -text cccc
pack .a .b .c -fill x

-fill 옵션없이 pack 명령을 수행한 것과 결과가 어떻게 다른지 비교해보자. -fill 옵션말고도 빈 공간을 채우는 방법으로 -expand 옵션을 사용하는 것이 있는데, -expand는 모든 위짓이 함께 공간을 채우는 것이 아니라, 특정 위짓에게 남은 공간을 모두 할당해준다. 그러나 -fill 옵션처럼 그 위짓이 빈 공간을 모두 채우는 것이 아니라, 잠재적으로 그 위짓에게 공간을 사용할 권리가 부여된 것이다. -fill 옵션을 함께 사용하여 빈 공간을 채울 수 있다. 다음의 예제를 실행하고 윈도우 크기를 늘여보자.


button .a -text aaaaaaaa
button .b -text b
button .c -text cccc
pack .a .b .c -side left

[그림 10]

오른쪽에 빈 공간이 생긴다. 이제 다음 각각의 pack 명령을 수행하여 -expand 옵션이 어떤 효과를 내는지 살펴보도록 하자.


pack .a .b .c -expand 1
pack .a .b -side left -expand 0
pack .c -expand 1 -fill x

[그림 11]
[그림 12]
[그림 13]

이제 위짓들을 정렬하는 방법에 대해 알아보자. 정렬을 위해서는 -anchor 옵션을 사용할 수 있는데, 옵션의 값으로 center(중앙), n(북), ne(북동), e(동), se(남동), s(남), sw(남서), w(서), nw(북서)의 방향값을 줄 수 있다.

pack .a -anchor nw

명령을 수행한 후, 윈도우의 크기를 늘여서 위짓이 어떻게 정렬되어 있는지 확인해보자.

-padx, -pady 옵션은 위짓과 다른 위짓(또는 윈도우 경계) 사이의 간격을 조정할 때 사용하고, -ipadx, -ipady 옵션은 위짓 내부의 텍스트나 이미지와 위짓 경계와의 내부 간격을 조정할 때 사용하는 옵션이다.

pack .a .b .c -padx 2m -pady 3m -ipadx 2m -ipady 2m

4장 이벤트(event)와 액션(action)

다음 표는 이벤트와 그 이벤트의 의미이다.

Button 마우스 버튼이 눌러졌음
ButtonPress 마우스 버튼이 눌러졌음
ButtonRelease 마우스 버튼이 눌러졌다가 다시 놓아졌음
Key 키가 눌러졌음
KeyPress 키가 눌러졌음
KeyRelease 키가 눌러졌다가 다시 놓아졌음
Enter 마우스 포인터가 윈도우 안으로 들어왔음
Leave 마우스 포인터가 윈도우 바깥으로 나갔음
Motion 마우스가 윈도우 내에서 움직임
Expose 윈도우가 다시 그려짐
Map 윈도우가 화면에 나타남
Unmap 윈도우가 화면에서 없어짐
FocusIn 키보드 포커스가 윈도우 안으로 들어왔음
FocusOut 키보드 포커스가 윈도우 바깥으로 나갔음
Gravity 윈도우의 gravity가 변화했음
Circulate 윈도우가 쌓여있는 순서가 변했음
Configure 윈도우의 위치가 크기가 변했음
Destroy 윈도우가 제거되었음
Property X 윈도우 속성이 윈도우에 쓰여졌음
Visibility 윈도우의 visibility가 변했음
Reparent 윈도우 매니저에 의해 윈도우의 parent가 바뀌었음
Colormap 윈도우의 컬러맵이 변화했음

일반적인 X 윈도우 시스템의 이벤트는 위와 같고, 마우스의 첫번째 버튼이 눌러진다든가 하는 특정한 이벤트는 ButtonPress-1과 같이 지정할 수 있다. 키보드의 경우, Control-1, Shift-M, Alt-V, Mod5와 같이 특정 키를 가리킬 수 있다.

이벤트에 액션을 연결시키는 명령은 bind 명령인데, 이벤트가 발생하는 범위를 지정할 수 있다.


bind all {puts "Entering %W at (%x, %y)"}

all 대신에 위짓의 이름을 쓰면 그 위짓 내부에서만 이벤트와 액션을 연결하는 바인딩(binding)이 효과를 가지게 된다. %W는 이벤트가 발생한 윈도우의 이름이고, %x, %y는 xy좌표이다. %K는 키 이벤트의 이름이고, %A는 키 이벤트를 ASCII코드로 바꾸어 주는 변수이다. 윈도우의 너비와 높이를 알려주는 것으로 %w, %h가 있고, 이벤트 종류를 알려주는 %T를 사용할 수도 있다.

5장 캔버스(canvas) 위짓

캔버스(canvas)는 그림을 그릴 수 있는 위짓이다. 캔버스에 그릴 수 있는 것으로는, line(선), oval(원과 타원), rectangle(사각형), polygon(다각형), arc(원호), text(텍스트), bitmap(비트맵), image(이미지), window 등이 있다.


canvas .can -width 300 -height 200
pack .can
.can create arc 130 40 50 120

이것은 (130, 40)부터 (50, 120)에 걸쳐 원호를 그리는 것이다. -fiil 옵션으로 내부를 특정 색으로 채울 수도 있다.


.can create bitmap 200 20 -bitmap error
.can create image 100 100 -image earth.gif

이 예제들은 비트맵과 일반 이미지를 그리는 것인데, -bitmap이나 -image 옵션의 값은 비트맵 파일의 이름이나 이미지의 이름이다. error의 경우에는 Tk에서 이미 정의된 비트맵인데, 이밖에도 gray25, gray50, hourglass, info, questhead, question, warning 등이 있다.

이미지를 다루기 위해서는 image를 먼저 만드는 과정이 필요하다. image라는 명령을 사용하는 것인데, 다음의 예를 참고하자.


image create photo x -file earth.gif
label .y -image x
pack .y

[그림 14]

x라는 이름으로 earth.gif를 이미지로 만들고, label에다가 얹는다. 버튼 위에도 얹을 수 있다. 그러면 이미지가 있는 버튼을 만들 수 있는 것이다. Tk에서는 GIF와 PPM/PGM 포맷의 image를 사용할 수 있다.


.can create line 100 120 150 130 -arrow first

다시 캔버스로 돌아가서 선을 그려보자. -arrow 옵션의 값으로는 last와 both를 줄 수도 있는데, 화살표모양을 어디에 달 것인가를 결정하는 것이다. 좌표를 더 많이 주고 -smooth 옵션의 값을 1로 주면 베지어 곡선(Bezier curve)을 그릴 수 있다.


.can create line 230 140 150 60 100 110 120 30 -smooth 1

원이나 타원의 경우 oval 오브젝트를 만들면 되는데, 원이나 타원이 들어갈 사각형의 두 대각점을 지정하면 된다. 사각형을 만드는 방법도 마찬가지이다. 다각형의 경우, 좌표를 나열하면 다각형을 그려준다.


.can create polygon 30 50 60 40 20 100 120 180 -fill orange

위짓을 넣기 위해 윈도우를 만들어 본다. 캔버스내에 윈도우의 위치가 정해지므로 위짓을 pack하지는 않는다.


button .can.b -text hello -relief raised
.can create window 100 100 -window .can.b

캔버스에 그림을 다 그렸으면 출력을 해야 하는 경우가 있다. postscipt 명령을 주면 캔버스의 그림을 PS형식의 파일로 저장한다.


.can postscript -colormode color -file result

그러나 필자가 실험해 본 결과로는, 안타깝게도 버튼 위짓이 출력되지 않았다.

[그림 14.5]

6장 텍스트 위짓(text widget)

텍스트 위짓은 텍스트를 보여주기 위한 위짓이다. 다른 위짓도 크기가 커지면 스크롤바를 달아주어야 하는데, 텍스트 위짓은 텍스트의 분량이 많을 것으로 예상을 하고 미리 스크롤바를 달아주는 것이 좋다.


text .t -yscrollcommand ".sc set"
entry .e -textvariable var
scrollbar .sc -command ".t yview"
pack .sc -side right -fill y
pack .t .e -side top -expand 1 -fill x

이제 텍스트 위짓에 파일의 내용을 올리도록 한다. 이것은 리스트박스에서 했던 것과 비슷한 작업이다. 리스트박스의 예제를 참고하도록 하자.


proc print filename {
set f [open $filename]
while {![eof $f]} {
.t insert end [read $f 100]
}
close $f
}
print /etc/passwd

텍스트와 관련하여 텍스트의 일부를 잘라 내어, 복사하고 붙이는 작업을 할 수 있는데, 이렇게 잘라 내거나 복사하려는 텍스트는 클립보드(clipboard)나 selection 메카니즘을 이용하게 된다. selection은 X 응용프로그램간의 텍스트의 저장을 Tcl에서 지원하게 위해 제공되는 것이고, 클립보드는 Tk에서 사용하는 데이터의 저장공간이다. selection은 화면 상에서 사용자에게 보이지만, 클립보드는 보이지 않는다. 위의 예제를 실행하고 마우스로 텍스트를 긁어서 엔트리 위짓에 복사해보자. 텍스트를 선택하게 되면 selection에 자료가 복사되는데, 다음과 같은 명령으로 확인할 수 있다.


selection get

클립보드에 자료를 복사하고 꺼내는 것은 다음과 같다.


clipboard append "Hello, world!"
selection get -selection CLIPBOARD

selection이나 클립보드에 저장된 자료를 제거하는 것은 get이나 append 대신 clear를 사용하면 가능하다.

텍스트를 입력할 때, 포커스를 지정하는 명령으로 focus가 제공된다. 다음은 frame1 위짓의 entry 위짓에 포커스를 맞추는 예이다.


focus .frame1.entry

포커스를 없애려면 focus none을 수행하면 된다.

7장 Dialog Window

대화 상자(dialog window)를 만드는 것은 tk_dialog를 이용하면 가능하다. 다음은 OK, Cancel, Quit라는 세 개의 버튼을 가지는 대화 상자를 생성한다. 디폴트(default)로 선택되는 버튼은 0번째 OK버튼이다. 비트맵으로 info를 사용하였다.


tk_dialog .dlg "Hello" "This is a hello." info 0 OK Cancel Quit

[그림 15]

사용자가 직접 대화 상자를 만들기 위해서는 toplevel 명령을 이용하여 toplevel 위짓을 구성하고, 그 위짓을 Diaglog 클래스로 선언하는 것이다. toplevel 위짓은 기본 윈도우 바깥에 윈도우를 하나 더 생성하는 역할을 한다.


toplevel .top -class Dialog
label .top.l -relief flat -text "My own dialog box"
button .top.b -relief ridge -text "OK" -command { puts "OK" }
pack .top.l .top.b -side left

[그림 16]

이벤트가 발생하면 그 이벤트는 그 윈도우와 그 하위에 있는 위짓에만 전달이 된다. 이것을 grab이라고 하는데, 이벤트를 특정 윈도우와 연결시키려면 다음과 같이 grab을 설정하면 된다.


grab set .dlg

반대로 이 설정을 풀려면 set 대신 release를 사용한다.

Tk에서는 변수의 값이 변하거나 윈도우의 visibility 상태가 바뀌거나, 또는 윈도우가 사라지게 될 때까지 기다리게 하는 tkwait 명령이 있다.


tkwait variable hello
tkwait visibility .top
tkwait window .dlg

다음은 OK 버튼과 Cancel 버튼을 가진 대화상자를 만들고, 대화상자에서 임의의 버튼을 선택할 때까지 기다리는 예제이다.


toplevel .top
grab set .top
button .top.ok -text OK -comman { destroy .top }
button .top.cancel -text Cancel -comman { destroy .top }
pack .top.ok .top.cancel -side left
tkwait window .top

8장 기타

위짓에 속한 기본 리소스가 아닌 윈도우 매니저가 관리하는 리소스는 wm 명령을 이용하면 된다. 예를 들어, 윈도우의 타이틀을 바꾸려면 위짓의 리소스를 바꾸어 해결할 수 있는 것이 아니라, 윈도우 매니저에게 요청해야 한다.


wm title .top "My dialog window"

이 윈도우를 아이콘으로 만들어(minimize) 보자.


wm iconify .top

윈도우 매니저가 관리하는 리소스를 바꾸거나 그 정보를 알아내기 위해서 wm 명령을 사용할 수 있다. 자세한 내용은 man -s n wm을 참조해야 할 것이다.

tkwait의 예제에서 나왔던 destroy 명령은 윈도우를 제거하는 명령이다. 윈도우를 제거하면서 관련된 명령이나 윈도우 상태 또한 제거한다. 윈도우가 쌓여있는 순서를 바꾸기 위해 raise나 lower 명령을 사용할 수 있다.

예제파일

MP3 연주기로 유명한 Mpg123은 텍스트 기반의 프로그램이다. 이것의 대표적인 X 인터페이스는 Tk3play이다. 다시 말하면, Mpg123은 텍스트 상에서 MP3 파일을 디코딩(decoding)해서 연주해주는 작업을 하고, Tk3play는 버튼이나 레이블을 이용하여 Mpg123을 제어하는 역할을 맡는다. 아래 예제는 필자가 Tk3play를 몰랐을 때, Mpg123을 X 윈도우 상에서 제어하는 인터페이스가 필요해서 작성했던 Tcl/Tk 스크립트이다. Tk3play의 아주 기본적인 몇 가지 기능을 가지고 있는 인터페이스라고 할 수 있다.


#!/usr/local/bin/wish -f
#
# mpgplay
#
# 이 스크립트는 wish를 이용한 mpg플레이어입니다.
# This script run mpeg3 audio player with wish.

# 아규먼트로부터 디렉토리명을 제외한 MP3 파일의 이름을 구한다.
set filenamelist [split [lindex $argv 0] /]
set listend [expr [llength $filenamelist] - 1]
set filename [lindex $filenamelist $listend]

# 타이틀과 MP3 파일명을 위한 레이블을 만들고
# Play, Stop, Quit을 위한 버튼을 만든다.
label .title -text ” Terzeron’s mp3 player ” -foreground black
-background pink -relief ridge
label .name -text $filename -foreground black -background white
-relief ridge
frame .ground -relief sunken
button .ground.play -text Play -foreground blue -background white
button .ground.quit -text Quit -foreground red -background white
button .ground.stop -text Stop -foreground purple -background white

# 레이블과 버튼 등의 위짓을 배치한다.
pack .title -side top -fill x
pack .name -fill x
pack .ground -fill x
pack .ground.play .ground.stop .ground.quit -side left -expand 1 -fill both

# 버튼과 키입력에 대한 이벤트 처리 프러시져를 연결시킨다.
bind .ground.play { playproc }
bind .ground.quit { quitproc }
bind .ground.stop { stopproc }
bind . { playproc }
bind . { stopproc }
bind . { quitproc }

# 기본값 지정
set pidno 0
set playing 0
set stopping 0

# Play 프러시져
proc playproc {} {
global argv pidno playing stopping fileloc
# 이미 연주 중이면 무시
if {$playing == 1} {
return
}
# 버튼의 색상과 relief형태를 바꾼다.
.ground.play configure -background yellow -foreground black -relief sunken
.ground.stop configure -background white -foreground purple -relief raised
if {$stopping == 1} {
# 연주 중에 멈춘 경우에는 CONT 시그널을 보내 mpg123을 동작시킨다.
exec /usr/bin/kill -CONT $pidno
} else {
# 아직 시작하지 않은 경우에는 mpg123을 fork/exec하여 실행시킨다.
set pidno [exec /usr/local/bin/mpg123 [lindex $argv 0] >& /dev/null &]
}
set playing 1
set stopping 0
}

# Stop 프러시져
proc stopproc {} {
global argv pidno stopping playing
# 연주가 멈춰 있는 경우나 아직 시작하지 않은 경우에는 무시한다.
if {$stopping == 1 || $playing == 0} {
return
}
# 버튼의 색상과 relief 형태를 바꾼다.
.ground.stop configure -background yellow -foreground black -relief sunken
.ground.play configure -background white -foreground blue -relief raised
set stopping 1
set playing 0
# STOP 시그널을 mpg123으로 보내 잠시 프로세스를 멈추게 한다.
exec /usr/bin/kill -STOP $pidno
}

# Quit 프러시져
proc quitproc {} {
global argv pidno playing stopping
# KILL 시그널을 보내 프로세스를 종료시킨다.
if [catch {if {$pidno != 0} { exec /usr/bin/kill -9 $pidno } } ] {
;
}
# top-level 위짓을 없앤다.
destroy .
# 끝낸다.
exit 0
}
# 스크립트가 실행하면 바로 Play 프러시져를 호출하여 연주를 시작한다.
playproc
[그림 17]

이 인터페이스는 Tk3play에 비하면 기능도 부족하고 깔끔한 위짓 배치를 보여주지도 못한다. 그러나 독자들이 ‘왜 Tcl/Tk 인가?’하는 의문에 답할 수 있는 한 가지 사례를 보여주는 예제 프로그램이 될 수 있을 것이다. 이 기사를 읽은 독자라면 누구나 Tcl/Tk를 이용하여 손쉽게 X 윈도우 프로그래밍을 할 수 있을 것이다.

Mpg123과 Tk3play는 다음 URL에서 구할 수 있다.
http://www-ti.informatik.uni-tuebingen.de/~hippm/mpg123.html
http://www.msc.cornell.edu/~bef2

서울대학교 전산과학과 데이타베이스 연구실 조영일

: