Lyle Johnson的《FXRuby:用Ruby创建精简的GUI》是一本关于FXRuby的新书。FXRuby是基于FOX工具包的一个Ruby GUI库。
出版商The Pragmatic Programmers专门为InfoQ的读者提供了独家的样章:“第8章:创建简单的小部件(Widgets)”(PDF)。
我们访问了该书的作者和FXRuby的维护者Lyle Johnson,谈一谈FOX工具包和FXRuby的设计和实现。
InfoQ: FXRuby是基于FOX工具包的。请你简要概括一下它的特点——例如对比本地小部件和仿真的小部件(widgets),以及支持的平台等。它有哪些特性、概念和优势,可以从众多的GUI工具包中脱颖而出?
Lyle Johnson: FOX是一个用C++实现的快速且轻量级的GUI工具包。它是跨平台的,不仅能很好地支持流行的操作系统(比如Windows、Mac OS X和Linux),也支持其他很多不常见的类Unix操作系统。FOX使用轻量级的(非本地的)小部件,也就是说FOX只依赖宿主平台的基本绘图能力,而 不是对已有的(本地的)小部件进行包装。这种方法有很多优势,包括跨平台的一致性和更强的扩展性。
FOX提供了创新的、独一无二的解决方案来进行事件处理和布局。我觉得这就是它和其它GUI工具包之间最大的区别。FOX的 开发者花了大量的时间来使用许多不同种类的GUI工具包,从中获取经验,最终创建了FOX。对他影响最深的是NeXT,还有用很多方法实现的API。在高 一点的层次上,FOX对3-D可视化(使用OpenGL)的支持特别好,这也吸引了很多人来使用FOX。
InfoQ: FOX工具包的背后都有谁——有哪些知名的应用程序是基于它的(不局限于Ruby应用程序)?
Lyle Johnson: FOX的开发者是Jeroen van der Zijp。他是我的老朋友,以前跟我合作过。我们曾经同在一家开发工程用的商业建模、仿真和分析软件的公司里工作,那时候Jeroen就开始开发FOX 了。鉴于它的起源,FOX常被用在那个领域的商业和开源软件中。举例来说,商业公司如ESI Group、Simulia和CAE都在他们的3-D建模和可视化产品中使用FOX。但是,我们也看到FOX和FXRuby用在许多其他领域的应用中,包括开发工具如FreeRIDE和Arachno Ruby IDE;视听工具如ReZound和Goggles DVD播放器;谁知道还有多少,很难去追踪。
InfoQ: FXRuby项目的背后有什么故事呢?你开发它的动机是什么?
Lyle Johnson: 我在2000年开始接触Ruby,那时还没有很多成熟的、支持良好的跨平台Ruby GUI工具包。我对FOX以及开发FOX用的Python绑定已经有了不少经验,因此我很自然地想要把FOX带入Ruby社区。我在最近的“工作”中无法 使用FOX,但我知道Ruby社区中有很多人都觉得它很有用,这驱使着我对它继续开发和完善。
InfoQ: FXRuby是如何实现的——用了多少C或者C++代码?你是怎样把FOX组件映射到Ruby架构中的?FXRuby能够随意使用FOX组件吗?还是你需要用C/C++来把它们映射到Ruby中?你用什么来把Ruby连接到FOX上——用一般的C扩展还是C++绑定?
Lyle Johnson: 我用了很多C++代码,但大部分的包装(wrapper)代码是用SWIG自 动产生的,由此实现了FOX和Ruby的绑定。FXRuby现在还不能动态地、即插即用地使用所有的FOX组件;你需要用SWIG来包装它们,并且遵循我 在FXRuby内部使用的一些约定,比如怎样与Ruby的垃圾收集器进行交互。实际上,FXRuby的一小部分(并且会越来越多)是用Ruby实现的。但 是,我喜欢向更有意义且不会严重影响性能的方向努力。
InfoQ: FOX工具包好像还包括了许多工具(定时器、异步I/O等)。FXRuby支持这些吗?如果现在不支持,你想要支持哪些?
Lyle Johnson: 我正在实现FOX所有的“功能”,只要它们是有用的,比如杂事处理、超时事件、异步I/O事件,还有像注册(registry)这样的服务。FOX还提供 了其它许多有用的类,比如收集器、线程和正则表达式,但都是多余的,因为Ruby标准库中已经提供了这些类。因此我没有把它们放进FXRuby。
InfoQ: FXRuby的Ruby化进行得怎样?比如,它是否支持Ruby块(事件处理等)?能不能使用Builder风格的GUI定义?
Lyle Johnson: 我下了很大功夫让API更加“Ruby化”。你可以传递块来作为事件处理器,而不需要单独写实例方法并和小部件(widgets)绑在一起。还有在其他许 多地方,只要你觉得应该使用块,我们都尽量用了。举例来说,当你处理一个对象集合(比如列表部件里的项目)时,你可以把一个块传递给该部件的each
方法。
由 于一个部件构造器可以接收一个块,并作为引用传递给正要构造的部件上,因此在某种程度上,你可以用嵌套的风格来构造GUI。换句话说,这些部件的变量可以 既不污染本地的命名空间,也不会破坏反映部件从属关系的嵌套代码。还有,FXRuby不是一个DSL类的Builder;你仍需要明确地构造每一个对象。
InfoQ: FXRuby和它的事件处理机制是怎样与Ruby 1.8.x的线程机制进行互动的?比如说,如果一个Ruby线程遇到一个正在阻塞的、需要长时间运行的系统调用——那么FXRuby GUI也会被阻塞吗?从另外一个角度说:FXRuby的事件队列是如何实现的?如果没有GUI事件进入FXRuby队列——其他的Ruby线程会被阻塞 吗?(也就是说FXRuby会不会在获取事件时阻塞?)
Lyle Johnson: 在FXRuby中处理线程一直是个问题,这要归咎于Ruby的线程实现机制。我一直不愿要求用户去使用打补丁的FOX或者Ruby,所以我们不得不想一些 办法来应付这两个系统上的事件循环。基本上,我们所做的就是每当我们的GUI有一些空闲时间,我们就让FXRuby做一些杂事,由此从Ruby线程调度器 那获得一点时间片。没错,如果一个Ruby线程遇到一个阻塞的调用,FXRuby也会被阻塞。而另一方面,在实践中,Ruby线程应该不会被FXRuby 所阻塞——尽管在理论上是有可能发生的,比如事件循环一直非常忙,以至于根本释放不出空闲时间。不过这也意味着程序的代码出了问题,应该先加以解决。
InfoQ: Ruby 1.9的线程系统(本地线程+大VM锁)需要什么改变吗?还是它已经提供了以前不曾提供的特性(对FXRuby来说)?
Lyle Johnson: 对FXRuby来说,Ruby 1.9只需要再做一点点改进就能够支持我们在Ruby 1.8中使用的基本方法。但我认为Ruby 1.9的线程系统可能提供了一些过去不可能有的机会。我会先尽可能详细地了解1.9的线程实现机制,然后再开始改进,并且会时刻记住,一定要保证对 Ruby 1.8的向后兼容性(也就是说,要是FXRuby在Ruby 1.9上运行的快一点还好,但如果用起来差别很大是绝对不行的)。
InfoQ: FXRuby和FOX工具包的布局管理策略是什么?能不能与其它工具包对比一下(不要过于强调FOX更好或者更糟,但也要提一下,这样才能让读者知道应该 用什么样的布局管理器)?你是否已经有了一些办法,或者知道一些办法让布局管理更加容易呢?比如,使用Builder记法(notation),或者使用 像Profligacy的LEL这样的东西。
Lyle Johnson: FOX提供了很多不同种类的布局管理器,从最基本的需求,到窗体里整齐排列小部件,还有更复杂的情况(例如分割面板和滚动窗口)。在管理布局的办法上, FOX和其它工具包最大的区别在于:在FOX中,布局管理器实际上就是部件,和其他部件一样。首先创建一个指定父部件(容器)的子部件,然后设置这些子部 件的布局属性,来告诉父部件如何来布置它们(例如,这个子部件要达到横向最大宽度,而那个子部件要保持固定的宽度)。
对于喜欢交互式GUI布局工具的人来说,可以使用Meinrad Recheis的foxGUIb。很多人都想为GUI布局提供一种DSL,但是据我了解,还没有人真正开始做。Lance Carlson的Anvil项目(正是为了此问题)看起来很有前途,但仍处于起步阶段。
InfoQ: 除了Ruby 1.8.x,你还在别的Ruby版本上测试FXRuby了吗?比如Ruby 1.9,或者像Rubinius和MacRuby这样系统?这些系统有没有什么特性——不管在哪方面——能让FXRuby表现地更好呢?
Lyle Johnson: FXRuby已经可以在Ruby 1.9上工作了,但我还不知道有多少人真正从中体会到了它的优势。我刚刚开始考虑如何去支持Rubinius,但它肯定是我下一个要支持的平台。 MacRuby也不是不可能。我只是还不知道会有什么技术问题,或者说,在这一点上,支持这一平台究竟有多大意义(要知道它只能运行在Mac OS X上)。当然,还有一些其他平台,比如JRuby和IronRuby,出于它们的实现方法,我恐怕不会支持它们了。
你可以获得由The Pragmatic Programmers出版的《FXRuby:用Ruby创建精简的GUI》全书(PDF和打印版都有)。
阅读英文原文:《Book Excerpt and Interview: FXRuby: Create Lean and Mean GUIs with Ruby》