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

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

ImageJ / Python プログラミング Tips: イベントリスナーにおいてイベントを区分する (2)

 では、次に複数の同じアイテム(部品)が並んだ場合、イベントが発生したときにそれらを区別するにはどうしたら良いのでしょうか? 例えば数値入力ボックスが複数並んだケースを考えます。この数値入力ボックスに値を入れたり、変更すると、メッセージボックスが現れ、入力数値を表示するというサンプルプログラムです。

実行状態

 結局、これはどのように対処するかというと、各数値入力ボックスに掛けるプレビューア・クラスは同一でも、各入力ボックス毎に別々のプレビューアのインスタンスを作って掛けることになります。別々のインスタンスを掛けることで入力ボックスの区別を行ってイベントに対応した動作を実行します。

----------------

# Interactive UI sample with event listeners    
    
from ij import IJ, ImagePlus 
from ij.gui import GenericDialog    
from java.awt.event import TextListener    
    
class DialogPreviewer(TextListener):    
    def __init__(self, nfield, index):    
        """ 
             nfield: a java.awt.TextField
        """    
        self.nfield = nfield
        self.index = index

        
    def textValueChanged(self, event):
        textStrings = self.nfield.getText()
        print "Text Changed", textStrings
        # print textStrings
        try:
            value = int(textStrings)
            IJ.showMessage("Input numeric" + str(self.index) + ": " + str(value))
        except:    
            return

def testImageUI():    
    gd = GenericDialog("interactive generic dialog test")    
    gd.addNumericField("Input numeric0", 0.0)
    gd.addNumericField("Input numeric1", 0.0)
    gd.addNumericField("Input numeric2", 0.0)
    gd.addNumericField("Input numeric3", 0.0)

    # The UI elements for the above two inputs
    nfields = []
    previewers = []
    for i in range(4):    
        print i
        nfields.append(gd.getNumericFields().get(i)) 
        previewers.append(DialogPreviewer(nfields[i], i))    
        nfields[i].addTextListener(previewers[i])
    gd.showDialog()    
    
    if gd.wasCanceled():    
        print "User canceled dialog!"    
    
testImageUI()    
----------------

 プログラムの解説です。

 まず冒頭で必要な各ライブラリを読み込みます。ここではJava のテキストリスナーを使いますので、

from java.awt.event import TextListener 

を読み込んでおきます。

 

 次に、クラス、DialogPreviewer を定義します。このプレビューアはテキストリスナーを使いますので、引数に入れておきます。そしてコンストラクタでこのクラスが使う引数を定義します。self 以外に、nfield (入力ボックス)と index (ボックスのインデックス番号) を引数として定義します。

 そしてテキストリスナーの唯一のイベント、textValueChanged に対応したメソッドを定義しています。入力ボックスの文字が変更されたら、入力ボックスから、textStrings という変数にボックスの内容を文字列として取得します。

 そして、この文字列を整数に直し、メッセージボックスに、入力ボックスのインデックス番号と、変更された値を表示します。もし整数化に失敗した場合は、何も行わず return で戻ります。

 最後にダイアログボックスの定義です。4つ、Numeric Field を配置し、その後に、for ループを使って、イベントリスナーを各ボックス毎に設置します。

 previewers.append(DialogPreviewer(nfields[i], i)) 

の文で、入力ボックス毎にDialogPreviewerのインスタンスを、previewers[0], previewers[1]... というように別々に定義し、各ボックス毎に別々のインスタンスを設置しています。

 なお、アイテムを配列化したもの (ここの例で言えば nfields) にまとめてイベントリスナーを設置することはできません。必ず部品一つ一つにイベントリスナーを設置する必要があります。

 また、異なるアイテムであれば、同一のインスタンスの、イベントプレビューアを実装したプレビューアの中に複数のアイテムを共存させることはできますが、同一の種類の複数のアイテムを同一のインスタンスの中に入れると、動作はするはするのですが、余計な回数のイベントが発生してしまいます。余計な回数の動作を避けるためには、プレビューアに対しアイテムごとにインデックス番号を指定して異なったインスタンスとして掛けることが必要です。

 また、同一の部品 (アイテム) に複数のイベントリスナーを掛けてはいけません。動作が不定になります。

ーーーーーーーーー

yasuo-ssi.hatenablog.com