Comments
Description
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