HOME Sather を試そう download 書き込む

14. GUI プログラミング


1. はじめに

Sather には Tk へのインターフェースがあり、システムに Tcl/Tk がインストールされていれば 簡単に GUI プログラムを書くことができます。

Sather の開発が停滞していることもあり、機能はそれほど豊富ではありませんが、普通の GUI を作る分には十分でしょう。 ネイティヴコードにコンパイルされるので、Tcl/Tk, Python/Tk などと比べて実行速度は格段に速いです。

2. ドキュメントとデモスクリプト

GUI プログラミングのドキュメントが $SATHER_HOME/Doc/Gui/Doc.fmk.ps にあります。また、 Sather Class Index にも記載があります。ただ、これらのドキュメントの記述は実際と少し違っているので、 ソースコードを覗いてみる必要があります。GUI 関係のライブラリーは $SATHER_HOME/Library/System/Gui にあります。

また、デモスクリプトが $SATHER_HOME/Library/System/Gui/Demos にあります。 Doc.fmk.ps の 2 頁に プログラム名と main のあるクラス名が載っている表があるので、それをみてコンパイルします。 例えば、pizza.sa は次のようにコンパイルします。コンパイルの際に -gui オプションをつけます。

sacomp -gui -main PIZZA_MAIN -o pizza pizza.sa
コンパイルすると山ほど Warning が出ますが、無事実行ファイルができます。 pizza を実行すると図1のような Widget が現れます。ピザの種類、サイズ、トッピングを選択すると値段が表示されます。

図1:pizza の実行画面

3. Hello World!

お決まりの GUI 版 Hello World! を書いてみます([code 1])。

[code 1]: tkhello.sa

01:     -- a simple Tk hello world.
02:     
03:     class MAIN is
04:        include GUI_UTIL;  -- このクラスを include すると init だけ自分で定義すれば GUI プログラムが書けます。
05:     
06:        init is
07:           label::=#TK_LABEL(root_window,TK_LABEL_CFG::std.text("Hello World!")); -- 下記参照
08:        end;
09:     end;
コンパイルして実行すると図2のような Widget が表示されます。
$ sacomp -gui tkhello.sa -o tkhello
$ ./tkhello

図2: Sather/Tk を使った簡単な Hello World。

3.1. GUI_UTIL

GUI_UTIL には 表1に示す GUI プログラムに必要な変数、メソッドが定義されており、プログラマーは init だけを自分で定義すれば 済みます。

表1: GUI_UTIL で定義されている変数およびメソッド
メソッド、変数 説明
(stab) init プログラマーはこの部分を自分で書く必要があります。
root_window トップレベルの widget です。
packer widget を pack するときに使います。TK_PACK::create と同じです。
quit_routine プログラムを終了するための ROUT です。
main 通常はいじる必要はありません。
startup_gui Widget を作ります。通常は使いません。
gui_loop イベントを待ちます。通常は使いません。

3.2. Widget class

Sather には TK_LABEL, TK_BUTTON などの widget クラスがあり、これらは $TK_WIDGET のサブクラスです。 Sather では Widget と Widget configure が別のクラスになっています。 Widget は 親 Widget, 設定、配置方法を指定して create されます。ただ、これらの3つの引数は省略可能です。 [code 1] の 7 行目では widget が1つしかないので配置方法が省略されています。 詳しくは $SATHER_HOME/Library/System/Gui/widget.sa をみてください。

3.3. Widget config class

Sather では Widget の設定は専用のクラスで行います。TK_LABEL の場合は TK_LABEL_CFG で設定を行います。 std メソッドはほとんど create と同じです。text メソッドで 表示するテキストを指定します。

4. ボタンと Widget の配置

次に TK_BUTTON と TK_PACK について説明します。例として、文字色と背景色が入れ替わるラベルを作ります(図3)。 "TOGGLE" ボタンを押すと白黒が入れ替わります。

図3:文字色と背景色が入れ替わる Hello World。

ソースコード (button_hello.sa) は [code 2] の様になります。
01:     -- a simple Tk hello world with toggle button.
02:     
03:     class MAIN is
04:        include GUI_UTIL;
05:     
06:        private attr label:TK_LABEL;                 -- Hello World を表示するラベル
07:        private attr label_config:TK_LABEL_CFG;      -- ラベル設定クラスのインスタンス
08:        private attr black_foreground:BOOL;          -- 黒文字のとき true
09:     
10:        toggle is                         -- ラベルの白黒を入れ替えるメソッド
11:           if black_foreground then
12:     	 label.configure(label_config.foreground("white").background("black")); -- configure メソッドを使って設定を変える。
13:     	 black_foreground:=false;
14:           else 
15:     	 label.configure(label_config.foreground("black").background("white"));
16:     	 black_foreground:=true;
17:           end;
18:        end;
19:     
20:        init is                           -- 初期設定メソッド
21:           label_config:=#;
22:           label:=#(root_window, 
23:     		label_config.text("Hello World!").padx(6.0).pady(6.0).foreground("black").background("white"),
24:     		packer.top_grow_horiz_n_vert.anchor(TK_ANCHOR::center));
25:           frame::=#TK_FRAME(root_window, packer.bot);
26:     
27:           button1::=#TK_BUTTON(frame,
28:     			  TK_BUTTON_CFG::std.text("EXIT").padx(6.0).pady(6.0),
29:     			  packer.left);
30:           button1.command(quit_routine);
31:           
32:           button2::=#TK_BUTTON(frame,
33:     			  TK_BUTTON_CFG::std.text("TOGGLE").padx(6.0).pady(6.0),
34:     			  packer.right);
35:           button2.command(bind(toggle));
36:           black_foreground:=true;
37:        end;
38:     end;

4.1. TK_PACK

Widget を配置するクラスです。GUI_UTIL を import すると packert というインスタンスが用意されています。 TK_PACK には表2に示すメソッドがあります。
表2:TK_PACK のインスタンス
メソッド 説明
top widget を上に配置します。
top_grow_horiz 上に配置して横に伸ばします。
top_grow_horiz_n_vert 上に配置して上下左右に伸ばします。
top_n_grow_vert 上に配置して上下に伸ばします。
bot widget を下に配置します。
bot_grow_horiz 下に配置して横に伸ばします。
bot_grow_horiz_n_vert 下に配置して上下左右に伸ばします。
bot_n_grow_vert 下に配置して上下に伸ばします。
left widget を左に配置します。
left_grow_vert 左に配置して上下に伸ばします。
left_grow_vert_n_horiz 左に配置して上下左右に伸ばします。
left_n_grow_horiz 左に配置して左右に伸ばします。
right widget を右に配置します。
right_grow_vert 右に配置して上下に伸ばします。
right_grow_vert_n_horiz 右に配置して上下左右に伸ばします。
right_n_grow_horiz 右に配置して左右に伸ばします。
external_padding(x, y:FLT) 上下左右のマージンを設定します。
internal_padding(x, y:FLT) 上下左右のパディングを設定します。
anchor(a:TK_ANCHOR) 配置の微調整をします。

4.2. TK_ANCHOR

TK_PACK で配置位置が一意に決まらない場合、配置の微調整をします。表3に示すメッソドがあります。
表3: TK_ANCHOR のメッソド
メッソド説明
center 配置可能範囲の中央に配置します。
n 配置可能範囲の上に配置します。
nw 配置可能範囲の左上に配置します。
w 配置可能範囲の左に配置します。
sw 配置可能範囲の左下に配置します。
s 配置可能範囲の下に配置します。
se 配置可能範囲の右下に配置します。
e 配置可能範囲の右に配置します。
ne 配置可能範囲の右上に配置します。

[code 2] のプログラムは図4に示すようにラベルが伸縮し、Hello World が常に中央に表示されます。

図4:button_hello の widget を引き伸ばしたところ。

4.3. TK_FRAME

Widget のグループを作るのに使います [code 2] ではボタンを横に2つ配置するの使っています (25,27, 32 行)。

4.4. TK_BUTTON

ボタンを作ります。ボタンを押したときの動作は command メソッドで定義します。このメソッドは引数も返値もない ROUT を引数にとります (30, 35 行)。

5. ラジオボタンで文字色と背景色を変える

ラジオボタンで文字色と背景色を変えられるようにした Hello World を図5に示します。

図5:ラジオボタンで文字色と背景色が選択できる Hello World。

[code 3] にソースコード (radio_hello.sa) を示します。

01:     -- a simple Tk hello world with radio buttons.
02:     
03:     class MAIN is
04:        include GUI_UTIL;
05:     
06:        private const fgcolors:ARRAY{STR}:=|"white","black", "red", "blue", "yellow"|;                     -- 文字色
07:        private const bgcolors:ARRAY{STR}:=|"whitesmoke", "dimgray", "salmon", "skyblue", "lightyellow"|;  -- 背景色
08:        private attr label:TK_LABEL;
09:        private attr label_config:TK_LABEL_CFG;
10:        private attr fg:TK_VAR;
11:        private attr bg:TK_VAR;
12:     
13:        config_label is
14:           label.configure(label_config.foreground(fg.query).background(bg.query));
15:        end;
16:     
17:        rbuttons(name:STR, colors:ARRAY{STR}, fr:TK_FRAME, v:TK_VAR, default:STR) is  -- ラジオボタンのセットを作るメソッド
18:           s:STR;
19:           r::=#TK_LABEL(fr, TK_LABEL_CFG::std.text(name), packer.top);
20:           loop
21:     	 s:=colors.elt!;
22:     	 b::=#TK_RADIO(fr, 
23:     		       TK_RADIO_CFG::std.text(s).variable(v).val(s), 
24:     		       packer.top.anchor(TK_ANCHOR::w));
25:     	 b.command(bind(config_label));
26:     	 if s=default then b.select; end;                                    -- select メソッドで最初に選択されるボタンを指定します。
27:           end;
28:        end;
29:     
30:     
31:        init is
32:           label_config:=#;
33:           fg:=#;
34:           bg:=#;
35:           label:=#(root_window, 
36:     	       label_config.text("Hello World!").padx(6.0).pady(6.0).foreground("black").background("white"),
37:     	       packer.top_grow_horiz_n_vert.anchor(TK_ANCHOR::center));
38:           frame::=#TK_FRAME(root_window, packer.bot);
39:     
40:           -- creating radio buttons
41:           fconfig:TK_FRAME_CFG:=TK_FRAME_CFG::std.borderwidth(3.0).relief_ridge;      
42:           rbuttons("foreground", fgcolors, #TK_FRAME(frame, fconfig, packer.left), fg, "black");   -- 文字色を選ぶラジオボタンを作成
43:           rbuttons("background", bgcolors, #TK_FRAME(frame, fconfig, packer.right), bg, "whitesmoke"); -- 背景色を選ぶラジオボタンを作成
44:        end;
45:     end;

5.1. TK_VAR

TK_RADIO や TK_CHECK で使う変数です。query メソッドで文字列に変換されます。

5.2. TK_RADIO

ラジオボタンです。 command メソッドで選択されたときの動作を指定します。

5.3. TK_RADIO_CFG

TK_RADIO を設定するクラスです。 variable メソッドで値を格納する TK_VAR を、value メソッドで格納される値(文字列)を指定します。

6. Hello World をキャンバス上に表示する。

次に TK_CANVAS の簡単な例を示します。このプログラムを実行すると図6に示すような widget が表示され、キャンバス上で マウス左クリックすると文字列がそこに移動します。また、マウスが文字列上に来ると文字が赤になります。

図6:TK_CANVAS を使った Hello World。キャンバス上でマウス左クリックすると文字列がそこに移動する。

[code 4] にソースコード (canvas_hello.sa) を示します。

[code 4] canvas_hello.sa

01:     -- a simple Tk hello world with Tk canvas
02:     
03:     class MAIN is
04:        include GUI_UTIL;
05:     
06:        private attr cvs:TK_CANVAS;
07:        private attr tag:TK_CANVAS_TAG;
08:        private attr atag:ARRAY{TK_CANVAS_TAG};
09:     
10:        mv(a:TK_EVENT_INFO) is                          -- マウス左クリックで Hello World を動かす関数
11:           cvs.delete(tag);                             -- もともとあった Hello World を消す。
12:           x,y:INT;
13:           a.mouse_coordinates(out x, out y);           -- マウスの位置を取得
14:           cvs.draw_text("Hello World! @(" + x + "," + y + ")",
15:     		    x.flt, y.flt, atag, TK_CTEXT_CFG::std.fill("navy"));   -- そこに新たに Hello World を表示する。
16:        end;
17:     
18:        rd(a:TK_EVENT_INFO) is
19:           cvs.text_configure(tag, TK_CTEXT_CFG::std.fill("red"));  -- 文字色を赤にする
20:        end;
21:     
22:        nv(a:TK_EVENT_INFO) is
23:           cvs.text_configure(tag, TK_CTEXT_CFG::std.fill("navy"));  -- 文字色を Navy にする。
24:        end;
25:     
26:        init is
27:           dummy:$OB:=void;       -- タイトルバーにタイトルを表示するにはこのおまじないが必要(いまいちダサい)
28:           TK_WINDOW_MGR::title(root_window, "Hello World! on Tk Canvas");   -- タイトルバーにタイトルを表示する。
29:           tag:=#;
30:           atag:=#(|tag|);
31:           cvs:=#(root_window,                  -- スクロールバー無しのキャンバスを作る。
32:     	     TK_CANVAS_CFG::std.background("cornsilk").hscroll(false).vscroll(false).scroll_region(0.0,0.0,200.0,200.0)); 
33:           cvs.draw_text("Hello World! @(100.0,100.0)", 100.0, 100.0, atag,TK_CTEXT_CFG::std.fill("navy"));  -- Hello World を (100,100) に描く。
34:           cvs.bind_event(TK_BUTTON_EVENT::B1, bind(mv(_)));  -- マウス左クリックで mv が呼ばれる。
35:           cvs.bind_event(TK_EVENT::Widget_enter, tag, bind(rd(_)));  -- マウスが文字列に入ると rd が呼ばれる。
36:           cvs.bind_event(TK_EVENT::Leave, tag, bind(nv(_)));  -- マウスが文字列から出ると nv が呼ばれる。
37:        end;
38:     end;

6.1. TK_CANVAS, TK_CANVAS_CFG

キャンバスを作るには TK_CANVAS を それを設定するには TK_CANVAS_CFG を使います。 キャンバス上には楕円、線、長方形、多角形、文字列を描くことができます。 詳しくは $SATHER_HOME/Library/System/Gui/canvas.sa をみてください。

6.2. TK_CANVAS 上のオブジェクト

TK_CANVAS 上のオブジェクトはそれようの設定クラスを用いて設定します。表4にオブジェクトと設定クラスを示します。

表4: TK_CANVAS 上のオブジェクトとその設定クラス
オブジェクト 設定クラス
TK_LINE_CFG
楕円 TK_OVAL_CFG
多角形 TK_POLY_CFG
長方形 TK_RECT_CFG
文字列 TK_CTEXT_CFG
window TK_CWIND_CFG

6.3. TK_EVENT, TK_BUTTON_EVENT, TK_KEY_EVENT

TK_CANVAS の bind_event で、動作と関連付けられるイベントです。 カーソルの移動などの TK_EVENT, マウスボタンに関する TK_BUTTON_EVENT, キーボードに関する TK_KEY_EVENT があります。 詳しくは $SATHER_HOME/Library/System/Gui/events.sa をみてください。

6.4. TK_EVENT_INFO

bind_event で関連付けられる動作は ROUT{TK_EVENT_INFO} です。
TK_EVENT_INFO にはマウスの位置を取得する mouse_coordinates(out x, out y) などのメソッドがあります。 詳しくは $SATHER_HOME/Library/System/Gui/events.sa をみてください。

6.5. TK_WINDOW_MGR

タイトルバーにタイトルをつけるときに使います。やり方は [code 4] の 27,28 行目をみてください。

7. Tk Text 版 more

最後に Tk_Text について説明します。ごく簡単な例としてファイル内容表示プログラムを書いてみました。実行すると図7に示すような Widget が表示されます。

図7:TK_TEXT を使ったファイル内容の表示。

コード (tkmore.sa) を [code 5] に示します。

[code 5] tkmore.sa

01:     -- a simple Tk version more
02:     
03:     class MAIN is
04:        include GUI_UTIL main->;
05:        
06:        private attr s,fn:STR;
07:     
08:        main(av:ARRAY{STR}) is        -- 引数をとるように main を書き換える
09:           if av.size=2 then
10:     	 fn:=av[1];              -- av[1] で与えられたファイルを開いて、
11:     	 f::=FILE::open_for_read(fn);
12:     	 if f.error then
13:     	    #ERR + "Cannot open " + fn +"\n";
14:     	    UNIX::exit(1);
15:     	 else
16:     	    s:=f.str;            -- その内容を読み込んで、
17:     	    f.close;             -- ファイルを閉じる
18:     	    GUI_APP_END::startup;
19:     	    init;
20:     	    GUI_APP_END::main_loop;
21:     	 end;
22:           else
23:     	 #ERR + "Usage: tkmore FILENAME\n";
24:           end;
25:        end;
26:     
27:        init is
28:           dummy:$OB:=void;
29:           TK_WINDOW_MGR::title(root_window, fn);     -- タイトルバーにファイル名を表示する。
30:           txt::=#TK_TEXT(root_window, TK_TEXT_CFG::std.hscroll(false).background("ivory")); -- 水平スクロールバーなしの TK_TEXT を作る。
31:           txt+s;   -- ファイルの内容を表示するのは '+' を使えばよい。
32:        end;
33:     end;

コンパイルして実行すると図7に示した画面が現れます。実行速度が速く、意外と便利なソフトです。

$sacomp -gui -o tkmore tkmore.sa
$./tkmore tkmore.sa

8. 終わりに

Tcl/Tk や Python/Tk (Tkinter) に比べれば機能は限られていますが、簡単な GUI なら Sather/Tk で書くことができます。 実行速度が速いのと、C や C++ で Tk を使うよりも格段に短いプログラムになるのがとりえです。

さらに詳しい情報は Sather Class Index などをみてください。 また、付録にここで示したコードをつけておきます。


HOME Sather を試そう download 書き込む