Common LispでGUIの必勝パターン

  • Windows上で
  • フリーのCommon Lispを使った
  • GUIアプリケーションの
  • 自分的必勝パターン

を作っておきたいと思って、ここ2,3日調べている。

結論としては、Clozure CLでCL-GTK2を使うのが今のところ一番楽でパフォーマンスも良いと思われる。

  • スレッドを使えないので、Win版SBCLの優先順位は下げる。
  • CLISPは遅いのでとりあえず最後の手段としておく。

開発環境構築手順

  1. Clozure CLをインストール http://trac.clozure.com/ccl
  2. GTK+2の最新ライブラリを取ってくる。 http://www.gtk.org/download-windows.html
  3. CL-GTK2-GTKをダウンロードしてきて展開し、ASDFでロードできるパスに加える。(詳しくは、ASDFのマニュアル参照。)
    1. CFFI、trivial-garbage、iterate、bordeaux-threads、closer-mopに依存しているのでこれも同様。
$HOME/ccl-init.lisp

asdf-installを一生懸命動かそうとするより、tar.gzを展開して、ASDFの.asdファイルサーチ機能を使ったほうがWindows上では楽だと思う。*1

ついでに、GTK+のライブラリのパスも通しておく。

(require 'asdf)

(in-package #:asdf)
(defvar *subdir-search-registry*
  `(,(merge-pathnames ".ccl/site/" (user-homedir-pathname))
     ,(merge-pathnames ".ccl/site/cl-gtk2-0.1.1/" (user-homedir-pathname)))
  "List of directories to search subdirectories within.")
(defvar *subdir-search-wildcard* :wild
  "Value of :wild means search only one level of subdirectories; value of :wild-inferiors means search all levels of subdirectories (I don't advise using this in big directories!)")
(defun sysdef-subdir-search (system)
  (let ((latter-path (make-pathname :name (coerce-name system)
                                    :directory (list :relative
                                                     *subdir-search-wildcard*)
                                    :type "asd"
                                    :version :newest
                                    :case :local)))
    (dolist (d *subdir-search-registry*)
      (let* ((wild-path (merge-pathnames latter-path d))
             (files (directory wild-path)))
        (when files
          (return (first files)))))))
(pushnew 'sysdef-subdir-search *system-definition-search-functions*)

(in-package :cl-user)

(require 'cffi)

(ccl::setenv
  "PATH"
  (concatenate
    'string
    (substitute
      #\/ #\\
      (namestring (merge-pathnames
		    ".ccl/libs/gtk+-bundle_2.20.0-20100406_win32/bin/"
		    (user-homedir-pathname))))
    ";" (ccl::getenv "PATH")))

(setf ccl:*default-external-format*
      (ccl:make-external-format :character-encoding :cp932
				:line-termination :dos)
      ccl:*default-file-character-encoding* :utf-8
      ccl:*default-socket-character-encoding* :utf-8)

プログラムを書く

(asdf:operate 'asdf:load-op :cl-gtk2-gtk)
(asdf:operate 'asdf:load-op :cl-gtk2-glib)

(defun hoge-main ()
  (gtk:within-main-loop
    (let ((window (make-instance 'gtk:gtk-window :title "はろー")))
      (gobject:connect-signal window "delete_event"
			      (lambda (w ev) (gtk:gtk-main-quit)))
      (gtk:widget-show window :all t)))
  (gtk:join-main-thread)
  (ccl:quit))

EXEのビルド

wx86cl.exe --load hoge.lisp
(ccl:save-application
  "hogehoge.exe"
  :toplevel-function #'hoge-main
  :prepend-kernel t)

これでexeが出力される。
GTK+ライブラリのbinに入っているDLLを同じディレクトリに置いて*2、ダブルクリックすれば実行できるはず。exeが超絶でかい(33MB)のはCommon Lispの長所なので、我慢してね。

日々WindowsでExcelやPowerPointをメインに使っている一般ピーポーにもLispの恩恵を!

今後の調査課題

  • 些細なことだけど、Clozure CLでsave-applicationしたexeのアイコンを変えたいので、方法を探す。
  • GtkBuilder(GladeでGUIを作って読み込む)が使えれば、かなり手早くGUIが作れるので、できるかどうか試してみる。CL-GTK2のマニュアルにあるっぽいので多分できる。
  • cl-win32oleを作ってる人がいるので、Excelとかの自動編集ができるGUIツールみたいなのもできるかも。

*1:MinGWからとってきたtarコマンドではgzipをforkできないっぽいし、Cygwinをわざわざ入れるのも面倒。(もちろん、自分のPCには普通に入っているけど、会社のいろんなPCでやることを想定しないといけない。)

*2:DLLが別置きだと、古いライブラリをリンクしてしまったりして動かない可能性がある