BLT는 벨 연구소에서 개발되고, 루슨트 테크놀로지에 저작권이 있는, Tk와 더불어 최대의 Tk 확장 라이브러리 세트입니다. Tk의 순수한 기능 확장으로 예전부터 잘 알려져 있습니다. BLT가 무엇의 약자인지는 매뉴얼을 읽어봐도 "원하는 대로 해석하세요"라고만 나와 있으니, "Beautiful Lady and Television"의 약자라고 우겨도 틀리지 않습니다.
BLT의 현재 버전은 2.4z (2002/10/15)이고, [이곳] 에서 다운로드할 수 있습니다. Tcl/Tk의 7.5, 7.6, 8.0 이후의 각 버전을 지원합니다. 2.4p부터는 스텁(stub) 메커니즘을 지원하여, 8.2용 BLT는 8.3 이후 버전에서도 사용할 수 있습니다. 반면, 7.* 버전에 대한 지원은 점점 사라지고 있는 듯합니다.
주요 기능으로는 다음과 같습니다.
- 다양한 2차원 X-Y 그래프
- 계층적 리스트박스
- GUI 위젯에 배경 이미지를 붙일 수 있는 확장
- 탭이 달린 패널
- "busy" 윈도우
- 테이블 형태의 지오메트리 매니저
- 윈도우를 Tk 이미지로 캡처하기
이 모든 기능은 우리 GUI 애플리케이션에 강렬한 외관을 부여해줍니다. 기능이 강력한 만큼 매우 복잡하지만, 함께 제공되는 HTML 문서가 잘 정리되어 있어 이것을 참고하면 쉽게 익숙해질 수 있습니다. 바로 사용해봅시다!
리눅스용 설치
리눅스에서는 익숙한 configure → make → make install 과정으로 설치하면 됩니다. 다만, 실행 프로그램(bltwish)을 만드는 것보다 공유 라이브러리(libBLT24.so)를 만들어 설치하는 쪽이 여러모로 더 편리합니다.
% sh configure
% cd src/shared
% make
# make install
% cd ../../library
% make
# make install
/usr/local에 설치한 경우, 공유 라이브러리 libBLT24.so는 /usr/local/lib/에, 초기 설정용 Tcl 스크립트는 /usr/local/lib/blt2.4에 복사됩니다.
윈도우즈용 설치
Windows용 BLT는 Tcl/Tk 와 함께 컴파일된 바이너리 버전을 자체 설치 파일 형태로 배포되기 때문에 설치가 매우 간단합니다. 파일을 다운로드하면, 아주 깔끔한 인스톨러가 등장합니다. Tcl/Tk 자체 설치 보다도 더 신경 써서 만든 듯한 설치 화면에서 제작자의 센스를 느낄 수 있습니다. (현재 제공되는 설치 파일은 16비트 윈도우즈 95 만 지원하는것 같습니다. 현재의 윈도우즈 7이나 11에서는 설치가 불가합니다. 리눅스의 설치 방법과 마찬가지로 msys2 환경에서 MinGW 컴파일러를 사용하여 빌드가 가능합니다.)
설치 위치를 물어보면, 이미 Tcl/Tk 가 설치된 디렉터리(예: `C:\Program Files\TCL`)를 지정하면 됩니다. 물론 다른 디렉터리(예: `C:\Program Files\BLT`)를 지정해도 됩니다.
설치가 완료되면, `bin` 서브디렉터리 아래에 WISH84.EXE에 BLT가 포함된 인터프리터인 "BLTWISH.EXE"와, load 명령어로 로드할 수 있는 DLL 파일인 "BLT24.DLL"이 생성됩니다. 일반적으로는 BLT24.DLL을 로드해서 사용하는 쪽이 더 편리할 것입니다.
BLT 사용하기
BLT 기능을 사용하는 Tcl 스크립트의 맨 앞에는 다음과 같은 코드를 작성합니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
이름공간(namespace)이 갑자기 등장하지만, 위와 같이 코드를 작성해두면 이후에 BLT의 기능을 사용할 때 이름공간을 신경 쓸 필요가 없어집니다. 그래서 매번 스크립트의 맨 앞부분에 이렇게 적어두는 것이 좋습니다.
벡터 (vector)
벡터는 Tcl의 배열과 비슷하지만, Tcl의 배열에는 없는 특징이 있습니다.
- 배열의 인덱스는 0부터 n까지의 숫자이다.
- 모든 요소는 부동소수점수(float)이다.
- 벡터를 명령어처럼 사용할 수 있다.
벡터를 생성할 때는 vector create를 사용합니다. 생성된 벡터의 각 요소의 초기값은 0(0.0)이 됩니다.
package require BLT
namespace import blt::*
# 벡터 x 생성 (0~9)
vector create x(10)
=> ::x
# 벡터 y 생성 (0~9)
vector create y(10)
=> ::y
# 벡터 z 생성 (1~10)
vector create y(1:10)
=> ::z
벡터를 삭제할 때는 vector destroy를 사용합니다.
# 벡터 x y z 삭제
vector destroy x y z
벡터의 각 요소는 인덱스(0~n)를 사용하여 배열처럼 접근할 수 있습니다. 인덱스에 end를 사용하면 마지막 요소를 지정할 수 있습니다. 또한, 콜론(:)을 사용하면 요소의 범위를 지정할 수 있습니다.
# 벡터의 초기화
foreach i {0 1 2 3 4 5 6 7 8 9} {
set x($i) $i
}
# 첫 번째 요소
puts $x(0)
=> 0.0
# 마지막 요소
puts $x(end)
=> 0.9
# 0~3 범위의 요소
puts $x(0:3)
=> 0.0 0.1 0.2 0.3
# 모든 요소
puts $x(:)
=> 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
# 1+2번째 요소
puts $x(1+2)
=> 3.0
인덱스에 min, max, mean을 사용하면 최소값, 최대값, 평균값을 구할 수 있습니다.
# 벡터의 초기화
foreach i {0 1 2 3 4 5 6 7 8 9} {
set x($i) $i
}
# 최소값
puts $x(min)
=> 0.0
# 최대값
puts $x(max)
=> 9.0
# 평균값
puts $x(mean)
=> 4.5
동적으로 벡터를 생성할 수도 있습니다. 벡터 생성 시 요소의 개수를 지정하지 않은 대신 "++end"를 사용해서 동적으로 요소를 할당합니다.
# 동적 벡터 d 생성
vector create d
# 벡터 초기화
foreach i {0 1 2 3 4 5 6 7 8 9} {
set d(++end) $i
}
# 모든 요소 출력
puts $d(:)
=> 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
벡터를 생성하면 커맨드로도 사용할 수 있습니다.
# 벡터 z를 생성
vector create z
# 요소 초기화
z set {9 8 7 6 5 4 3 2 1 0}
# 요소 개수 확인
z length
=> 10
# 요소를 오름차순 또는 내림차순 정렬
z sort
z sort -reverse
# 범위의 요소 반환
z range 0 end
=> 0.0 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0
# 검색한 요소의 인덱스 반환
z search 5.0
=> 5
# 요소 추가
z append {10.0 11.0}
# 모든 요소에 1.0을 더하기
z expr { z + 1 }
# -5에서 5까지 1씩 증가시켜 요소 설정
z seq -5 5 1
# 정규화
z normalize
# 데이터를 보간(보충)
z populate z2 10
# 복제
z dup zz
# 요소 삭제
z delete 1 2
# 요소 병합
z merge v1 v2
# 난수 설정
z random
# 요소를 시프트
z offset -1
# 벡터를 Tcl 배열에 매핑
z variable ary
이 벡터는 아래에서 설명할 그래프 그리기에 리스트 데이타 대신에 사용할 수 있습니다.
선 그래프 그리기
BLT에서 제공하는 그래프는 모두 X-Y 2차원 그래프입니다. 그래프의 종류로는 `graph`, `barchart`, `stripchart`가 있습니다. 하지만 기본 사용법은 거의 동일하므로 여기서는 대표적으로 `graph`를 사용합니다. `graph`, `stripchart`는 선 그래프, `barchart`는 막대그래프입니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
wm title . "BLT 샘플 1"
set temperature {5 10 15 20 25 30 35 40 45}
set di {81.2 72.5 43.4 5.6 18.2 60.9 98.2 99.9 99.8}
pack [set g [graph .g -plotbackground black]]
$g configure -title "온도와 사람의 불쾌지수 관계"
$g element create line1 -xdata $temperature -ydata $di
$g element configure line1 -label {2001.7 조사} \
-symbol square -pixels 4 -color "#ffcc00" -fill "#800000" \
-outline "#ff0000" -outlinewidth 1 \
-dashes {2 4 2} -linewidth 2 -trace increasing
# symbol: square, circle, diamond, plus, cross, splus, scross, triangle, ""
$g element show
$g axis configure x -title 온도
$g axis configure y -title 불쾌지수
# 마우스 조작으로 확대, 축소
bind $g <ButtonPress-1> {
%W axis configure x -min [%W axis invtransform x %x]
%W axis configure y -min [%W axis invtransform y %y]
}
# 원래대로 되돌리기
bind $g <ButtonPress-3> {
%W axis configure x -min {}
%W axis configure y -min {}
}
$g legend configure -position right -relief groove -font fixed -fg blue
$g crosshairs configure -hide no -color red -dashes {2 2} -linewidth 2
Blt_Crosshairs $g
$g grid configure -hide no -dashes { 2 2 }
처음으로 X축과 Y축 데이터를 준비합니다. 기본적으로 두 축 모두 숫자 축입니다. 아래 예시처럼 각각 숫자로 이루어진 두 개의 리스트를 준비해도 되고, 위에서 설명한 벡터(vector)를 사용할 수도 있습니다.
여기서는 "기온(섭씨)"과 "불쾌지수(%)" 데이터를 예로 들었습니다. 40도에서 45도로 갈 때 불쾌지수가 줄어드는 것은, “그 사이에 깨달음의 경지에 도달해서 불도 시원하게 느껴진다”는 이상한 답변을 한 사람이 한 명 있었기 때문입니다.
set temperature {5 10 15 20 25 30 35 40 45}
set di {81.2 72.5 43.4 5.6 18.2 60.9 98.2 99.9 99.8}
아래와 같이 그래프 위젯을 생성하고 Tk 위젯처럼 `pack` 할 수 있습니다.
graph .g -plotbackground black
pack .g
.g configure -title "온도와 사람의 불쾌지수 관계"
이제 그래프 위젯(.g)에 데이터를 붙여서 선그래프로 만듭니다. `element create` 명령을 사용합니다.
.g element create line1 -xdata $temperature -ydata $di
선(데이터 시리즈)에 대한 각종 옵션을 `element configure`로 설정합니다.
.g element configure line1 -label {1999.7 조사} \
-symbol square -color red -dashes {2 4 2} -linewidth 2
.g element show
- -label : 범례에 표시될 이름
- -symbol : 데이터 점의 모양
- -color : 선 색상
- -dashes : 선의 점선 패턴
- -linewidth : 선 두께
아래와 같이 좌표축 설정을 하고
.g axis configure x -title 온도
.g axis configure y -title 불쾌지수
`axis configure`의 -min, -max 옵션을 이용하면 그래프를 줌인/줌아웃 할 수 있습니다.
# 왼쪽 클릭: 확대
bind .g <ButtonPress-1> {
%W axis configure x -min [%W axis invtransform x %x]
%W axis configure y -min [%W axis invtransform y %y]
}
# 오른쪽 클릭: 원래대로 복원
bind .g <ButtonPress-3> {
%W axis configure x -min {}
%W axis configure y -min {}
}
범례, 크로스헤어, 격자선 등도 세밀하게 설정할 수 있습니다.
.g legend configure -position right -relief groove -font fixed -fg blue
.g crosshairs configure -hide no -color red -dashes {2 2} -linewidth 2
Blt_Crosshairs .g
.g grid configure -hide no -dashes { 2 2 }
계층 구조 리스트박스 (Hierarchical Listbox)
계층 구조 리스트박스란, 쉽게 말해 Windows의 탐색기 왼쪽 부분(트리 구조 폴더 리스트)과 같은 GUI 위젯 입니다. BLT의 hierbox 위젯을 사용하면, 일반적인 Tk 위젯처럼 동일하게 생성할 수 있습니다.
참고로 hierbox 위젯은 BLT 2.4w 버전에서 완전 상위 호환인 treeview 위젯이 등장하면서, 현재는 향후 폐지 예정으로 간주됩니다. 최신 프로젝트에서는 treeview 위젯 사용을 권장합니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
wm title . "BLT 샘플 2"
entry .ea -state disabled
set f [frame .fa -rel groove]
set h [hierbox $f.h -yscrollcommand "$f.scrv set" -height 200]
scrollbar $f.scrv -orient vertical -command "$f.h yview"
grid $f.h -row 1 -column 1
grid $f.scrv -row 1 -column 2 -sticky ns
foreach e {ea fa} {pack .$e -side top -fill x}
$h configure -separator {::}
foreach a {::java ::javax ::javax::swing ::java::io ::java::net ::javax::swing::table
::javax::naming ::javax::naming::directory} {
$h insert end $a
}
bind $h <Double-Button-1> {
if {"[set i [%W curselection]]" != ""} {
.ea configure -state normal
.ea delete 0 end
.ea insert end "[%W get $i] : [%W get -full $i]"
.ea configure -state disabled
}
}
hierbox 커맨드는 일반 Tk 위젯처럼 사용하고, insert, delete, curselection, get 등 주요 조작법도 listbox와 거의 동일합니다.
hierbox .h -yscrollcommand {.scrv set} -height 200
scrollbar .scrv -orient vertical -command {.h yview}
계층 구조(트리)를 만들려면, 먼저 `configure`의 `-separator` 옵션으로 계층 구분 문자를 지정합니다. 예를 들어, "::java::awt"라는 아이템은 루트 아래 "java" 폴더, 그 아래 "awt" 폴더가 생기는 구조를 의미합니다.
.h configure -separator {::}
foreach a {::java ::java::awt ::java::io ::java::net ::java::awt::event} {
.h insert end $a
}
아래와 같이 트리 아이템을 더블 클릭시 현재 선택된 노드의 풀 패스를 표시합니다. `get -full` 옵션을 사용하면, 트리의 전체 경로가 반환됩니다.
bind $h <Double-Button-1> {
if {"[set i [%W curselection]]" != ""} {
.ea configure -state normal
.ea delete 0 end
.ea insert end "[%W get $i] : [%W get -full $i]"
.ea configure -state disabled
}
}
막대 그래프
BLT의 그래프에는 정말 많은 옵션이 있습니다. 그것들을 전부 나열하고 설명하다 보면 중간에 질려버릴 수 있기에 생략하고, 선 그래프 부분에서 소개했던 예제를 다시 정리해서 이번에는 처음부터 막대 그래프를 그려보겠습니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
wm title . "BLT 샘플"
set xdata {1 2 3 4 5}
set ydata {65 30 40 90 55}
barchart .sc \
-plotbackground "#c0c0c0" -plotrelief sunken \
-plotborderwidth 2 -background "#808080" \
-relief groove -bd 2 -title "My Brain is Compact"
.sc element create ELEMENT1 \
-xdata $xdata -ydata $ydata -label "This is the Legend" -fg "#700040"
proc getSubjectByID {sc value args} {
set subjects {dummy 국어 수학 물리 지리 영어}
return [lindex $subjects $value]
}
.sc axis configure x -color white -command "getSubjectByID" \
-title 교과 -titlecolor "#c0c0c0"
.sc axis configure y -color white -min 0 -max 100
.sc legend configure -position right -relief groove -font 7x14 -fg #000060
.sc crosshairs configure -hide no -color black -linewidth 2 -dashes ""
Blt_Crosshairs .sc
.sc grid configure -hide no -color "#808080" -dashes {1 4 1}
image create photo Image1 -file copy.gif
for {set i 0} {$i<[llength $xdata]} {incr i} {
set lab [label .sc.lab$i -image Image1]
.sc marker create window \
-coords [list [lindex $xdata $i] [expr [lindex $ydata $i]-5] ] \
-anchor n -window $lab
}
pack .sc -side top
button .cmda -text 닫기 -command exit
pack .cmda -anc e -padx 4 -pady 4
먼저 BLT 라이브러리를 불러옵니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
데이터는 아래와 같습니다.
국어 | 수학 | 물리 | 지리 | 영어 |
65 | 30 | 40 | 90 | 55 |
점수 참 형편 없네요 ㅎㅎ 이처럼 한쪽 축이 숫자 축이 아닌 경우엔 약간의 트릭이 필요합니다. 일단 데이터는 아래와 같이 작성해둡니다.
set xdata {1 2 3 4 5}
set ydata {65 30 40 90 55}
먼저 `barchart` 명령으로 막대 그래프를 그릴 영역을 일반 Tk 위젯과 마찬가지로 만듭니다.
barchart .sc \
-plotbackground "#c0c0c0" -plotrelief sunken -plotborderwidth 2 \
-background "#808080" -relief groove -bd 2 \
-title "My Brain is Compact"
이처럼 생성 시에 모든 옵션을 정의할 수도 있지만, 예를 들면
barchart .sc
# ...
.sc configure -plotbackground "#c0c0c0" \
-plotrelief sunken \
# ...
이런 식으로 나중에 `configure` 서브커맨드를 사용해서 옵션을 여러 번 추가하거나 변경할 수 있습니다. 즉 일반 Tk 위젯처럼 `cget` & `configure`로 조작할 수 있다는 얘기입니다. 참고로 `-plotbackground`, `-plotrelief`, `-plotborderwidth`는 그래프의 그려지는 범위의 색상이나 테두리 스타일을 지정하는 옵션이고, `-background(-bg)`, `-relief`, `-borderwidth(-bd)`는 그 바깥 영역의 스타일을 지정하는 옵션입니다. 뭐, 직접 써보면 바로 알 수 있으니, 여기서 자세한 설명은 생략합니다.
다음으로 그래프의 "데이터 시리즈"를 만듭니다. 데이터 시리즈란, 쉽게 말해 그래프의 데이터를 표현하는 것이고, 막대 그래프라면 막대, 선 그래프라면 선입니다. 아래의 `ELEMENT1`처럼 적당한 이름을 정해 `element create` 서브커맨드로 생성합니다.
.sc element create ELEMENT1 \
-xdata $xdata -ydata $ydata -label "This is the Legend" -fg "#700040"
여기서 데이터 -xdata, -ydata에 데이터(숫자 리스트)를 지정함으로써, 실제로 그래프에 반영됩니다. X축 데이터는 "국어", "수학"과 같은 숫자가 아닌 값을 넣으면 안되기에 임시로 $xdata로 {1 2 3 4 5} 로 넣어 두었습니다.
참고로 이 element도, 일단 element create 서브커맨드로 만든 후에, "element configure" 서브커맨드를 사용해서
.sc element configure ELEMENT1 -fg "#700040"
처럼 설정할 수 있습니다.
이렇게 옵션을 추가하거나 변경할 수 있습니다. 이후 나오는 axis, crosshairs, grid, marker 등도 모두 이 element와 같은 방식으로, 단지 옵션의 종류만 다릅니다.
다음은 X축을 셋팅해 봅니다. 여기서는 앞서 사용한 {1 2 3 4 5} 를 {국어 수학 물리 지리 영어} 로 바꿔주는 처리를 하고 있습니다.
proc getSubjectByID {sc value args} {
set subjects {dummy 국어 수학 물리 지리 영어}
return [lindex $subjects $value]
}
.sc axis configure x \
-color white -command "getSubjectByID" \
-title Subject -titlecolor "#c0c0c0"
axis는 기본적으로 "x"와 "y"가 이미 생성되어 있기 때문에 "axis create" 같은 명령을 쓸 필요 없이 바로 "axis configure x"로 옵션을 지정할 수 있습니다. 여기서 중요한 점은 -command "getSubjectByID" 옵션입니다. -command를 지정하면, X축에 -xdata로 전달한 숫자 배열 대신 이 명령이 호출되어 반환된 문자열이 표시됩니다. 이 명령 뒤에는 barchart 컴포넌트의 위젯(여기서는 .sc)과 현재의 값이 인자로 자동으로 붙습니다. 이렇게 하면 X축에는 {1 2 3 4 5} 대신 {국어 수학 물리 지리 영어} 가 표시됩니다.
Y축도 방법도 동일합니다. 여기서는 -min과 -max를 지정해서 그려지는 그래프의 값 범위를 지정하고 있습니다. -min과 -max를 넓히거나 좁히면 줌 인/아웃 기능을 쉽게 넣을 수 있습니다.
.sc axis configure y -color white -min 0 -max 100
legend(레전드)는 "범례"를 의미합니다. 범례에 표시되는 각 데이터 시리즈의 설명은 element configure 서브커맨드의 -label 옵션으로 지정합니다. legend configure 서브커맨드는 범례를 표시하는 직사각형 영역의 꾸밈 방식을 지정하는 것입니다.
.sc legend configure \
-position right -relief groove -font 7x14 -fg blue
크로스헤어(십자선)는 이전 페이지에서도 나왔듯이, 마우스가 그래프 위를 움직이면 거기에 따라 십자 형태로 움직이는 교차선입니다. 크로스헤어는 기본적으로 "표시하지 않음"이기 때문에, 표시하려면 "-hide" 옵션을 "no"로 해야 합니다.
.sc crosshairs configure \
-hide no -color black -linewidth 2 -dashes ""
Blt_Crosshairs .sc
또한 실제로 크로스헤어를 표시하려면 위와 같이 Blt_Crosshairs라는 하위 라이브러리 프로시저를 호출해야 합니다.
그리드는 페인트 툴이나 드로잉 툴에서 익숙한 격자무늬입니다. 이것도 기본적으로 표시되지 않으니, 역시 "-hide no"로 해야 표시됩니다. 크로스헤어 옵션에도 있었던 -dashes는 점선의 "끊김 정도"를 지정하는 것입니다. 말로 설명하기 어렵기 때문에 적당히 값을 바꿔서 시험해 보세요. -dashes ""로 하면 실선으로 표시되고, 기본값도 실선입니다.
.sc grid configure \
-hide no -color "#808080" -dashes {1 4 1}
마지막으로 중요한 마커가 남았는데, 이는 감각적으로는 캔버스 위젯(canvas)처럼 그래프 영역 내에 사각형, 원 같은 도형, 텍스트, 이미지, 그리고 다른 Tk 컴포넌트를 배치할 수 있습니다. 아래 예시에서는 각 막대의 맨 위에 이미지 데이터를 붙인 라벨을 올리고 있습니다.
image create photo Image1 -file copy.gif
for {set i 0} {$i<[llength $xdata]} {incr i} {
set lab [label .sc.lab$i -image Image1]
.sc marker create window \
-coords [list [lindex $xdata $i] [lindex $ydata $i]] \
-anchor n -window $lab
}
마지막으로 이 .sc를 pack 등으로 배치하면 막대 그래프가 완성됩니다.
pack .sc -side top
button .cmda -text Close -command exit
pack .cmda
Tk 위젯에 배경 이미지 붙이기
BLT에서는 라벨, 버튼, 체크 버튼, 라디오 버튼, 프레임, 스크롤바, 탑레벨 위젯의 배경에 photo 이미지로 만든 이미지를 배경 이미지로 셋팅 할 수 있도록 확장되어 있습니다. 사용 방법은 간단하게, -tile 옵션에 이미지 이름을 지정해주기만 하면 됩니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
wm title . {BLT 샘플}
image create photo Image1 -file bg.gif
frame .f -tile Image1
message .f.ma -text {Hello. This is a sample of tile-extended widgets.} \
-fg #700040 -font {Helvetica 12 normal}
button .f.cmda -text OK -command exit -tile Image1
pack .f.ma -side top -padx 4 -pady 4
pack .f.cmda -side top -ipadx 10 -padx 4 -pady 4
pack .f
이 기능을 효과적으로 사용하면, 여러분의 애플리케이션이 보기좋게 변신됩니다.
스냅샷 기능
BLT에는 Tcl/Tk 윈도우의 스냅샷을 찍어서, Tk의 photo 이미지로 복사하는 기능이 있습니다. 캔버스에 그린 도형을 비트맵으로 저장할 수 있어 매우 편리합니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
canvas .can -bg white
.can create polygon {20 30 40 80 60 90 90 75 50 40 20 30} \
-outline yellow -fill red -tag TagPolygon
.can create oval 100 80 130 110 -outline blue -fill cyan -width 5 -tag TagOval
foreach t {TagPolygon TagOval} {
.can bind $t <Button-1> { set StartX %x ; set StartY %y }
.can bind $t <Button1-Motion> "
set dx \[expr %x-\$StartX\]; set dy \[expr %y-\$StartY\]
set StartX %x; set StartY %y; %W move $t \$dx \$dy"
}
pack .can
bind .can <Button-3> {
image create photo Image1
winop snap .can Image1
Image1 write output.gif -format gif
image delete Image1
}
스냅샷을 찍는 것은 winop snap이라는 명령어로, 인자로 찍고 싶은 윈도우와 저장할 photo 이미지의 이름을 지정해주면 됩니다.
winop snap .can Image1
위의 이미지는 위의 코드로 생성된 GIF 이미지 파일입니다.
탭셋(Tabset)
탭셋은 탭을 붙일 수 있는 GUI 위젯입니다.
탭셋은 위의 이미지와 같이 탭을 클릭하면 그 탭에 연결된 윈도우를 보여주는 기능입니다. 이런 GUI 위젯을 Tcl/Tk에서 사용할 수 있게 해주는 확장은 여러 종류가 있지만, BLT의 탭셋도 꽤 쓰기 쉽고 유용한 위젯이라고 생각됩니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
wm title . "BLT 샘플"
pack [set ta [tabset .ta]] -side top
$ta insert end tab1 -text "그림"
frame $ta.f1
set can [canvas $ta.f1.can -width 200 -height 200 -bg white]
bind $can <Button-1> { set sx %x; set sy %y }
bind $can <Button1-Motion> {
set colorSpec [format "#%%02x%%02x%%02x" $Color(RED) $Color(GREEN) $Color(BLUE)]
%W create line $sx $sy %x %y -fill $colorSpec -width $LineWidth
set sx %x; set sy %y
}
pack $can
$ta tab configure tab1 -window $ta.f1
$ta insert end tab2 -text "선의 설정"
set fa [frame $ta.f2]
set LineWidth 4
set scaa [scale $fa.scaa -from 1 -to 10 \
-variable LineWidth -length 140 -ori h -label "선의 굵기"]
pack $scaa -side top -anc w
foreach e {red green blue} {
set Color([string toupper $e]) 128
set sca$e [scale $fa.sca$e -from 0 -to 255 \
-variable Color([string toupper $e]) -ori h -label [string toupper $e]]
pack [set sca$e] -side top -anc w
}
$ta tab configure tab2 -window $ta.f2
$ta insert end tab3 -text "파일"
set fa [frame $ta.f3]
set lm [label $fa.la -text "출력 모드"]
set fm [frame $fa.fm -rel groove -bd 2]
set modeStrings {컬러 그레이 이진}
set modes {color gray mono}
set OutputColorMode color
foreach e {0 1 2} {
set r [radiobutton $fm.rad$e -variable OutputColorMode \
-text [lindex $modeStrings $e] -value [lindex $modes $e]]
pack $r -side left -padx 4
}
set lf [label $fa.lf -text "출력 파일명"]
set ff [frame $fa.ff -rel groove -bd 2]
entry $ff.ea -width 30 -textvariable OutputFileName
button $ff.ba -text "찾아보기..." -command {
if {"[set a [tk_getSaveFile -title "출력 파일명 지정"]]" != ""} {
set OutputFileName $a
}
}
pack $ff.ea $ff.ba -side left -padx 4
set b [button $fa.b -text "출력" -command {
if {"$OutputFileName" == ""} {
tk_messageBox -message "파일을 지정해 주세요."
} else {
$can postscript -file $OutputFileName -colormode $OutputColorMode
tk_messageBox -message \
"출력 완료: $OutputFileName ([file size $OutputFileName]bytes)"
}
}]
pack $lm $fm $lf $ff $b -side top -anc w
$ta tab configure tab3 -window $ta.f3
pack [set ba [button .ba -text "닫기" -command exit]] \
-padx 5 -pady 5 -anc e -side top
사용법은 매우 간단합니다. BLT 라이브러리를 로드한 뒤, tabset이라는 명령어로 일반 Tk 위젯처럼 이름을 지정해서 생성합니다. tabset의 옵션은 frame 명령어의 옵션과 비슷해서, -relief, -borderwidth, -width 등을 지정할 수 있습니다.
pack [set ta [tabset .ta -relief groove -borderwidth 2]] -side top
다음으로, 각 탭을 클릭했을 때 표시될 GUI를 만듭니다. 이것은 탭별로 하나씩 frame을 만들고, 그 안에 각 탭의 GUI를 넣으면 편리합니다. 여기서 추가할 frame 등 위젯은 반드시 tabset 위젯(여기서는 .ta)의 자식 위젯이어야 합니다.
frame .ta.f1
# ... 이하에 GUI 위젯을 추가
frame .ta.f2
# ... 이하에 GUI 위젯을 추가
# ...
각 탭에 표시될 GUI를 만들었으면, 탭과 그 GUI를 "연결"합니다. 이는 아래와 같이 tabset 위젯의 insert 서브커맨드를 사용해 tabset에 탭을 추가하고, 탭의 제목 부분은 -text 옵션으로, 탭 GUI의 "내용"이 될 위젯을 -window 옵션으로 지정합니다.
$ta insert end tab1 -text "그림"
$ta tab configure tab1 -window $ta.f1
tab1이나 tab2 같은 이름은 각 탭마다 자유롭게 붙일 수 있는 고유한 "이름"입니다. 이렇게 지정된 이름은 tab configure 서브커맨드를 사용하여 각 탭을 제어하는데 사용 됩니다. 이렇게 "그림" 탭을 클릭하면, 프레임 .ta.tab1에 지정된 GUI가 표시되게 됩니다.
트리뷰(Treeview)
BLT 버전 2.4w부터 트리뷰(treeview) 위젯이 추가되었습니다. 이것은 2.4o에서 2.4v까지 존재했던 계층구조테이블(hiertable)의 상위 호환으로, 왼쪽 절반은 계층형 리스트박스와 같은 트리, 오른쪽 절반은 테이블 형식의 정보 표시 영역을 제공합니다. 오른쪽은 워크시트처럼 직접 키보드 입력으로 값을 편집할 수는 없지만, 그래도 상당히 강력한 위젯입니다. 2.4w에서 이 treeview가 등장함에 따라, hierbox 위젯과 hiertable 위젯은 호환성을 위해서만 남겨놓은 상태 입니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
wm title . "BLT 샘플(treeview)"
treeview .ta -separator / -width 400 -height 160
set paths {/korea /korea/taejeon /korea/boondang
/korea/boondang/pankyo /korea/busan /china}
set labels {한국지사 대전지점 분당지점 판교출장소 부산지점 중국지사}
for {set i 0} {$i<6} {incr i} {
set ids([lindex $paths $i]) \
[.ta insert end [lindex $paths $i] \
-label [lindex $labels $i] ]
}
.ta entry configure $ids(/china) -foreground red
.ta column insert end "emps" -text 직원수
.ta column insert end "equips" -text 설비대수
.ta column insert end "start_date" -text 시작연월
.ta entry configure $ids(/korea/boondang) \
-data {emps 45 equips 130 start_date 200010}
.ta selection set $ids(/korea/taejeon)
.ta open $ids(/korea)
bind .ta <Double-Button-1> {
set id [%W curselection]
set path [%W get -full $id]
set label [%W entry cget $id -label]
tk_messageBox -message "/$path ($label) 이(가) 클릭되었습니다."
}
pack .ta
트리뷰는 treeview 명령어로 만듭니다. 일반 Tk 위젯과 마찬가지로 뒤에 옵션을 붙일 수 있습니다.
treeview .ta -separator / -width 400 -height 160
리스트박스처럼 스크롤바와 함께 사용할 경우가 많으리라 생각합니다. 하지만 가장 중요한 옵션은 -separator로, 트리에 표시할 아이템의 계층을 나타내는 경로(Path)의 구분 문자를 지정합니다. 여기서는 "/"를 사용했지만, "."이나 "::" 등도 많이 씁니다. 트리에 아이템을 추가하려면 insert 서브커맨드를 사용합니다. 이것은 리스트박스의 insert 명령과 거의 같습니다.
set paths {/korea /korea/taejeon /korea/boondang
/korea/boondang/pankyo /korea/busan /china}
set labels {한국지사 대전지점 분당지점 판교출장소 부산지점 중국지사}
for {set i 0} {$i<6} {incr i} {
set ids([lindex $paths $i]) \
[.ta insert end [lindex $paths $i] \
-label [lindex $labels $i] ]
}
end 위치에는 새로운 아이템을 추가할 위치를 지정하는 인덱스나 키워드를 사용할 수 있습니다. end도 그중 하나입니다. 자세한 것은 매뉴얼을 참고하세요. 그리고 그 뒤에 "/korea/boondang/pankyo" 와 같은 경로를 지정하고, 실제로 트리에 표시되는 문자열은 -label로 지정합니다. 이 명령은 아이템의 ID(정수) 를 반환합니다.
한 번 추가한 아이템의 설정을 변경하려면 entry configure 서브커맨드를 사용합니다. 예를 들어, 경로 "/china"의 라벨을 빨간색으로 바꾸려면,
.ta entry configure $ids(/china) -foreground red
와 같이 하면 됩니다. 마찬가지로 값을 얻으려면 entry cget 서브커맨드를 사용합니다.
set label [.ta entry cget $ids(/china) -label]
여기까지가 왼쪽에 표시된 부분의 설명이었습니다.
오른쪽 부분을 만들려면, 먼저 column insert 서브커맨드로 왼쪽부터 차례로 컬럼을 추가합니다. end 뒤의 문자열은 각 열을 고유하게 식별할 컬럼 ID를 적당히 지정합니다. 이때 -text 옵션으로 헤더 문자열을 지정할 수 있습니다.
.ta column insert end "emps" -text 직원수
.ta column insert end "equips" -text 설비대수
.ta column insert end "start_date" -text 시작연월
그리고 각 아이템에 대해 -data 옵션으로 표시하고 싶은 데이터를 지정합니다. 그 데이터는 "컬럼 ID"와 "값"을 번갈아 나열한 리스트 형태입니다.
.ta entry configure $ids(/korea/boondang) \
-data {emps 45 equips 130 start_date 200010}
이 과정을 각 아이템에 대해 반복하면 트리뷰가 완성됩니다.
BLT 그래프의 다양한 기능
여러 막대그래프를 나란히 그리기
아래는 인구 그래프로 아래의 이미지와 같이 동일한 X축, Y축에 대해 여러 개의 막대그래프를 그릴 수 있습니다. 주의해야 할 점은, BLT에서는 기본적으로 한 번에 4개(X축 2개, Y축 2개)까지 그릴 수 있는데, 좌표축이 다른 막대그래프를 여러 개 그리려고 하면 뒤쪽 막대가 앞쪽 막대를 가려서 제대로 보이지 않습니다. 이런 경우는 뒤에서 설명할 꺾은선그래프를 사용하는 것이 좋습니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
wm title . "5대 도시의 인구"
barchart .bc \
-background "#808080" -barwidth 1 -plotbackground "#c0c0c0" \
-plotrelief sunken -plotborderwidth 2 \
-title "5대 도시의 인구" -relief groove -bd 2 -barmode aligned
set x_dummy {1 2 3 4 5}
set Popu1992 {10446 3829 2371 2065 1185}
set Popu2025 {9210 3210 2296 2959 1439}
proc getPrefName {bc n args} {
set prefName {dummy 서울 부산 대구 인천 광주}
return [lindex $prefName $n]
}
.bc element create ELEM1992 -hide no \
-xdata $x_dummy -ydata $Popu1992 -label "1992년 조사" \
-fg "#700040" -bg white -relief sunken -stipple gray50 \
-bindtags TagBarElems
.bc element create ELEM2025 -hide no \
-xdata $x_dummy -ydata $Popu2025 -label "2025년 조사" \
-fg "#006000" -bg white -relief sunken -stipple gray50 \
-bindtags TagBarElems
.bc element bind TagBarElems <Button-1> {
# invtransform은 윈도우 좌표를 좌표축의 좌표로 변환합니다.
# "invtransform" 다음의 x, y 등은 좌표축의 이름입니다.
set x [.bc axis invtransform x %x]
set y [.bc axis invtransform y %y]
tk_messageBox -message "안녕하세요! $x $y"
}
# 좌표축은 기본적으로 x,x2,y,y2의 4가지가 정의되어 있고, x와 y는 기본적으로
# -hide no, x2와 y2는 -hide yes입니다.
.bc axis configure x \
-color white -command getPrefName -title 부현 -titlecolor #c0c0c0 \
-showticks 1
.bc axis configure y \
-color white -min 0 -max 12000 -hide no -showticks 1 \
-title "인구" -titlefont {{굴림} 14 normal}
.bc legend configure -position right -relief groove -bd 2 \
-font 7x14 -fg #000060
.bc grid configure -hide no -color "#808080" -dashes {1 4 1} \
-mapx {} -mapy y
pack .bc -side top
pack [button .cmda -text 닫기 -command exit] -anc e
막대그래프를 나란히 표시하려면, `barchart`의 `element create` 서브커맨드를 그 수만큼 실행하면 됩니다. 물론, 이 때 각 element의 `-xdata`는 동일하게 설정해야 합니다. 또한 여러 막대의 나열 방식을 `barchart` 위젯의 `-barmode` 옵션으로 지정할 수 있다는 것입니다.
- infront: 뒤에 표시되는 아이템이 앞의 아이템 위치를 덮어써서 표시합니다.
- stacked: 막대그래프를 누적 표시합니다.
- aligned: 옆으로 나란히 표시합니다.
- overlap: 옆으로 나란히 표시하지만, 약간 앞의 아이템과 겹치게 표시합니다.
위의 예시에서는 `aligned`를 사용하였으므로, 각 막대가 `element create`를 실행한 순서대로 왼쪽에서 오른쪽으로 나란히 정렬되어 있습니다. 참고로, 좀 더 멋을 내기 위해 막대그래프에 그림자를 추가하여 3D 느낌을 내거나 여러 효과를 주고 있는데, `-stipple` 옵션을 사용하면 속도가 느려지므로, 보통은 사용하지 않는 것이 좋습니다.
여러 개의 꺾은선그래프를 나란히 그리기
아래는 야채 생산량 데이타로 서로 다른 Y축에 꺽은선 그래프를 여러개 그린 예 입니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
wm title . "BLT 샘플"
graph .g -background "#808080" -plotbackground "#c0c0c0" \
-plotrelief sunken -plotborderwidth 2 \
-title "야채 생산량" -relief groove -bd 2
set x_dummy {1 2 3 4 5}
set CornData {4040 4563 6512 7328 6684 5879}
set PoData { 340 328 457 813 489 507}
proc getYear {gc n args} {
set y {dummy 1993 1994 1995 1996 1997 1998}
return [lindex $y $n]
}
.g element create ELEM_AREA -hide no \
-xdata $x_dummy -ydata $CornData -label "옥수수(왼쪽)" \
-color white -dashes {1 5 1} -fill #600000 -pixels 4 \
-linewidth 1 -outline #f00000 -outlinewidth 1 -symbol square \
-bindtags TagElems -mapy y -mapx x
.g element create ELEM_POPU -hide no \
-xdata $x_dummy -ydata $PoData -label "고구마(오른쪽)" \
-color yellow -dashes {1 5 1} -fill #000060 -pixels 4 \
-linewidth 1 -outline #0000f0 -outlinewidth 1 -symbol circle \
-bindtags TagElems -mapy y2 -mapx x
.g element bind TagBarElems <Button-1> {
tk_messageBox -message "안녕하세요!"
}
# 좌표축은 기본적으로 x, x2, y, y2의 4가지가 정의되어 있고
# x와 y는 기본적으로 -hide no, x2와 y2는 -hide yes입니다.
.g axis configure x \
-color white -command getYear -title 연도 -titlecolor #c0c0c0 \
-showticks 1
.g axis configure y \
-color white -min 0 -max 10000 -hide no -showticks 1
.g axis configure y2 \
-color white -min 0 -max 1000 -hide no -showticks 1
.g legend configure -position right -relief groove -bd 2 \
-font 7x14 -fg #000060
.g grid configure -hide no -color "#808080" -dashes {1 4 1} \
-mapx "" -mapy y
pack .g -side top -padx 4 -pady 4
pack [ttk::button .cmda -text 종료 -command exit] -anc e -padx 8
. configure -bg #206020
막대그래프 때처럼 element create를 각각 다른 데이터에 대해 여러 번 실행하면, 같은 X축, Y축 위에 여러 개의 꺾은선그래프를 그릴 수 있습니다. 하지만 이것만으로는 막대그래프 예제와 거의 다르지 않기 때문에, 여기서는 서로 다른 Y축 위에 각각 꺾은선그래프를 그려 보았습니다.
이를 위해서는 먼저, graph 위젯의 axis configure 서브커맨드로 두 번째 Y좌표축인 "y2"축을 설정해줘야 합니다. 특히 -hide no를 반드시 해야 합니다. Y2축은 기본값이 "숨김"이기 때문입니다. 따로 조작하지 않으면, 주 Y축(y)은 왼쪽에, y2축은 오른쪽에 나타납니다.
다음으로 element create로 각 꺾은선을 만들 때, -mapy 옵션으로 해당 꺾은선이 참조할 Y좌표축을 지정합니다. 여기서는 옥수수를 왼쪽 "y"축에, 고구마를 오른쪽 "y2"축에 대응시켰습니다. 아래 꺽은선그래프의 그리기 옵션에 대해 정리해두겠습니다.
- -color : 선 색상 (기본값: 검정)
- -dashes : 선을 점선(파선)으로 할 때 패턴 (기본값: {} → 실선)
- -linewidth : 선 두께 (기본값: 0 → 선 없이 심볼만 표시)
- -fill : 심볼의 채우기 색 (기본값: {} → 채우지 않음)
- -outline : 심볼 테두리 색 (기본값: {} → 테두리 없음)
- -outlinewidth : 테두리 두께 (기본값: 1)
- -symbol : 심볼 모양 (square, circle, diamond, plus, cross, splus, scross, triangle, {}=심볼 없음 중 선택)
- -pixels : 심볼 크기
기본값으로는 심볼이 보이지 않으니 주의 하세요. 그리고 기본 심볼 크기가 너무 크니, 반드시 -pixels 옵션으로 크기를 조정하면 됩니다.
꺾은선그래프와 막대그래프를 함께 표시하기
두 개의 꺾은선그래프를 표시하는 방법을 응용하면, 꺾은선그래프와 막대그래프를 동시에 그리는 것도 아주 쉽습니다. 기본적으로 graph 위젯에서, element create 대신 bar create를 쓰면 됩니다.
package require BLT
namespace import blt::*
namespace import -force blt::tile::*
wm title . "BLT 샘플"
graph .g -background "#808080" -plotbackground "#c0c0c0" \
-plotrelief sunken -plotborderwidth 2 \
-title "야채 생산량" -relief groove -bd 2
set x_dummy {1 2 3 4 5}
set CornData {4040 4563 6512 7328 6684 5879}
set PoData { 340 328 457 813 489 507}
proc getYear {gc n args} {
set y {dummy 1993 1994 1995 1996 1997 1998}
return [lindex $y $n]
}
.g element create ELEM_AREA -hide no \
-xdata $x_dummy -ydata $CornData -label "옥수수(왼쪽)" \
-color white -dashes {1 5 1} -fill #600000 -pixels 4 \
-linewidth 1 -outline #f00000 -outlinewidth 1 -symbol square \
-bindtags TagElems -mapy y -mapx x
.g bar create ELEM_POPU -hide no \
-xdata $x_dummy -ydata $PoData -label "고구마(오른쪽)" \
-fg #000060 -bg white -relief raised -bd 2 \
-bindtags TagElems -mapy y2 -mapx x
.g element bind TagBarElems <Button-1> {
tk_messageBox -message "안녕하세요!"
}
.g axis configure x \
-color white -command getYear -title 연도 -titlecolor #c0c0c0 \
-showticks 1
.g axis configure y \
-color white -min 0 -max 10000 -hide no -showticks 1
.g axis configure y2 \
-color white -min 0 -max 1000 -hide no -showticks 1
.g legend configure -position right -relief groove -bd 2 \
-font 7x14 -fg #000060
.g grid configure -hide no -color "#808080" -dashes {1 4 1} \
-mapx "" -mapy y
pack .g -side top -padx 4 -pady 4
pack [ttk::button .cmda -text 종료 -command exit] -anc e -padx 8
. configure -bg #206020
똑똑하게 범례(legend)도 자동으로 변경됩니다.
원형그래프 (piechart)
아쉽게도 BLT에는 원형그래프를 제공하지 않습니다. 대신 tkpiechart를 사용하시면 됩니다.