以下に示す SimpleDialog は、多くのプログラムで多用される、操作者との単純な応答を行うパネルです。まず、その定義は、
SimpleDialog[message_, texts_]:=Module[
{w, tl, fr, i, b, l = Length[texts], r}, (ア) 局所シンボルの定義
w = Window[];
tl = TextLabel[w, Text -> message,
PadX -> 20, PadY -> 10]; (イ) 引き数 message の表示
fr = Frame[w]; (ウ) ボタンを並べる Frame
i = 1;
Scan[( (エ) Do を用いてもよい。
With[{m = #}, (オ) # は texts の各要素
b[i] = Button[fr, Text -> m, (カ) 各ボタン b[i] の定義
Command :> TkReturn[m], (キ) コマンドの設定
BD -> 4, PadX -> 10, PadY -> 10,
Side -> "left", Relief -> "ridge"]];
i++)&,
texts];
b[l][Relief] = "raised"; (ク) l は texts の長さ
Bind[b[l], "<Key-Return>", (ケ) 最後のボタンをデフォルト
TkReturn[texts[l]]]; (Rreturn キーで反応)
b[l][Focus$Set]; (コ) キー応答のための焦点
r = TkWait[]; (サ) 応答待ち
w =.; (シ) この Window を削除
Update[]; (ス) 表示を更新
r]; (セ) 押されたキーラヴェルを返す
です。この使い方は
シンボル = SimpleDialog[メッセージ, ボタンリスト]
のようにします。ここでメッセージは文字列、ボタンリストはボタンの表面に書く文字列のリストです。 例えば、
result = SimpleDialog[
"Do you want to save?",
{"Cancel", "Don't save", "Save"}];
の様にすると、図のような Window が現われます。 ここでいずれかのボタンをクリックすると、その名称が返され、シンボル result に割り当てられます。
図 SimpleDiaplogの例
また、最後のボタン (この例では "Save") はデフォルトとして (ケ)(コ) により Return キーを押しても クリックと同じ結果になるように設定されています。
SimpleDialog は Module を利用した関数で、作られるウインドウ及び内部の部品はすべて (ア) で定義した局所シンボルに割り当てられ、実行後には (シ)(ス) によりすべて消去されるようになっています。
作成されるボタンの数は制限ありません。内部では (エ) の Scan により、ボタンは b[i] に割り当てられています。(オ) で With が用いられていますが、それは (キ) のコマンドの TkReturn[m] の m の値を前もって決めたいためです。With により、m は 値 #、即ち texts の各要素になるわけですが、ここを Command :> TkReturn[#] と書きますと、:> の右辺が評価されないため、# がそのまま残ってしまい、後で TkReturn[#] としても # が未定義になってしまいます。
(サ) では TkWait[] により応答を待ちます。この SimpleDialog 自身が別のボタンの応答コマンドとして使用される場合などでは、TkWait[] が幾重にも重なりますが、それは構いません。
実際のアプリケーションではグラフを描くウインドウの大きさを操作者が必要に応じて変更したくなる場合が多々あります。次の例はこのような場合にそのウインドウの中に描かれたグラフを、そのウインドウの大きさに応じて再描画するものです。
FFS;
$DisplayFunction = CanvasDrawer;
w = Window[];
c1 = Canvas[w, Height -> 100, Width -> 150,
Side -> "left",
Expand -> True, Fill -> "both"]; (ア)
c2 = Canvas[w, Height -> 100, Width -> 150,
Side -> "left",
Expand -> True, Fill -> "both"]; (イ)
pl:=(
Canvas$Widget = c1;
Plot[Sin[x],{x,-Pi,Pi}];
Canvas$Widget = c2;
Plot[Cos[x],{x,-Pi,Pi}]); (ウ)
pl;
Bind[w, "<Configure>", pl]; (エ)
TkWait[];
この例は (ア)(イ) で作った左右に並ぶ二つの Canvas c1, c2 に、(ウ) のコマンド pl でそれぞれにグラフを出力します。ここで、Canvas の属性に Expand-> True, Fill -> "both" と指定することにより、ウインドウの伸縮に Canvas が追随して変化することを保証します。そして、(エ) でコマンド pl とウインドウの属性の変更に伴うイヴェント"<Configure>" を結合することにより、ウインドウの伸縮の度に再描画してグラフの大きさを追従させることができます。(ただし、その場合、各 Canvas は最初に指定したサイズを最小サイズとして保持しようとするため、あまりウインドウが小さくなると、二つのグラフの大きさが不均等になります。)
この例を 図に示します。
図 イヴェント "<Configure>"によりウインドウのサイズの変更にグラフの大きさを追従させることができる。(A) 初期状態。(B) マウスによりウインドウを拡大した例
さて、次の例は、Plot で書いたグラフ上でドラッグした領域を拡大してプロットしなおすという、世の中によくある機能を実現したものです。
FFS;
StartSelect := Module[ (ア)
{{x, y, c= {X, Y, Widget}/.$Event},
With[{c},
c[Create$Line]={x, y, x, y,
Fill -> "blue", Stipple -> "gray50",
Tags -> "selection"}];
Bind[c, "<Motion>", DragSelect]; (イ)
Bind[c, "<ButtonRelease-1>", RedrawSelect]; (ウ)
selstart=selend={x,y}]; (エ)
DragSelect := Module[ (オ)
{{x, y, c= {X, Y, Widget}/.$Event},
With[{c, x0 = selstart[[1]], y0 = selstart[[2]]},
c[Delete] = "selection";
c[Create$Line] = {x0, y0, x, y0, x, y, x0, y, x0, y0,
Fill -> "blue", Stipple -> "gray50",
Tags -> "selection"}; (カ)
selend = {x,y}]];
RedrawSelect := Module[ (キ)
{c = Widget/.$Event},
c[Delete] = "selection";
If[selstart <> selend,
z0 = (selstart - Canvas$Offset) / Canvas$Scale;
z1 = (selend - Canvas$Offset) / Canvas$Scale;
pl[z0, z1]];
Bind[c, "<Motion>"]; (ク)
Bind[c, "<ButtonRelease-1>"]]; (ケ)
pl[z0_,z1_] := (コ)
Plot[f[x],
{x, Min[z0[[1]], z1[[1]]], Max[z0[[1]], z1[[1]]]},
PlotRange ->
{Min[z0[[2]], z1[[2]]], Max[z0[[2]], z1[[2]]]}];
plinit := pl[{-1, -1.2}, {1, 1.2}];
w = Window[];
c = Canvas[w, Width -> 600, Height -> 400];
$DisplayFunction = CanvasDrawer;
Canvas$Widget = c;
f[0] = 0;
f[x_] := Sin[1/x];
plinit;
Bind[c, "<Button-1>", StartSelect]; (サ)
Bind[c, "<M-Button-1>", plinit]; (シ)
TkWait[];
この例はまず初期状態では図(A)のようなプロットをつくります。 そこで図のようにマウスボタン1 を押しながら長方形をドラッグし、ボタンを離すと (B)のように選択部分が x、y 共に拡大されて再プロットされます。
図 (A) マウスでドラッグした範囲を (B) 拡大して表示する
この選択、拡大の動作は 3 つのイヴェントによって行われます。まず、ボタンの押し下げで、これは (ア) の"StartSelect" を呼びます。"StartSelect" はその中で (イ)(ウ) にあるように、マウスカーソルの移動とボタンの解放を "DragSelect" と "RedrawSelect" に結合します。そして (エ) で選択範囲の起点を記録します。次に "DragSelect" はその時点の選択範囲を四辺形で書き直します。その四辺形にはタグ "selection" がつけられています。最後に "RedrawSelect" は選択範囲を確定させ、Canvas 座標を Plot のデータ座標に換算して再プロットします。また、(ク)(ケ) のように、移動とボタンの解放のふたつのイヴェントへの結合を解除しています。このようにして、図 (B)にあるような拡大図が得られます。
また、この例では (シ) で メタ-ボタン1 のイヴェントにより、プロットを初期状態に戻すようにしてあります。