登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Chun Tian (binghe)

超越自我,洞察宇宙

 
 
 

日志

 
 

用 LispWorks 开发 GUI 应用程序  

2008-11-06 11:54:07|  分类: Lisp |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

下面演示用 LispWorks 开发一个简单的 GUI 程序的全过程。LispWorks 的 CAPI 图形库是跨操作系统可移植的,几乎完全相同的代码在 Windows 和 Mac OS X 下都可以编译出 native 界面的 GUI 程序,在 Linux 和 Unix 上则是依赖于 Motif 图形库。LispWorks 在非 Mac 版本上提供了一个 Interface Builder 工具,用于可视化生成 GUI 窗体代码,本文先用这个工具生成最基本的代码模板,然后再继续编写整个演示程序,这个程序的功能是计算一个文本输入控件里的整数的阶乘,然后将结果显示在另一个文本控件里。注意,本文显示的过程只支持 LispWorks 专业版以上的环境,但最后得到的代码在免费的个人版里也是可以运行的。

首先启动一个全新的 LispWorks 环境:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

LispWorks 在 Windows 下的版本默认界面是一个 MDI 窗口,它提供了许多工具,大部分在 Tools 菜单里都可以找到,这里我们只使用 Interface Builder:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

点击上述菜单项以后就可以打开一个 Interface Builder 窗口,同时也会自动新建一个 Interface,一个独立于 LispWorks MDI 窗体的新窗口会显示出来:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

Interface Builder 窗口里有三个标签,分别是 Layouts,Menu 和 Code。Layout 是一棵控件关系树,目前还只有树根;Menu 是菜单设计器;Code 是实时更新的模板代码,初始状态如下:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

我们首先利用 Interface Builder 生成一个代码框架,有经验的程序员其实是不需要 Interface Builder 的,复杂的 UI 也无法使用这个工具,对于本例中比较简单的情况,刚好适用。现在双击 Layout 窗口里当前处于选中状态的 Interface-1,可以看到一个属性窗口弹出:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

属性窗口里显示了该 Interface 也就是窗口本身的所有属性,大部分还都是空的,其中 Name 表示窗口类的符号名称,Title 是一个字符串,代表窗口标题。在 Common Lisp 里,任何窗体或者控件都是类的实例,定义窗体本质上是继承窗体类的过程,Name 就是这个新定义类的名称。Common Lisp 是不区分符号大小写的,默认输出大写,因此这里看到的是全部大写的 INTERFACE-1。我们把它修改成 HELLO,然后把窗口标题改为 "LispWorks/CAPI",让后点击属性窗口的 OK 按钮,让修改生效:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

完成后的样子见上图,接下来我们开始为窗体添加控件。LispWorks 支持各种控件定位方法,包括 VB 时代的绝对坐标定位,Java Swing 的网格定位,以及类似于 GTK 的行列容器定位等,不支持 VC 那种以对齐为基础的定位方法。从实用角度来说,行列定位是最方便的,也就是通过描述控件之间的行列关系,然后让控件根据整个窗体的尺寸自行调整自己的位置和尺寸。这种位置关系在 CAPI 里称为 layout,以后将会看到,所有描述信息都是窗口类定义的一部分,并非像 QT 和 Cocoa 那样用单独的 UI 描述文件来保存,也不是像没有 GLADE 的 GTK 那样,完全用命令式的方法构建窗口。我们使用基本的行列定位,上图中红色圆圈里分别是 Column(列) 和 Row (行) 的容器控件,先点击 Column,在 Interface-1 下面建一个列,然后在这个列里再建一个行。(如果操作失误了,删除控件的简单方法是先选中它,然后使用 Edit 菜单里的 Cut 把它删掉) 我们在行容器里依次添加 Title 和 Text-Input 和 Push-Button,然后在列容器里 (已有一个行容器) 再添加一个 Editor,成功后的样子如下图:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

注意到在行容器里,三个控件是上边沿对齐的,这样很不好看,我们把它改为中央对齐。方法是双击 Layout 窗口里的 Row-Layout-1 容器控件,在弹出的属性窗口里寻找一个叫 Y Adjust 的属性,并将其内容设置成 :CENTER。接下来我们通过属性表修改了几个控件的名字和它们显示的文字:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

这样看起来就舒服多了,控件的添加就到此为止。下面我们开始添加菜单,将 Interface Builder 窗口里的标签切换到 Menu 上,然后使用窗口下方的菜单元素构造两个简单的菜单:File -> Quit 以及 Help -> About,注意顶级菜单 (File) 是一个 Menu,而其下级菜单 (Quit) 是一个 Item,成功后的样子如下所示:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

现在看看代码,上述操作得到的代码如下所示:

(in-package "COMMON-LISP-USER")

(capi:define-interface hello ()
  ()
  (:panes
   (label
    capi:title-pane
    :text "Input an integer:")
   (input
    capi:text-input-pane)
   (calc
    capi:push-button
    :text "Calculate")
   (output
    capi:editor-pane)
  (:layouts
   (column-layout
    capi:column-layout
    '(row-layout output))
   (row-layout
    capi:row-layout
    '(label input calc)
    :y-adjust :center))
  (:menu-bar file help)
  (:menus
   (help
    "Help"
    ("About"))
   (file
    "File"
    ("Quit")))
  (:default-initargs
   :best-height 110
   :best-width 244
   :layout 'column-layout
   :title "LispWorks/CAPI"))

这是一个扩展了的 Common Lisp 类定义,其中红色的部分定义了窗体里的每一个控件,绿色的部分定义了 Layout 也就是控件之间的位置关系,而黄色的部分定义了菜单。这个演示程序的目的是计算阶乘,通过文本输入框输入的一个整数,在点击按钮后,其阶乘结果将显示在下方的文本编辑框里,核心功能就是这个 N! 函数,使用尾递归方法计算函数 f(n) = n * f(n-1):

(defun n! (n)
  (declare (type bignum n))
  (labels ((iter (i acc)
             (if (zerop i)
                 acc
               (iter (1- i) (* i acc)))))
    (the bignum (iter n 1))))


上面的 Interface Builder 窗口中的成果使用菜单 File 中的 Save 可以保存所有代码到一个文件里,然后我们修改代码,添加所有必要的参数和回调函数,最后的结果是下面这个样子,我用彩色标示所有添加了的代码部分:

(in-package "COMMON-LISP-USER")

(defun n! (n)
  (labels ((iter (i acc)
             (if (zerop i)
                 acc
               (iter (1- i) (* i acc)))))
    (iter n 1)))

(capi:define-interface hello ()
  ()
  (:panes
   (label
    capi:title-pane
    :text "Input an integer:")
   (input
    capi:text-input-pane
    :reader hello-input)
   (calc
    capi:push-button
    :text "Calculate"
    :callback-type :interface
    :selection-callback 'cal-callback
    :default-p t)
   (output
    capi:editor-pane
    :reader hello-output
    :buffer-name "hello"
    :line-wrap-marker nil))
  (:layouts
   (column-layout
    capi:column-layout
    '(row-layout output))
   (row-layout
    capi:row-layout
    '(label input calc)
    :y-adjust :center))
  (:menu-bar file help)
  (:menus
   (help
    "Help"
    (("About" :selection-callback 'show-about
              :callback-type :none)))
   (file
    "File"
    (("Quit" :selection-callback 'quit
             :callback-type :none))))
  (:default-initargs
   :best-height 110
   :best-width 244
   :layout 'column-layout
   :title "LispWorks/CAPI"))

(defun cal-callback (interface)
  (let ((input (parse-integer (capi:text-input-pane-text
                               (hello-input interface))
                              :junk-allowed t)))
    (when input
      (setf (capi:editor-pane-text (hello-output interface))
            (format nil "~A~%" (n! input))))))

(defun show-about ()
  (capi:display-message "Copyright 2008, Chun Tian (binghe)"))

(defun hello ()
  (capi:display (make-instance 'hello)))

下面分别介绍一下所有新增代码的用处。红色部分是计算阶乘的核心函数以及其唯一一次被调用的位置;绿色部分为 input 和 output 控件定义了 reader 函数,它们被使用的位置也用相同的颜色标识出来,作用是快速访问一个 Interface 示例中的成员;天蓝色部分定义了回调函数 (callback),回调函数是联系 UI 和功能的纽带,用户点击按钮以后相应的回调函数将会被调用,而回调类型 (callback-type) 则决定了在调用回调函数时使用哪些参数,这里只使用窗体示例作为参数。除了 Lisp 以外我没有见到任何一种 UI 库支持这种特性:可以灵活调整回调函数的参数表。橙色部分为 editor 文本编辑控件关联了一个新的 buffer 以保存文本,并且将折行时显示的默认字符设置为空;最后黄色部分是回调函数代码 cal-callback 首先得到文本编辑框里的字符串,转化成整数以后调用阶乘函数,然后将结果输出到编辑框里;show-about 函数则在用户点击了菜单的 about 项之后显示一个弹出式消息。最后,粉色部分是整个代码的入口函数,调用 HELLO 之后整个窗口显示,程序开始运行:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

LispWorks 不但可以在编译加载了上述程序以后通过调用 HELLO 函数的方式启动程序,也有能力将这个程序编译成独立的可执行文件。编译过程稍有繁琐,不在这里给出了,运行独立可执行程序的过程见下图:

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)

更难能可贵的是,上述程序在源代码级别是跨操作系统可移植的,在 Mac OS 和 Linux 版的 LispWorks 下运行或者导出成这两个平台下的独立可执行程序也是可以的。下面两张图分别是在 Mac OS X 和 Linux 下运行程序后的样子:(注意 Mac OS 的菜单统一显示在屏幕最上方,所以这里看不到应用程序的菜单)

用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)用 LispWorks 开发 GUI 应用程序 - 冰河 - Chun Tian (binghe)


这就是我所说的──专业级开发工具。现在大家可以理解我为何要砸锅卖铁买 LispWorks 了吧?

(本文相关资料,以及 Windows 版的独立可执行文件,如有需要者请发邮件给我)

后记:函数 n! 的定义中去掉了对参数和返回值的类型声明。
  评论这张
 
阅读(16951)| 评论(12)

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018