Book Excerpt and Interview: FXRuby: Create Lean and Mean GUIs with Ruby
InfoQ: FXRuby is built on the FOX Toolkit. Could you give a quick overview of its characteristics - i.e. native widgets vs. emulated widgets and supported platforms. Does it have any particular features/concepts/strengths that make it stick out from the sea of GUI toolkits?
Lyle Johnson: FOX is a fast and lightweight GUI toolkit, implemented in C++. It is cross-platform and works well not only the more popular operating systems (such as Windows, Mac OS X and Linux) but also on a number of other more obscure Unix-ish operating systems. FOX uses lightweight (non-native) widgets, which means that FOX relies on only the basic drawing capabilities of the host platform instead of providing wrappers around existing (native) widgets. This approach offers a lot of advantages, including consistent behavior across platforms and more opportunities for extensibility.
FOX offers some really innovative and unique solutions to problems like event handling and layout, and I think that's a big part of what distinguishes it from other GUI toolkits. FOX's developer has spent a lot of time in the trenches, working with a lot of different kinds of GUI toolkits, and he was able to draw from that experience in building FOX. He was especially influenced by NeXT, and the API and approach reflect that in a number of ways. On a little bit higher level, FOX has especially strong support for 3-D visualization (using OpenGL), and that draws a lot of people to FOX as well.
InfoQ: Who's behind the FOX Toolkit - are there notable apps built with it (not necessarily Ruby apps)?
Lyle Johnson: FOX is developed by Jeroen van der Zijp, an old friend and former co-worker of mine. Jeroen originally developed FOX while we were both working for a company that developed commercial modeling, simulation and analysis software for engineering applications. Given its origins, it is perhaps not a surprise that FOX is used in a lot of commercial and open source software in that application domain. For example, commercial companies like ESI Group, Simulia and CAE are using FOX for their 3-D modeling and visualization products. But we also see FOX and FXRuby used in a number of other sorts of applications, including development tools like FreeRIDE and the Arachno Ruby IDE; audiovisual tools like the ReZound and Goggles DVD player; and who knows what else. It's sort of difficult to keep track.
InfoQ: What's the story behind the FXRuby project? What's your motivation to work on it?
Lyle Johnson: When I started working with Ruby in late 2000, there weren't a lot of options in terms of mature, well-supported cross-platform GUI toolkits for Ruby. I already had a good bit of experience working with FOX, and with developing a Python binding for FOX, and so it was a really natural thing for me to try to bring FOX to the Ruby community. I don't get to use it much in my current "day job", but I know that a lot of people in the Ruby community do find it useful and that drives me to continue to develop it and make it better.
InfoQ: How is FXRuby implemented - how much C or C++ code is involved. How do you map the FOX components into Ruby constructs? Is FXRuby capable of using arbitrary FOX components or do you have to map them to Ruby using C/C++ code? What do you use to connect Ruby to Fox - regular C extensions or some C++ bindings?
Lyle Johnson: A lot of C++ code is involved, but I use SWIG to automatically generate most of the wrapper code that implements the binding between FOX and Ruby. FXRuby isn't currently capable of using arbitrary FOX components in a sort of dynamic, plug-and-play type arrangement; you need to use SWIG to wrap those as well, and follow some of the conventions that I use internally in FXRuby for things like interacting with Ruby's garbage collector. A small but growing part of FXRuby is actually implemented in Ruby, however, and I'd like to move in that direction where it makes sense and doesn't seriously impact performance.
InfoQ: Looking at the FOX toolkit, there seem to be many utilities included as well (timers, asynchronous I/O, etc). Do you support any of them in FXRuby - if not, which would be interesting to support?
Lyle Johnson: I'm implementing all of the FOX "utility" functionality that I think makes sense at this point: things like handling chores, timeout events and asynchronous I/O events, as well as services like the registry. FOX does provide a lot of other really useful classes for things like collections, threads, and regular expressions, but they're redundant with classes already provided by Ruby's standard library, so I'm not exposing those in FXRuby.
InfoQ: How Ruby-fied is FXRuby? e.g. does it take blocks where that makes sense (event handling, etc). Is it possible to use Builder-style GUI definitions?
Lyle Johnson: I've made a lot of effort to make the API "Ruby-fied". You can pass blocks as event handlers instead of having to write separate instance methods and hook widgets to those methods. We use blocks in most other places where you'd expect to be able to use them as well. For example, any time you're dealing with a collection of objects (like the items in a list widget), the widget provides an
eachmethod that you can pass a block to.
You can construct GUIs in a nested fashion, to some extent, since each widget's constructor takes a block into which it passes a reference to the widget being constructed. So, in other words, you're not having to pollute the local namespace with variables for all of those widgets, and the nested appearance of the code reflects the parent-child relationships between the widgets. But it's not a DSL like Builder; you're still explicitly constructing objects.
InfoQ: How does FXRuby and it's event handling interact with Ruby 1.8.x threading? Eg. if a Ruby thread issues a blocking, long running syscall - is the FXRuby GUI blocked too? In the other direction: how's the event queue of FXRuby implemented? If no GUI event come into the FXRuby queue - are other Ruby threads blocked (ie. does FXRuby block on event fetching).
Lyle Johnson: Dealing with threads in FXRuby has always been somewhat of a problem, due to Ruby's threads implementation. What I never want to do is require the user to use a patched version of FOX or Ruby, so we have to find ways to work around the two systems' event loops. Basically, what we do is set up a chore in FXRuby that yields a small slice of time to Ruby's thread scheduler whenever we get some idle time in the GUI. So yes, if a Ruby thread issues a blocking call, FXRuby's going to get blocked too. Now, on the flip side, Ruby threads shouldn't ever be blocked by FXRuby in practice. It could happen in theory, if the event loop stays so busy that there is no idle time to give up, but that tends to indicate a problem in the application code that needs to be addressed.
InfoQ: Does Ruby 1.9's threading system (native threads + Big VM Lock) require any changes or does it offer opportunities (for FXRuby) that weren't possible before?
Lyle Johnson: The changes for threads in Ruby 1.9 required only minimal changes for FXRuby to maintain the same basic approach as we use for Ruby 1.8. I think Ruby 1.9's threading system might offer some opportunities that weren't possible before, however. I'm still trying to get a handle on exactly how threads are implemented in 1.9 before I start making any changes, keeping in mind that I need to at least make it appear backwards-compatible with the Ruby 1.8 implementation (i.e. it's OK that an FXRuby application runs faster under Ruby 1.9, but it's not OK if it exhibits fundamentally different behavior).
InfoQ: What's FXRuby's or the FOX Toolkit's strategy for layout management? Can you compare its layout management to other toolkits (not so much about whether Fox is better/worse, but mostly so readers can get an idea what kind of layout managers to expect). Do you have or know of any ways of making layout management (with FXRuby or FOX) easier - e.g. using Builder notation or something like Profligacy's LEL:
Lyle Johnson: FOX provides a number of different kinds of layout managers, covering both the basic needs to arrange widgets side by side in a form as well as some more complicated layout situations (like split panes and scrolling windows). A significant difference between FOX's and some other toolkits' approaches to layout management is that in FOX, the layout managers are widgets just like any other widget. You create widgets as children of a particular parent widget (a container) and then set layout hints on those child widgets that indicate to the parent how those widgets should be laid out (e.g. this widget should stretch to take up as much horizontal space as possible, but this widget over here should maintain a fixed width).
For people who like interactive GUI layout tools, there's Meinrad Recheis' foxGUIb. There have been a number of attempts to provide a sort of DSL for GUI layout, but as best I can tell, none of those has really taken off. Lance Carlson's Anvil project (which sort-of addresses this issue) looks promising but it's still in an early stage.
InfoQ: Have you tried out FXRuby with other Ruby versions beside Ruby 1.8.x? What about Ruby 1.9 or systems like Rubinius, maybe MacRuby? Do these systems have features that would help you - in any way - to make FXRuby even better?
Lyle Johnson: FXRuby already works with Ruby 1.9, although I don't know how many people are really taking advantage of that yet. I have only just started looking into what it will take to support for Rubinius, but that is definitely the next platform that I'm planning on targeting. MacRuby's not out of the question, but I just don't know what the technical issues are or, for that matter, how much sense it would even make to try run on that platform (given that it's Mac OS X only). And of course there are some platforms, like JRuby and IronRuby, that will probably never be supported due to the implementation approach.
You can get the full book "FXRuby: Create Lean and Mean GUIs with Ruby" (as PDF, Print or both), published by the The Pragmatic Programmers.
Boris Lublinsky Nov 20, 2014
Shannon Ewan & Ahmed Sidky Nov 19, 2014