目次
前
次
ボタンメソッド実装
GUI上に配置した各ボタンオブジェクトは、専用の
メソッド(仕事)をもたせて、システム全体での
動作を確定します。
次の順で、メソッドを記述していきました。
- fload
- fsave
- fexec
- fcancel
- clrParameters
- exit
fload
ファイルリストから、ユーザーにファイル名を指定
してもらい、該当ファイルから情報取得。取得情報
を連想配列に格納するのが仕事です。
動作シーケンスを書き出すと、以下。
- ファイルを指定してもらう
- ファイルオープン
- 6行に対して、1行ごとの情報を読取り、連想配列に格納
- ファイルクローズ
コードにまとめます。
proc fload {} {
#
global tFileName xexe_temp xexe_time
#
set theFileName [tk_getOpenFile -filetypes {{"profile code file" {.txt}}}]
# show file name
set xtmp [split $theFileName /]
set cFileName [lindex $xtmp end]
set tFileName [lindex [split $cFileName .] 0]
# open
set fd_in [open $theFileName "r"]
# read context
set i 0
while { [gets $fd_in sbuf] >= 0 } {
# separate hash item
set iname [lindex $sbuf 0]
set itemp [lindex $sbuf 1]
set itime [lindex $sbuf end]
# store
set xexe_temp($iname) $itemp
set xexe_time($iname) $itime
# increment
incr i
}
# close
close $fd_in
}
温度、時刻の連想配列は、グローバル変数xexe_tempと
xexe_timeに分けておきます。
fsave
ファイル名をFileNameエントリーから取得し
該当ファイルを新規作成後、温度、時刻の2
情報を1行にまとめて保存します。
動作シーケンスを書き出すと、以下。
- ファイル名取得
- ファイルオープン
- 6行に対して、1行ごとに連想配列から情報をまとめて格納
- ファイルクローズ
コードにまとめます。
proc fsave {} {
#
global tFileName xexe_temp xexe_time
#
if { $tFileName == "" } {
tk_messageBox -message "not entry File Name" -type ok
} else {
# create file name
set cFileName "$tFileName.txt"
# open
set fd_out [open $cFileName "w"]
# store
for {set i 0} {$i < 6} {incr i} {
# get items
set xfirst [format "P%1d" $i]
set xsecond $xexe_temp($xfirst)
set xthird $xexe_time($xfirst)
# concatenate
set xtmp "$xfirst $xsecond $xthird"
# save
puts $fd_out $xtmp
}
# close
close $fd_out
}
}
ファイル名がない場合、ファイルを生成できないので
エラーメッセージを表示し、メソッドを終了します。
fexec
ボタンのクリックで、1秒周期で温度、時刻を計算後
表示するのが主たる仕事です。
計算する前に、必要な情報が揃っていて時刻については
昇順に並んでいるかを確認します。時刻が昇順でないと
エラーとして動作を中断終了します。
時刻が昇順になっているかは、次のコードで確認します。
# judge
set xp0 [calc_time $xexe_time(P0)]
set xp1 [calc_time $xexe_time(P1)]
set xp2 [calc_time $xexe_time(P2)]
set xp3 [calc_time $xexe_time(P3)]
set xp4 [calc_time $xexe_time(P4)]
set xp5 [calc_time $xexe_time(P5)]
set xflag 0
if { $xp0 > $xp1 } {
incr xflag
}
if { $xp1 > $xp2 } {
incr xflag
}
if { $xp2 > $xp3 } {
incr xflag
}
if { $xp3 > $xp4 } {
incr xflag
}
if { $xp4 > $xp5 } {
incr xflag
}
時刻を配列から取得して、隣接するステート間で
昇順になっているかを判定しています。
計数フラグ$flagを用意して、昇順でないとき
+1して増やしておきます。計数フラグの値が
0でないときには、時刻の並びが正しくないと
判断できます。
時刻の算出は、次の手続きで求めます。
proc calc_time {x} {
# separate
set stmp [split $x :]
# calculate
set result [expr [lindex $stmp 0] * 60 + [lindex $stmp end]]
return $result
}
文字列を「:」で2分割。
左右の文字列を数値として、重みを乗算し
オフセットを加算する方法を採用しました。
温度と時刻の1秒ごとの計算は、シフトレジスタを
利用して求めます。
リストxlistに、数値が次のように格納されているとします。
xlist = "xa xb xc xd xe xf"
隣合う数値の差分を求めるために、リストの先頭に0を追加
したリストをつくります。
ylist = "0 $xlist"
xlistの要素数分だけ差分を求めて、リストに格納。
set result ""
for {set i 1} {$i < 6} {incr i} {
set xx [lindex $xlist $i]
set yy [lindex $ylist $i]
set r [expr $xx - $yy]
set result "$result $r"
}
シフトレジスタを使って求められた差分値から
温度と時刻の瞬時値を計算して表示すると目的
のメソッドになります。
温度と時刻の瞬時値を計算するには、経過時間が必要。
経過時間は、カウンタ値$ecntに格納されているので、
数値から時刻を算出する関数を用意します。
数値から時刻に変換する関数の定義は、以下。
proc conv_time {x} {
# default
set result "0:00"
# quotient
set q [expr $x / 60]
# residue
set r [expr $x % 60]
#
set result [format "%d:%02d" $q $r]
return $result
}
ここまで用意したなら、メソッドの定義が可能。
メソッドのコードは以下。
proc fexec {} {
#
global eflag elast ecnt xexe_temp xexe_time xdif tdif ddif bdif
# judge
set xp0 [calc_time $xexe_time(P0)]
set xp1 [calc_time $xexe_time(P1)]
set xp2 [calc_time $xexe_time(P2)]
set xp3 [calc_time $xexe_time(P3)]
set xp4 [calc_time $xexe_time(P4)]
set xp5 [calc_time $xexe_time(P5)]
set xflag 0
if { $xp0 > $xp1 } {
incr xflag
}
if { $xp1 > $xp2 } {
incr xflag
}
if { $xp2 > $xp3 } {
incr xflag
}
if { $xp3 > $xp4 } {
incr xflag
}
if { $xp4 > $xp5 } {
incr xflag
}
# message
if { $xflag > 0 } {
tk_messageBox -message "Time incorrect !" -type ok
} else {
# last count
set elast $xp5
# clear counter
set ecnt 0
# calculate (temperature)
set xtmp "$xexe_temp(P0) $xexe_temp(P1)"
set xtmp "$xtmp $xexe_temp(P2) $xexe_temp(P3)"
set xtmp "$xtmp $xexe_temp(P4) $xexe_temp(P5)"
set ytmp "0 $xtmp"
set result ""
for {set i 1} {$i < 6} {incr i} {
set xx [lindex $xtmp $i]
set yy [lindex $ytmp $i]
set r [expr $xx - $yy]
set result "$result $r"
}
set xdif $result
# calculate (time)
set xtmp "$xp0 $xp1 $xp2 $xp3 $xp4 $xp5"
set ytmp "0 $xtmp"
set result ""
for {set i 1} {$i < 6} {incr i} {
set xx [lindex $xtmp $i]
set yy [lindex $ytmp $i]
set r [expr $xx - $yy]
set result "$result $r"
}
set tdif $result
# calculate boundary
set sum 0
set bdif "0"
for {set i 0} {$i < 4} {incr i} {
set xx [lindex $tdif $i]
set sum [expr $sum + $xx]
set bdif "$bdif $sum"
}
# calculate displacement
set result ""
for {set i 0} {$i < 5} {incr i} {
set xx [lindex $xdif $i]
set yy [lindex $tdif $i]
set r [format "%0.1f" [expr ($xx * 1.0) / $yy]]
set result "$result $r"
}
set ddif $result
# enable
set eflag 1
set ecnt 0
# loop
show_time
}
}
メソッド中で、周期動作の手続きを起動します。
この手続きの内容は以下。
proc show_time {} {
#
global ecnt eflag elast
# show
draw_hand
# handlign
if { $eflag == 1 } {
incr ecnt
# ? complete
if { $ecnt >= $elast } {
set eflag 0
}
}
after 1000 show_time
}
周期処理で表示に関係する手続きを利用。
表示に関係する手続きのコードは以下。
proc draw_hand {} {
#
global cPathV cTimeV cTempV eflag xexe_temp ecnt ddif bdif
# show
if { $eflag == 1 } {
# judge block
set bb [which_range $ecnt]
set bbx [format "P%d" $bb]
# path
set cPathV [format "P%d -> P%d" $bb [expr $bb+1]]
# start value
set beginv $xexe_temp($bbx)
# displacement
set dispx [lindex $ddif $bb]
set dispy [lindex $bdif $bb]
# calculate
set ctemp [expr $beginv + $dispx * ($ecnt - $dispy)]
set ctemp [expr int($ctemp)]
set cTempV [format "%d" $ctemp]
# time
set cTimeV [conv_time $ecnt]
} else {
set cPathV ""
set cTimeV "0:00"
set cTempV ""
}
}
区間で利用する直線の傾きが異なるので、計算に利用する
パラメータを、他で作成したリストから取得して使います。
ラベル、エントリーで使う文字列は「-textvariable」で
オブジェクトに渡すことができるので、該当する文字列を
更新すると、表示に反映される仕様を有効利用してます。
区間を特定するため定義した関数は以下。
proc which_range {x} {
#
global bdif
# separate
set b0 [lindex $bdif 0]
set b1 [lindex $bdif 1]
set b2 [lindex $bdif 2]
set b3 [lindex $bdif 3]
set b4 [lindex $bdif 4]
# judge
set result 4
if { $b0 <= $x && $x < $b1 } {
set result 0
}
if { $b1 <= $x && $x < $b2 } {
set result 1
}
if { $b2 <= $x && $x < $b3 } {
set result 2
}
if { $b3 <= $x && $x <= $b4 } {
set result 3
}
return $result
}
fcancel
ボタンのクリックで、周期処理での計算と表示を
スキップすると考えました。
周期ごとの計算をしなければよいので、フラグ制御に
してある部分で論理値の判断ができるようにします。
proc fcancel {} {
#
global eflag
# desable
set eflag 0
}
フラグの論理値で、固定文字列を表示するだけに
メソッドdraw_handを定義したので、余計なことを
考えずにすみます。
clrParameters
ボタンのクリックで、デフォルト文字列を
2つの連想配列に格納すると考えました。
連想配列の初期化に加えて、ファイル名も
「unknown」にしておきます。
proc clrParameters {} {
#
global tFileName xexe_temp xexe_time
# file name
set tFileName "unknown"
# temperature
set xexe_temp(P0) 20
set xexe_temp(P1) 20
set xexe_temp(P2) 20
set xexe_temp(P3) 20
set xexe_temp(P4) 20
set xexe_temp(P5) 20
# time
set xexe_time(P0) 0:00
set xexe_time(P1) 0:00
set xexe_time(P2) 0:00
set xexe_time(P3) 0:00
set xexe_time(P4) 0:00
set xexe_time(P5) 0:00
}
exit
exitボタンの役目は、アプリケーションを終了し
制御をOSに戻すこと。exitコマンドは組込まれて
いるので、呼び出すだけになります。
ボタンオブジェクト定義のときに、メソッドも格納
してあります。
button .btnExit -font {{MS ゴシック} 16 bold} -bg gray -text "exit" -width 10 -command "exit"
目次
前
次