[Part I. GIMP基礎功]

[Part II. 一種效果的誕生]
[Part III. Script-Fu的秘密]



顯示具有 第21章 Script-Fu和Scheme入門 標籤的文章。 顯示所有文章
顯示具有 第21章 Script-Fu和Scheme入門 標籤的文章。 顯示所有文章

21.1 Script-Fu Console 與 Scheme 基本語法

Script-Fu 訊息視窗
  • 執行[影像] / 濾鏡 / Script-Fu / Console【註1】,會出現Script-Fu 訊息視窗,其下方的文字框內,可輸入欲執行的命令。

  • 上圖展示了Script-Fu 訊息視窗,目前尚未輸入任何指令。
  • 在GIMP裡,並沒有提供Scheme語言的除錯工具,造成了撰寫Script-Fu程式碼的不便,因此,後來有人發展了Perl、TCL、Python來控制GIMP的函式。


練習Scheme語言的語法
  • 可在Script-Fu 訊息視窗內,練習Scheme語言的語法。
  • 下圖是Script-Fu 訊息視窗的部份截圖,示範簡單的Scheme語法。

簡單的例子:

  • 在文字框內,輸入 (* 3 5) ,按下Enter鍵,得到15。
  • 輸入 (+ 6 7),按下Enter鍵,得到13。
  • 輸入 (- (* 3 5) (+ 6 7)),運算後,結果為2。
  • 輸入(let* ((x 3) (y 5))(* x y)),運算後,結果為15。
錯誤訊息:
  • 若再輸入 (* x y),會得到「錯誤訊息」。
  • 離開let* ()的括號外,原本已設定的區域變數x、y,就變成沒有宣告,故有錯誤訊息。


括弧要對齊
  • 請注意,左括弧與右括弧的對齊。
(let* ((x 3) (y 5))(* x y))

  • 這條式子,可看成:
(let* (
(x 3)
(y 5)
)
(* x y)
)


Scheme語法的規則

完整的敘述
  • 括號()特別多。
  • 每一個完整的敘述(statement),都要用括號()括起來。
  • 括號()內的第一項,一定是函數名稱或運算子。
  • (let* ((x 3) (y 5))(* x y)),是完整的敘述,括號要對齊。

註解
  • 分號;為註解符號。

運算式
  • 運算式為前序(prefix)運算式。
  • 運算式(* 3 5)相當於3*5。
  • 3*5是中序(infix)運算式。
  • 若你不習慣前序表示式,你可以將運算子視為函數。
  • * 視為函數,3、5視為參數。
  • 在中序運算式3*5, 運算子*與運算元3、5中間不會有空白(space);但前序運算式(* 3 5),*與3中間一定有空白。

前序運算式
  • (* 3 5)相當於3*5;(* 6 7) 相當於6*7。
  • ((- (* 3 5) (+ 6 7)) ((- (* 3 5) (+ 6 7)) 相當於3*5-6*7。

區域變數
  • 使用 let* ()來宣告與使用區域變數。
  • 語法:(let* (variables) expressions)
  • 以(let* ((x 3) (y 5))(* x y))為例子,左式設定 x=3、y=5,而後執行x*y的運算。
  • 離開let* ()的括號外,原設定的區域變數就變成沒有宣告。

函數定義、宣告與使用
  • 語法:(define (name param-list) expressions)
  • 使用define來定義函數。
  • name表示函數名稱。
  • param-list表示輸入函數的參數列。
  • 參數型態:參數不用設定參數型態,幾乎所有script語言都是如此。
  • 回傳值:函數內的最後一個敘述為回傳值。
  • (define (Multiply X Y) (* X Y) ),左式設定一個乘法函數
  • (Multiply 3 5) 得到15。
  • 猜猜看 (Multiply (Multiply 3 5) 2) 會得到多少?

串列
  • 語法:‘(a b c)
  • 使用撇號 ‘ 來定義串列,撇號是指apostrophe。
  • 例如:‘(1 6 8) ,撇號指示在括號內的是串列,不是單值。
  • 串列(list)相當於陣列,儲存多個單值。
  • (let* (x 5) x),在Script-Fu訊息視窗,鍵入左式得到5。
  • (let* (x ‘(1 6 8)) x) ,在Script-Fu 訊息視窗,鍵入左式得到(1 6 8)。
  • (let* (x ‘(“Gimp” 1.2 “v”)) ,串列的原素可以混合各種資料型態。

串接
  • 使用cons來連結串列產生新的串列
  • (cons “Gimp” (1 6 8) )
  • 上式的輸出結果:(“Gimp” 1 6 8)

抓取串列內元素
  • (car ‘(“Gimp” 2 3 4))
  • car抓取串列內第一個元素
  • 上式的輸出結果:”Gimp”

  • (cdr ‘(“Gimp” 2 3 4))
  • cdr抓取串列內除第一個元素外的其餘元素。
  • 上式的輸出結果:(2 3 4)

  • (car (cdr ‘(“Gimp” 2 3 4)))
  • car(cdr())抓取第二個元素
  • 上式的輸出結果:2


選單及項目名稱的英文:
【註1】[Image] / Filters / Script-Fu / Console

21.2 scm 檔的存放位置與 Script-Fu 的功用簡介

.gimp-2.6 目錄與 scripts 目錄
  • 第一次啟動 GIMP 程式時,GIMP程式會在「用戶的根目錄」,也稱為家目錄,產生「.gimp-2.6」的隱藏目錄,儲存與GIMP相關的資料。
  • 「.gimp-2.6」目錄之下,還有許多子目錄,起始時,這些目錄都是空的,沒有檔案。
    • brushes 目錄,儲存用戶所製作的筆刷檔案。
    • patterns 目錄,儲存用戶所繪製的圖樣檔案。
    • scripts 目錄,儲存用戶所撰寫的命令稿檔案。
  • 也可從網路搜集與下載筆刷、圖樣、命令稿......。


GIMP 的目錄,以 Windows XP 為例
  • GIMP 程式安裝目錄:C:\Program Files\GIMP-2.0\
  • GIMP 用戶目錄:C:\Documents and Settings\user_name\.gimp-2.6\
  • 對於 Windows 用戶,第一次啟動 GIMP 程式時,GIMP 程式會產生目錄「.gimp-2.6」。
  • 在 MS Windows 裡,點號不代表「隱藏目錄」。


程式安裝目錄
  • GIMP 程式的安裝目錄之下,也有一些子目錄,用來儲存 GIMP 程式既有的 brush 檔案、pattern 檔案、script-fu 檔案、plug-in 檔案......。
  • 子目錄,如下圖所示。



用戶專屬目錄
  • 目錄「.gimp-2.6」如下圖所示,此用戶的名稱就是「user」。

  • 目錄「.gimp-2.6」內的資料夾與設定檔,如下圖所示。



參考別人的Script-Fu程式
  • 在撰寫自己所需的 Script-Fu 程式之前,可以先複製一個 Script-Fu 程式,先研究 Script-Fu 程式的運作。
  • 也可以使用既有的 Script-Fu 程式,作為參考與修改的範本。
  • 在22.1節,會需要到「C:\Program Files\GIMP-2.0\share\gimp\2.0\scripts\」目錄之下,複製一份 news-text.scm,貼到「.gimp-2.6\scripts」目錄之下。
  • 學習 Script-Fu 的最好方法,就是閱讀 scripts 目錄下的 scm 檔,scripts 目錄下有超過100個 scm 檔。


Script-Fu主要分兩類:
  • 有一類的Script-Fu,是作用在既有的影像與圖層,例如,「[影像] / 濾鏡 / 動畫 / Waves」,讓單一圖層變為多圖層的波浪動畫。
  • 另一類的Script-Fu,是在「[影像] / 檔案 / 建立」選單內,執行後,產生一個新的影像視窗,顯示結果。例如,下圖的Comic Book字體效果。


以 Comic Book 漫畫書風格的字體為例
  • 下圖是 GIMP 2.6.5版的擷圖,2.6.7版將 Create 譯為「建立」。
  • 下圖是,Comic Book 所在的位置。

  • 下圖是,執行 Comic Book,使用預設參數值所得到的字體。


彌補文字工具的不足
  • GIMP 的文字工具只提供了字型與顏色的參數。
  • 「[影像] / 檔案 / 建立 / Logos」選單內,有許多文字效果,可彌補文字工具的功能不足。
  • 這些 Script-Fu 程式產生的文字效果,很多都用了陰影效果。
  • Comic Book 字體雖沒有陰影效果,但有邊框效果。
  • 陰影、亮邊、發光、邊框的效果,一直都是字體特效變化中常用的技巧。
  • 因此,若仔細研究字體效果的 Script-Fu,會發現很多程式碼撰寫的方式雷同。


組合GIMP處理影像的動作
  • GIMP 的 Script-Fu 就好像 UNIX 的 Shell Script 。
    • Script-Fu 所使用的語言是 Scheme,一種類似 Lisp 的語言。
    • 透過 Script-Fu 的 Scheme 語言,可以組合GIMP處理影像的動作,達到想要的功能,讓這些處理影像的操作自動完成。

  • 使用 GIMP 的視窗介面,處理影像的動作,都有對應的函數。
    • 執行[影像] / 求助 / Procedure Browser【註1】,產生 Procedure Browser 對話盒。
    • 這對話盒提供了 GIMP 所有函數的輸入變數的資料型態、輸出變數的資料型態、該函數的功能......相關資訊。
    • 例如,gimp-edit-copy、gimp-edit-paste、gimp-edit-cut對應的便是最常使用的影像複製、影像貼上、影像剪下的動作。

  • 在 GIMP 視窗介面,選單內的 Script-Fu,也都有對應的函數。
    • 可在 Procedure Browser 對話盒內找到其對應的函數。
    • [影像] / 圖片 / 縮放圖片【註2】,這動作所對應的函數為 gimp-image-scale 。
    • [影像] / 檔案 / Create / Logos / Comic Book【註3】,這動作所對應的函數為 script-fu-comic-logo 。


查詢特定的函數
  • Plug-In Browser 或 Procedure Browser,都用於查詢函數的相關資料。
  • 下圖是「求助」選單。

  • 下圖是 Procedure Browser 對話盒,查詢 gimp-image-scale 的資料。


  • 下圖是,影像視窗內,「Scale Image」指令所在的位置。



Script-Fu 不是濾鏡
  • 透過雖說 Script-Fu 不是濾鏡,但「濾鏡」選單內的項目,有超過30項功能是 Script-Fu 。
  • 「How to write a GIMP plug-in」這篇文章沒有提到 Script-Fu ,因此可以說「Script-Fu 不是外掛」。
  • Script-Fu 不是濾鏡,這句話是有語病的,更正確的說法是, Script-Fu 不是外掛(plug-in),外掛是C語言撰寫的,外掛最終編譯為二進位碼的函式庫, Script-Fu 是純文字的命令稿,以直譯的方式來執行。


Script-Fu 不是外掛
  • Script-Fu 不是外掛,這句話好像還是有語病。管理 GIMP 外加功能、擴展功能的網站,其站名為 GIMP Plugin Registry,網站內也提供 Script-Fu 的相關資訊。
  • 雖然, Script-Fu 是直譯式的程式, GIMP 啟動時,會搜尋特定目錄,將所有的 Script-Fu 函式都予以註冊,因此 Script-Fu 函式不能同名。
  • scripts 目錄下有超過100個 *.scm 檔,這些是學習影像處理技巧的最好教材。


在GIMP的圖形操作介面, Script-Fu 分散到許多選單之中
  • 有些在面版的選單內,例如,筆刷面版的選單內,有製作筆刷的 Script-Fu 。
  • 有些在「[影像] / 濾鏡 / 動畫」選單內。
  • 有些在「[影像] / 濾鏡 / 裝飾」選單內。
  • 有些在「[影像] / 濾鏡 / Alpha to Logo」選單內。
  • 為數最多的 Script-Fu 安置在「[影像] / 檔案 / 建立」選單內。


選單及項目名稱的英文:
【註1】[Image] / Help / Procedure Browser
【註2】[Image] / Image / Scale
【註3】[Image] / File / Create / Logos / Comic Book

21.3 關於 Script-Fu 的地位,我有惑,新版真的比舊版好嗎?

譯詞影響寫作很大
  • 第21章~第30章,與 Script-Fu 相關的文章,大部分,在 GIMP 1.2 版時期,就發表在網路上,如今將大量的文章修改,以符合 GIMP 2.6版。
  • 我的困惑來自於軟體的改版,軟體改版對文件寫作的人,是一件苦惱的事。
  • 軟體介面細微的改變,有時會讓文章要改寫很多地方。只希望有心學習 GIMP 與 Script-Fu 的讀者,和我一樣,能夠克服這小小的阻礙。加油了!


  • 我的疑惑是新版真的比舊版好嗎?
  • 撰寫 Script-Fu 程式與使用 Script-Fu 程式時,2.2版、2.4版優於2.6版。舊版優於新版。讀者應該很難理解原由,且看我細細分析。



注意!
由於不同版的 GIMP , Script-Fu 函式還是有些微的差異,這一系列文章 Script-Fu 的程式碼是拷貝2.6.7版,許多圖片雖是2.4版介面的擷圖,包括圖層面版、濾鏡對話盒、 Script-Fu 對話盒,但這不影響文章的閱讀。

注意!
2.6.7版譯詞較之2.6.6版更完整,沒譯的詞少了一些。這一系列文章的 GIMP 介面中文譯詞主要是2.6.7版。若使用2.6.6版的譯詞,會提醒 讀者。例如,在2.6.7版,Colors的譯詞是「顏色」(作為選單時),之前的版本是「色彩」,我個人偏好「色彩」一詞。


缺譯的譯詞
  • 多數的 Script-Fu ,在2.6.7版,都是沒有翻譯的。
  • 有些在2.2版已有譯詞,在此,一併提醒讀者。
  • 下列都是採用2.2版的譯詞。
    • 第22章,Newsprint Text 譯詞是「印刷文字」,Logos 譯詞是「標誌」。
    • 第23章,Alien Glow 譯詞是「異樣發光」。
    • 第26章,Land 譯詞是「陸地」,Patterns 譯詞是「圖樣」。
    • 第27章,Camouflage 譯詞是「迷彩圖案」。
    • 第28章,Circuit 譯詞是「電路圖」。
    • 第29章,Lava 譯詞是「溶岩」。應該是「熔岩」比較好。
    • 第30章,Old Photo 譯詞是「舊照片」。


舊版的設計,較適合 Script-Fu
  • 通常,大家都會認為舊的比較不好!由於自由軟體是免費下載、免費使用,因此,多數的使用者都是有新版的軟體,就更新為新版的。
  • 如果我說,撰寫與使用 Script-Fu 程式時,2.2版、2.4版優於2.6版,讀者應該很難理解我所說的,讀者應該都是用最新版,即使讀者用過舊版的,也不太會去留意 Script-Fu 相關的功能。
  • 「28.1 電路有點短路」、「29.1 熔岩不夠火紅」這兩節,也可印證此論點「新版的軟體不一定比較完美」。舊版的電路圖、 熔岩圖感覺比較像是電路圖、 熔岩圖。


  • Script-Fu 有一項很重要的特色,就是 Script-Fu 分為兩類,一類需要作用在既有的影像上,一類是無中生有產生一張影像。2.6版的視窗介面,移除了工具箱上的「擴展」選單,因此,不容易辨識出 Script-Fu 的這一項的特色。
  • 有些時候,寫程式時,是不需要用到面版視窗與影像視窗。


  • 2.4版的工具箱視窗,與 Script-Fu 相關的功能,都在「擴展」選單內。這樣,對於撰寫與測試 Script-Fu 程式,是比較方便,比較簡捷。介面也比較「簡潔」,因為可能不會操作到面版視窗與影像視窗。
  • 下圖是2.4版的工具箱視窗。

  • 2.4版選單的安排, 另一項優點是, Script-Fu 與濾鏡的區隔較明顯。


  • 2.6版工具箱上沒有選單,因此,若要專注於撰寫與測試 Script-Fu 程式,可以將選單獨立為單一視窗。
  • 下圖,將2.4版的 Script-Fu 選單獨立為單一視窗。
  • 2.6版也可以這樣做。



2.6版的設計忽略了 Script-Fu
  • 2.6版的 Script-Fu 選單安置在「濾鏡」選單內。
  • 下圖,將2.6版的 Script-Fu 選單獨立為單一視窗。
  • Console就是 Script-Fu Console,就是 Script-Fu 訊息視窗。


  • 同理,也可將「求助」選單獨立為單一視窗,方便查詢 Script-Fu 函數。

  • Script-Fu 對 GIMP 有其重要的地位,但用戶卻是很容易就忽略。
  • 用戶忽略就算了,開發人員若是也忽略,那可就不太好了!
  • 自由軟體最有趣之處就是,每個人都有能力左右它的發展,但有時卻又無法完全決定它的未來。
  • 閱讀 Script-Fu 程式碼,開頭的地方會讀到許多人名,這些人都是在 GIMP 1.2版之前對 GIMP 有貢獻的程式開發人員。


歷史是這樣記載的
  • 當初 GIMP 的創始人Spencer Kimball 和 Peter Mattis 修了一門編譯器的課程,這是 Fateman 教授 CS164 的課程。
  • Spencer Kimball 和 Peter Mattis 為了實作 Scheme 語言的直譯器,而有了開發影像處理軟體的想法。


  • 如果,真是如此, GIMP 是因為 Script-Fu 而存在的,最初, GIMP 圖形介面的環境,是因為命令稿的語言而存在的。
  • 在Spencer Kimball 和Peter Mattis大學畢業、投身社會之前,他們已完成了一套堪用的影像處理軟體。
  • 畢業之後,他們就將 GIMP 的開發轉移給別的人員。


目錄位置的移動
  • 新的開發人員,為了增加新的功能,自然有新的想法。
  • 與 Script-Fu 相關的功能,自從2.2版之後,目錄就不斷地移動位置,我總有個疑惑,有這必要嗎?
  • 因為, GIMP 不會因為這樣的改變,選單的操作就變得更方便。
  • 終究,還是老樣子,想要執行某個功能,就要記住它所屬的選單與子選單。


  • 若說「新版比舊版差」,卻是有些沉重。
    • 說沉重,因為 GIMP 功能太多,與 Script-Fu 相關的功能不斷地移動位置。
    • 結果,在選單內,有些 Script-Fu 不知已經沉到那兒了。
  • 就這樣,位置移來移去,有幾個很不錯的 Script-Fu , GIMP 不再收錄了,真是可惜!
  • 只希望,新一批的 GIMP 程式開發人員,不要讓 Script-Fu 越來越少!

21.4 關於 Script-Fu 的地位,重有感,新版與舊版細微的差異

  • 因為,寫作時,不可能所有選單項目、對話盒、面版都附圖。
  • 因此,讀者首先要明瞭,作者個人的用語方式與表達方式,還要留意操作的 GIMP 的版本為何。
  • 學會了Script-Fu,就會明瞭,為何作者要採用「 [影像] / … / …」與「 [Image] / … / …」的描述方式。
  • 因為這樣很接近 Script-Fu 程式描述選單的方式「<Image>/…/…」。

注意!
對於GIMP選單的描述,理應用新的譯詞。 但對於Colors選單,我個人偏好「色彩」一詞。加上,之前的網誌《GIMP基礎功與範例教學》是使用「色彩」一詞,那時是使用2.6.5版。因此,當你讀到 「執行[影像] / 色彩」,請注意,在2.6.7版,是指「顏色」選單。

注意!
2.4版將Image譯為影像,2.6版將Image譯為圖片,由此延伸2.6版的譯詞是採用「圖片視窗」,但在書中,還是常可讀到「影像視窗」。

注意!
在影像處理的軟體裡,有時影像、圖片、檔案是相同的意思。「新增影像」(新增圖片)暗示了新增一個檔案,只是檔案尚未儲存到硬碟裡。由於GIMP獨特的多重視窗介面,「新增圖片」會新增一個「影像視窗」。

注意!
在影像處理的軟體裡,有時影像、圖片、檔案是相同的意思。「新增影像」(新增圖片)暗示了新增一個檔案,只是檔案尚未儲存到硬碟裡。由於GIMP獨特的多重視窗介面,「新增圖片」會新增一個「影像視窗」。

  • 現在,GIMP 是2.6.7版,2.6.7版與2.6.6版的中文譯詞又有些不同。
  • 最明顯的不同,就是Colors選單譯成了「顏色」選單,原本是「色彩」選單。


  • 我在GIMP發佈2.4版時,已大幅修改過這一系列介紹 Script-Fu 的文章,只是那時候沒有全部修改完,也沒有發表。如今是2.6.7版,再將這些文章修改後,重新發表。
  • 這一系列文章的擷圖仍,是維持採用GIMP 2.4版的介面外觀的擷圖,與2.6.7版相較,差異很小,除非仔細看。通常這樣的差異,對於想要學習寫程式的用戶,是可容許的,因為寫程式要用英文。


  • 差異 1. 下圖是中文2.4版影像視窗,視窗標題上顯示「2 layers」,在中文2.6版,視窗標題上顯示「2個圖層」。



  • 差異 2. Background圖層,沒有Alpha Channel,不具有處理透明的能力,因此,在2.6版,Background會以粗體字顯示。下圖是中文2.4版的圖層面版, Background並非粗體字。

  • 差異 3. 請注意,在上圖,Lock的中文譯詞是「鎖定」,原意為Lock Alpha Channel,鎖住Alpha色版,讓既有的透明效果不再受任何影響。

  • 差異 4. 在2.6版,面版的名稱是粗體字,「圖層」兩字是粗體。

  • 差異 5. 在2.6版,很多對話盒,都多了一些粗體字的名稱,來強調該項目。

21.5 增加 Python-Fu 解說,重新調整第21章~第30章的內容

如果 Scheme 有點困難,就換用 Python
  • 以 Scheme 語言為基礎的 Script-Fu ,可能有點困難。
  • 那麼,就試試以 Python 語言為基礎的 Python-Fu 。
  • 最近新增了 Python-Fu 的內容,與 Script-Fu 兩相對照,重新調整第21章~第30章的內容。


如果撰寫完整的 Python-Fu 檔案有點麻煩,那就撰寫單行可執行的程式碼
  • 完整的 Python-Fu 檔案,參數的設定是透過對話盒,有其優點,只是測試的過程比較麻煩。
  • 單行可執行的程式碼,貼到 GIMP Python Console 就可測試其效果。
  • 單行可執行的程式碼,很適合 Python-Fu 功能的摸索與學習。


  • 除了第21章~第30章有 Python-Fu 的內容,下面再列舉一些有 Python-Fu 內容的章節。


從這兒開始探索 Python-Fu

21.6 單行可執行的程式碼,很適合用於學習 Script-Fu 與 Python-Fu

三種單步執行程式的方式
  • 第一種方式:只修改註冊於PDB內的函數名稱
  • 第二種方式:修改成在 Console 內可一行一行執行的 Scheme 程式碼
  • 第三種方式:修改成在 Console 內可一行一行執行的 Python 程式碼


本節介紹「第二種方式」,以 news-text.scm 為例
  • 本節介紹單步執行的「第二種方式」,可「單行」執行的 Scheme code 。
  • news-text.scm 完整的程式碼可參考 22.5 節。
  • 也可到 GIMP 系統的 scripts 目錄下找到該檔案。
  • 最主要修改的部份是,將「對話框接收輸入變數值」修改成「直接指定輸入變數值」。
  • 先要瞭解,news-text.scm 程式碼的執行流程。


不使用 let* 改用 define
  • 原本的程式使用 let* 定義與指定區域變數值。
  • (let* (()()()...) ()() ...) 這樣的語法括號太多。
  • 若要一行一行分析程式碼的意義,let* 語法,括號有好幾層,分析時,會造成一些干擾。
  • 現在不使用 let* 改用 define 來定義與指定變數值。


如何用人工的方式判斷,左右括號對齊
  • 心中有個計數器,一開始為 0 。
  • 從左至右掃描程式碼,在心中默數。
  • 看到左括號 ( 加 1 。
  • 看到右括號 ) 減 1 。
  • 掃描完,若復歸為 0 ,則表示左右括號在數量上是對齊的。
  • 掃描的過程中,計數器值若為負值 ,則表示此時右括號多於左括號 ,這是也是不合理的。

  • news-text.scm 以這一行 Scheme code 為例

(define grey (/ (* density 255) 100))
1            2  3             2   1 0

  • news-text.scm 之中,下面這一行應該是最複雜的。
  • 讀者可自行練習,判斷括號對齊與否。

(gimp-floating-sel-anchor
(car (gimp-text-fontname img text-layer
(/ (+ 20 blur-radius) 2) (/ (+ 20 blur-radius) 2)
string 0 TRUE font-size PIXELS font)))


捨棄函數 script-fu-newsprint-text,直接指定輸入變數值
  • 修改成最多的地方就是下列的這部份。
  • 修改後的程式碼,不再需要預先定義函數 script-fu-newsprint-text ,但是要預先設定變數名稱、變數值 。

(define (script-fu-newsprint-text string font font-size cell-size density blur-radius text-color bg-color)
(let* (
(text-ext ......)
(width ......)
(height ......)
(img ......)
(bg-layer .....)
(text-layer ......)
(text-mask ......)
(grey .....)
)
  • 從下列的程式碼得知變數值的型態與大小 。
SF-STRING _"Text" "Newsprint"
SF-FONT _"Font" "Sans"
SF-ADJUSTMENT _"Font size (pixels)" '(100 2 1000 1 10 0 1)
SF-ADJUSTMENT _"Cell size (pixels)" '(7 1 100 1 10 0 1)
SF-ADJUSTMENT _"Density (%)" '(60 0 100 1 10 0 0)
SF-ADJUSTMENT _"Blur radius" '(0 0 100 1 5 0 0)
SF-COLOR _"Text color" "black"
SF-COLOR _"Background color" "white"

21.7 以印刷字 news-text.scm 為例,摸索 Scheme 轉為 Python 的技巧

轉換的過程,需要一些時間摸索
  • 本節的重點,在於轉碼的技巧。
    • 本節介紹單步執行的「第三種方式」,修改成在 Console 內可一行一行執行的 Python 程式碼 。
    • 接續前節,使用前節更改後的 Scheme 程式碼,將它改成 Python 程式碼 。

  • 摸索的過程:
    • 要先熟悉 Scheme 與 Python 的基本語法。
    • 一開始,先用手工更改,慢慢瞭解兩者語法的差異。
    • 而後,利用文字編輯器「搜尋」與「取代」的功能,修改程式碼。
    • 經過一番摸索,整理需要修改之處,利用 Python 「正規表示式」的功能,將 Scheme code 轉為 Python code。
    • 經過一番摸索,最終,將「轉碼的流程」寫成 Python 程式 scm_to_py_.py 。
    • 多數的修改,都可由 scm_to_py_.py 自動完成。


摸索「變數值指派的程式碼」的修改技巧
  • 有些地方,可以直接手動更改。
  • 有些地方,手動更改,太累人了。
  • 文字編輯器都有「搜尋」與「取代」的功能。
  • 善用「搜尋」與「取代」,可幫助修改程式碼。

(define string "Newsprint")
(define font "Sans")
(define font-size 100)
(define cell-size 7)

(define density 60)
(define blur-radius 5) ; 原本為 0 我改用 5
(define text-color "black")
(define bg-color "white")

  • 可分段使用「搜尋」與「取代」的功能。
  • 先專注分析一小段程式碼 Scheme code 與 Python code 的差異。
  • 例如,上一段程式碼,是用於「變數值指派」。
    • (define ...) 開頭的 (define 是要刪除的
    • (define ...) 結尾的 ) 是要刪除的
    • 將「空白」取代為 等號 =
  • 「取代」之後的程式碼,如下所示。

string = "Newsprint"
font = "Sans"
font_size = 100
cell_size = 7

density = 60
blur_radius = 5 # 原本為 0 我改用 5

text_color = "black"
bg_color = "white"

  • 但是 Python 無法解讀 "black" 與 "white" 的意義,要再改為 (0, 0, 0) 與 (255, 255, 255)。


摸索「指派影像物件、圖層物件的程式碼」的修改技巧

(define text-ext (gimp-text-get-extents-fontname string font-size PIXELS font))
(define width (+ (car text-ext) 20 blur-radius))
(define height (+ (list-ref text-ext 1) 20 blur-radius))
(define img (car (gimp-image-new width height RGB)))
(define bg-layer (car (gimp-layer-new img width height RGB-IMAGE "Background" 100 NORMAL-MODE)))
(define text-layer (car (gimp-layer-new img width height RGBA-IMAGE "Text layer" 100 NORMAL-MODE)))

  • 上一段程式碼是用於「指派影像物件、圖層物件」。
    • (define ...) 開頭的 (define 是要刪除的
    • (define ...) 結尾的 ) 是要刪除的
    • 利用換行符號的逃脫字符 \n 可輕鬆刪除結尾的 )
    • 也就是,在文字編輯器「取代」功能的對話框內,將 )\n 取代為 \n
    • 將 (car 取代為 等號 =
    • 將(...... layer-two 0)「空白」取代為 等號 =
    • 將剩餘的「空白」取代為 "逗點加空白"「, 」
    • 將 (gimp 取代為 pdb.gimp
    • 將 減號 - 取代為 _ 底線
  • 其它不符合 Python 語法之處,直接手動更改。
  • 「取代」之後的程式碼,如下所示。

(define text-ext (gimp-text-get-extents-fontname string font-size PIXELS font))
(define width (+ (car text-ext) 20 blur-radius))
(define height (+ (list-ref text-ext 1) 20 blur-radius))
(define img (car (gimp-image-new width height RGB)))
(define bg-layer (car (gimp-layer-new img width height RGB-IMAGE "Background" 100 NORMAL-MODE)))
(define text-layer (car (gimp-layer-new img width height RGBA-IMAGE "Text layer" 100 NORMAL-MODE)))
(define text-mask 0)

21.8 Scheme 轉為 Python 的「轉碼程式」

總結:Script-fu 與 Python-fu 架構一樣,語法有很大的差別
  • Python-fu 有物件導向的呼叫方式語法。
    • 同樣功能的函式, Python-fu 有物件導向的呼叫方式。
    • pdb.gimp_image_new(width, height, RGB)相當於 gimp.Image(width, height, RGB)
  • 減號 - 要改成底線 _ ,gimp-image-new 要改成 pdb.gimp_image_new
  • Python 的敘述 statement 通常以行為單位,Scheme 則用括號()括住。
  • Python 的敘述 statement 可用分號 ; 來區隔,但「PEP 8 -- Style Guide for Python Code」 建議用換行來區隔不同的敘述。
  • Python 的註解是井號 # 開頭,Scheme 的註解是分號 ; 開頭。


「轉碼程式」的 Python 程式碼 scm_to_py_.py
  • 經過一番摸索,可以將「轉碼的流程」寫成 Python 程式。
  • 這個「轉碼程式」無法轉換加減乘除四則運算。
  • 這個「轉碼程式」無法完全正確轉換,最終還是需要一些手工的校正。
  • 這個「轉碼程式」使用正規表示式,將 Scheme code 轉為 Python code
    • tmp.py 表示 Python code 的檔案
    • tmp.scm 表示 Scheme code 的檔案
    • 將所有的程式碼儲存在 scm_to_py_.py 的檔案內
    • 將 scm_to_py_.py 檔案權限設為可執行
    • 準備好 tmp.scm
    • 終端機下,鍵入 ./scm_to_py_.py
    • 執行後,就會得到 tmp.py

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

import re
f_py = open('./tmp.py', 'w')
f_scm = open('./tmp.scm', 'r')

# 請注意 Python 的縮排規則,否則可能會出錯
# 還要注意 Python 各種的逃脫字符(escape characters)
# 所有的「正規表示式」都包裹在這個 for 迴圈之內
for line in f_scm:
  # 取代註解符號
  line = re.sub('\;', '#', line)
  # 刪掉(define
  line = re.sub('\(define ', '', line)
  # 函數參數值的間隔符號,空白改為逗號
  p_with_pdb_if = re.compile('pdb')
  if p_with_pdb_if:
    line = re.sub(' ', ', ', line)

  # car 是取出函數的回傳值串列的"頭"(第一個元素)
  # 等號 = 表示參數值的指派,或接收函數的回傳值
  # 多數的函數回傳值只有一個元素(或一個物件)
  # 因此,這樣的修改是可行的
  line = re.sub('\, \(car\, \(', ' = ', line)
  line = re.sub('\-1', 'neg1', line)
  # Scheme 與 Python 函數命名規則略微不同
  # Scheme 函數名稱以減號 - 串接
  # 改為以底線 _ 串接
  line = re.sub('\-', '_', line)
  line = re.sub('gimp_', 'pdb.gimp_', line)
  line = re.sub('script_fu_', 'pdb.script_fu_', line)
  line = re.sub('plug_in_', 'pdb.plug_in_', line)
  line = re.sub('RUN_NONINTERACTIVE, ', '', line)
  # Python-Fu 預設就是 RUN_NONINTERACTIVE
  # 因此,刪掉 RUN_NONINTERACTIVE,
  # Scheme 與 Python 括號 () 的使用方式有很大的差異
  line = re.sub('\)', '', line)
  line = re.sub('\(', '', line)
  #
  p = re.compile('pdb.[a-z|_]*\, ')
  if re.search(p, line):
    m = re.search(p, line)
    find_p = m.group(0)
    new_p = re.sub(', ', '(', find_p)
    line = p.sub(new_p, line)

  # 過濾出 pdb.開頭 \n 結尾 的字串
  p = re.compile('pdb.[a-z|A-Z|0-9|\'|\"|\(||_|\s|\,|\.|+|-|\*|/]*\n')
  if re.search(p, line):
    m = re.search(p, line)
    find_p = m.group(0)
    new_p = re.sub('\n', ')\n', find_p)
    line = p.sub(new_p, line)

  # 直接用取代的方式,修正括號 () 的錯誤
  line = re.sub('gimp_context_push', 'gimp_context_push(', line)
  line = re.sub('gimp_displays_flush', 'gimp_displays_flush(', line)
  line = re.sub('gimp_context_pop', 'gimp_context_pop(', line)

  # 讓等號 = 出現在變數值指派的地方
  p_with_pdb_if = re.compile('pdb|if')
  p = re.compile('[0-9|TRUE|FALSE]\n')
  if not re.search(p_with_pdb_if, line):
    if re.search(p, line):
      m = re.search(p, line)
      find_p = m.group(0)
      line = re.sub('\, ', ' = ', line)

  p = re.compile('set\!\, [a-z|_]*\, ')
  if re.search(p, line):
    m = re.search(p, line)
    find_p = m.group(0)
    new_p = re.sub(', ', ' = ', find_p)
    line = p.sub(new_p, line)

  line = re.sub('set\!\, ', '', line)
  line = re.sub('set\! \= ', '', line)

  p = re.compile('^if')
  if re.search(p, line):
    m = re.search(p, line)
    find_p = m.group(0)
    line = re.sub('\, TRUE', ':', line)
    line = re.sub('\, =\, ', ' ', line)

  line = re.sub('neg1', '-1', line)
  #也可使用 print 直接將結果印在標準輸出
  #print line,
  f_py.write(line)
  #for 迴圈結束,重要的敘述,都在這個 for 迴圈之內

f_scm.close()
f_py.close()

21.9 Python 轉為 Scheme 的「轉碼程式」

可以先寫好 Python-Fu ,再將它轉為 Script-Fu
  • 下面這四篇文章,提供了扎實的 GIMP 視窗介面的練習。
  • 文章的後面,列出了,等價的 Python-Fu 程式碼。
  • 對大多數人而言,Python 是比 Scheme 容易。
  • 讀者可利用,下面的「轉碼程式」,將 Python 轉為 Scheme 。


「轉碼程式」的 Python 程式碼 scm_to_py_.py
  • 經過一番摸索,可以將「轉碼的流程」寫成 Python 程式。
  • 這個「轉碼程式」無法轉換加減乘除四則運算。
  • 這個「轉碼程式」無法完全正確轉換,最終還是需要一些手工的校正。
  • 這個「轉碼程式」使用正規表示式,將 Python code 轉為 Scheme code
    • tmp.scm 表示 Scheme code 的檔案
    • tmp.py 表示 Python code 的檔案
    • 將所有的程式碼儲存在 py_to_scm_.py 的檔案內
    • 將 py_to_scm_.py 檔案權限設為可執行
    • 準備好 tmp.py
    • 終端機下,鍵入 ./py_to_scm_.py
    • 執行後,就會得到 tmp.scm

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

import re
f_py = open('./tmp.scm', 'w')
f_scm = open('./tmp.py', 'r')

# 請注意 Python 的縮排規則,否則可能會出錯
# 還要注意 Python 各種的逃脫字符(escape characters)
# 所有的「正規表示式」都包裹在這個 for 迴圈之內
for line in f_scm:
  # 取代註解符號
  line = re.sub('\#', ';', line)

  p_with_pdb = re.compile('pdb')
  p = re.compile('pdb.[a-z|A-Z|0-9|\'|\"|\(||_|\s|\,|\.|+|-|\*|/]*\)\n')
  if re.search(p_with_pdb, line):
    if re.search(p, line):
      m = re.search(p, line)
      find_p = m.group(0)
      new_p = re.sub('\(', ' ', find_p)
      line = p.sub(new_p, line)

  if re.search(p_with_pdb, line):
    if re.search(p, line):
      m = re.search(p, line)
      find_p = m.group(0)
      new_p = re.sub('\, ', ' ', find_p)
      line = p.sub(new_p, line)

  # 開頭加上(define
  p_with_eq_sign = re.compile(' = ')
  if re.search(p_with_eq_sign, line):
    line = "(define " + line

  # 注意 line 原本就附帶有換行符號 \n
  # 因此不能 line = line + ")"
  # 這樣會把右括號 ) 推擠到下一行的開頭
  p_with_eq_pdb = re.compile(' \= pdb.')
  if re.search(p_with_eq_pdb, line):
    line = re.sub(' \= pdb.', ' (car ', line)
    line = re.sub('\n', '', line) + ")\n"

  p_with_eq = re.compile(' = ')
  if re.search(p_with_eq, line):
    line = re.sub(' = ', ' ', line)
    line = re.sub('\n', '', line) + ")\n"

  p_with_plug = re.compile('plug')
  p = re.compile('plug_in_[a-z|_]* ')
  if re.search(p_with_plug, line):
    if re.search(p, line):
      m = re.search(p, line)
      find_p = m.group(0)
      print find_p,
      new_p = find_p + "RUN-NONINTERACTIVE "
      line = p.sub(new_p, line)

  # 直接用取代的方式,修正括號 () 的錯誤
  line = re.sub('pdb.gimp_context_push\(\)', '(gimp_context_push)', line)
  line = re.sub('pdb.gimp_displays_flush\(\)', '(gimp_displays_flush)', line)
  line = re.sub('pdb.gimp_context_pop\(\)', '(gimp_context_pop)', line)
  # 修正括號 ( 的錯誤
  line = re.sub('pdb.', '(', line)
  # 修正括號 - 的錯誤
  line = re.sub('_', '-', line)
  #也可使用 print 直接將結果印在標準輸出
  #print line,
  f_py.write(line)
  #for 迴圈結束,重要的敘述,都在這個 for 迴圈之內

f_scm.close()
f_py.close()