シリアル通信で送受信したデータをtextウィジェットでバイナリエディタ風に
31 32 33 34 35 36 37 38 01 02 03 04 05 06 07 8a | 12345678........
42 43 44 61 62 63 64 | BCDabcd
みたいに逐次追加で表示しようとしている時にtagを使ってみたので、そのメモ。
最初、『詳解Tcl/Tk GUIプログラミング』を参考に、textを2つとscrollbarを1つを横並びに配置して、一方に16進数、他方にASCII文字を追記しつつ、1つのscrollbarで両方のtextを同時スクロールするように作り初めたけど、scrollbar絡みのカラクリを理解しきれず訳が分からなくなってしまって挫折しました。
そこでtextとscrollbarはオーソドックスな1:1の関係のままとして、tagをプレースホルダ的に使う方法を考えてみました。
まずtextに16バイト分の空欄を作ります。この時、16進数表示用プレースホルダとしてHEX0, HEX1, ... HEX14, HEX15というtagを、ASCII表示用プレースホルダとしてASCII0, ASCII1, ... ASCII14, ASCII15というtagを付与しておきます。
# $window is path name of the text widget.
for {set i 0} {$i < 16} {incr i} {
$window insert end " " HEX$i
$window insert end " "
}
$window insert end "| "
for {set i 0} {$i < 16} {incr i} {
$window insert end " " ASCII$i
}
$window insert end "\n"
そして以下のように1バイトづつ追記していくと、いい感じに表示が更新されました。
# $window is path name of the text widget.
# $lenght is number of shown data.
# $byte is new data.
set column [expr {$length % 16}]
incr length
binary scan $byte H2 hex
$window replace {*}[$window tag range HEX$column] $hex
if {[string is print $byte]} {
set ascii $byte
} else {
set ascii "."
}
$window replace {*}[$window tag range ASCII$column] $ascii
ここで意外だったのが、replaceでHEX1というtag範囲を置換するとHEX1というtagは範囲を失う(tagとしては存在し続ける)という挙動でした。
今回は別に実害は無いんですが、(labelウィジェットの-textvariableのように)何度も更新する値のためのプレースホルダとして利用したい場合はreplaceコマンドの引数でtagを再定義する必要がありそうです。