使用 JRuby 和 Swing 进行跨平台开发

来源:developerWorks 中国 作者:James Britt
  
使用 Ruby 除了可以构建 Web 和控制台应用程序外,还可以编写复杂的 GUI 桌面应用程序,这些桌面应用程序可以不作修改地在多种平台上运行。得益于 JRuby,Ruby 的传统 C 实现的健壮替代品,Ruby GUI 工具包可以使用用于 Java 平台的 UI 工具。本文介绍一个用 JRuby 和 Swing 构建应用程序的库 Monkeybars,并讲解一个示例应用程序。

Ruby 目前被作为一种用于构建 Web 应用程序的编程语言而著称,主要通过 Ruby on Rails 框架使用。但是,这种语言还可以用于开发图形化桌面应用程序。在本文中,您将学习更多关于使用 Ruby 进行桌面应用程序开发的知识,并完成一个详细的示例,这个示例使用 Monkeybars — 一个基于 Swing 和 JRuby 的开源库 — 创建一个 GUI 桌面应用程序。

面向桌面的 Ruby

标准的 Ruby 发行版包括用于 Tk 绑定的代码,Tk 是一组开源的、跨平台部件集,用于创建图形化桌面应用程序。这可以带来很大的方便。但是,在从源代码安装 Ruby 时,应确认同时还有 Tk 依赖关系,并确保编译设置包括 Tk。如果在 Windows® 上使用方便的 “一键式(one-click)” 安装程序包安装 Ruby,那么仍然需要采取额外步骤使 Tk 工作,因为它不再支持自动安装。

即使为 Ruby 设置了 Tk,它仍然有点沉闷。在某些目标平台上,它们看上去相当丑陋。而且,创建复杂的接口令人生畏。Tk 最适合用于较小的 GUI 需求。

可用工具包

Tk 的这些弱点促使了其他面向 Ruby 的 GUI 工具包选项的开发(请参阅 参考资料 查看相关链接)。有些值得注意的选择是:

  • FxRuby:FxRuby 是用于 Fox 的 Ruby 绑定,这是一个用 C 编写的 GUI 工具包。它可用于使用 RubyGems 的安装。对于 Windows,有一个二进制 gem;用于其他平台的 gem 则需要编译本地代码。

  • WxRuby: WxRuby 是用于跨平台 wxWidgets C++ GUI 工具包的一个绑定,通过它可以创建拥有本地外观的桌面应用程序。它可用于使用 gem 的安装。

  • QtRuby:QtRuby 提供对 Qt 工具包(在 KDE 桌面系统中使用的工具包)的 Ruby 绑定。对于 Windows 安装,有一个 gem,但是对于其他平台则只有源代码。

  • GTK-Ruby:GTK 是在 GNOME 中使用的 UI 工具包。要使之运行,需要编译本地代码。

  • Shoes:Shoes 是最近出现的一个与 Ruby GUI 部件有关的工具包。与这份列表中提到的其他工具包不同的是,它是专门为 Ruby 设计的。可以使用特定于平台的安装程序来安装它。

  • Swing:Swing?是的,正是这个与每个 Java 运行时环境捆绑的 GUI 库。如果您运行 JRuby,那么可以使用 Swing。

除了其中一个外,所有这些都是用 C 或 C++ 编写的 GUI 或部件库,它们都有相应的绑定,以允许在其他语言,例如 Ruby、Python 和 Perl 中调用它们。在几乎所有情况下,您都要面对多种考虑,例如安装和发行。

如何选择 GUI 库

当然,选择使用哪个 GUI 工具集取决于您的特定需要。需要考虑以下内容:

  • 一套丰富的部件或组件
  • 可靠的实现
  • 在多种平台上的可用性(主要是 Macintosh、Win32、KDE 和 GNOME)
  • 在托管平台上的本地感观
  • 是否受到积极的维护
  • 易于创建定制的部件
  • 未受限的许可
  • 可接受的成本
  • 可用于加快开发的已有的框架和库
  • 成熟的 IDE 和窗体布局工具
  • 测试工具和框架
  • 易于打包和部署

如果只需偶尔抛出消息框,或者请求用户进行某些简单的输入,那么前面列出的所有工具包都适用。对于简单的需求,只需重点考虑平台可用性、适合的部件以及适当的成本。如果打算发布应用程序,那么需要检查工具包许可。另外,还必须确保用户已经有所需的环境,或者很容易将所有需要的库和部件放到一个独立的应用程序或一个安装包中。

但是,对于复杂的应用程序,需求就变得严格起来。对于任何超越了简单窗体的应用程序,几乎肯定需要有一个窗体设计工具。另外还需要一套丰富的可用部件;不过,可以重用一个已有的数据采集器或文件浏览器组件,而不必自己编写它们。

每个不同的基于 C 的 Ruby GUI 工具包都有其擅长的一面,但是它们当中没有明显的胜者。对于常规的 Ruby 跨平台桌面开发,并没有显而易见的选择。在不同程度上,它们都有安装、文档化、设计工具、打包和部署方面的问题。值得注意的是,逐一对比特性,它们当中没有哪个能超过非 C 语言编写的工具包。





利用 Java 技术

JRuby 是用于 Java 平台的一个 Ruby 实现(请参阅 参考资料)。它使您可以通过 JVM 执行 Ruby 代码。在 JRuby 下运行的 Ruby 代码还可以装载和使用 Java 库,包括 Swing。

对于 JRuby + Swing 的一些争议

从好的方面看,Swing 组件的数量非常多;从坏的方面看,它们是 Swing 对象;虽然可以将它们装载到 Ruby 代码中,但是必须知道它们五花八门的 API 和怪异的语法。虽然 Swing 有丰富的文档和示例,但是除非编写围绕这些组件的 Ruby 包装器,否则必须亲自处理一些原始的 Swing 代码。

第二个常见的反对理由是,Swing 看上去可能不如某些工具包,例如 Qt 那么漂亮或自然。但是,这不是一成不变的,Swing 现在的外观已经相当不错了。

在 Java 平台与基于 C 的工具之间进行权衡时,必须做出一些选择,决定什么才是最重要的。例如,Swing 可以完全免费使用,而 Qt 对于商业和开源应用程序则有不同的成本和限制。另一方面,Qt 组件的感观对于您的程序来说可能更好一些。

Java 平台的某些方面使得 JRuby 成为一个好的选择:

  • 它比较稳定,并且经过良好的测试
  • 它有较强的社区和供应商支持
  • 它有很多良好的文档
  • 有很好的 IDE 和 UI 布局工具可供选择
  • 可免费使用(不管是在成本方面还是许可方面)
  • 用户机器上可能已经安装了 Java 运行时

如果用(J)Ruby 编写应用程序,并使用 Swing 实现 UI,则只需确保用户有最新版本的 Java 运行时,并在打包应用程序时包括 JRuby JAR 文件。由于已经有了用于 JRuby 应用程序打包的 Ruby 工具,所以这些不成问题。

(J)Ruby + Swing 的开发人员选项

对于从 Ruby 中使用 Swing,有很多选项:

  • 原始的、手工编写的对 Swing 对象的内联调用:在最简单的情况下,可以像引用任何其他 Ruby 对象那样引用 Swing 对象:
    panel = Java::javax::swing::JFrame.new("JRuby panel")
    panel.show

  • “Builder” 和领域特定语言(DSL)风格的库:全部以手工代码构造面板和窗体,然后添加组件,这样可以提高开发速度。有一些库使 Swing 交互更具 Ruby 风格。例如:
    • Cheri::Swing 使用 Ruby 块语法生成 Swing 代码。

    • 另一个库 Profligacy 则围绕原始的 Swing 调用提供了一个 Ruby 包装器,它可以帮助您用更少的原始 Java 代码编写更多的 Swing 代码。为了恰当地使用 Swing 组件,仍然需要熟悉 Swing API 文档。
    这些方法都假设面板、窗体和布局是用手工代码创建的。虽然用纯 Swing 代码做这些事情已经是一个进步,但是仍然无法处理复杂的用户界面。

  • “我们不关心 Java 类来自何处” 方法:第三种方法假设用于 Swing 对象的已编译的 Java 类已经存在,并试图使用 Ruby 代码简化 Swing 对象的创建。

最后是 Monkeybars 库采用的方法(请参阅 参考资料)。现在已经有很多非常好的、免费的图形化 Swing UI 布局编辑器。和之前提到的 GUI 工具包(例如 Fox 和 GTK)的使用一样,对于不常见的对话框,不需要 UI 编辑器。但是,除此之外,这类工具是不可替代的,对于一个高级的桌面应用程序,无视这些工具而手工编写 UI 并无益处。

Monkeybars

Monkeybars 项目源于 David Koontz 在 Happy Camper Studios 中的产品开发,并在此基础上有所发展,试图创建可测试、可维护的复杂的 Ruby 桌面应用程序。它受到了积极的维护,并且可以完全免费使用。

Monkeybars 是一个开源 Ruby 库,它使用一种模型、视图、控制器(MVC)设计模式将已有的 Java Swing 类(即定义 Swing UI 的已编译 Java 类)与 Ruby 代码连接起来。MVC 的目的在于将视图逻辑和 UI 组件与应用程序逻辑相分离。

由于使用 Java 语言和 Swing 库,Monkeybars 建立在成熟、健壮的技术之上。与目前用于 JRuby 的其他 Swing 库不同,它非常适合于构造大型的、复杂的、多面板的应用程序。您将看到,创建 Monkeybars 应用程序需要承担一定的开销,所以对于简单的窗体来说它可能不是最好的选择。但是,对于有以下需求的 JRuby 桌面应用程序来说,它是一个合理的选择:

  • 可靠的跨平台部署(确保用户安装了最新的 JVM)
  • 有大量具有任意复杂度的 UI 部件可供选择
  • 复杂的 UI 窗体和面板构造和交互
为什么使用 Swing 而不是 SWT?

Monkeybars 使用 Swing 而不是 Standard Widget Toolkit(SWT),原因如下:

  • Swing 是 Java 编程的一部分;使用 Java 编程的用户也具备 Swing。
  • Swing 可对组件的行为进行更细粒度的控制。
  • Swing 组件在外观方面具有更大的灵活性。

和 Profligacy 一样,Monkeybars 不隐藏 Swing API。但是,由于它使用编译过的 UI 类,因此可以充分利用任何工具或应用程序来生成真正的布局。取决于应用程序的复杂性,几乎不可避免的是,在编写 Ruby 代码的过程中,某些时候需要参考 Swing 组件 API 文档和代码示例才能知道怎么做(但是由于 JRuby 与 Java 库的良好集成,很容易将那样的 Swing 代码包装在一个 Ruby API 中,以便于重用)。使用 Monkeybars 构建的程序可能有不同程度的复杂性,但是可以遵循一些基本的模式来使代码易于维护。

实现 MVC 的 Monkeybars 方法

MVC 模式已经有很长的历史了,并且有很多变体。对于 Monkeybars,基本前提是对于每个 Swing 帧(也就是容纳各种组件或 widget 的 UI 对象;在某些情况下,可能是一个模态面板),有 3 个 Ruby 文件:一个模型、一个视图和一个控制器。模型类存放实际的业务逻辑,并管理与应用程序部分对应的数据。视图或控制器用于作为与模型进行交互的手段,而模型并不知道它们的存在。将对视图和控制器的引用放在模型之外,可以使模型更易于开发和测试。

视图是另一个 Ruby 文件,它引用一个特定的 Java 类,这个 Java 类包含编译过的 Swing 代码。视图管理 Swing 组件与模型数据的交互。视图可以直接与模型交互,但是它也使用模型的一个副本,以便将数据传递给控制器。在设计模型类时要谨记这一点,因为它最终要为两个目的服务。模型的主实例保持长期状态,并提供应用程序逻辑;而视图使用的副本实际上是一个可任意使用的数据访问对象。模型应该易于实例化,并为视图使用的任何数据提供基本的访问方法。

视图并不直接与控制器交互。相反,有一个信号系统来抽象控制器与视图之间的交互。这种解耦使得视图和控制器更易于测试。

这段描述有意简化和忽略了一些细节。在幕后,视图与控制器之间有更紧密的交互。基础设施需要某种手段来协调行为。Monkeybars 的目的不是束缚您的手脚,而是帮助您创建易于测试和维护的代码。但是,作为开发人员,适当的时候可以随意绕过计划中的 API。

在控制器类中,可以定义 Swing 事件(例如单击按钮或更改文本字段)的处理程序,并控制模型的状态。控制器保留对模型主实例的引用。它并不直接与视图通信。

当一个控制器需要从视图获取数据时,视图提供模型的一个副本,其中包含当前的 UI 内容。然后,控制器可以决定用该数据更新模型的主实例,或者根据这些值采取某种操作。控制器还可以告诉视图更新自身,并传回更新后的值。在示例应用程序中,您将看到这一点。





使用 Monkeybars 的一个示例 JRuby Swing 应用程序

为了让您感觉一下如何使用 Swing 和 Ruby 创建一个桌面应用程序,我将带您完成一个简单的用 Monkeybars 创建的程序(要获得完整的示例源代码,请访问 下载 中的链接)。

准备工作

首先,需要准备好一些东西:

  1. 获得一个 jruby-complete.jar(参考资料 中有下载链接)。

  2. 安装 Monkeybars 库。可以作为 gem 安装它:
    sudo gem install monkeybars
    

    也可以从 gitorious.org 上的库中获取最新的源代码(参见 参考资料)。

  3. 安装 rawr gem:
    sudo gem install rawr

    严格地讲,rawr 对于编写 Monkeybars 应用程序不是必需的,但是它提供了很多有用的 rake 任务,可以将 JRuby 应用程序转换成可执行的 JAR 文件。本文的示例将使用它。

应用程序的基础

示例应用程序是一个 “flash card” 程序,它读取一个文本文件,其中定义有很多 “卡片”。它循环运行,直到被关闭,它周期性地短暂显示和隐藏自己。基本上,它是一个学习工具。对于这个示例,这些卡片是一组德语单词和短语。该程序还读取一个配置文件,该文件定义一个卡片定义文件的位置和一些设置(显示/隐藏速度,窗口大小)。

这个示例的目标是:

  • 展示 Monkeybars 代码生成器的使用,它可以自动创建普通的文件
  • 展示 Monkeybars 应用程序的基本结构
  • 演示 Monkeybars MVC 组每个部分的创建
  • 展示 Monkeybars 如何处理应用程序数据到 UI 组件的映射
  • 展示如何将应用程序打包为可执行的 JAR 文件

使用 Monkeybars 应用程序生成器脚本

安装后,Monkeybars 提供一个命令行脚本,用于创建一组初始的应用程序文件。要开始一个新的 Monkeybars 项目,可以执行随 gem 安装的 monkeybars 脚本。将项目命名为 monkey_see:

$ monkeybars monkey_see

这样会在给定的路径下(或者,如果只提供应用程序的名称,则在当前目录下)创建一个新的目录,并添加用于新应用程序的核心文件和目录。

使用 rawr 将代码自举到 Java 环境中

rawr 是源于 Monkeybars 的另一个 Ruby 库。它处理各种打包任务,并提供一个命令行脚本,用于创建一个基本的 Java 类,Monkeybars 应用程序可以使用这个 Java 类来作为 Java 程序执行(而不是通过 JRuby 使应用程序作为一个 Ruby 程序运行)。

对于这个 Monkeybars 应用程序,为了使用 rawr,可进入项目目录,并执行 rawr 脚本:

$ cd money_see; rawr install

使用 Monkeybars rake 任务生成文件

您已经看到了 Monkeybars 如何划分模型、视图和控制器。惯例是将这些文件放在相同的目录中。Monkeybars 提供了一个 rake 任务,用于生成这些文件。您可以创建其中任何一个文件或全部 3 个文件(后者更常见):

$ rake generate ALL=src/flash

该命令在 src/ 下创建一个名为 flash 的子目录,其中有 3 个文件:flash_controller.rb、flash_view.rb 和 flash_model.rb。前两个文件有从基本 Monkeybars 类继承而来的骨架类。模型代码则不是这样;Monkeybars 并不假定您如何管理应用程序逻辑和数据;那完全由您决定。





创建 UI

对于应用程序的界面,需要一个 Swing 类,以显示 flash-card 数据。如何创建这个 Swing 类由您决定。Monkeybars 中没有任何东西使它与任何特定的 UI 工具或 Swing 代码生成器绑定。按照惯例,Swing 文件与它的相关元组放在同一个目录中(src/flash/FlashFrame.java)。您需要知道类包,以便将它传给视图类(使用 flash 包,并将类命名为 FlashFrame)。

您的屏幕布局看上去应该如图 1 所示:


图 1. 应用程序的屏幕布局
应用程序的屏幕布局

一些关键点:应该使用一个 JTextPane 来显示 flash-card 的内容,以便使用 HTML 来格式化所呈现的文本。另外,对于文本面板和按钮,还应该使用容易理解的名称。这样一来,在处理视图时,就更容易知道一些关于 UI 组件的信息。这个程序的代码是用 Ruby 编写的,所以应使用 Ruby 命名惯例:将文本命名为 pane card_pane,将两个菜单项命名为 edit_menu_item 和 quit_menu_item。另外再为它们提供快捷键。

帧本身的名称并不重要。视图类可以直接按名称引用组件。

定义模型

模型管理指定 UI 背后的应用程序逻辑和数据。一个 Monkeybars 程序通常对于每个 Java 窗体有一个模型。示例应用程序只有一个模型,用于处理 flash-card 数据。模型代码需要能够从一个已知的位置装载数据,并提供一个公共方法用于提供那些数据。

为简单起见,将数据存储在应用程序运行时所在子目录下的一个文本文件中。这里不要硬编码 HTML,而是使用 Textile 标记,并使用 RedCloth Ruby 库来转换它。每个卡片条目由一个分隔字符串分隔开。

使用第三方的库

Textile 是一个文本标记格式,用于使用简单的纯文本惯例定义 HTML。例如,为了表示 <em>italicized</em>,可以编写 _italicized_。RedCloth 是一个以 gem 的形式提供的 Ruby 库,它可以将 Textile 格式的文本转换成 HTML。

Rubygems 使得安装和使用第三方的库变得非常容易,但是,由于您想将代码打包到一个 JAR 中,并且还可能发布它,所以需要确保与应用程序相关的所有代码都被打包。为此,解压 RedCloth gem,并将 redcloth.rb 文件复制到项目的 ruby/lib/ 目录中:

$ cd /tmp; gem unpack RedCloth

这将创建 /tmp/RedCloth-3.0.4 /(除非安装了不同版本的 gem)。将 /tmp/RedCloth-3.0.4/lib/redcloth.rb 复制到 monkey_see 项目的 lib/ruby/ 目录。

通常,不属于应用程序核心部分的任何 Ruby 库都应该放在 lib/ruby/ 中(这是惯例)。如果使用 gem,那么需要解压实际的库文件,并将它们添加到项目中。在本文的后面,您将看到怎样告诉程序如何发现这些文件。

关键模型方法

load_cards 方法从磁盘读取文本文件,划分出每个卡片,并将结果赋给 @cards 实例变量。

select_card 方法随机选取一张卡片,并将它赋给 @current_card 实例变量。您将使用 attr_accessor 来定义用于读取和设置这个变量的方法。

无论 UI 中显示哪张卡片,都可以在那里编辑它。经过编辑之后,update_current_card 方法获取 @current_card 的内容,并重新将它插入到 @cards 数组中。save 方法将 @cards 数组写回到磁盘。

current_card 方法的值就是要呈现的值,为了呈现这个值,需要一个视图类。

定义视图类

Monkeybars 视图类是 Java Swing 类的所有者。如果打开 flash_view.rb,可以看到,它调用了一个类方法 set_java_class。这应该被设置成为这个视图定义的 Swing 类。在您的代码中,这是 flash.FlashFrame。

通常,一个 Monkeybars 视图类需要做 3 件事:

  • 将数据传入和传出 Swing 组件
  • 管理各种以视图为中心的行为(例如大小和位置)
  • 对控制器发出的信号做出响应

映射数据

Monkeybars 提供了一个 map 方法,通过该方法可以定义如何将模型方法连接到 Swing 控件。最简单的用法是连接一个 UI 组件方法和一个模型方法:

map :view => :card_pane.text, :model => :current_card

该映射使用默认行为,即使之成为一个直接的、双向的关联。也就是说,card_pane 组件的 text 方法的结果被传递到模型的 current_card= 方法。当根据模型更新视图时,这个过程又反过来了:model.current_card 填充 card_pane.text.(注意:JRuby 处理 Ruby/Java 命名转换,所以实际的 Swing 方法 setText 可以使用 set_text = 来调用。)

这种简单的映射常常很好用,但是在某些时候,由于数据类型、格式或某个应用程序逻辑的不同,您不希望直接进行数据交换。Monkeybars 允许在数据交换中使用中介。可以为映射传递一个 :using 参数(即指向一个数组的一个 hash 键),该参数表明当将数据从模型转移到视图和从视图转移到模型时所使用的替代方式(使用 :using 的另一个原因是处理这样的情况,即需要用不符合常规的 getProperty 和 setProperty 模式的组件方法或子对象来操纵 Swing 组件的值或状态)。

对于您的代码,需要从模型中获取一个 Textile 格式的字符串,并在将它赋给 card_pane text 属性之前将它转换成 HTML。为此,您将创建 to_html 方法。另外,您不想直接从视图中更新模型的 current_card 值。在视图中,您将有专门的代码用于编辑卡片,所以在本应使用 view-to-model 之类的方法名的地方,您将使用 nil。

结果就是这样的映射:

 map :view  => :content_pane.text, 
     :model => :current_card, :using => [:to_html, nil ]

您还希望 Swing 帧以特定的方式显示。默认情况下,Swing 帧出现在屏幕的左上角。对于您的应用程序,您希望它显示在屏幕的右上角。您还将给它一个幻灯片效果,使它不至于突然地出现和消失。

管理 Swing 对象

视图类有一个专用的实例变量 @main_view_component,它引用相应的 Swing 类。视图代码通过这个对象与 Swing 组件交互。例如,为了更改 flash-card 文本面板的内容,可以编写:

 @main_view_component.card_pane.text = "Some new text"

但是,这种代码实际上正是需要视图类存在的理由,所以 Monkeybars 让您不必显式地使用 @main_view_component,而是直接引用它的组件:

 card_pane.text = "Some new text"

基本 Monkeybars::View 类使用 method_missing 来拦截那样的代码,看它是不是一个组件引用,如果是,则将请求委托给 @main_view_component。

方法是在 Swing 类上调用的,不过需要显式的引用:

@main_view_component.width = 500

为了取得精美的幻灯片效果,视图类有一些用于操纵 Swing 帧的高度和位置的方法,以便图形化地对它进行缩放,使得在每个呈现周期中,它从屏幕的顶端滑下,然后又缩回。

处理来自控制器的请求

Monkeybars 被设计用来解耦 MVC 元组的关键部分。由于视图有对 Java Swing 对象的直接引用,因此它通常是最难以测试的部分。Monkeybars 的目的是减少视图与模型和控制器的直接交互。但是,控制器负责处理 UI 事件。不可避免的是,这意味着控制器需要指导视图做出响应。但是,控制器并不直接与视图类通信。相反,它使用信号。

稍后您将看到控制器端的情况。在视图中,您需要使用 define_signal 方法定义信号处理程序。它带有 2 个参数,一个是信号名称,另一个是用于处理那个信号的视图方法:

define_signal :name => :initialize,  
              :handler => :do_roll_up

处理程序方法必须带 2 个参数:一个是模型(从控制器传入),另一个是传输对象。传输对象是一个短暂的 hash,用于在控制器与视图之间来回移动数据。视图中有为 UI 的初始位置、幻灯片出现、幻灯片消失定义的信号,还有 2 个分别为开始和结束卡片编辑而定义的信号。每种信号的处理程序都很短。下面是 do_roll_up method:

  def do_roll_up model, transfer
    hide
    move_to_top_right
    roll_up
  end

编辑序列是通过菜单事件触发的。Edit 菜单项控制编辑。在视图中,编辑序列意味着设置 card_pane.editable = true,然后用原始的 Textile 卡片文本换出用 HTML 呈现的内容。另外还需要更改组件的内容类型,以便它正确地呈现纯文本。

当编辑完成时,则执行相反的过程。面板呈现 HTML,而 editable 则被设置为 false。视图只负责管理 Swing 组件的状态;控制器则指示模型执行文本更新和保存。

定义控制器类

嵌套的控制器

Monkeybars 控制器通常是作为单独的类使用的;通常不需要多个实例。但是,有一个例外值得注意,那就是嵌套控制器的使用,在此情况下,一个帧或面板有多个子组件,它们是同一个类的不同实例,这超出了本文的讨论范围。实际上,您可以将一组复杂的帧、面板和组件划分到不同的 MVC 元组中。一个例子就是地址簿,一个顶级 Swing 帧呈现多个地址对象,而每个地址对象又是一个 address_entry MVC 集合的实例。

您的 Swing 对象有一些菜单项,但是不必将任何用于这些菜单项的代码放到视图类中。那些代码属于控制器。控制器处理所有 UI 事件,例如单击按钮、选择菜单以及更改文本字段。Monkeybars 为此作了安排,在默认情况下,来自 Swing 代码的所有事件都被毫无察觉地处理。对于您关心的那些事件,必须定义事件处理程序。在这个示例应用程序中,需要捕捉的事件是单击菜单。

事件处理程序的形式如下:

    def your_component_name_action_performed
      # code
    end

(如果想在代码中使用实际的 Swing 事件,也可以在定义处理程序时以实际的 Swing 事件作为参数。)

要处理 Quit 菜单项,只需退出:

  def quit_menu_item_action_performed
    java.lang.System.exit(0)
  end   

Edit 菜单动作则复杂一点:

  def edit_menu_item_action_performed
    if @editing
      @editing = false
      signal :end_edit
      update_card 
    else
      @editing = true
      signal :begin_edit
      update_model view_model, :current_card
    end
  end   

以上代码通过使用信号驱动视图来处理编辑模式的开关。要注意的关键一点是,卡片文本是如何通过使用控制器的模型实例(通过信号暗中传递给视图)和 view_model 方法提供的视图的模型副本进行转移的。

每当控制器需要用户界面的当前状态时,它可以使用 view_state 方法来引用视图的模型副本和当前的传输对象。由于从 view_state 获取模型副本非常常见,所以 Monkeybars 提供了 view_model 方法。

您的控制器还有一个用于实现初始呈现的方法和一个用于显示/隐藏显示序列的方法。这两个方法都使用信号将实际的显示代码交给视图处理。





编排应用程序

除了一个或多个 MVC 元组外,Monkeybars 应用程序还使用 2 个关键的助手文件来准备和运行代码。

这 2 个文件都在 src/ 目录中。manifest.rb 文件设置库装载路径,并允许根据程序是直接从文件系统运行还是从一个 JAR 文件运行来定义包括哪些文件。

之前,您已将 redcloth.rb 添加到 lib/ruby/ 中。为了让应用程序能找到这个文件,需要将那个目录添加到装载路径中。对于 lib/java/ 目录也是如此。所以,应确保 manifest.rb 包括以下几行:

 add_to_load_path "../lib/java"
 add_to_load_path "../lib/ruby"

main.rb 也是在 src/ 中。这是应用程序的 Ruby 入口点。除了其他事情外,它定义一个全局错误处理程序,在此将放置在执行主应用程序逻辑之前要运行的特定于平台的代码。

示例程序使用一个简单的循环:

begin
  flash_card = FlashController.instance
  flash_card.init_view :flash_interval_seconds => 8,
                       :show_for_seconds => 20,
                       :window_height => 200,
                       :data_src => 'data/cards.rc'

  while true do
    flash_card.present 
  end

rescue => e
  show_error_dialog_and_exit(e)
end





执行代码

关于生成的 JAR 文件的一些说明

rawr:jar 创建的 JAR 文件并不包含执行程序所需的所有东西。特别是,任何包含程序所需的库的 JAR(例如 jruby-complete.jar 和 monkeybars-0.6.4.jar)都没有放在 monkey_see.jar 中。

当 rawr:jar 运行时,它在 package/deploy/ 下创建一组文件。这些是执行应用程序所需的文件,它们必须在一起发布。通过 build_configuration.yaml 文件可以操控将哪些文件放入到 JAR 中,以及将哪些文件和目录复制到部署目录中。例如,data/cards.rc 文件可以放到 monkey_see.jar 中,但是由于这个应用程序允许编辑,所以它应该作为一个外部文件,以便写回更改。

有了代码以及合适的数据文件后,就可以运行程序。使用一个 rawr rake 任务创建一个可执行的 JAR 文件。如果在项目的一开始运行了 rawr install,那么它会在 src/org/rubyforge/rawr/ 下创建一个 Main.java 文件。从 JAR 运行程序需要一个 Main Java 类;rawr 可以生成这个文件,它包含查找和解释 main.rb 文件的基本代码(或者,如果没有找到该文件,它将创建一个这样的文件,并使用该文件)。

rake rawr:jar 任务编译该代码,并将文件打包到一个 JAR 中。build_configuration.yaml 文件用于协调此过程。在创建 JAR 之前,要先编辑这个文件,以反映应用程序的细节。

要启动程序,首先构建 JAR 文件:

$ rake rawr:jar

然后调用它:

$ java -jar package/deploy/monkey_see.jar

应该可以看到 flash-card 屏幕从右上角滚下,停留一会儿,然后又向上滚回。

当窗口可见时,可以使用菜单项编辑当前显示的卡片。如果要退出,可以使用 Quit 菜单项(如果添加了快捷键 Alt+Q 的话,也可以使用该快捷键)。





结束语

作为 Ruby 的传统 C 实现的健壮的、可用的替代品,JRuby 意味着 Ruby GUI 工具包可以超越其他使用 C 实现的工具包,而使用为 Java 平台提供的 UI 工具。由于 Swing 是 Java 运行时安装的一个标准部分,Swing 组件为 (J)Ruby 提供了一个成熟的、可立即使用的图形工具包。使用 Java 平台意味着应用程序很容易在多个平台上构建、打包和发布给用户。通过使用 Monkeybars 库,Ruby 开发人员可以更轻松地构建易于测试和维护的复杂的桌面应用程序。

本文的示例非常简单,它主要介绍在 JRuby Swing GUI 开发中可以做什么事情。在 Monkeybars 站点可以找到更多的信息和更复杂的示例。(责任编辑:A6)


时间:2008-10-28 15:24 来源:developerWorks 中国 作者:James Britt 原文链接

好文,顶一下
(0)
0%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量