Common Lisp 的無限可能——GTK+ 3 與 CL 的快速圖形程序開發及簡單的 CL 介紹

Ma Kai posted @ Feb 14, 2013 07:08:20 PM in 编程 with tags gtk gtk3 lisp common-lisp , 5677 阅读

章甲、虎頭

大家也許已經不止一次從 Lisp 的狂熱愛好者那裏聽說了「Lisp 真是世界第一語言」「Lisp 最 NB」之類的話。俺不想說那些沒用的玩意,今天就是上來發個小小的示例,GTK+3 和 Common Lisp 在一起是什麼模樣?

注意,上面說的我區分了 Lisp 和 Common Lisp。以下 Lisp 皆指 Cmmon Lisp,如果你是 Scheme 黨且對此不適,你可能現在就會自覺右上角了。(無論,本文和Scheme一點關係也沒有嘛!)

章乙、設定

節甲、安裝虛擬機

首先你需要一個目前最流行的 Lisp 虛擬機——SBCL(Steel Bank Common Lisp),別笑,名字就是這樣。它是從老牌 Lisp 虛擬機 CMUCL 中復刻出來的。它還有一個同宗平輩小兄弟叫 Clozure CL,也挺不錯,而且是有一個商業公司維護它,SBCL 只是一些 Lisp 愛好者維護它(但是 SBCL 的代碼質量非常高,也是效率最高的 Lisp 虛擬機!),不過我試着在 Windows 上用 Clozure CL 設定 Quicklisp 時總是失敗,就放棄了,Fedora 也沒有打包它,所以我就繼續用 SBCL 了。

本來我是想用「解釋器」來表達這個概念,但是怕有人以爲是解釋執行,所以我就改用「虛擬機」來說。不過可能又有人以爲是類似 Java 那樣的。所以我在這裏澄清一下,CL 中的虛擬機或者說解釋器,其實是類似 JIT 的東西,成熟的 Lisp 虛擬機都是可以高效地把 Lisp 代碼轉換爲機器碼,SBCL 就是其中的佼佼者。

上文「復刻」表示英文單詞「fork」。

Fedora 下安裝它只需:

pkcon install sbcl

然後嘗試在命令行中鍵入 sbcl,回車就打開了它。嘗試在 * 之後輸入

(format t "世界你好!")

當你看到

世界你好!
NIL

這樣的輸出時,一個可用的 SBCL 環境已經設定完畢。是不是比 Java 之流簡單得多?當然和 Python 一類差不多。

節乙、設定 Quicklisp

目前來說,Quicklisp 是所有 CL 開發者必不可少的東東,它借助 ASDF 的熊力,使得 Lisp 開發大大簡化。而且設定非常簡單,你只需要參考它網站上的說明即可。

節丙、下載 gtk-cffi

現在才是我們主角出場的時刻!它就是 gtk-cffi!它是目前來說唯一的用於 gtk3 的 Lisp 庫。要下載它你需要 Git——一個版本控制軟件。Fedora 下要一氣呵成的做法是:

pkcon install git
git clone https://github.com/Kalimehtar/gtk-cffi

現在記住你把 gtk-cffi 裝在哪個目錄裏了,一會要用。

節丁、加載 gtk-cffi

由於 Fedora 打包有些奇妙的問題,所以你首先需要創建一些符號鏈接,使得一會加載 gtk-cffi 不會失敗。

### 首先
# 如果你是 64 位系統
cd /usr/lib64
# 如果你是 32 位系統
cd /usr/lib
### 切入 root 權限
su
### 現在開始創建!
ln -s libgtk-3.so{.0,}
ln -s libglib-2.0.so{.0,}
ln -s libgio-2.0.so{.0,}
ln -s libgobject-2.0.so{.0,}
#(好像還缺一些,缺什麼你就自己用類似的方法幹吧。)
### 回到普通權限
exit

其實問題主要還是在於 Quicklisp 目前沒有包含 gtk-cffi,所以我們必須一個一個部件手動加載。

(require :asdf)
(push #P"/gtk-cffi的目錄/utils/" asdf:*central-registry*)
(push #P"/gtk-cffi的目錄/gdk/" asdf:*central-registry*)
(push #P"/gtk-cffi的目錄/gio/" asdf:*central-registry*)
(push #P"/gtk-cffi的目錄/g-lib/" asdf:*central-registry*)
(push #P"/gtk-cffi的目錄/g-object/" asdf:*central-registry*)
(push #P"/gtk-cffi的目錄/gtk/" asdf:*central-registry*)
(push #P"/gtk-cffi的目錄/ext/" asdf:*central-registry*)

很麻煩對吧?那就在 Quicklisp 的 BUG 報告這裏讓他們趕緊加上 gtk-cffi!

好了,到現在已經一切就緒,你只需要坐等以下命令完成:

(ql:quickload :gtk-cffi)

我需要提醒你的是,你下次想用 gtk-cffi 的話又要重新來過!嫌麻煩那我建議你寫到 ~/.sbclrc 裏面去。

章丙、試試看!

節甲、一個上來就能用的例子

直接就上一個好用的例子吧。

(defpackage #:example
  (:use #:cl  #:gtk-cffi #:g-object-cffi))
(in-package #:example)

(gtk-init)
(defparameter *window*
  (gtk-model
    'window :width 300
            :height 200
            :title "我的第一個 Common Lisp / GTK+3 程序"
            :signals '(:destroy :gtk-main-quit)
      ('label :text "簡單吧?好玩吧?那就一起來玩吧!")))
(show *window*)
(gtk-main)

你可以把它保存成一個文件比如說 ~/win.lisp,然後在 SBCL 的命令行中輸入

(load "~/win.lisp")

其實那個輸入「命令」的地方叫做 REPL(Read-Eval-Print Loop,入解出環),是 Lisp 特色的快速迭代開發模型。比如說在 Emacs 中,你可以在上面編輯文件,下面有一個 SLIME REPL,你就可以極度方便地寫代碼。可以先在下面寫好,然後直接測試,完成後複製到上面。

節乙、一些背景知識

如果你嘗試不進行 defpackage 就直接用 gtk-init 之儔,那麼你一定會得到一個錯誤提示!defpackage 類似於很多語言的命名空間。而那個 :use 則是類似於 C++ 的 using namespace XXX 和 Python 的 import XXX。如果你真的不想用 defpackage ,那麼就需要類似於 C++ 那樣幹了,你要在每個函數調用前加上包名,調用 gtk-init 會變成這樣:

(gtk-cffi:gtk-init)

至於 gtk-model 嘛,嘿嘿,這就是宏的力量!

關於 (show) 函數,你可能覺得奇怪,但是放心好了,那就是 GtkWidget 類的函數!哈哈,別驚訝,你可能真的沒有看出其中有類,那麼我在這裏就推薦你看看我以前寫的 CLOS 簡單入門吧。

章丁、蛇尾

現在你已經爲了一個小小的代碼示例折騰了這麼長時間,非常感謝你瞧得起我的文章。到現在爲止,你已經搭建起了一個完全可用的 Common Lisp 運行環境,如果你覺得 Common Lisp 很好玩,那麼請一定要多多研究唷!覺得在終端裏面幹這幹那很不順手,那麼我強烈推薦 Emacs + SLIME!不喜歡 Emacs 的話,其實用一些比較簡單的編輯器也可以……就是缺少了 SLIME 的強大功能。

順便,蛇年大吉!萬事如意!向本命年的 Python 致以慰問。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter