Tcl/Tk 入門

キャンバスウィジェットでお絵描き

最終更新: [2008/06/18]
Copyright

キャンバスウィジェットを作成し
それに線を引いてみる.

ふつうの「お絵描き」というのとは,ちょっと違うかもしれません.ここでいっ ているのは,Tcl/Tk の簡易性/対話性を利用した図の作成です.

まずは,キャンバスウィジェットを作成する.ここの例は簡単なので wish コマ ンドを対話的に起動して1行ずつ実行していくとその動作がわかりやすいだろう.
canvas .c -width 500 -height 500
pack .c

これで,ウィンドウが開かれる.横幅と縦幅をそれぞれ 500 とした.".c" とい うのがウィジェットの名前で,ここではとても簡単なものにしている.これに線 を引いたりする命令 (コマンド) を渡すには,"ウィジェット名 コマンド コマ ンドオプション..." の形式をとる.ここでの「コマンド」は「ウィジェットコ マンド」と呼ばれる.線を引く場合は ".c create line x0 y0 x1 y1 ...." と なる."xN yN" の項は2つ以上の座標の組を指定する.これを使ってさきに開い たキャンバスに斜めに線を引くと次のようになる.
.c create line 0 0 500 500
線に色を付けるには "-fill 色名" とする.例えば red や blue などがある. これを指定して線に色がつくことを確認してみよう.(色名の参照ツールには xcolors や tkcolor などがある).

coords というウィジェットコマンドがあって,これを使うと生成したウィジェッ ト内のアイテムの座標を変更することができる.さきほど引いた線もキャンバス 内アイテムで座標を変えて表示することができる.アイテムは生成毎に ID (番 号) が割りふられているので,これを元に変更する座標を指定する.
set l_id [.c create line 0 0 500 500]
.c coords $l_id 50 50 450 450
create コマンドで生成した line アイテムの ID を l_id という変数 に保存しておいて,coords コマンドでそれを参照している.角括弧 "[","]" で 囲まれた箇所は先に実行される.つまり set コマンドで l_id に値を代入する より前に .c の create コマンドが実行される.


これは線がぐるぐる回る,といった繰り返し処理の場合の表示更新に使うことが できる.ここではそれを示す.after コマンドと tkwait コマンドで一定時間毎 の繰り返し処理を実現している.
set width 500
set height 500
set max 50
set r 200
set pi [expr atan2(1,1)*4]
set update 100

canvas .c -width $width -height $height
pack .c
bind . <q> {exit}

set x0 [expr $width/2 ]
set y0 [expr $height/2]
set i 0
set l_id [.c create line $x0 $y0 [expr $x0+$r] $y0]
while {1} {
  set th $i*2*$pi/$max
  set x [expr $x0+$r*cos($th)]
  set y [expr $y0-$r*sin($th)]
  .c coords $l_id $x0 $y0 $x $y
  update
  after $update {incr i}
  tkwait variable i
  if {$i>=$max} {set i 0}
}
coords コマンドを使用しているので,いちいち前回の座標を削除したりせず, 新しい座標の指定のみで描画できている点に注目してほしい.

動きをもう少し複雑にした例が circular.tk にあ る.興味があれば参考にするといいだろう.

coords コマンドは便利ではあるが,その生成 ID を保存し参照しなければなら ないので,図形エディタなどとは性質の異なるデータプロットアプリケーション などを作成する場合には,かえって処理が煩雑になる可能性がある.この場合に は毎回新たに与えられるので,coords で座標更新を行う恩恵がそれほど得られ ないからだろう.だから,データ点列を多数プロットする場合には,毎回 ".c delete all" などですべてをきれいに削除して,一から描き直すのがいいようで ある.


さきの例は一定時刻毎に表示位置を更新していくものだったが,角度を対話的に 指示するにはどうすればよいだろうか?これには scale ウィジェットを使用す ることが考えられる.scale ウィジェットはマウスでドラッグすることのできる スライダを生成し,その位置に応じた値を参照することができる.変数を指定す ればその値を指定変数に納めることも可能である.
scale ウィジェット名 \
  -from 開始値 -to 終端値 \
  -variable 対応変数名 -command 更新時の呼び出し関数名
'-from' などの各オプションは必要に応じてつければよい.

これを使うとマウスを使って対話的に表示角度を指定することができる.プログ ラム例は次のようになる.ここでの線の表示更新は,"delete all" ですべてを 削除してから新たに作成し直すことで行った.
set width 500
set height 500
set pi [expr atan2(1,1)*4]
set deg2rad [expr $pi/180]

set r 200

proc plotLine {{w} {x1} {y1} {x2} {y2}} {
  global width height

  set x1 [expr $width/2+$x1]
  set y1 [expr $height/2-$y1]
  set x2 [expr $width/2+$x2]
  set y2 [expr $height/2-$y2]

  $w create line $x1 $y1 $x2 $y2
}

proc rotateLine {{w}} {
  global th r
  global deg2rad

  $w delete all
  set tmp [expr $th*$deg2rad]
  set x [expr $r*cos($tmp)]
  set y [expr $r*sin($tmp)]

  plotLine $w 0 0 $x $y
}

canvas .c -width $width -height $height
scale .scl -from "-180" -to 180 \
  -variable th -command "rotateLine .c; set null"
pack .c .scl -side left -fill y -expand yes
bind . <q> {exit}
.scl と名前をつけたスケールウィジェットで,値を変更するとそれに応じて関 数 rotateLine が呼び出される.スケールウィジェットでは指定コマンドにその スケールの値が引数として渡されるので,ここではこの引数を呼び出し関数側へ 渡さないように "set null" の処理を強引に付け足して,無視するようにしてい る.もちろん,この引数を使用するように関数を組んでもよい.


実行例

さらに,キャンバス中をマウスでクリックするとその座標に対応して角度が変わ る,といった処理を実現できると便利である.これはイベントバインドを使う. 先の例に次の処理を付加することですぐに実現可能である.
proc calcAngle {{x} {y}} {
  global width height
  global deg2rad

  return [expr atan2($height/2-$y, -$width/2+$x)/$deg2rad]
}

bind .c <Button-1>       {set th [calcAngle %x %y]; rotateLine .c}
bind .c <Button1-Motion> {set th [calcAngle %x %y]; rotateLine .c}
イベントとして左のマウスボタンが押されるか押したまま移動(ドラッグ)する かが発生した場合に,その座標を拾って表示更新を行う.このバインド名は Button-1 と Button1-Motion になる.これらのイベントが起こった場合に calcAngle 関数を使って座標値を角度に変換し th 変数に納め,表示更新を行っ ている.座標値の参照は %x, %y という表記で可能である.

演習問題

  1. 棒の長さを調節するスケールウィジェットを新たに追加しなさい.(変数 r を更新する)
  2. キャンバス上をマウスでクリックするとその位置に棒の端がくるように棒 の長さを変更するようにしなさい.(calcAngle 関数と同様の処理を行う関 数を別に作成する.あるいは calcAngle 関数の動作を変更する.後者の場 合には関数名は処理を適切に表すように変更したほうがいいだろう.)

ここまでに見てきたように Tcl/Tk を使うと変数の内容を設定したり,その内容 を変更したりすることが対話的に実現可能(ということは変更が簡単)なので, 多数の設定パラメータを持つシミュレータなどでの利用にその効果が期待され る.


canvas ウィジェットには,表示内容を PostScript 形式で出力するコマンド postscript がある.これを使うとそのときどきのスナップショットを保存する ことができ,便利である.これをキーバインドに割り当ててみよう.次のように なる.
bind . <Alt-p> {.c postscript -file "tmp_canvas.ps" -colormode color}
割り当てたキーは ALT-p である.ここではファイル名を tmp_canvas.ps と決め 打ちにした.必要があれば,適宜ファイル名を入力させるダイアログボックスを 開いたりすることになるだろう.

出力形式は現在のところ PostScript しか存在しないが,外部コマンドの pstoedit を使用することで,Tgif/GNUPLOT/JavaApplet などの各種形式へと変 換することが可能である.(ただし,pstoedit は特定のフォントセットの設定 にしか対応していないので日本語 Tcl/Tk から生成された PostScript だと,文 字も図形として処理されてしまう.非日本語化 Tcl/Tk を使う必要がある.そも そも,canvas ウィジェットから出力される PostScript が日本語対応していな かったかも..??).


|| Tcl/Tk 入門||