Python-fuバッチ起動まとめ

とりあえずまとめておこう。
以下、Ubuntu上でのお話なのでWinやMacな人は知らない。
Python-fuを使うにあたってつまづいたこといろいろ。

日本語が使えない
    1. とりあえず先頭行:#!/usr/bin/python
    2. 二行目に# coding: UTF-8
    3. と書いておけばOKなのだが二行目に書くという点が曖昧で間にスペース行が入り込んで原因解明に難儀した。
      1. どうやっても日本語で蹴られる。コメントだろうがお構いなし。
バッチによる起動方法:
    1. Gimpをバッチ起動する。ImageMagicとか色々あるけどGimpGUIと一体を成して使用したい。
      1. そもそもGimp自身がCPUをほぼ1個しか使用できない制約があるのが原因。
    2. いくつか方法があるが基本的にどれも動かない
      1. オフィシャル

gimp --no-interface --batch '(python-fu-console-echo RUN-NONINTERACTIVE "another string" 777 3.1416 (list 1 0 0))' '(gimp-quit 1)'
→動かねー

      1. IBMでのご紹介(いつのバージョンで動かしたのやら)

gimp -i -b '(python-fu-resize "myimage.png" 200 200 TRUE)' -b '(gimp-quit 0)'
→動かねー。--verboseつけて見てたら、script-fu経由呼び出しに成っている模様。

      1. 結局ググって見つけた方法(processWaker.py)

gimp --no-interface --console-messages --no-data --no-splash --batch-interpreter python-fu-eval --batch - < ./mansiki/processWaker.py

バッチ起動時の注意点
  1. Gimpは基本起動時に指定のディレクトリ配下のすべてのプラグインを呼び出す
    1. ~/.gimp2.6/plug-ins
    2. python-fuの場合は実行権限が必要。ただ、プラグインだけあって拡張子は関係ない模様。
      1. なので、上記のコマンド実行をどこかに紛れさせたプラグインがあるとプロセス起動の無限ループに陥る。
  2. もし、起動時に実行されたくなければ関数にして起動完了後に読み込んだスクリプトで関数を呼び出す。
    1. なので原則実行スクリプトGimpが起動時に読み込むディレクトリに置くべきではない。~/.gimp2.6/plug-insとか。一個ディレクトリを中で掘るだけでもいいので別ディレクトリとしておくこと。
  3. gimpfuのパスは通ってる、でも自分のディレクトリのパスは通ってない
    1. なので使いたかったらパスを追加しましょう。その後に呼び出すモジュールをインポート。
    2. sys.path.insert(1, './mansiki')
  4. 最後の戻り値設定
    1. これやんないと処理が終了してくれない=プロンプトが帰ってこない
      1. gimpfu.pdb.gimp_quit(1);
      2. なのでgimp起動状態で呼び出すスクリプトの最後には必須。(例ではprocessWaker.py)
      3. ただ、途中の処理で例外がとんだままだとCtl+Cで停止するまでプロンプトは帰ってこない。上は飽くまでもうまくいった場合の話。
registされた関数の呼び出し。
  1. 通常のGUIモードで使用するために書いたモジュールの呼び出し方法、この方法は基本GUI経由で呼び出すものはこれだけと割りきっておいた方がいいかも。
    1. Python的に呼び出したければパスを追加する。
      1. なので使いたかったらパスを追加しましょう。
      2. sys.path.insert(1, '/usr/lib/gimp/2.0/python')
      3. sys.path.insert(1, '~/.gimp2.6/plug-ins') #フルパスが確実かも
    2. gimp的に呼び出す
      1. gimpfu.pdb.xxx_yyy_zzz(各種引数、imageもdrowableも必要)
      2. この時、gimpfu.registerの第一引数に指定したnameが使用される。
      3. ただしSyntax的に-は_に変換される。ので-を_で置き換えた名称の関数がある前提で呼び出す。(xxx_yyy_zzzはxxx-yyy-zzzで登録されている。)
      4. で、呼び出すのはいいのだが、標準出力に大量のusageが出てくるのがなんとも。何か間違えテイルかも。
バッチ起動時の暫定的な考え方
  1. これは経験則であり、作者がどういった意図でこうしているのかは不明。ただ、使う側としては次の様に考えて置きたい。
  2. 基本スクリプトはpyで書く
    1. sys.path.insert(1, '/usr/lib/gimp/2.0/python')
      1. これ書いておけばGimpのモジュールgimpfuが使えて基本何とかなる。また同様に、自分の呼び出しもジュールがある場合は同様にパスを追加しておくこと。
  3. registerにてGimpに登録したもの処理もこっちの書いておけば共有化が可能。

以上。
バッチができると、クラスターで処理とか、sshの向こう側に処理を委託とか出きるもので・・・

以下サンプル

.gimp-2.6/plug-insがカレントディレクト

./mansiki/processWaker.py

#!/usr/bin/python
# coding: UTF-8

import sys,gimpfu

sys.path.insert(1, './mansiki')
import callProcess

def python_fu_make_img_ng(img, drawable):
    pass

callProcess.executeMultiProcess()

gimpfu.pdb.gimp_quit(1);

./mansiki/callProcess.py

#!/usr/bin/python
# -*- coding: utf-8 -*-

import os,sys
from Queue import Queue
sys.path.insert(1, './mansiki')
num_worker_threads =2
cmd = "gimp --no-interface --console-messages --no-data --no-splash --batch-interpreter python-fu-eval --batch - < ./mansiki/makeImage.py "
    
print 'os.getpid():'+str(os.getpid())
print 'os.getppid():'+str(os.getppid())
import threading

# Queue
class AsyncCall(threading.Thread):
    def __init__(self, q):
        threading.Thread.__init__(self)        
        self.q = q

    def run(self):
        while True: 
           item = self.q.get() 
           print item
           os.system(item)
           self.q.task_done() 

def executeMultiProcess():
    
    q = Queue(0) 
    source=[cmd,cmd,cmd,cmd]
    for item in source:
        q.put(item) 
        print item
    
    for i in range(num_worker_threads): 
        task = AsyncCall(q)
        task.start()
    
    q.join()       #

./ImageMake.py

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys

sys.path.insert(1, '/usr/lib/gimp/2.0/python')
import gimpfu,time
from datetime import date

def hello_world():
    gimpfu.pdb.gimp_message("hello world!")
    dst="/home/alfx61/img"
    image = gimpfu.pdb.gimp_image_new(120,120,0)
    layer = gimpfu.pdb.gimp_layer_new(image,120,120,0,"test",0,0)
    now = date.today() 
    
    filename=dst+"/"+now.strftime("%Y%m%d%H%M%S")+str(time.time())+"A.png"
    gimpfu.pdb.file_png_save_defaults(image, layer, filename, filename)
    gimpfu.pdb.python_fu_hello_world(None,None)
    pass

hello_world();

gimpfu.pdb.gimp_quit(1);

./mansiki/processWaker.py

#!/usr/bin/python
# coding: UTF-8
import gimpfu
from datetime import date

def python_layer_splitter(img, drawable, dst):
  # each layer
  image = gimpfu.pdb.gimp_image_new(120,120,0)
  layer = gimpfu.pdb.gimp_layer_new(image,120,120,0,"test",0,0)
  now = date.today() 
  
  filename=dst+"/"+now.strftime("%Y%m%d%H%M%S")+".png"
  gimpfu.pdb.file_png_save_defaults(image, layer, filename, filename)

gimpfu.register(
        # nameimage
        "python-fu-Image-Make",
        # blurb
        "python-fu Image Make",
        # help
        "make png files namede by time",
        # author
        "ryunosinfx <ryunosinfx._{at}_.goo.ne.jp>",
        # copyright
        "public domain",
        # date
        "2009",
        "<Image>/Python-Fu/ImageMake...",
        # imagetypes
        "",
        # params
        [
          (
            # ディレクトリ選択、 変数名、ラベル、初期値
            gimpfu.PF_DIRNAME, "arg0", "出力ディレクトリ", ""
            )
          ],
        # results
        [],
        # function
        python_layer_splitter)

gimpfu.main()