LabVIEWを触ったことがない方に向けて、それなりのプログラムが書けるようになるところまで基本的な事柄を解説していこうという試みです。
シリーズ16回目としてLabVIEWで効率よくプログラムを実行するためのイベントストラクチャを扱います。
この記事は、以下のような方に向けて書いています。
- イベントって何?
- イベントストラクチャの使い方が分からない
- イベントが多くてデバッグが進まない
もし上記のことに興味があるよ、という方には参考にして頂けるかもしれません。
なお、前回の記事はこちらです。
効率は大事
さて、ケースストラクチャも使えるようによって、いわゆる条件分岐ができるようになりました。繰り返し処理も既にやりましたし、条件によって処理を分けることもできるようになって、いろいろできることが増えてきましたね。
繰り返しも条件分岐も、ストラクチャ(プログラムの構造)に関するトピックでした。今回は、これらの構造の中でもプログラムの効率に関連するストラクチャである、イベントストラクチャについて扱います。
簡単に言うと、「イベントが起きたときにしか実行しない処理」を作るときに使用します。逆に言えば、今までは特にイベントなど関係なくプログラム全体を実行していたことになります。

イベントって何?と思うかもしれませんが、一番わかりやすいのは「ユーザーの操作」です。これ以外にも山ほどイベントの種類はありますが、すべてをいきなり覚える必要はないので、まずは最低限覚えた方がいいイベントについて扱っていきます。
例を示すために、とても簡単な次のようなプログラムを考えてみます。

プログラムとしてはあまり面白くないですが、実行すると数値制御器の値に2が足されて結果という数値表示器に表れます。これがWhileループが止まるまで実行されます。早速実行してみましょう。

予想通りになっているはずです。気になるのは、反復端子が延々増加していくことくらいでしょうか?
このプログラムで何を見せたかったかというと、「プログラム、すごい勢いで実行されているよね」、ということです。
反復端子は、ループが一回回るごとに1表示が増えるものでした。これがすごい勢いで増えているということは、すごい勢いでループが回っている、中のコードが繰り返されている、ということになります。
ただ、考えてみてください。これって無駄じゃないですか?確かに、数値制御器の値に処理を加えた結果を表示器に表したいのであればこのように書くんですが、ユーザーが数値制御器の値を変えるよりもずっと早くWhileループ回っていますよね?
意図してこのようなプログラムを書くのであれば別ですが、例えばユーザーがフロントパネル上で制御器の値を変化させたことによって変化したときだけ、その部分のコードが実行(2を足すという処理が行われる)されればプログラムとしては十分ですよね?

以前ループの説明をした際に、待機の関数を置いていました。この関数、指定した時間の間ループが進むのを待機してくれるので、「ひっきりなしにループが回る」ことを防ぐという意味では効果があるのですが、逆に言えばこの時間経過しないとやはりLabVIEWは反応してくれない状態となります。

それに、待機の関数をおいていても結局はユーザーが数値制御器の値を変更するまで、ループは「空回り」している状態と言えます。もちろん、空回りだろうと、プログラムとしては絶えず動いている(その証拠に反復端子が変化している)わけです。
プログラムってパソコン上で動いていますよね?みなさんも経験あると思いますが、PC上でインターネットやWord、Excel、Youtubeなどのアプリをたくさん開いていると処理が重くなってしまいます。
つまり、難しい話は置いといたとしてもプログラムがひっきりなしに動き続けている以上、PCに負荷がかかってしまうわけです。この例のように「空回り」だったとしてもプログラムが動いていることに変わりはありません。最悪、VIの他の部分もいろいろな動作が遅くなってしまいます。これでは効率がいいプログラムとは言えません。
イベントストラクチャを使う
そこで扱うのがイベントストラクチャです。最初にお話したように、イベントストラクチャは「イベントが起きたときにしか実行しない」ようなコードを書くときに使用します。
例えば先ほどの例で見せたプログラムは下の図のような形で書き直すことができます。

上の図に示したブロックダイアグラムはどちらも同じWhileループで、イベントストラクチャの中身だけ変えています(二つWhileループを用意したわけではありません)。ケースストラクチャで二つケースを用意するのと同じ感覚です。
動作を見て実際の動きを確認するために、まずはこのプログラムを作ってみます。イベントストラクチャはループやケースストラクチャ同様、ストラクチャのパレットにあります。ブロックダイアグラム上に配置するのも、ループなどと同じノリで四角い枠を用意します。
このとき、イベントストラクチャは基本的にwhileループの中で使用します。

図のように用意出来たら、「タイムアウト」と書かれたイベント名を表示した欄(イベントセレクタラベルといいます)で右クリックして「このケースで処理されるイベントを編集」を選択します。

今回は、「数値制御器の値が変わった」というイベントを検出するとします。一覧に「数値制御器」とあるのでこれを選択すると、一番右側の表示部分に「値変更」とあるのでその状態でOKで終了します。

その後、このイベント内に数値制御器を置きます。これで「数値制御器が押された」というイベントに対してのイベントストラクチャの用意が完成しました。
今回の場合には、数値制御器の値がユーザーの操作によって変更されたとき、その値変更というイベントをイベントストラクチャが検知します。
イベントストラクチャがイベントを検知するとその中に書かれた処理が実行されます。他のイベントと入れ子になったりしないように、このイベントケースの中に必ずイベント発生元である数値制御器を置きます。

では、同じようにWhileループを止めるためのブール制御器についてもイベントを作ります。イベントラベルを右クリックして「イベントケースを追加」します。こちらも、「ユーザーがブール制御器をクリックした」というイベントによって値が変化(FALSEがTRUEになる)ことを検知するため「値変更」を選択します。そしてそこからTRUEの定数を条件端子にいれています。

なお、ケースストラクチャのときには出力トンネルは毎ケースごとに一つずつ定義する必要がありましたが、イベントストラクチャでは何も配線していないと自動的に「デフォルトを使用」状態になります。

これで準備ができました。プログラムを実行してみると、見た目は変わらない・・・と思いきや、反復端子の値が0から変化しません。つまり、ループはまだ1回目の処理を終えていない状態です。
ここで数値制御器の値を変化させてみると、表示器の値が変わります。表示器の値が変わったということは、当然、ブロックダイアグラム上で制御器の値が表示器に渡ったことを意味しています。つまり、「数値制御器の値変更」というイベントをLabVIEWが検知し、そのイベントの中に書かれたコードを実行した状態になっています。
Whileループの反復端子も1になっているので、「数値制御器の値変更」イベントが終わって次のループに入っていることが分かります。

次にフロントパネルのブール制御器をクリックしましょう。プログラムが終わることを確認できたでしょうか?ブール制御器の値が変更されたというイベントを検知し、TRUEの定数を条件端子に渡した状態となります。
動作についてより詳しく確認したいのであれば、実行のハイライトの出番です。実行のハイライトのマークをブロックダイアグラム上でクリックして有効の状態にしてからプログラムを実行します。
すると、プログラムの動作が反復端子の値が表示器にわたってからは止まっているように見えます。

例えば数値制御器をフロントパネル上で操作すると、ブロックダイアグラムでは動作が再開し、数値制御器の値変更イベントが実行されるようになります。
また、フロントパネルのブール制御器をクリックすると、ブロックダイアグラム上では確かにプログラムが再び動き出して条件端子にTRUEが渡ることが確認できます。

ここまでの動きでわかるように、イベントストラクチャは定義されたイベント、今回で言えば数値やブールの制御器の値が変更するという動作、が起こるまで一切実行されません。
つまり、それだけ無駄のない状態となります。無駄がないのでPCへの負荷も抑えられ、より効率的なプログラムが作れます。
イベントの種類
イベントストラクチャで定義できるイベントは様々あります。その中でも、ブール制御器の値変更のイベントは特によく使うと思います。それぞれのブール制御器毎にイベントを作れるので、フロントパネルにユーザーが操作できるボタンを複数並べて、このボタンを押したらこの動作、あのボタンだと別の動作、ということが実現できます。
変わり種だと、ユーザーが誤ってフロントパネルを右上の×印を押して閉じてしまうという動作(これもイベントの一種です)をしてしまっても、それを「無かった」ことにするような組み方もできます。
ただしこれを実現するためには、イベントに二つの種類があることを覚える必要があります。
- 通知イベント:LabVIEWがイベントを検知次第すぐに実行するコードを書ける
- フィルタイベント:LabVIEWがそのイベントそのものをなかったことにできるようなコードを書ける
ひとつは、イベントストラクチャがイベントを検知して、すぐにその中身のコードを実行するようなイベントです。これは通知イベントと呼ばれています。
一方で、イベントストラクチャがイベントを検知するところまでは一緒ですが、その後にそのイベントをなかったことにできるタイプのイベントがあり、これをフィルタイベントと呼んでいます。
見分け方は、フィルタイベントには?がついていることです。「このイベントが発生したけれど、本当に実行して大丈夫?」といった具合です。

試しに、上に書いた「×印を押してもなかったことにする」プログラムを書いてみます。先ほど書いた数値計算をさせていたプログラムが便利なのでこれをそのまま使います。
イベントは今は数値制御器あるいはブールの値が変更されたときの値変更イベントしか定義されていません。(これには?がついていないので通知イベントということになります)
ではイベントをさらに追加してみましょう。上の図のようにイベントソースとして「このVI」を選び、イベント名として「パネルを閉じる?」を選択します。
すると、イベントストラクチャの右側に、「破棄?」というノードが表れます。

緑字なのでブールデータを配線します。ここにFALSEを入れると、「破棄する?→いいえ」ということで、イベントを破棄しないことになります。ややこしい表現ですが、イベントを破棄しないということはイベントを続行するという意味なので、(フロント)パネルをそのまま閉じるという動作になります。
一方で、TRUEを入れると、イベントを破棄するという状態となるので、パネルを閉じるという動作そのものを破棄、なかったことにします。

この仕組みを作ることで、ユーザーが間違えてウィンドウを閉じてしまう様な動作をしても閉じないようにする、あるいはある条件を満たした後は閉じれるようにする、といったプログラムが組めます。
例えば、ユーザーに「本当に閉じていいですか?」と聞くようなダイアログボックスを表示させて選ばせることができます。そのためには2ボタンダイアログ(ダイアログ&ユーザーインタフェースにあります)が便利です。

フィルタイベントは他にもありますが、ここで全てを紹介できるわけではないので少しずつ慣れていければいいと思います。
イベントストラクチャの注意点
プログラムの効率を上げるため、そしてユーザーの操作をきっかけに何か処理を行わせるような機能を持たせるためにもこのイベントストラクチャはぜひ使い方を覚えておくべき内容です。ただし、いくつかの注意点があります。
- 基本的にwhileループの中で使う
- プログラムの終わらせ方に注意する
- 一つのイベントが終了するまではフロントパネルがロックされる
- 一度実行されるとプログラムが終了するまでイベントを検知し続ける
一つずつ確認していきますね。
基本的にWhileループの中で使う
まず、イベントストラクチャは基本的にwhileループの中で使用します。
実際のプログラムでは単一のイベントのみを検出するということはなく、様々なイベントを検出することになると思います。そのイベントごとにイベントケースを実行させる必要があります。
イベントの検出回数はほとんどの場合決まっていないと思うので、ForループではなくWhileループにします。
何度もイベント検出を繰り返すから、という一見単純な理由ですが、誤った使い方をするとフロントパネルがフリーズしてしまう可能性があります。
後述する内容にも関わってくるのですが、イベントストラクチャはあるイベントが起きたら確実にそのイベント内の処理が実行されるようにしないとプログラムが正しく動作しない(フリーズしたようになる)という特徴があります。
もしイベントを検出してもループの中にイベントストラクチャが入っていないと二つ目以降のイベントケースの中身の処理は永遠に実行されません(強制終了するしかなくなります)。そのためにイベントストラクチャとWhileループはセットと覚えるようにします。

例えば上の図のようなプログラムを書いた場合、OKボタンを押すと右側のWhileループが始まることになります。この時点でもし再びOKボタンを押すと、プログラム自体は動き続ける(ループの反復端子の値が変わる)のですが、フロントパネル上の操作が一切効かなくなります。
そのため、イベントストラクチャはプログラムが終わるまでずっとイベントを検出し続けられるようにWhileループに入れることが前提になっています。
プログラムの終わらせ方に注意する
単純な話で、イベントストラクチャが入ったWhileループもプログラム終了時には終わっている必要がありますよ、ということです。
イベントストラクチャを内包したWhileループは、イベントストラクチャしかない場合が多いと思います。VIが終わるときには、VIに書かれたすべての処理が終わっている必要があるので、当然この「イベントストラクチャを内包したWhileループ」も終わっている必要があります。
これまでの例で見てきたように、停止ボタンについてのイベントが登録されていて、そのイベントとしてWhileループの条件端子にTRUEを渡すというイベントが登録されている場合には問題ありません。
ですが場合によっては、必ずしも「イベントストラクチャを内包したWhileループ」用の停止ボタンは用意していないこともあると思います(わざわざこのWhileループを止めるだけの停止ボタンを用意しない場合など)。かといって何らかの方法でこのWhileループが終わらないとVI全体は終了することができません。
いくつか手段はあるのですが、手っ取り早いのは「タイムアウト」イベントの実装になります。ただしこれは「指定した時間が立ったらタイムアウトイベントの内容を実行する」というイベントであり、つまりWhileループに待機関数を置いた状態とさほど変わりません。

プログラムが終わるときは、この「イベントストラクチャを内包したWhileループ」も終わる時だ、という組み方をすれば問題ありません。むしろ、そのように組むことがほとんどです。
ただ、そのように組んでいなかった場合に、「プログラムの処理全体が終わったはずなのにまだ実行状態になっている」という場合には、イベントストラクチャの入ったWhileループの状態を疑ったほうがいいかもしれません。
一つのイベントが終了するまではフロントパネルがロックされる
次に、イベントストラクチャはイベントケース内にある処理が終わるまでフロントパネルをロックするという性質があります。上に書いた、Whileループとセットで使わないとフリーズしたように見えるということに関連してきます。
例を見るために、下のようなプログラムを考えます。(ブール制御器の機械的動作はすべて放されたらラッチとしています)

このプログラムを動かして、まずブールを触らずに数値制御器を動かします。数値表示器は制御器の値を即座に反映するはずです。
次に、ブール制御器の遅延ボタンを押して、その後すぐに数値制御器を動かしてみてください。数値表示器は動きましたか?何も反応が無いように見えます。

これはイベントストラクチャの大きな特徴で、「あるイベントが検出されるとその中身のコードが実行され終わるまでフロントパネルをロックする」という状態になっています。
上のプログラムの例で説明すると、ブール制御器の値変更イベント内においた待機の関数が実行され、5秒間プログラムがここで待機している状態となります。すると、LabVIEWはこの5秒の間フロントパネルをロックして、ユーザーの操作を受け付けないようになります。5秒経つとこのイベントケースが終わることでロックが解かれ、プログラムを再び動かせるようになります。
このロックの状態になってしまうことがLabVIEWでイベントストラクチャを扱い始めた頃には厄介に思われると思います。
この規制、実は解くことができます。イベントを定義する画面をよく見ると、ロックを許可というチェックボックスがあるので、ここを操作することでロックしない状態となります。

ただし、ロックをしなくさせるのは本来のイベントストラクチャの趣旨に反するので、極力このロックをしないという選択肢は使わないようにした方がいいと思います。
対処方法としては、単純に「各イベントケースの中では、一瞬で終わるような操作のみを実行させる」という方法が考えられます。例えあるイベントで複雑な処理を開始させる場合であったとしても、です。そんなプログラムを書くうまいテクニックがあるのですが、少しアドバンスな書き方になるので、今回は扱いません。
習いたての今はとりあえず「ロックされてしまうことがあるんだな、時間がかかる処理はイベントストラクチャ内におかないように気を付けよう」ということを覚えておいてください。
一度実行されるとプログラムが終了するまでイベントを検知し続ける
最後に、イベントストラクチャは一度実行されるとプログラムが終了するまでイベントを検知し続けるという特徴があります。この特徴は少し厄介なのですが、画面のロックと同様に初心者だと気づきにくい仕様になります。
例えば、下の図のようなプログラムを書いたとします。二つの停止ボタンはどちらも機械的動作はラッチとしています。

このプログラムでは、まず左のwhileループの中に入ったイベントストラクチャで定義されたイベントが実行されます。
第一ループ停止ボタンを押すことで左のwhileループから抜け出し、右のwhileループに入ります。「こんなプログラムの書き方効率悪いって言っていたじゃないか」という声が聞こえてきそうですが、そうです、これはあくまでイベントストラクチャの性質を示し見やすさを重視した例です。あしからず。
ただ、全く同じ形ではなくても、結果的にプログラムの流れを考えたときに上の図のプログラムと似たような形のプログラムを書いてしまっているということはあり得るので注意して下さい、というお話です。
さぁこのときに、第二ループ停止ボタンを押して右のwhileループを止めてしまう前に、左のWhileループのイベントで登録されている制御器、数値制御器あるいは第一ループ停止ボタンを押してみましょう。

数値が変わっていないですよね?というか画面がロックされていますよね?これは、左のWhileループ中のイベントストラクチャで定義されていたブールの値変更イベントが起きてイベントストラクチャがこのイベントを検出した、という状態です。「イベントストラクチャが入っているWhileループは終了しているにもかかわらず」、です。
第一ループ停止ボタンを押しても上の図のように「押されっぱなし」の状態で止まってしまい他のボタン操作を受け付けなくなります。
さぁ、イベントのロックの解除にはどうなる必要があったでしょうか?そう、これらが解消されるには、「イベントケース内にある処理が終わる」必要がありました。
ところが困ったことに、プログラム上では左のWhileループは既に抜け出してしまっています。つまり、イベントは検知されていてもそのイベントケースの中身が実行されることがないのです。
つまり、プログラム的にフロントパネルのロックを止めることができなくなった状態です。こうなると、プログラム停止ボタンを押す以外に選択肢はありません。
このように、イベントストラクチャは一度実行されるとこのストラクチャが入ったWhileループを抜けた後でもイベントを検出します。そしてプログラムの構造上そのイベントケースの中身が実行されることは永久にないので、強制終了するしかなくなるプログラムとなります。
これを回避するには、前述の通り「イベントストラクチャが入ったwhileループが終わるタイミングでプログラムそのものも終わる」ような書き方にする必要があります。
ここまで書くと、イベントストラクチャには結構クセがあるようにおもわれるかもしれません。しかし、効率のいいプログラムを書くのに避けては通れません。ルールを守って書いていくうちに自然となれると思います。
イベントがある場合のデバッグについて
効率の良いプログラムを書きましょうということでイベントストラクチャを紹介しました。しかし一方で、使い方にクセがあるというお話もしました。
イベントストラクチャに慣れていないうちは特に、イベントストラクチャを入れたプログラムのデバッグに苦労するかもしれません。
そこで、イベントストラクチャのあるプログラムでデバッグを行う際にオススメの方法を最後に紹介しようと思います。
それが「イベント検査ウィンドウ」です。表示メニューから表示させることができます。

使い方は簡単で、このウィンドウを出した状態でプログラムを実行するだけです。
このウィンドウ、これから実行される予定のイベントを表示したり、どのようなイベントがどの順番で実行されたかを表示してくれます。
そのため、イベントが複数あってデバッグが大変というときにもプログラムの流れを確認するのにとても便利に使用できます。

さて、効率の良いプログラムを書く際にはぜひ覚えてもらいたいイベントストラクチャ、いかがでしたでしょうか?なかなか使い方に戸惑うことがあるかもしれません。
最初は無理せず簡単にイベントとして定義できそうなものをイベントストラクチャで定義するだけでもいいと思います。大事なのは使ってみようという気持ちと慣れる努力です。また説明しておいて何ですが、最初から無理して効率のいいプログラムを書こうと意気込む必要もないと思います。
いや逆に、これを活用していきたいというやる気のある方向けにイベントストラクチャの応用としての記事も用意しました。これまでの話もかなりボリュームがありましたがさらに知りたいよ、という方はのぞいてみてください。
ここまでくれば、プログラムを書くために必要な構造の話はほとんど終わりです。初心者の方向けの記事なのでうまい書き方やテクニックについては触れていないものの、基本的な使い方がマスターできていればそれなりにプログラムを書けると思います。
構造についての話は一通り済んだので、今後数回はプログラム作りで知っておくと便利な内容を紹介していこうと思います。自作のオリジナル関数を作ることや、既存の制御器表示器に様々な付加的な機能を追加することなど扱っていこうと思います。
もしよろしければ次の記事も見ていってもらえると嬉しいです。
ここまで読んでいただきありがとうございました。
コメント