Tixいろいろ (3)
 標準Tkで提供されるウィジェットは、 どれもプリミティヴャな機能しか持っていません。 そのため集積的なアプリケーションともなるとたくさんのウィジェットを組み合わせて作りこむ必要があり、 全体のコードが見えにくくなる要因にも成りかねません。 一方、Tkのウィジェット連のシンプルさを補うべく開発された Tixには実用的なアプリケーションを短期間で開発するための高機能なウィジェットが揃っています。 特にここで紹介するウィジェットはファイルシステムのディレクトリ構造に深く関わるウィジェットで、 ファイルの操作を行うアプリケーションの開発に威力を発揮することでしょう。

●ディレクトリリスト
 ディレクトリリストは、ハイアラキカルリストを発展させたウィジェットです。 形状はかばんの絵がついただけですが、 最初からファイルシステムを走査してディレクトリ構造を表示する機能を持っています。 かばんの絵がちょっといまいちなのですが、 これはインストールディレクトリに含まれるビットマップを強引に書きかえれば自分の好みのアイコンに変えることもできます。 次の例は、実際に使える画像ビューアで、 スクリーンショット左上がディレクトリリストです。 ここで選択したディレクトリに含まれるファイルの一覧が右上のリストに表示され、 これをダブルクリックすると、選んだファイルが画像であればその内容を下のスクロールするキャンバスの中に表示します。


package require Tix
package require pife
wm title . "Image Viewer"

tixDisplayStyle text -stylename redstyle -fg #800040
tixDisplayStyle text -stylename redrightstyle -fg #800040 -anchor e
tixDisplayStyle text -stylename rightstyle -anchor e

# 画面左上、ディレクトリリストを作ります。
# tixDirListの-width -heightはピクセル数です。
# またhlistサブウィジェットのデフォルト背景色は灰色です。
set da [tixDirList .da -root /]
$da subwidget hlist configure -bg white

# 画面右上、ファイルリストを作ります。
# tixScrolledHListは複数のカラムをつけることができません。
# そこで、通常のtixHListとスクロールバーを使います。
set fa [frame .fa]
set ha [tixHList $fa.ha -column 2 -separator / -width 50 \
        -xsc "$fa.scrh set" -ysc "$fa.scrv set"]
scrollbar $fa.scrv -ori v -command "$ha yview"
scrollbar $fa.scrh -ori h -command "$ha xview"
grid $ha -row 1 -col 1
grid $fa.scrv -row 1 -col 2 -sticky ns
grid $fa.scrh -row 2 -col 1 -sticky ew

$ha col width 0 -char 15
$ha col width 1 -char  8

# 画面下、画像表示部分を作ります。
# tixScrolledWindow
set sw [tixScrolledWindow .sw -width 450 -height 200]
set swf [$sw subwidget window]
canvas $swf.can -width 300 -height 300 -bg white
pack $swf.can

# コールバックの登録
$da configure -browsecmd "CBdirselected $ha"
$ha configure -command   "CBfileselected $da $ha $swf.can"

# ファイルが選択されたときのコールバックプロシージャ。
# 下のキャンバスに画像を表示します。
proc CBfileselected {dlist hlist can filename} {
    # tixDirListから現在選択されているディレクトリの絶対パスを得るには
    # -valueオプションを使います。
    set dname [$dlist cget -value]
    set filename [file join $dname $filename]
    if {[catch {
        $hlist configure -cursor watch; update
	image create photo Image1 -file $filename
    }]} {
	$hlist configure -cursor {}; update; return
    }
    $can delete all
    $can create image 0 0 -anc nw -image Image1
    $can configure -width [image width Image1] -height [image height Image1]
    $hlist configure -cursor {}; update
}

# tixDirListがシングルクリックされたときのコールバックプロシージャ。
# 選択されているディレクトリの絶対パスが自動的に引数につけられます。
proc CBdirselected {hlist absdirpath} {
    set entries [glob -nocomplain [file join $absdirpath *]]
    set imageexts {.bmp .gif .jpg .tif .tiff .png .xpm}

    # tixHListの項目をクリア
    $hlist delete all

    # ファイル名を追加します。tixHListの「階層」機能は使わず、
    # 単に縦横に項目を並べるために使っています。
    foreach fullpath $entries {
        set e [file tail $fullpath]
	$hlist add $e -itemtype text -text $e
        $hlist item create $e 1 -itemtype text \
          -text [file size $fullpath] -style rightstyle
        # もしファイルが画像っぽい拡張子をもっていたら、赤で目立たせます。
        if {[lsearch $imageexts [file extension $e]] != -1} {
	    $hlist item configure $e 0 -style redstyle
	    $hlist item configure $e 1 -style redrightstyle
	}
    }
}

grid $da -row 1 -col 1
grid $fa -row 1 -col 2
grid $sw -row 2 -col 1 -columnspan 2

スクリーンショット

 ディレクトリリストはtixDirListコマンドで作ります。 その使い方はとても簡単で、 とりあえず作って表示すればそれだけでディレクトリ構造をたどることができます。 デフォルトではカレントディレクトリが選択された状態になっていますが、 マウス操作ではなくプログラムでディレクトリを選択したい場合は、 -valueまたは-directoryオプションでそのディレクトリを指定すればOKです。 またどれかのディレクトリがクリックされた瞬間、またはダブルクリックされるかENTERキーが押された瞬間に何か処理をさせたい場合には、 それぞれ-browsecmdオプション、 -commandオプションでその処理をするコマンドを指定します。 ここで指定したコマンドの後ろには、 現在選択されているディレクトリ名が自動的につけられます。

●ディスプレイスタイル
 ディスプレイスタイルは、TixでのGUIを習得するうえで重要な概念ですが、 ウィジェットではありません。Tixのウィジェットのうち、

  • tixGridのセル
  • tixHList、tixTList、tixTree、tixDirList、tixDirTreeの項目
これらの場所に使うテキストや画像、ウィンドウに対して文字の色やフォント、 背景色、右詰・左詰などの書式を指定するために、 それらの指定をワンセットにしたものです。
tixDisplayStyle text -stylename redrightstyle -fg #800040 -anchor e
# ...
$hlist item configure $e 1 -style redrightstyle
ディスプレイスタイルはtixDisplayStyle で作ります。フォントは-font、テキストの色は-foregroundや-selectforeground、 背景色は-backgroundや-selectbackgroundなど、 標準Tkのウィジェットオプションと同じなので分かりやすいでしょう。 しかし、右詰・左詰を表すオプションには要注意。一見、 「-justify right」で右詰になりそうですが、はずれ。ブブー、 正解は「-anchor e」です。これには私もはまりました。
ディスプレイスタイルには上のように-stylenameで名前をつけることができ、 この値を-styleオプションで指定すればOKなのですが、-stylename を省略すると適当な名前を生成して返してくるので、 これを下のように使うこともできます。
set redright [tixDisplayStyle text -fg #800040 -anchor e]
# ...
$hlist item configure $e 1 -style $redright

●スクロールドウィンドウ
 標準Tkで、ウィンドウの大きさを超えるキャンバスやフレームなどを作り、 スクロールバーによってスクロールできるようにすることは可能ですが、 毎回かなりの量の決まったコードを書かなくてはいけません。 Tixのスクロールドウィンドウはフレームとスクロールバーを自動的に用意してくれるウィジェットで、 後はスクロールさせたい中身を載せるだけという所までお膳立てをしてくれます。

set sw [tixScrolledWindow .sw -width 450 -height 200]
set swf [$sw subwidget window]
canvas $swf.can -width 300 -height 300 -bg white
pack $swf.can
スクロールドウィンドウはtixScrolledWindowコマンドで作ります。 これもラベルフレームと同様、その上に直接他のウィジェットを載せることはできません。 subwidget windowサブコマンドでウィンドウ部分にあたるサブウィジェットを取り出し、 その中にスクロール表示したいウィジェット(ここではキャンバス1個だけですが、 もちろん複数個載せることもできます)をpackします。

●ディレクトリツリー
 ありゃ?ディレクトリリストの他にディレクトリツリーなるウィジェットもあるというのはどういうことかしら? とりあえず、サンプルをご紹介することにしましょう。 これは手前みそですが、 DPU拡張で追加している「magic」(ファイル名からそのファイルの種類を推測して返すコマンド) を使った簡単なツールです。 左半分がディレクトリツリーです。ここでどれかのディレクトリをつつくと、 そのディレクトリに含まれるファイルのリストが右に現れ、 その怪しげなmagicコマンドでファイルの種類を推測した内容が表示されます。


package require Tix
package require dpu

set trea [tixDirTree .trea \
  -browsecmd "CBDirTreeClicked .f2.ha" -directory C:/Install \
  -showhidden 1]
set f [frame .f2 -rel groove -bd 2]
set ha [tixHList $f.ha -separator . -header 1 -columns 2 \
        -yscrollcommand "$f.scrv set" -xscrollcommand "$f.scrh set" \
        -width 40]
scrollbar $f.scrv -ori v -command "$f.ha yview"
scrollbar $f.scrh -ori h -command "$f.ha xview"
grid $f.ha -row 1 -col 1
grid $f.scrv -row 1 -col 2 -sticky ns
grid $f.scrh -row 2 -col 1 -sticky ew

$ha header create 0 -text 名前
$ha header create 1 -text 種類

proc CBDirTreeClicked {ha dirpath} {
    set dirpath [lindex $dirpath 0]
    # 今の行を全て削除
    $ha delete all
    # エントリを追加
    set i 0
    foreach e [glob -nocomplain [file join $dirpath *]] {
	$ha add $i -itemtype text -text [file tail $e]
	if {[file isfile $e]} {
	    # 通常ファイルの場合、その説明を追加
	    if {[catch {set description [magic $e]}]} {
		set description "(cannot open)"
	    }
	    $ha item create $i 1 -text $description
	}
	incr i
    }
}

pack $trea $f -side left -fill y
# end.

スクリーンショット

 ディレクトリツリーを作るコマンドはtixDirTreeです。 幾つかのオプションをつけていますが、-showhidden は隠しディレクトリ(UNIXではピリオドで始まる名前を持つディレクトリ) を表示するかどうかを指定します。選択されているディレクトリを変えたい場合は -directoryオプションでディレクトリを指定します。
set trea [tixDirTree .trea \
  -browsecmd "CBDirTreeClicked .f2.ha" -directory C:/Install \
  -showhidden 1]
 で、他の多くのウィジェットと同様、ディレクトリツリーも -browsecmdで単発クリックされたとき、-commandでダブルクリックされるかENTERが押された場合のコールバックプロシージャを指定することができます。 コールバックプロシージャには、 選択されたディレクトリのフルパスのリストが引数として渡されます。 重大な注意をひとつ。これは 「フルパス」ではなく「フルパスのリスト」です! 両側に{ }がついている場合は、lindexで取り出しましょう。 またWindowsの場合は、このパスはバックスラッシュ区切りで渡されます。
proc CBDirTreeClicked {ha dirpath} {
    set dirpath [lindex $dirpath 0]
    # ...
}
 さて最初の疑問に戻って、 ディレクトリリストとディレクトリツリーはどー違うのか? ということなんですけど、 よーく見て比べると、むしろものすごく分かりやすい違いが明らかになると思います。 ディレクトリリストは、 選択されているディレクトリから始祖までの直系しか常に表示されないのです。つまり、選択ディレクトリの隣のディレクトリや、 選択ディレクトリの親ディレクトリの隣のディレクトリや、(以下略) はリストに現れないのです。 ディレクトリツリーは選択ディレクトリの隣のディレクトリも表示されるほか、 マウスでクリックするたびにサブディレクトリが展開されたり閉じられたりします (ハイアラキカルリストはそのままではサブ項目を展開したり閉じたりはされないことを思い出してください)。 Windowsのエクスプローラの左半分、という機能に近いのは当然、 ディレクトリツリーの方です。
 ありゃ、ではディレクトリリストの存在意義は?

拡張レビュー分室 top
(first uploaded 2001/04/22 last updated (not ever), MISUMI URANO)