省型旧形国電の残影を求めて

戦前型旧形国電および鉄道と変褪色フィルム写真を中心とした写真補正編集の話題を扱います。他のサイトでは得られない、筆者独自開発の写真補正ツールや補正技法についても情報提供しています。写真補正技法への質問はコメント欄へどうぞ

ImageJ / Python Tips: ImageJ UI用プレビュー・プログラミングを行う際の盲点

 今回はかなりニッチで個人的なプログラミング用の備忘録です。

 Python (Jython) で、ImageJ用 プラグインをプログラミングをしていたときのことです。ユーザインターフェースのプログラミングを行っていて、パラメータ入力用ダイアログボックスから、色々パラメータを入力することでプレビュー画面をそれに応じて変えようとしていたのですが、いくらコードを工夫しても、どうしても意図通りの動作になりません。さんざん、その原因を追求した挙句、ようやくその原因がわかりました。根本的な誤解をしていたのです。

 例えば、以下のような構造のプレビューアーをプログラミングしていたとします。

 

class Xpreviewer(ItemListener, TextListener):

    def __init__(variables list and objects list):

 

    self.variable1 = variable1

    self.variable2 = variable2

     ︙

     ︙

    self.objenct1 = object1

    self.objenct2 = object2                                          

     ︙

 

    def textValueChanged(self, event):

    def itemStateChanged(self, event):

 

    def execute(self)

 

 このうち、実行モジュールで画像プレビューの描画を行うものとします。そして、テキストフィールドが変更されたり、その他のオブジェクトが変更されたりしたとき、それらのパラメータの変更に応じて、実行モジュール (メソッド) が呼び出され、プレビューの書き換えが行われるものとします。つまり、イベント対応モジュール (メソッド) の中から実行モジュールが呼び出されます。

 

 この時、私は当初、変更されたパラメータを変数に入力するコードを、それぞれのパラメータが変更されるイベントに対応したモジュールの中に書き込んでいました。そして、パラメータを変数に入力した後、各イベントモジュールの最後で実行モジュールを呼び出していました。

 そして実行モジュールが呼び出されると、そこで入力されたパラメータに基づいてプレビューを書き換えるというような形でプログラミングしていました。

 

 で、当初はそれでうまく動いていたので、このコードに問題があるとは思いませんでした。

 ところが、ダイアログを呼び出す際の条件を変えると、どうしても意図通りに動いてくれません。時に結果は意図通りになりますが、時に意図通りに動作しないということが起こるようになりました。なぜ、当初は意図通りに動いてくれていたプログラムが、ちょっと条件を変えると、動作が不定になるのか、散々調べたのですが、原因がよく分かりません。

 やっと突き止めた原因は次のようなものでした。実はダイアログを呼び出す条件を変えたときに、どのように変えたかというと、ダイアログを呼び出す際に、予めあるパラメータはオン、あるパラメータはオフ、というような条件を設定しました。当初は、ダイアログを呼び出すとき、パラメータは基本的にそろえていました。つまりダイアログをロードするときに、チェックボックスはすべてオフでそろえており、ダイアログ上でユーザが変更しない限りオンにならないという前提でした。その前提を崩したときに、動作が不定になったのです。

 結局、これは次のようなメカニズムで発生することが分かりました。最初からあるパラメータをオンにしてロードしたとします。しかし、改訂前はすべてオフという前提でしたので、内部の変数のデフォルト値はオフのまま引き継いでいました。しかし、現在の設定では、ダイアログ上のパラメータの設定は、イベントが発生しない限り、変数に読み込まれません。イベントが発生しないまま(つまりダイアログ上で何かクリックしないまま)、プレビューの書き換えに進んでしまうと、ダイアログ上で表示されている設定(オン)と、描画されたプレビュー画像(オフのまま)が一致しないという事態が起こります。ただクリックをやり直したりすると、設定が変数に読み込まれ、設定とプレビューが一致するようになるので、操作によって、設定が反映されたり、反映されなかったりという事態が発生することが分かりました。

 当初うまく動いていたのは、デフォルト値がオフでそろっており、ダイアログ上でイベントを発生させない限りオンになることはなかったので (イベントの発生なくパラメータがオンということはあり得ない)、ダイアログ上の設定とプレビューの不一致が発生せず、問題にならなかった、ということです。

 

 結局この対策としては、ダイアログからの変数の読込を、イベント発生モジュールの中に書くのではなく、実行モジュールの中に書くということです。そして、イベント発生モジュールの中では、実行モジュールの呼び出しのみ定義すべきです。もし、イベント発生モジュールの中で変数の評価を行うとすれば、それは、実行モジュールを呼び出すかどうかを決定する変数に限定すべきです。

 なお、この問題のもう一つの解決案として、冒頭のコンストラクタにも変数入力のコードを書き込んでおく、というものがあります。そうすればパラメータの初期値をいろいろ変えてもそれが変数に適切に読み込まれます。しかし、その場合は、同じコードがあちらこちらにダブって分散することになります。最終的な実行モジュールにまとめて書いた方がコードとしてはすっきりします。

 この問題は、結局、テキストボックスやチェックボックス等でパラメータを指定するのだから、それらのオブジェクトに関連したイベント発生対応モジュールの中で変数の読み込みを定義するのが当然という、私の思い込みから発生したと言えます。