あのサンプルで見るTktable

さてもう少し、というか少しは実用的なサンプルを作ってまいりたい、 というときにいつも出てくるのが、またアイツ!
テキストファイルからあのデータを読み込んで、 表示、編集できるサンプルです。 (このページのサンプルでは編集後の保存はできません)
まず、マスターデータとして、 下のようなタブ区切り形式のテキストファイルを作り、 "shiten.txt" の名前で保存します。

00100	札幌	1	札幌市清田区美しが丘九条9丁目99	30
00200	仙台	2	仙台市青葉区柏木9-9-99	32
00301	新宿	3	東京都新宿区新宿九丁目99−99	50
... (略) ...

Tclプログラムは以下の通りです。 ファイルの読み取りのために、 Standard Tcl Library(Tcllib)のfileutil、textutilパッケージを使っているので、 Tcllibをインストールしている必要があります。

package require Tktable
package require fileutil
package require textutil

# データファイルを読み、$dataVarに配列として格納する。
proc readDataFile dataVar {
    upvar $dataVar data
    set content [::fileutil::cat "./shiten.txt" -encoding cp932]
    set rows [::textutil::split::splitx $content {[\r\n]+} ]

    set data(0,0) "支店コード"
    set data(0,1) "支店名称"
    set data(0,2) "地域"
    set data(0,3) "所在地"
    set data(0,4) "従業員数"

    set y 1
    foreach e $rows {
        set row [::textutil::split::splitx $e "\\t+"]
        set x 0
        foreach cell $row {
            set data($y,$x) $cell
            incr x
        }
        incr y
    }
}

# 入力検証用のコールバック処理。
# フォーカスが離れたときではなく、1文字タイプされるたびに呼ばれる。
proc cbValidateCmd {col newValue} {
    if {$col == 2 || $col == 4} {
        if {! [string is integer $newValue]} {
            tk_messageBox -message "数字を入力してください:$newValue"
            return 0
        }
    }
    return 1
}

readDataFile data

scrollbar .scrv -ori v -command ".t yview"
scrollbar .scrh -ori h -command ".t xview"

table .t -rows [expr [array size data]/5] -cols 5 \
  -anc w -bd 3 -rel groove \
  -height 300 -maxheight 300 \
  -colstretchmode all -rowstretchmode all \
  -highlightbackground #ffa0a0 -highlightcolor red \
  -insertbackground #a0ffa0 -insertwidth 4 \
  -variable data -titlerows 1 -titlecols 0 \
  -roworigin 0 -colorigin 0 \
  -xscrollcommand ".scrh set" \
  -yscrollcommand ".scrv set" \
  -selectmode extended -cursor arrow \
  -rowseparator "\n" -colseparator "\t" \
  -validate 1 -validatecommand "cbValidateCmd %c %S" \
  -font {Helvetica 12 normal}

.t tag configure data 
.t width 2  8
.t width 3 32
.t tag configure title \
  -bg "midnightblue" -fg "white" \
  -font {Helivetica 12 bold} -anc center

.t tag configure active -fg red   -bg #cc8080
#.t tag configure flash -fg blue  -bg #8080cc
.t tag configure sel    -fg green -bg #80cc80

.t tag col colEmpNum 4
.t tag configure colEmpNum -anc e

grid .t -row 1 -column 1
grid .scrv -row 1 -column 2 -sti ns
grid .scrh -row 2 -column 1 -sti ew

bind . <Control-q> exit


プログラムの中頃に、やたらとべたべたオプションがついている長〜い1文がありますが、 これがTktableのtableウィジェットを作っているコマンドです。 (それは前のページでやりましたね) えーブランクを思い出して、前のページでご紹介しきれていないぶぶんを、 1つずつ順番にやっていきたいと思います。


スクロールバーとウィジェットの大きさの制御

tableウィジェットは、何もオプションを指定しないと、 -cols、-rowsで指定した数のデータを全て表示するに足るだけの大きさの表示領域を確保しようとします。 そうではなく、スクロールバーを右や下につけて、 一定のサイズ以下に領域を制限したいという方が普通です。 そのためにtableコマンドに-width、-heightオプションがあるのですが、 効かないことがあります。 これらは「望ましいサイズ」を指定するもので強制ではないからです。 しかし、せっかくスクロールバーをつけたのに、 ウィンドウのサイズが広がってしまうのでは意味がないですね。 では強制的なサイズ上限を指定する方法はあるのか? あります。-maxwidthと-maxheightを代わりに使えば、 この指定が無視されてこれより大きくなってしまうことはありません。 これこそ、まさに僕が望んでいたMaxやまびこ並みのマックスというわけだ。(意味わからん)


入力値の検証(バリデーション)

数値しか入力を受け付けない、といった入力値の検証(バリデーション)は、 tableコマンドの2つのオプション-validateと-validatecommandを使って実現できます。
"-validate 1" とすることで、この仕組みを有効にします。 (デフォルトは無効。-validatecommandだけ指定しても無意味です)
-validatecommandには、編集対象セルに値が入力されるたびに実行されるコマンドを指定します。 このコマンドには、Tkのbindコマンドのように、 「%c」などのコマンド置換(マニュアルでは"Command Substitution"と呼ばれています) が使えます。一覧は次の通りです。
文字列値の意味
%c対象セルの列番号。(-selectioncommandでは、選択セルの列数)
%C"%r,%c"と同じ意味。
%i現在のカーソル位置(-selectioncommandでは、選択セルの数。 -commandでは、1が値の設定、0が値の読み取りの意)
%r対象セルの行番号。(-selectioncommandでは、選択セルの行数)
%s編集される前の値。
%S編集された新しい値。
%Wtableウィジェットの名前。
上の例では、列番号2(左から3番目の列=地域コード) と列番号4(左から5番目の列=従業員数) の列のセルに数字でない文字が入力されたら、 エラーダイアログを出して編集を無効にしています。 (-validatecommandが実行したコマンドの値が0ならば、編集内容はキャンセルされる) ただし、この検証はキーを1つ打つたびに行われます。 編集が終わってフォーカスが離れようとしたときではありません。 その動作、つまり編集が終わってフォーカスが離れようとしたタイミングだけ、 バリデーションを働かせる方法は、下の-browsecommandを使います。


アクティブセルが移動したときのイベント

tableの-browsecommandには、アクティブセルが移動したときに実行する処理が書けます。 これを使って、セルの編集が終わり、 他のセルに移動したタイミングで元のセルの入力値検証(バリデーション)が可能です。
上のサンプルの-validateと-validatecommandを削除し、 代わりに以下のオプションを追加します。

-browsecommand "cbBrowseCmd %W %s %S"

また、cbBrowseCmdプロシージャを次のように定義します。

proc cbBrowseCmd {table lastCell newCell} {
    if {$lastCell eq ""} { return }
    global data
    lassign [split $lastCell ","] row col
    set value $data($lastCell)
    if {$col == 2 || $col == 4} {
        if {! [string is integer $value]} {
            tk_messageBox -message "数字を入力してください:$value"
            $table activate $lastCell
        }
    }
}

文字列値の意味
%s元のセルの位置。"行,列"の形式です。
%S新しくアクティブになったセルの位置。"行,列"の形式です。
%Wtableウィジェットの名前。
プログラムが起動して最初の編集のときは、 「元のセル」が存在しないので、%sは空文字列になります。 それで、-browsecommandは-validatecommandと異なり、 0を返せば元のセルの編集がキャンセルされるとかそのようなことはないので、 せめて自分でアクティブセルを元のセルに戻して、 「正しい値に直さない限り、逃げられない」ようにします。
ただし、その状態のままtable外のボタンなどを押して処理が進められないように、 最終的には全データの検証は必要です。


-anchor=セル内の文字の配置のデフォルト値

各セル内の文字列は、デフォルトで中央揃えになりますが、 これはあまり一般的ではないと思います。 より一般的な、「見出しは中央揃え、データは左揃え」 にするには、tableウィジェットの-anchorでw(西、つまり左)を指定し、 その後title部分にだけ、tag configureコマンドで -anchor center(中央揃え)を指定するのがいいと思います。 この例のように、数字のデータ列は右揃えにすることもできます。


-borderwidth(-bd)と-relief

これらのオプションは、tableウィジェットの外縁部分に適用されると思いきや、 そうではありません。各セルの周囲、すべてに適用されます。


title以外の暗黙タグ名

最初から定義されているタグ名には title、active、flash、selがあります。 activeは現在編集中のセル、selは選択されたセルです。 flashは、"-flashmode 1"で、フラッシュモード (セルの値が変更された際に一時的に点滅する)ときの表示です。
sel状態には、 -selectmode extendedでマウスでドラッグしたり、 列見出しをクリックして列全体を選択したりなどの場合になります。 そのほか、 マウスでセルをクリックしてからactive状態になるまでの間 =マウスボタンを押してから離すまでの間だけ、一時的にsel状態になります。

拡張レビュー分室 top
(first uploaded 2010/09/24 last updated 2011/01/09, MISUMI URANO)