プログラミングについて

Share

このプラグインのプログラミング考慮した点などについて、以下にまとめておきます。

全般

  • このプラグインは、gimp python (python-fu)のgimpfuモジュールを利用して動作します
  • 画素の選択は、パスツールのアンカー点により行います。
    • 元々は、Gimpのピクセル情報のように、Gimpの画像ウィンドウ上のマウスイベントに応じて、表示するダイアログの情報を更新することを考えていました。しかし、あれこれ調べてみてもどうも対応するAPIがないようなので、パスツールの利用に落ち着きました。
    • このため、ダイアログ表示後に、Gimp本体側で操作を加えても、このプラグインには反映されません。
    • パスツールのアンカー点のみを利用しています。このため、結合している線分を曲線状に変形しても影響を受けません。

色変換関連

  • Gimp本体はICCプロファイルを用いた色管理に対応しています。そこで将来的に、作業用色空間(既定のRGBカラースペース)がsRGB以外になった場合にも対応可能なように考え、CMMを利用した色空間の変換を行うようにしました。
    • 現状は、作業用色空間(既定のRGBカラースペース)はGimpビルトインのsRGBを想定しています。
    • ただし、作業用色空間について何らかのチェックを行っているわけではないので、デフォルト色空間として別の色空間(例えば、Adobe RGB)を用いても計算はできますが、意図した計算結果にはなっていないので注意してください。
  • LittleCMSによる色空間変換は、以下の条件で行っています。
    • sRGBプロファイル、CIEL*a*b*プロファイルともLittleCMSが内部的に保持しているビルトインプロファイルを使っています。
    • CIEL*a*b*プロファイルは、ICC v4のものを生成しています。
    • CIEL*a*b*プロファイルのホワイトポイントは、D65(6504K)としています。
    • 変換時のintentは、INTENT_ABSOLUTE_COLORIMETRICとしています。
    • 変換時のフラグは、cmsFLAGS_NOTPRECALCとしています。
    • これらの指定は、LittleCMSのチュートリアルとは異なりますが、数値的な正確さを持つために変更しています。

(参考にしたサイト、一部)

ホワイトポイント

レンダリングインテント

 

GUI関連

PyGtkによるコーディングにおいて、なるべくコードの見通しがよくなるようにクラスを利用した設計にしました。なお、今回はGUIの設計にGladeは使っていないので、Gladeを使うとまた変わってくるかと思います。以下では、主に部品間の連携に関わる箇所について説明しています。その他の箇所については直接コードを参照してください(PyGtkのコーディングがある程度できれば難しくないと思います)。

 

スピンボタンと座標値及び画素値のラベルをまとめて、PyGtkの部品(クラス)としました。この際、

 

class PixelInfoPanel(gtk.VBox):

 

のように、VBoxから派生したクラスとしました。このクラスの__init__において、

 

self.spin_button.connect("value-changed", self.cb_spin_button_value_changed)

 

のように、値の変更に対応するコールバックを追加しています。このコールバックは、

 

def cb_spin_button_value_changed(self, widget):
 ''' ラベルを更新
 '''
 # stroke_id が未設定
 if (self.stroke_id < 0):
 return

 ptidx = int(self.spin_button.get_value())

 # 選択済みアンカー点がない
 if ptidx < 0:
 return

 pt  = self.stroke_list[self.stroke_id][ptidx].pt
 self.rgb = self.stroke_list[self.stroke_id][ptidx].col

 ct = ColorTransform()
 self.lab = ct.transform(self.rgb)

 self.xlabel.set_text("%d" % pt.x)
 self.ylabel.set_text("%d" % pt.y)

 self.pxinfo[0].set_text("%f" % self.rgb.v1)
 self.pxinfo[1].set_text("%f" % self.rgb.v2)
 self.pxinfo[2].set_text("%f" % self.rgb.v3)
 self.pxinfo[3].set_text("%f" % self.lab.v1)
 self.pxinfo[4].set_text("%f" % self.lab.v2)
 self.pxinfo[5].set_text("%f" % self.lab.v3)

 

上記のように、自動的に自分のクラス内のラベルを更新します。

一方、色空間での距離を表示するラベルもクラスにまとめています。

 

class DistanceInfoPanel(gtk.VBox):

 

このクラス内部での部品どうしの連携はないので、__init__では特にコールバックの設定はしていませんが、

 

self.event_box.add_events(gtk.gdk.STRUCTURE_MASK)

 

として、イベントボックスでイベントを受けられるように、マスクの追加を行っています(本当はこれもインターフェース用の関数を用意するべきです)。外部からイベントボックス宛のイベントを操作可能なように、インターフェース用の関数をいくつか作成しています。

 

def connect(self, event_text, cb, *userdata):
''' 外部からイベントボックス宛のシグナルハンドラを設定
'''
self.event_box.connect(event_text, cb, *userdata)

def emit(self, event_type, event_obj):
''' 外部からイベントボックス宛にイベントをemit
'''
self.event_box.emit(event_type, event_obj)

これらの関数は、単にイベントボックスの対応する関数を呼び出すだけになっています。

 

全体のダイアログクラスとして、DisplayDialogクラスを作成し、この__init__において、コンボボックス(ドロップダウンリスト)及び、上記の2つのクラスのインスタンスを生成し、いくつかのコールバックを設定しています。

 

self.combobox.connect("changed", self.cb_strk_cmbbox_changed)
self.panel[0].connect("value-changed", self.cb_pxinfo_panel, self.combobox)
self.panel[1].connect("value-changed", self.cb_pxinfo_panel, self.combobox)
self.dist_panel.connect("configure-event", self.cb_dist_panel, self.panel[0], self.panel[1])

最初の行は、ドロップダウンリストの変更が生じた際に呼ばれるコールバックです。次の2行が、 PixelInfoPanelクラスのインスタンス(アンカー点に対応して2つある)が変更された際に呼ばれるコールバックです。最後の行のものが、DistanceInfoPanelに対するコールバックです。

 

ドロップダウンリストに対するコールバックでは、PixelInfoPanelに対して、どのストロークが選択されたかを通知しています。

 

def cb_strk_cmbbox_changed(self, widget):
''' ストローク番号選択時のコールバック
'''
stroke_id = self._cmbbox_get_stroke_id(widget)
if (stroke_id == None):
return
self.panel[0].set_stroke_id(stroke_id)
self.panel[1].set_stroke_id(stroke_id)

このようにすることで、自動的にPixelInfoPanelのvalue-changedイベントが発生するので、以後の連携が可能になります。

 

一方、PixelInfoPanelのコールバック関数は、他のオブジェクトとの連携のための操作を行います。具体的には、

 

def cb_pxinfo_panel(self, widget, data):
''' アンカー点変更に伴う、距離表示更新のためのコールバック

アンカー点変更に伴う、色情報の更新はPixelInfoPanelクラス内で定義済み
'''
# 距離表示の更新のためのイベント呼び出し
self.dist_panel.emit("configure-event", gtk.gdk.Event(gtk.gdk.CONFIGURE) )

のように、DistanceInfoPanelのインスタンスに対して、configureイベントを発行するという処理のみを行います。なお、同じvalue-changedに対して、PixelInfoPanelクラス内部で画素値を更新するコールバックも設定しているので、値の更新はそちらで行われます。

 

DistanceInfoPanelのコールバックでは、引数で与えられたオブジェクトを使い、ラベル値の更新を行います。

 

こうすることで、GUIをいくつかの構成部品に分けてコーディングし、また連携をさせています。