...

Tcl/Tk で遊ぼう – 簡単なGUI 作成 –

by user

on
Category: Documents
23

views

Report

Comments

Transcript

Tcl/Tk で遊ぼう – 簡単なGUI 作成 –
Tcl/Tk で遊ぼう
– 簡単な GUI 作成 –
只木進一
2000 年 5 月 17 日
1
Tcl/Tk とは
X は UNIX 上の標準的 GUI システムである。また、server-client 型であることから、ネットワー
クを介した利用が可能であるという利点を持っている。C を使った X ウィンド ウプログラミング
は、最も下層に属する Xlib 、ウィジェットを操作する Xt 、そして特定のウィジェット集合 ( Atena
widget や Motif など ) を使って行われる。しかし 、これらのプログラミングは 、あまり簡単では
ない。
そこで、X 上で簡単に X アプ リケーションを構築する手段が Tcl/Tk である。Tcl/Tk の主な特
徴は以下のようなものである。
• Tcl/Tk は、X 上の GUI を構築するスクリプト型言語。
• スクリプト型言語 Tcl とから利用出来るツールキット Tk から構成される。
• C/C++ とのインターフェイスを持つ。
• Windows や Mac でも動かせる。
• LISP 的なツールキット STK や Perl から使えるツールキット PTK など もある。
2
簡単な例
まず、ウィジェットを作って表示する簡単なプログラムを示す (Program 2.1) 。これは、簡単に
文字を表示し 、QUIT ボタンで停止するものである。
Program 2.1 hello.tcl
#!/usr/local/bin/wish -f
#
# Hello World
label .hello -text "Hello World" -fg "red" -bg "yellow"
button .command -text QUIT -command exit
pack .hello .command -fill x
• label や button は 、それぞれラベルウィジェットとボタンウィジェットを作るコマンド で
ある。
1
Tcl/Tk で遊ぼ う– 簡単な GUI 作成 –(2000 年 5 月 17 日)
2
• ウィジェットは階層化され 、一番上はルートウィジェット . である。.hello と .command は
ルートウィジェットの直下に作られるウィジェットである。
• それぞれのウィジェットの属性に関するオプションを指定することが出来る。
• 生成されたウィジェットは、pack コマンドで、実現される。pack コマンドは、ウィジェット
の詰め込み方を制御出来る。
3
簡単な例 II
次に、副手続きを使った例 (Program 3.1) を示す。この例では、スライドバーを動かして数値を
入力することが出来る。
Program 3.1 scale.tcl
#!/usr/local/bin/wish -f
#
# scale.tcl
set IntValue 0
scale .scale -label Value -from 0 -to 255 -length 10c \
-orient horizontal -command SetValue
label .value -textvariable IntValue -fg purple
button .command -text QUIT -command exit -relief raised
pack .scale .value .command -fill x
proc SetValue value {
global IntValue
set IntValue [.scale get]
}
• 副手続きは proc として定義される。
• 副手続き内の変数は局所変数である。
• 副手続きから外部の変数を参照したい場合には、引数として渡すか、global 宣言を使う。
• 値の代入は set を使う。
• 変数の値の参照は $ を変数名の前に付ける。
4
予め定義されているウィジェット
Tcl/Tk で予め定義されているウィジェットの一覧を表 4.1 に示す。それぞれに対してオンライ
ンマニュアルと例題が用意されている。
また、後述の C とのインターフェイスの機能を使えば 、新たなウィジェットを定義することも出
来る。
Tcl/Tk で遊ぼ う– 簡単な GUI 作成 –(2000 年 5 月 17 日)
3
表 4.1: Tcl/Tk で予め定義されているウィジェット
frame
toplevel
label
button
checkbutton
radiobutton
message
listbox
5
枠を作る
別のウィンド ウを作る
色、文字、ビットマップを表示する
コマンドボタンを作る
チェックボタンを作る
ラジオボタンを作る
メッセージを表示する
表を表示する
scrollbar
scale
スクロールバーを作る
entry
menu
一行入力用ウィジェットを作る
プルダウン メニューを作る
menubutton
メニューを表示するボタンを作る
text
canvas
編集可能なテキスト表示用ウィジェットを作る
スライド バーを作る
作図用ウィジェットを作る
色見本を作る
次に X で定義されている色の見本を表示するプログラム (Program 5.1) を示す。ここでは 、X
で定義されている色データを /usr/X11R6/lib/X11/rgb.txt から読み取り、その中から正規表現
を使って色の名前を切り出し 、listbox ウィジェットに表示している。listbox ウィジェットで色
を選択すると、label ウィジェットにその色が表示される。色の設定は、既に存在している label
ウィジェットに config コマンド を使うことで行う。また、listbox ウィジェットには、スクロー
ルバーが付き、そこの操作でスクロールアップ及びダウンが可能になっている。
• open を使ったファイルのオープン
• 繰り返しや条件分岐
• regexp を使った正規表現の取り扱い
• config キーワード による、既に存在しているウィジェットの属性変更
• bind によるウィジェットへの動作の追加
• listbox ウィジェットに、scrollbar を付ける
Tcl/Tk で遊ぼ う– 簡単な GUI 作成 –(2000 年 5 月 17 日)
Program 5.1 color3.tcl
#!/usr/local/bin/wish -f
# カラーのデータベースを読む
set colordb "/usr/X11R6/lib/X11/rgb.txt"
proc list_color {w} {
global colordb
set fr [open $colordb r]
$w delete 0 end
foreach line [split [read $fr] \n] {
if {[regexp {( *[0-9]+) ( *[0-9]+) ( *[0-9]+)[^a-zA-Z]*([a-zA-z].*)$}\
$line match r g b colorname]} {
$w insert end $colorname
}
}
close $fr
}
# カラーを設定する
proc setcolor {w y} {
global colorname
set i [$w nearest $y]
set colorname [$w get $i]
.color config -bg $colorname
}
button .command -text QUIT -command exit -relief raised
set ct [frame .colortable -relief sunken]
set colorname none
label .color -textvariable colorname -fg black
pack $ct -fill both -expand yes
pack .command .color -fill both -expand no
# スクロールバーの設定
scrollbar $ct.xscroll -command "$ct.colorlist xview" -orient horizontal
scrollbar $ct.yscroll -command "$ct.colorlist yview" -orient vertical
pack $ct.xscroll -side top -fill x -expand no
pack $ct.yscroll -side left -fill y -expand no
# リストボックスの設定
listbox $ct.colorlist -width 40 -height 20\
-xscroll "$ct.xscroll set" -yscroll "$ct.yscroll set" -selectmode single
pack $ct.colorlist -side left -fill both -expand yes
# クリック時の動作の定義
bind $ct.colorlist <Button-1> {setcolor %W %y}
# カラーデータベースを読み表示する
list_color $ct.colorlist
4
Tcl/Tk で遊ぼ う– 簡単な GUI 作成 –(2000 年 5 月 17 日)
6
5
ファイルブラウザを作る
現実的応用例であるファイルブラウザの例を示す (Program 6.1) 。ここでは、外部コマンド にな
る UNIX のコマンド ls を Tcl/Tk のコマンド exec によって起動し 、その結果を listbox ウィ
ジェットに保存している。
この例では、ディレクトリを選択するとそのディレクトリへ移動するが 、ファイルを選択すると
何も起こらない。ファイルを選択した場合にそのファイルを開くようにすればアプリケーションの
中に組み込んで利用することが出来る。
Program 6.1 list1.tcl
#!/usr/local/bin/wish -f
proc list_file {w} {
set dir [pwd]
set flist [exec ls -a $dir]
$w delete 0 end
foreach line $flist {
if {[file isdirectory $line]} {
$w insert end ${line}/
} else {
$w insert end ${line}
}
}
}
proc fileopen {w y} {
set i [$w nearest $y]
set filename [$w get $i]
if {[file isdirectory $filename]} {
cd $filename
list_file $w
}
}
button .command -text QUIT -command exit
set ft [label .filetable -relief sunken]
pack $ft -fill both -expand yes
pack .command -fill both -expand no
-relief raised
scrollbar $ft.xscroll -command "$ft.filelist xview" -orient horizontal
scrollbar $ft.yscroll -command "$ft.filelist yview" -orient vertical
pack $ft.xscroll -side top -fill x -expand no
pack $ft.yscroll -side left -fill y -expand no
listbox $ft.filelist -width 40 -height 20\
-xscroll "$ft.xscroll set" -yscroll "$ft.yscroll set" -selectmode single
pack $ft.filelist -side left -fill both -expand yes
bind $ft.filelist <Double-Button-1> {fileopen %W %y}
list_file $ft.filelist
7
C/C++とのインターフェイス
C/C++ とのインターフェイスについて述べる。正確には 、Tcl/Tk と C/C++との連係とは 、
Tcl/Tk のコマンド インタプ リタ wish に代わる新しいコマンド インタプ リタを作ることに対応す
Tcl/Tk で遊ぼ う– 簡単な GUI 作成 –(2000 年 5 月 17 日)
6
る。具体的に行うべきことは 、Tcl/Tk の状態を初期化し 、Tcl/Tk スクリプトから起動可能な新
しいコマンド を定義することである。ここでは 、簡単な図を作図するプログラムを例にして説明
する。
Program 7.1 main.cc その 1
#include
#include
#include
#include
#include
<stdlib.h>
<iostream.h>
<string.h>
<tk.h>
"state.h"
//#define NumState 2
static State *state;
static int NumState;
int Tcl_AppInit(Tcl_Interp*);
int CreateState(ClientData clientData, Tcl_Interp* interp,
int argc, char** argv);
int MoveState(ClientData clientData, Tcl_Interp* interp,
int argc, char** argv);
int main(int argc,char *argv[])
{
Tk_Main(argc,argv,Tcl_AppInit);
exit (0);
}
int Tcl_AppInit(Tcl_Interp* interp)
{
if (Tcl_Init(interp) == TCL_ERROR) return TCL_ERROR;
if (Tk_Init(interp) == TCL_ERROR) return TCL_ERROR;
Tcl_CreateCommand(interp, "CreateState", CreateState,
(ClientData)Tk_MainWindow(interp),
(Tcl_CmdDeleteProc *)NULL);
Tcl_CreateCommand(interp, "MoveState", MoveState,
(ClientData)Tk_MainWindow(interp),
(Tcl_CmdDeleteProc *)NULL);
return TCL_OK;
}
Program 7.1 及び 7.2 は 、Tcl/Tk の初期化及び新しいコマンド CreateState 及び MoveState
の定義である。Tcl/Tk の初期化関数 Tk_Main が初期化を行うため 、新しいコマンド を Tk_Main
に渡す関数 Tcl_AppInit に渡している。Tcl_Interp は Tcl インタプ リタであり、そのインタプ
リタに新しいコマンドが追加される。
Program 7.3 及び 7.4 は描画されるラベル付き円のクラスの記述である。Tcl/Tk の canvas ウィ
ジェットが持っている描画機能を使っている。ラベルと円を一つのタグに結びつけて、一体として
動くようになっている。
Program 7.5 及び 7.6 は新しく作成されたインタプ リタ test を使ったスクリプトである。新し
く定義されたコマンド CreateState と MoveState を使うことが出来る。
Program 7.7 は新しいインタプ リタ test を構成するための Imakefile である。
Tcl/Tk で遊ぼ う– 簡単な GUI 作成 –(2000 年 5 月 17 日)
Program 7.2 main.cc その 2
int CreateState(ClientData clientData, Tcl_Interp* interp,
int argc, char** argv)
{
char a[256];
NumState = atoi(argv[2]);
state = new State[NumState];
for(register int i=0;i<NumState;i++){
state[i].set_coordinate(300,100+50*i);
sprintf(a,"q%d",i);
state[i].set_label(a);
state[i].Create(interp,argv[1]);
}
return 0;
}
int MoveState(ClientData clientData, Tcl_Interp* interp,
int argc, char** argv)
{
int j;
for(register int i=0;i<NumState;i++){
if(strcmp(argv[1],state[i].readlabel())==0)j=i;
}
state[j].set_coordinate(atoi(argv[2]),atoi(argv[3]));
return 0;
}
Program 7.3 state.h
class State{
private:
int x,y; // 表示座標
int size;
char label[256];
public:
State(void){};
int set_coordinate(int xx,int yy){x=xx;y=yy;};
int set_label(char *ll){sprintf(label,"%s",ll);};
const char *readlabel(void) const {return label;};
int Create(Tcl_Interp*,char*);
};
7
Tcl/Tk で遊ぼ う– 簡単な GUI 作成 –(2000 年 5 月 17 日)
Program 7.4 state.cc
#include
#include
#include
#include
<stdlib.h>
<iostream.h>
<tk.h>
"state.h"
int State::Create(Tcl_Interp* interp,char* path)
{
char string[256];
size=50;
// 円を描く
sprintf(string,"%s create oval %d %d %d %d -fill orange -width 5",
path,x-size/2,y-size/2,x+size/2,y+size/2);
Tcl_Eval(interp,string);
//ラベル名をタグとして登録
sprintf(string,"%s addtag %s closest %d %d", path,label,x,y);
Tcl_Eval(interp,string);
// ラベルを描く
sprintf(string,"%s create text %d %d -text %s -fill black", path,x,y,label);
Tcl_Eval(interp,string);
//ラベル名をタグとして登録
sprintf(string,"%s addtag %s closest %d %d", path,label,x,y);
Tcl_Eval(interp,string);
return 1;
}
8
Tcl/Tk で遊ぼ う– 簡単な GUI 作成 –(2000 年 5 月 17 日)
Program 7.5 Main.tcl その 1
#!test
set bf [frame .buttonframe]
set cf [frame .canvasframe]
pack $bf $cf
button $bf.quit -text QUIT -command exit
button $bf.start -text START -command {start $cf}
pack $bf.quit $bf.start -side left
proc start {w} {
set nn [getInput "Enter Number of State"]
scrollbar $w.xscroll -command "$w.canvas xview" -orient horizontal
scrollbar $w.yscroll -command "$w.canvas yview" -orient vertical
pack $w.xscroll -side top -fill x -expand no
pack $w.yscroll -side left -fill y -expand no
canvas $w.canvas -width 500 -height 500\
-xscrollcommand "$w.xscroll set" -yscrollcommand "$w.yscroll set"
pack $w.canvas -side left -fill both -expand yes
bind $w.canvas <Button-1> {CanvasMark %x %y %W}
bind $w.canvas <B1-Motion> {CanvasDrag %x %y %W}
CreateState $w.canvas $nn
}
# マウスでオブジェクトを選択
proc CanvasMark {x y w} {
global canvas
#マウスでクリックした位置に最も近いオブジェクトのタグを探す
set t [$w gettags [$w find closest $x $y]]
regexp {([^ ]*) *(.*)$} $t match t0 t1
#そのタグと座標を格納する
set canvas($w,obj) $t0
set canvas($w,x) $x
set canvas($w,y) $y
}
9
Tcl/Tk で遊ぼ う– 簡単な GUI 作成 –(2000 年 5 月 17 日)
Program 7.6 Main.tcl その 2
# マウスでオブジェクトを移動
proc CanvasDrag {x y w} {
global canvas
set dx [expr $x - $canvas($w,x)]
set dy [expr $y - $canvas($w,y)]
$w move $canvas($w,obj) $dx $dy
set canvas($w,x) $x
set canvas($w,y) $y
MoveState $canvas($w,obj) $x $y
}
proc getInput {st} {
global dumdum
set t [toplevel .newtop]
label $t.message -text $st
entry $t.entry -textvariable dumdum
button $t.ok -text OK -comman {set okok 1}
pack $t.message $t.entry $t.ok -side top
tkwait visibility $t
focus $t.entry
grab $t
tkwait variable okok
grab release $t
destroy $t
return $dumdum
}
Program 7.7 Imakefile
SRCS = main.cc state.cc
OBJS = main.o state.o
EXTRA_INCLUDES = -I/usr/local/include/tcl8.0jp -I/usr/local/include/tk8.0jp
CXXEXTRA_INCLUDES = $(EXTRA_INCLUDES)
LIBS = -lX11 -ltcl80jp -ltk80jp
AllTarget(test)
NormalLibraryObjectRule()
NormalCplusplusProgramTarget(test,$(OBJS),NullParameter,NullParameter,$(LIBS))
DependTarget()
10
Fly UP