Widget Demo 解体新書

Widget Demo 解体新書:キャンバスアイテムデモ

いろんなことをしているデモではありますが,アイテム貼り付けとドラッグに関 してだけ解説します.

アイテム貼り付け

アイテム貼り付けに関しては簡単です.単にその位置にそれぞれ作成していくだ けです.ここでは3つの曲線アイテムのみを対象にしましょう.デモに形式を似 せるために領域を示す格子枠も付けます.

では,まずキャンバスから作成しましょう.幅・高さの単位は "c"(センチメー トル)になっています.

canvas .c -width 15c -height 10c 
pack .c
bind . <q> exit
キャンバスサイズに関してはデモと同じものを使っています.

次のキャンバス貼り付けを行ないます.アイテムの種類を示すタイトル

.c create text 15c .2c -text "Curves" -anchor n
と3つの 曲線を並べます.
.c create line 11c 4c 11.5c 1c 13.5c 1c 14c 4c -smooth on \
	-fill blue -tags item
.c create line 15.5c 1c 19.5c 1.5c 15.5c 4.5c 19.5c 4c -smooth on \
	-arrow both -width 3 -tags item
.c create line 12c 6c 13.5c 4.5c 16.5c 7.5c 18c 6c \
	16.5c 4.5c 13.5c 7.5c 12c 6c -smooth on -width 3m -cap round \
	-stipple @[file join $tk_library demos images gray25.bmp] \
	-fill red -tags item
オプション -stipple で模様付け(gray25.bmp を指定)を行なっています.ま た,もう一つの -tags オプションでは item タグを指定し後のドラッグ処理で 使うためのタグ付けを行なっています.

枠線はおまけ的なものです.3本引きます.

.c create line 0c 8c 30c 8c -width 2
.c create line 10c 0c 10c 24c -width 2
.c create line 20c 0c 20c 24c -width 2

領域スクロールを可能とする

ここまでのソースを実行するとわかりますが,作成アイテムがキャンバスの表示 枠からはみだしてしまいます.このときスクロールバーを必要に応じてキャンバ スの縦と横に付けます.これにはキャンバスのスクロール領域の設定とスクロー ルイベントに関する設定が必要です.

縦と横のスクロールバーを作成します.実行するコマンドとしてキャンバスコマ ンドの表示位置変更 (xview, yview) を指定します.

scrollbar .x -command ".c xview" -orient horiz
scrollbar .y -command ".c yview"
次に存在するキャンバスウィジェットに対してスクロール領域の設定とスクロー ル操作に対応しているスクロールバーがどれかを指示します.スクロールバーの 指定はオプションの -xscrollcommand と -yscrollcommand のそれぞれが対応し ます.
$c configure -scrollregion {0c 0c 30c 24c} \
	-xscrollcommand ".x set" \
	-yscrollcommand ".y set"
ウィジェット配置に pack を使っていましたが,スクロールバーも配置するので これを grid に変更しておきましょう.次のようになります.
grid .c .y -sticky news
grid .x    -sticky ew
以上でアイテムの表示はできました.

アイテムドラッグ

このデモではアイテムをマウスで掴んで移動(ドラッグ)することができます. この処理の実現について解説します.ポインタ(マウス)のそれぞれのイベント について次の処理が対応します.
イベントイベント名処理
ポインタがアイテム上に乗るEnterアイテム色を青に
ポインタがアイテムから外れるLeaveアイテム色を戻す
アイテム上でボタン1が押されるButton-1ドラッグ開始
アイテム上でボタン1が押されたままポインタ移動Button1-Motionドラッグ
アイテム上でのボタン1の押下を解除ButtonRelease-1ドラッグ終了
これらのイベントに対応した処理の設定を先に示します.
.c bind item <Enter> itemEnter
.c bind item <Leave> itemLeave
bind $c <Button-1> "itemStartDrag $c %x %y"
bind $c <Button1-Motion> "itemDrag $c %x %y"
イベントの種類は「a. キャンバスのアイテム上で発生するもの」「b. キャンバ ス中で発生するもの」の2つに大別されます.上記ソースの上2行が a. にあた り,下の2行が b. にあたります.個々のアイテムの色をポインタが上に乗った かどうかで処理する場合には,そのアイテムへのイベントバインドが必要であり, アイテムを移動する場合はキャンバス中での座標を必要とするためキャンバス全 体へのバインドが必要となるからです.なお,ドラッグ終了での処理はドラッグ 中のものとここでは同一ですので特に設定していません.

次に個々のイベント設定で呼び出されるプロシージャを作成していきましょう. ポインタがアイテム上に乗ったときの処理 itemEnter からです.これは簡単で 単に対象となるアイテムの色を SteelBlue2 に変更するだけです.ポインタが上 に存在するアイテムには current という特殊名が与えられますので,これを使 用しアイテム current の色を変更するという指定にします.(ポインタ下に対 象となるアイテムが存在しない場合はアイテム名 current は有効となりません).

.c itemconfig current -fill SteelBlue2
ただし,後で元の色に戻す必要があるので,これを保存しなければなりません. キャンバスの itemconfig コマンドで現在色を取得します.また,色値の保存用 に fillCol 変数を global 指定で使用します.これらを含めてプロシージャは 次のようになります.
proc itemEnter {} {
    global fillCol
    
    set fillCol [lindex [.c itemconfig current -fill] 4]
    .c itemconfig current -fill SteelBlue2
}
itemconfig の -fill オプションで返される値は4つありその最後の値が現在色 なので,lindex で最後の値を取り出しています.

ポインタがアイテム外に出た場合には fillCol 変数に保存していた色に戻して やります.使用する変数の global 指定をお忘れなく.

proc itemLeave {} {
    global fillCol
    
    .c itemconfig current -fill $fillCol
}
ドラッグ処理に移りましょう.bind で呼び出されるプロシージャには「キャン バス名,押下座標X,押下座標Y」の引数が付きますのでこの値を受け,その座 標を保存します.これには lastX, lastY のグローバル変数を使っています.こ れが itemStartDrag プロシージャです.
proc itemStartDrag {c x y} {
    global lastX lastY

    set lastX $x
    set lastY $y
}
そして itemDrag プロシージャではポインタの移動をアイテムの移動に対応させ るには移動中の現在のポインタ座標と lastX, lastY に保存された座標との差だ けアイテムを移動させるようにします.また,itemDrag プロシージャはポイン タが移動している限り繰り返し呼び出されますので,それへの対応として,現 在のポインタ座標を次の lastX, lastY とするように設定します.
proc itemDrag {c x y} {
    global lastX lastY

    .c move current [expr $x-$lastX] [expr $y-$lastY]
    set lastX $x
    set lastY $y
}
以上です.なお実際のデモではキャンバス座標の取得を行なうのにスクロール量 を反映(スクロール時にはキャンバス上の座標と画面上での表示座標に食い違い が生じるため)させる canvasx, canvasy コマンドも使っていますが,ここでは 差を移動量としているのでスクロール量の反映は必要としません.ですので,簡 略化のため割愛しました.

これを実行すると次のようになります(スクリプトソース).


実行結果