| 次のページ >>

Emacs で Script-Fu を書いて実行させる / script-fu-shell.rb

環境: Ubuntu Studio 9.04 / Gimp 2.6.6 / Ruby 1.8.7 / Emacs 23.0.91.1

ダウンロード

ここから script-fu-shell.rb をダウンロードして適当なところに置いてください。
http://github.com/sonota/script-fu-shell/blob/master/script-fu-shell.rb

script-fu-shell.rb を単体で動かしてみる

Gimp を起動し、メニューから [フィルタ > Script-Fu > サーバスタート]
(Gimp のバージョンによってメニュー位置が違います)

「サーバスタート」をクリックしてサーバを開始させます。

端末で
$ ruby script-fu-shell.rb
と実行するとプロンプトが表示され、 Gimp本体の Script-Fu コンソールを使うのと同じ要領で S式を書くと 対話的に実行されます。

たとえばプロンプトに続けて
(gimp-display-new (car (gimp-image-new 640 480 0)))
と入力しエンターキーを押すと、Gimp側で新規画像が表示されます。

Emacs から使う

手軽に試してみる

Emacs には最初から cmuscheme という Scheme用の便利なライブラリが付属しており、 これをそのまま利用することができます。

まず手軽に試してみたいという場合は scratch バッファで
(setq scheme-program-name "~/test/script-fu-shell.rb")
などと書いて C-xC-e で評価し「Scheme インタプリタとして script-fu-shell.rb を使う」ように設定します。

次に M-x run-scheme すると、Script-Fu用のバッファが開きプロンプトが表示されます。 適当に S式を入力して試してみてください。

プロンプトの位置にカーソルを置いて M-p, M-n で履歴を前後に辿れます。

さらに便利に使う

.emacs に次を追加して保存。

(setq scheme-program-name "###/@@@/script-fu-shell.rb") ;; パスを書き換えてください。
(autoload 'scheme-mode "cmuscheme" "Major mode for Scheme." t)
(autoload 'run-scheme "cmuscheme" "Run an inferior Scheme process." t)

;; シェルを別ウィンドウで開く
(defun scheme-other-window ()
  "Run scheme on other window"
  (interactive)
  (switch-to-buffer-other-window
   (get-buffer-create "*scheme*"))
  (run-scheme scheme-program-name))
;; キーバインド
(define-key global-map
  "\C-cS" 'scheme-other-window)

新たに Emacs を起動し、 M-x scheme-mode で scheme-mode にし、 その状態で C-cS(scheme-other-window)で次の画面のような状態になります。

上がスクリプトを書くウィンドウ、下が Script-Fu シェル。

たとえば上のバッファで (+ 1 (* 2 3)) と書き、 最後の括弧の次にカーソルを移動させて C-x C-e(scheme-send-last-sexp)すると (+ 1 (* 2 3)) という S式がシェルに送られ、 評価した結果が表示されます。

キーバインドなど

cmuscheme をそのまま利用しているだけなので、キーバインドなどは同じものが使えます。
まずはリージョンをシェルに送る scheme-send-region(C-cC-r)や カーソル位置のトップレベルの S式を送る scheme-send-definition(C-cC-e)、 カーソル以後の S式にリージョン設定する mark-sexp(C-M-SPC) あたりを使ってみると良いと思います。
参考: Karetta|Gaucheプログラミング(立読み版)|EmacsからGaucheを使う


scheme-other-window を実行した後元のウィンドウに戻って欲しいのと、 C-cS が押し辛いので私は次のようにしています。

(defun scheme-other-window ()
  "Run scheme on other window"
  (interactive)
  (let ((win (selected-window)))
	(switch-to-buffer-other-window
	 (get-buffer-create "*scheme*"))
	(run-scheme scheme-program-name)
	(select-window win)
	))

(define-key global-map
  "\C-c\C-s" 'scheme-other-window)

その他

今のところ次のような不具合があります。。。

  • ドット対を評価するとエラーになる。
  • tracing の結果がうまく表示されない。

Script-Fu ではありませんが、Emacs で Scheme を書くのって実際どんな感じなの? というのが垣間見れる動画があったので貼っときます。

Gaucheでライフゲームを作ってみた‐ニコニコ動画(ββ)


Gimp: 記事のもくじ

Script-Fu メモ

メモ。

コーディング

新旧の違いなど

2.2 以前の Script-Fu: SIODベース。
2.4 以後の Script-Fu: TinySchemeベース。Tiny-Fu。


関数について調べる場合


概ねこの図のような関係になっているので、

  • Gimp に固有の関数についてはプロシージャブラウザで調べる
  • TinyScheme の部分については「TinyScheme」「Scheme」 などのキーワードと関数名や機能名を組み合わせて検索する

という感じになります。


ある関数が Script-Fu で使えるかどうか調べる

Script-Fu コンソールに関数名だけ入力して評価(実行)

> number->string
#<CLOSURE> ←使える
> number2string
Error: eval: unbound variable: number2string ←使えない(number2stringは束縛されていない=定義されていない)

tracing

メッセージが大量に出て??とりますが何もないよりは助かります。


> (tracing 1) ←トレースを有効にする

Gives: 0
> (number->string "foo") ←エラーの起こる S式

Eval: (number->string "foo")
Eval: number->string
Eval: "foo"
Apply to: ("foo")
Eval: (anyatom->string n number?)
Eval: anyatom->string
Eval: n
Eval: number?
Apply to: ("foo" #)
Eval: (if (pred n) (atom->string n) (error "xxx->string: not a xxx" n))
Eval: (pred n)
Eval: pred
Eval: n
Apply to: ("foo")
Eval: (error "xxx->string: not a xxx" n)
Eval: error
Eval: "xxx->string: not a xxx"
Eval: n
Apply to: ("xxx->string: not a xxx" "foo")Error: xxx->string: not a xxx "foo" 

gimp-shell.el

SIODベースの Script-Fu 用に書かれているためかそのままでは動きませんが、 Emacs ユーザの方は一度試しみてはいかがでしょうか。


Script-Fuサーバのプロトコル

  • TCPソケットを使う。
  • S式の評価結果が文字列の場合はその文字列を返し、それ以外は "Success" という文字列を返すだけ。 なので、結果が文字列以外の場合は細工をしないと詳細が分からない。
  • 複数のトップレベルの S式を送りたい場合は (begin ... ) で囲む。

ここらへんが参考になるかと:

その他


Gimp: 記事のもくじ

gimp-shell.el / gimp-mode を使ってみた

てきとーメモ。

gimp-shell.el

環境: Ubuntu Studio 8.04 / Gimp 2.4.5 / Emacs 23.0.60.1

そのままでは動かなかったので、 以下の箇所を修正。

;; 修正前
  (gimp-send-string 
   "(define (int--emacs-tostring item)
      (cond ((number? item) (number->string item 10))
 	((string? item) (print-to-string item (string-append (substring item 0)
                                                             \"                                 \" )))
 	((null? item)   \"()\")
        ((eq? t item)    \"t\")
 	((pair? item) 
 	 (string-append \"(\" 
		(unbreakupstr 
 			 (map int--emacs-tostring item) \" \") \")\"))
        (TRUE item)))")

;; 修正後
  (gimp-send-string 
    "(define (int--emacs-tostring item)
      (cond
        ((number? item) (number->string item 10))
        ((string? item) item)
        ((null? item) \"()\")
        ((eq? #t item) \"#t\")
        ((pair? item) 
          (string-append
            \"(\" 
       	    (unbreakupstr 
       	      (map int--emacs-tostring item) \" \")
            \")\"))
        (TRUE item)))")

「とりあえず動く」ようにしただけなので、 もっといいやり方があるはず……。

あとは send-last-sexp、send-definition があるだけでも違うので以下を適当に追加:

;; この2つの関数は cmuscheme から。ほぼそのまま。
(defun gimp-send-last-sexp ()
  "Send the previous sexp to the Script-Fu process."
  (interactive)
  (gimp-send-region (save-excursion (backward-sexp) (point)) (point)))

(defun gimp-send-definition ()
  "Send the current definition to the Script-Fu process."
  (interactive)
  (save-excursion
   (end-of-defun)
   (let ((end (point)))
     (beginning-of-defun)
     (gimp-send-region (point) end))))

...

;; キーバインド
(define-key gimp-mode-map "\C-c\C-r" 'gimp-send-region)
(define-key gimp-mode-map "\C-c\C-e" 'gimp-send-definition)
(define-key gimp-mode-map "\C-x\C-e" 'gimp-send-last-sexp)

使ってみる。 Gimp 側で Script-Fuサーバを開始させた後、 M-x gimp-shell で対話的に操作できる gimp-shell バッファが開く。
「You>」で始まるのがこちらの入力、 「Gimp>」で始まるのが Gimp の出力。

Gimp> Success
Gimp> (EMACS-CALL gimp-set-cache-functions (1213 (gimp-context-get-foreground 
gimp-drawable-is-gray gimp-path-get-current gimp-vectors-set-linked 
gimp-context-set-default-colors gimp-parasite-attach gimp-clone-default 
gimp-drawable-fill gimp-image-parasite-attach plug-in-name2layer 
(略。大量に出る)
gimp-layer-set-preserve-trans gimp-temp-PDB-name gimp-palette-set-background 
gimp-palette-set-foreground gimp-patterns-set-pattern gimp-convert-indexed 
gimp-layer-set-visible gimp-gradients-set-gradient)))
Gimp> Script-fu is running ...
You> 123
Gimp> 123
You> "foo"
Gimp> foo
You> nil
Gimp> ()
You> ()
Gimp> ()
You> '(1 . 2)
Gimp> Error: car: argument 1 must be: pair 

You> '(1 2)
Gimp> (1 2)
You> TRUE
Gimp> 1

ドット対の場合がおかしい。


Gimp の関数も使ってみる:

You> (define img (car (gimp-image-new 200 100 0)))
Gimp> Success
You> img
Gimp> 6
You> (gimp-display-new img) ;;=> 新しい画像ウィンドウが開く
Gimp> (7)
You> 

M-p で履歴を上(古い方)に、 M-n で下(新しい方)に辿れる。


gimp-shell を動かしたままで 別のバッファ(たとえば scratch)に移動して M-x gimp-mode で gimp-mode にする。

バッファに適当な Script-Fuスクリプトを書き、 リージョンを設定して M-x gimp-send-region すると、選択した部分を実行した結果が gimp-shell バッファに出力される。


20091014 追記: gimp-schell.el をいじるより cmuscheme とかをいじって Script-Fu に対応させた方が……などと考えましたが、 バージョンによって Script-Fu の仕様がいろいろ変わっていてくじけました……。

Script-Fu: 現在のレイヤーをレベル補正して緑色にする

黒い線で下描き書いて、緑色にして、透明な新規レイヤーを上に作ってなぞる、 という作業で使っています。 関数名適当ですごめんなさい。

; anobota-level-greenize.scm

(define (script-fu-anobota-level-greenize img drawable)
  ; アンドゥしたらここに戻る
  (gimp-image-undo-group-start img)
  
  ; レベル補正
  ; 最後の5つの引数は 入力レベル黒、白、中間(灰色)、出力レベル黒、白
  (gimp-levels drawable 2   0 255 1 255 255)

  ; 表示に反映
  (gimp-displays-flush)
  
  ; ここまでがアンドゥの対象
  (gimp-image-undo-group-end img)
)

(script-fu-register
  "script-fu-anobota-level-greenize"
  "<Image>/Script-Fu/misc/緑化" ; このスクリプトのメニュー位置
  "レベル補正色で緑に" ; このスクリプトの説明
  "sonata" ; 作者名
  "(c) 2007" ; コピーライト
  "2007-06-27" ; 製作日
  "RGB*"  ; スクリプトが動作可能なモード
  SF-IMAGE "Image" 0       ; 引数1
  SF-DRAWABLE "Drawable" 0 ; 引数2
)

Gimp: 記事のもくじ

Script-Fu: 透明な新規レイヤーを作成して追加

ただそれだけの簡単なスクリプトです。 マウス操作がだるいのでショートカットキー Insert を割り当てて使っています。

現在選択しているレイヤーのすぐ上に追加します。

; anobota-add-transparentlayer.scm

(define (script-fu-anobota-add-transparentlayer-ts img drawable)
(let (
       (width  0)
       (height 0)
       (temp   0)
      )
  ; アンドゥしたらここに戻る
  (gimp-image-undo-group-start img)
  
  ; 幅と高さを取得
  (set! width (car  (gimp-image-width img)))
  (set! height (car (gimp-image-height img)))

  ; 新規レイヤー生成
  (set! temp (car (gimp-layer-new img width height 1 "new layer" 100 0 )))
  ; 透明塗りつぶし(?)
  (gimp-drawable-fill temp TRANS-IMAGE-FILL)
  ; 作ったレイヤーを追加
  (gimp-image-add-layer img temp -1)

  ; 表示に反映
  (gimp-displays-flush)

  ; ここまでがアンドゥの対象
  (gimp-image-undo-group-end img)
))

(script-fu-register
  "script-fu-anobota-add-transparentlayer-ts"
  "<Image>/Script-Fu/misc/ts-透明な新規レイヤーを追加" ;このスクリプトのメニュー位置
;  "<Image>/Script-Fu/misc/透明な新規レイヤーを追加" ; このスクリプトのメニュー位置
  "透明な新規レイヤーを追加する(TS)" ; このスクリプトの説明
  "sonota" ; 作者名
  "(c) 2007"    ; コピーライト
  "2007-06-21"  ; 日付
  "RGB*, GRAY*, INDEXED*"  ; スクリプトが動作可能なモード
  SF-IMAGE    "Image"    0 ; 引数1
  SF-DRAWABLE "Drawable" 0 ; 引数2
)

Gimp: 記事のもくじ



** ホームに戻る

| 次のページ >>