読者です 読者をやめる 読者になる 読者になる

プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

【Gimp】CのプラグインサンプルのPythonでの記述

GimpのプラグインのサンプルをPythonで記述する際の練習をかねて、元々Cで書かれていたサンプルを、Pythonで書き直してみました。

元々のCプラグインのサンプルはGimp Developpersのプラグイン開発について説明しているページにあります。プラグイン全般に関わる考え方や、基本的な説明もあるので、(英語が苦手でなければ)一度目を通しておくとよいと思います。

なお、元のCのサンプルコードをそのままPythonで書き直すようにしたため、Pythonとしては効率的ではない箇所もあるかもしれないことをご留意ください。

また、時間計測のコードが含まれているので、コマンドラインからGimpを起動すると、各プラグインの実行時間が表示されます。不要であれば、時間計測部分を削除してください。

(参考にしたサイト)

Gimp Developpers のプラグイン開発のページ

 

サンプル1:hello world

Gimp Developpersの最初のサンプルが、hello worldなので、Pythonのサンプルも同じものを作成してみました。

Cのサンプルでは、メッセージを表示するために、GLib関数のg_messageを呼び出していたのですが、pythonからの呼び出し方がよく分からなかったので、 Gimpのmessage関数で代用しました。

(サンプルコード)

hello.py

 

(後日, 2009/12/01追記)

Gimpのmessage 関数(Pythonからだと、普通はgimp.message("~") )の呼び出しは、文字列の最後に改行文字("\n")がある場合と無い場合で動作が異なります。ある場合は、ダイアログで表示されますが、無い場合は、メインウィンドウのステータスバーの部分に表示されます。

 

Pythonプラグインの動作について

ところで、pythonプラグインの動作ですが、Cプラグインと同様にGimp起動時とプラグイン実行時にそれぞれ別プロセスとして実行されています。

pythonの場合、スクリプトにかかれている命令はすべて実行されるので、main()(gimpfuモジュールの関数)に到達すると当然これが実行されます。そうすると、どうやって起動時のプラグイン登録と起動後のプラグイン実行を判断しているのか、という点が疑問に思えてきました。

そこで、Pythonプラグインの最後にpythonのsleep関数 を呼び出し、psで実行時の様子を観察すると、下記に示すように、プラグイン起動時の引数が異なっている事がわかりました。多分、これらの引数を利用して、gimpfu.main関数において、処理を適切に制御しているものと思われます。

(プラグイン登録時の引数)

プラグイン登録時の起動引数

(プラグイン実行時の引数)

プラグイン実行時の起動引数

 

サンプル2:ぼかし

元々のCの2つ目のサンプルは、ぼかし(blur)のサンプルです。Pythonのサンプルもこれに合わせて作成しました。Pythonの場合はオブジェクトで処理を行うために、何点か異なる点があります。

  • プラグインを処理する関数に対して、drawableがオブジェクトとして渡ってくるので、drawableを取得する処理が不必要になります。
    • オリジナルのCプラグインのサンプルでは、 run関数のなかで、drawable = gimp_drawable_get (param[2].data.d_drawable); として、drawableオブジェクトを取得しています。
  • gimp_pixel_rgn_initを呼び出す代わりに、drawable.get_pixel_rgnメソッドを呼び出します(メソッド名が若干異なる)
  • Pixel Regionからの画素値の取得は、Cのような gimp_pixel_rgn_{get|set}_pixel関数を呼び出すのではなく、Pixel Region のオブジェクトに対して[]表記を与えて要素を取得・設定します。
  • Pixel Regionから取得した画素値は、バイナリ値をstring型として表したものになっています。ここでは、ord/chr関数を使い、数値←→文字データの変換を行いました。なお、structパッケージのpack/unpack関数も試したのですが、この処理フローに合わせて使用したら、処理速度が遅かったので今回は使いませんでした。

以下に、画素単位のアクセス/ライン単位のアクセス/タイルを使用しぼかし半径を可変にしたバージョン、の3つのサンプルを載せておきます。

なお、今回作成したPythonスクリプトの作りが悪いせいか、ライン単位アクセス版よりタイルキャッシュ版のほうが遅くなっています。Pythonに適した高速化は検討する余地がまだまだありそうです。

 

(サンプルコード)

画素単位のアクセス(Cプラグインサンプルのmyblur1.cに相当)

ライン単位のアクセス(Cプラグインサンプルのmyblur2.cに相当)

タイルキャッシュを使用し、ぼかし半径を可変(Cプラグインサンプルのmyblur3.cに相当)

 

サンプル3:ぼかし+GUI

Python-Fuでは入力時にパラメータを設定するGUIは自動的に作成されます。したがって、C版のプラグインと異なり、GTK+(PyGtk)のコードを書かなくても、パラメータ入力用のGUIを使うことができます。

具体的にはregister関数による登録時に引数を指定するようにすればよいだけです。ただし、プログレスバーに関しては次のような振る舞いをします。

  • Python-Fuが自動で作成するGUIにもプログレスバーが含まれています。このため、対話的に実行した場合は、このプログレスバーが更新されます。
  • 前回の値で実行の場合(run_modeがRUN_WITH_LAST_VALSになる場合)は、画像を表示しているウィンドウのプログレスバーが更新されます。

以下にサンプルコードを載せておきます。

(サンプルコード)

myblur4.py(Cプラグインサンプルのmyblur4.cに相当)

 

サンプル4:GUIにプレビュー機能を追加

Gimp PythonのAPIの説明ページには解説がないのですが、gimpui.py(私の環境では /usr/lib/gimp/2.0/python/にありました)をimportすればGimpのwidgetがいくつか使えるようになります。実際、gimpfu.pyが作るダイアログは内部的にこのモジュールを使っています。

そこで、直接gimpui.pyをimportして、独自にプレビューを持つダイアログを作成することを試してみました。プレビューはCのサンプルと同じく、GimpDrawablePreviewで出すことを試してみました。

ところが、やってみるとGimp Pythonで取得できるGimpDrawablePreviewオブジェクトでは、draw_regionメソッドを呼び出しても画面描画が更新されないようです(理由は不明です。ひょっとしたら、やりかたがまずいだけかもしれません)。

このため、サンプル4は当面ペンディングにして、Gimp Python側の状況を見て、対応を考えたいと思います。

何かの参考になるかも知れませんので、不完全ですがここまでのサンプルコードも示しておきます。

(サンプルコード)

myblur5_uncomplate.py(不完全版)(Cプラグインサンプルのmyblur5.cに相当するものを目指すが不完全な状態)

 

ライセンス

このページに含まれるすべてのサンプルは、元にしたCのプラグインのライセンスと同じく、クリエイティブコモンズ 表示-非営利-継承 2.5 に従います。