【Raspberry Pi】Tcl/TkとC言語で距離センサーの値を表示するGUIをつくろう 第3回
北本です。
前回は、Tcl/Tkで生成したウィンドウ上のボタンを押すタイミングでセンサーの測距値を取得し、それをウィンドウ上のラベルに表示させることに成功しました。しかし、毎回クリックして値を取得しなくてはならないというのは不便です。今回は、周期処理を用い、一定間隔で自動的に値の取得および表示することを目指してみることにします。
仕様は以下のようなものとします。
・STARTボタンを押下すると200ミリ秒周期で測距値を取得してラベルに表示する。
・STARTボタンは1度押下されるとSTOPボタンに変わり、それを押下する周期的な測距値の取得を停止する。
・STOPボタンは1度押下されると、STARTボタンに戻る。
要するに、測距値を周期的に取得・表示する状態とそれを行わない状態がトグルボタンで切り替えられるものを作ります。
コードは下記の通りです。
test.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | #include <tk.h> #include <wiringPi.h> #include <wiringPiI2C.h> #include <stdio.h> int getValue(ClientData, Tcl_Interp*, int, const char*[]); int main(int argc, char *argv[]){ Tcl_Interp *interp = Tcl_CreateInterp(); // インタープリタの作成 Tcl_FindExecutable(argv[0]); // コマンド名を頼りに実行ファイルを探索 Tcl_Init(interp); // Tcl初期化 Tk_Init(interp); // Tk初期化 Tcl_EvalFile(interp, "./gui.tcl"); // 第2引数に指定したファイルを読み込みインタープリタで処理 Tcl_CreateCommand(interp, "get", getValue, NULL, NULL); // コマンドを作成 Tk_MainLoop(); // イベント処理用のループ Tcl_Finalize(); // 後処理 return 0; } int getValue(ClientData clientData, Tcl_Interp *interp, int argc, const char *args[]){ int fd = wiringPiI2CSetup(0x40); int upper = 0; // 上位11-4ビット int lower = 0; // 下位3-0ビット int value = 0; int count = 0; upper = wiringPiI2CReadReg8(fd, 0x5E); // 上位 lower = wiringPiI2CReadReg8(fd, 0x5F); // 下位 value = upper << 4 | lower; char str[256]; sprintf(str, ".valueLabel configure -text \"%d(%x)\n\"", value, value); Tcl_Eval(interp, str); // 文字列をTclのスクリプトとして実行 delay(200); count++; return TCL_OK; // 正常に完了したことを示すTCL_OKを返す } |
gui.tcl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | label .valueLabel -text "-" -font {Monospace 24} button .startStopButton -text "START" -font {Monospace 24} -command startStop pack .valueLabel .startStopButton set running "false" proc mainLoop {} { global running if {$running == "true"} { get } after 200 mainLoop } proc startStop {} { global running if {$running == "true"} { set running "false" .startStopButton configure -text "START" } else { set running "true" .startStopButton configure -text "STOP" } } mainLoop |
C言語側(test.c)は、前回と全く同一です。一方、gui.tclの行数がかなり増えました。
まず、ウィジェットの生成や配置を行っている最初の3行ですが、前回の反省を活かして、ラベルとボタンに関して「-font {Monospace 24}」でオプション設定をしてフォントの種類およびサイズを変更しています。これで見やすくなっているはずです。あとは、ウィジェットやコマンドの名称、テキストが前回と異なっていますが、やっていることはほぼ同じです。
では、今回追加された5行目以降を見ていきましょう。
5~13行目、15~24行目の
1 2 3 | proc xxx {} { ... } |
というまとまりは、何らかのプログラミング言語を触ったことがある方なら察しはつくでしょうが、プロシージャといい、C言語等でいう関数に相当するものです。呼び出す際には、C言語などのように「xxx()」と括弧を付けたりする必要はなく、ただ単に「xxx」と書くだけです。今回は引数のないプロシージャしか定義していませんが、もし「proc xxx { a b } { … }」の用に引数付きのものを呼び出すのであれば、「xxx 10 20」(aに10, bに20が入る)のような感じに記述します。
5行目の「set running “false”」では変数runningに文字列”false”を代入しています。Tclにおいて変数の宣言は不要で、値が代入された時点で変数とみなされます(JavaScript等と似た感じ)。ちなみに、ここでは、”false”という文字列を代入していますが、Tclにはブール型がないので文字列で真偽値を表そうとしています(C言語のように0, 1を使ってもよいかもしれませんが、こっちの方がわかりやすいと思うので)。なお。変数に格納された値を参照する際は、「$running」のように頭に$を付けます。また、8、16行目には「global running」とありますが、プロシージャ外で定義された変数をプロシージャ内で参照する場合は、このような宣言が必要になります。
少し中途半端ですが、長くなりましたので続きは次回に。