Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Platform-Independent C++ GUI Toolkits


January 1997/Platform-Independent C++ GUI Toolkits

Writing GUI-based code that is portable across multiple systems can be a daunting task. Here's a review of several free toolkits to help you.


After an early and daunting experience with GUIs and an unrewarding fling with C++, I set both aside for several years. Recently, in response to a challenge at work, I undertook to do some programming in X. I figured it would give me a chance to try out C++ for real, and I could leverage one of these cool GUI libraries I had read about on the Net. Some of those libraries claimed to be portable between MS-Windows and X, and having seen how different these two beasts were, I wondered if that could be done effectively.

I was more than a little surprised at how naturally C++ applied to GUI programming. Even with my meager understanding of C++, the notions of member functions, inheritance, and polymorphism all seemed necessary and appropriate in the context of GUI programming. Using one of the toolkits discussed in this article, I managed to fortify my understanding of C++ and object-oriented programming, actually produce the program I had volunteered to write, and get cross-platform GUI programming experience to boot!

Whether you are relative newbie or a seasoned C++ and/or GUI programmer, there are a substantial number of toolkits available which might be useful to you. I find portability particularly interesting and valuable in a toolkit, so in this article I will focus on some C++ GUI toolkits that are portable between at least two distinct platforms. I assume that readers will find this article more valuable if they can actually try out the various toolkits, so I chose to review those ones that are, for the most part, freely available. These include Amulet, Fresco, V, YACL, and wxWindows. There are some excellent commercial packages available as well, but I think the free packages are worth investigating. Even if you use one of them for only a project or two, they generally don't take long to learn and provide an excellent introduction to the joys of cross-platform portability at the GUI level. You can always switch to a commercial package later if you need professional support.

Amulet

Amulet comes to us from Carnegie Mellon's School of Computer Science. Version 2.0 had been in beta for several months, and was recently promoted from beta to "official."

The hierarchical structure of Amulet abstracts the underlying graphics system using a layer called "Gem" (for Graphics and Events Manager; all the components of Amulet have similarly clever acronyms with geological connotations). Gem handles graphics primitives such as lines and arcs, and mouse and keyboard events. A separate version of Gem exists for each of the supported platforms, which include X, Windows 95/NT, and MacOS. It is possible to program at the Gem level, but programmers are advised to take advantage of the higher level facilities of Amulet. Above Gem, the "Ore" layer manages an object inheritance mechanism (described later) and does constraint handling, while the "Opal" layer supports simple graphical objects and text. At the highest level, Widgets combine the lower graphical layers with nongraphical objects called Interactors, the result being fully functional user-interface objects. All of the upper layers of Amulet are platform independent, building entirely on the Gem layer.

Currently, Amulet's Widgets look a lot like Motif widgets, regardless of the target platform. There are plans to implement look-and-feel at the Amulet Widget level to support Windows and Macintosh styles (again regardless of platform), but this is not yet implemented.

Virtually every object that one creates in Amulet is of type Am_Object. Interestingly, Amulet does not subclass for different objects such as windows, buttons, and so on, but instead uses a system of "slots," along with a prototype-instance mechanism, for defining new objects with different properties. Every Am_Object has a dynamic list of key/value pairs, called slots, which define such properties as position, size, and color. When a new object is created, it can inherit from and share slot values with its prototype (parent) object. Generally, an instantiated object will share all values with its prototype object unless the new object explicitly sets a slot value of its own. Slots, both key and value, can be added to and deleted from an Amulet object, so that it need not to carry around any extra baggage for unused or unset slot values. Of course, a prototype-instance hierarchy has to start somewhere, so Amulet provides a number of objects in the Amulet library which can serve as default prototype objects. The library includes both graphical objects and nongraphical objects such as Interactors. Interactors handle GUI events, such as mouse clicks and keypresses, and can be attached to graphical objects.

Setting Am_Object slot values is done with the Set method. Set works much like C++'s << and >> I/O operators. A call to Set returns a reference to the object itself, so that further calls to Set may be applied to the resulting object. Thus, Amulet's convention for creating objects often appears as:

Am_Object poly = Am_Polygon.Create("my_poly")
    .Set(Am_LEFT, 100)
    .Set(Am_TOP, 100)
    .Set(Am_WIDTH, 100);

Note the lack of punctuation between the .Set calls. This self-referential mechanism is used throughout Amulet. After creating a few objects, for example, you can add them to a window, w, by saying,

w.Add_Part(poly) .AddPart(circ) .AddPart(rect);

Programming with Amulet feels a lot like programming with Motif or Xview in C, where the ubiquitous key-value parameter lists are analogous to Amulet's Set operations. But Amulet's Set is type-safe, and doesn't require the null parameter at the end of the list.

The Amulet library is free, even for commercial use. The documentation is also free, but you cannot distribute it without the permission of CMU. Amulet can be found on the Internet at http://www.cs.cmu.edu/~amulet.

Fresco

Fresco is a project of the Fujitsu Advanced Software Lab, and is under standardization by the X Consortium. Technically, Fresco is only an API specification for graphical user interfaces, but a reference implementation snapshot is regularly made available on the Internet. The most recent snapshot at the time of this writing is from May 1996.

Fresco evolved from Stanford University's "InterViews" toolkit for UNIX/X, but Fresco itself runs on most popular flavors of UNIX, Windows 95/NT, and MacOS. Fresco's perks include advanced graphical embedding, thread support, Tcl-based scripting, and, perhaps most significantly, compliance with the Object Management Group's CORBA standard for object distribution.

The Fresco interface is defined using CORBA's Interface Definition Language (IDL). IDL is not tied to a specific language, but can be mapped to various languages, including C, C++, and Smalltalk. The reference implementation provides a C++ mapping of the Fresco IDL specification.

Fresco's fundamental graphical object is the glyph. There are layout and transformation glyphs that manage other glyphs, visible glyphs capable of drawing themselves, and specialized glyphs called viewers that handle user interaction. Visible glyphs can mix arbitrarily with layout and transformation glyphs, and hierarchical associations can be made so that operations such as rotation and resizing easily propagate down through the hierarchy. In compliance with CORBA, glyphs and other objects in Fresco are transparent across a network. For example, this means that given a pointer, p, the statement p->Draw may actually reference an object on a remote machine, depending on what p points to. In a C++ implementation of Fresco, p might point to an object of one of several classes derived from a virtual base class which supports Draw. One derived class might invoke a draw method on a local display, and another might generate an RPC call to draw a remote object.

Fresco's object embedding capabilities are impressive. An example program called Fdraw provides a selection of operations, such as Move, Select, and Rotate, which can operate on simple drawing primitives, such as rectangles, ovals, and polygons, or even complete new instances of Fdraw itself. It is rather remarkable to see a fully functional "application" instantiated in the client area of another instance of the same application. The child Fdraw is fully functional, and can be independently moved, rotated, scaled, and drawn into.

Since Fresco is not bound to a specific language, it relies on an implementation-independent means of creating new objects, precluding the need for C++'s new operator much of the time. Fresco uses kits, which can be thought of as "object factories" for churning out new instances of objects. Different kits exist for different kinds of objects. For example, LayoutKit provides layout and alignment objects, and the FigureKit creates two-dimensional figures such as ellipses and rectangles. A program generates a reference to a kit at run time with a call such as:

WidgetKit_var widgets =
    Fresco_resolve_object(WidgetKit, "widgets");

and can then start pulling objects out of the kit,

Button_var button = widgets->push_button(...);
Viewer_var scrollbar = widgets->scroll_bar(...);

I left out the parameters of these calls for simplicity. Since kits are resolved at run time, it is possible to have multiple versions of a kit, each of which implements look and feel differently, and select the one you want on the fly.

For all its flexibility, Fresco may have a slightly steeper learning curve in the beginning. I haven't done any substantial coding with it outside of tweaking the example programs, but it is certainly one of the more intriguing toolkits I've seen.

Fresco is free for commercial and non-commercial use. It is available on the Internet, at http://www.faslab.com/fresco/HomePage.html. CORBA information can be found at the OMG web site, http://www.omg.org.

V

Dr. Bruce E. Wampler, of the University of New Mexico, wrote the popular Grammatik grammar checker several years ago. More recently, he has been developing a portable GUI framework called, simply, V. V version 1.10 is portable between X, using the Athena widget set, and Windows 3.1/Win32, with support for OS/2 forthcoming. Wampler designed V to be simple, elegant, and easy to use. Furthermore, V is meant to be useful in a learning environment, so its source code might be suitable material for the study of object-oriented methodologies. The documentation that accompanies V is similarly designed, and should provide a very good introduction to both GUI and object-oriented programming for anyone new to either of these two disciplines.

The V documentation recommends programming with the Standard V Application model. This model allows you to write programs that conform to the Model-View-Controller (MVC) object-oriented paradigm. The process of building a typical V program is very similar to that used with wxWindows, described later (V shares quite a few ideas from wxWindows). You derive a main application class, say myApp, from the vApp class, and declare a single static instance of myApp in the source file. The ubiquitous main function is not declared by the user. Recall that UNIX programs normally require main to be defined, whereas MS-Windows uses WinMain. By hiding either main or WinMain inside the V library (depending on the platform), V allows user code to be completely portable between V-supported platforms.

The myApp contructor instantiates a command window, derived from vCmdWindow, which serves as the "View" part of the MVC model and typically contains a menu, a status bar, and a canvas. V uses null-terminated (usually static) arrays to generate menus and other objects. This is a clear and simple approach, but it requires the user to remember the null terminator, and doesn't facilitate generating menus on the fly.

And what about the M and C of MVC? The "Controller" element is realized by the myApp class, which coordinates GUI activities with separate hierarchies of data structures. The "Model" element is comprised by these independent hierarchies, which implement an application's unique GUI-independent functionality.

Like wxWindows, V supports PostScript printing under UNIX/X, and offers standard Windows printer support under Windows.

V is free under the terms of the GNU Library General Public License. This may have implications if you want to sell anything you build with V. Please read the GNU documentation that accompanies V if you have questions about it. V can be found on the web at http://www.cs.unm.edu/~wampler/vgui/vgui.html.

YACL

Developed by Dr. M. A. Sridhar of the University of South Carolina, Yet Another Class Library, or YACL, is a part of Sridhar's effort to provide a complete portable programming environment. Not only does YACL support portable coding between Windows 3.1/NT, X/Motif, and OS/2, but it includes a distinct and rich set of container and I/O classes for use with or without a GUI (even works with MS-DOS!). These other classes include sophisticated file I/O and container classes, but I don't want to digress from our theme.

The YACL distribution does not include substantial documentation. Instead, the user is referred to his book, Building Portable C++ Applications with YACL, published by Addison-Wesley (ISBN: 0-201-83276-3). The YACL web site contains only scant details on YACL's class hierarchies. However, there are over three dozen example programs in the distribution, so while I'm sure the book is useful, it is possible to at least experiment with YACL before investing in the book.

By studying the sample programs and library sources, it is fairly easy to ascertain a general outline for creating a YACL application. The GUI part of YACL encompasses a set of classes that begin with the prefix "UI_", and do things you might expect a user interface to do. The class UI_Application serves as the top-level application class, and most of it is defined in the YACL library. The user, however, must supply the UI_Application::Main method, where a visual object will usually be instantiated, as follows:

int UI_Application::Main( int argc,
    char *argv[] ) { 
    MakeTopWindow(new TestWindow);
    Run();
    return 0;
}

MakeTopWindow and Run are also UI_Application methods, but they reside in the YACL library. TestWindow is typically a class derived from UI_CompositeVObject (composite visual object), which would allow for potentially complex windows containing buttons, menus, and other standard GUI fare.

As with V and wxWindows, YACL protects the user from having to worry about using WinMain vs. main when targeting MS-Windows and other platforms. The main function lives in the YACL library, and invokes exactly one instance of class UI_Application, so by defining UI_Application::Main the user gets to control the startup of the program.

YACL is freely available for commercial and non-commercial use. The main YACL web site is http://www.cs.sc.edu/~sridhar/yacl.html. YACL can also be FTP'd from ftp.cs.sc.edu in the /pub/yacl directory.

wxWindows

Dr. Julian Smart, of the Artificial Intelligence Applications Institute, UK, is the primary author and maintainer of wxWindows, a toolkit which builds on other toolkits, such as Motif and XView under UNIX, and the various flavors of MS Windows. A Mac version and an Xt version are also under development.

wxWindows wraps an extensive C++ API around the native GUI libraries, giving true native look-and-feel to applications while providing a uniform interface to features common among different GUI environments, such as menus, buttons, sliders, and other widgets. In addition to the usual widgets and graphics functions, wxWindows provides a sophisticated document/view framework, portable printing support using the Windows printer drivers on Windows platforms and PostScript under UNIX, IPC mechanisms, a portable help system, bitmap support, and a host of other features.

I chose wxWindows as a basis for my example program because I have been using it for some time, and because I found the API to be among the most intuitive and comfortable to work with. I haven't studied the entire reference manual, but generally, when I wonder if there's a wxThisOrThat to handle a certain functionality, most often wxThisOrThat is available and I can use it.

A few months ago, I had been working on a Tetris game using wxWindows, when I stumbled across the Call for Papers on cross-platform GUI toolkits in CUJ. So, if you continue reading this, you're going to learn a bit about wxWindows and my little game. I don't claim to be a particularly good coder, so I probably could have encapsulated things a bit differently and more efficiently in the implemention of my game. But this article is about GUI toolkits, not programming style, so please bear with me.

If you haven't done much with inheritance and overriding methods functions in C++, wxWindows will give you a chance to get your feet wet. The first thing you must do in creating a wxWindows application is to subclass from wxApp. wxApp is the top-level application class, and encompasses the functionality usually associated with main in C and C++ programs. As with V and YACL, you do not normally define main in a wxWindows application. Instead, you declare exactly one instance of the class you derived from wxApp (my example program Wxtris derives a class called TrisApp), and supply all necessary startup code in the OnInit method. OnInit should finish by returning a pointer to a wxFrame object (or subclass thereof), which will serve as your main application window (Listing 1) . This wxFrame can contain any number of instances of wxPanel, wxCanvas, or wxTextWindow, which support the features their names respectively imply.

The other interesting classes in Wxtris include TrisField (Listing 2) , which inherits from both wxCanvas and wxTimer, essentially resulting in a drawing area that "ticks." Timer notification is handled by the method wxTimer::Notify, which TrisField overrides to provide its own handling (Listing 3) . A TrisField includes an array of pointers to TrisCell objects, each of which remembers its size and position and can be commanded to draw itself onto its parent TrisField (Listings 4 and 5) . A TrisPiece object represents the piece currently in play, and consists only of a location and a set of offsets relative to that location. The TrisCells referenced by these offsets are drawn during game play.

Wxtris game play is fairly obvious if you've ever played Tetris. When the main program window comes up, select File->Start from the menu, and pieces start falling one at a time. You rotate and move pieces with the numeric keypad or cursor keys. Filling up a horizontal row causes it to disappear and increases your score. The game ends when a piece enters the field on top of another piece, which implies that the field is full. Level and score are maintained on the left side of the program window. I'm happy to note that Wxtris play feels virtually identical on all the platforms I tested it on, namely Windows 3.1/Watcom C++ 10.0, Solaris 2.5/Motif/Sun CC, and Linux/Xview/gcc.

In working with wxWindows, I have discovered a fair number of quirks and rough edges. For example, wxFrame::OnSize may be called before a frame window has even been brought up under Windows 3.1, whereas under Motif this never happens. This can cause serious problems if you dereference possibly uninitialized pointers in OnSize. wxTimer::Start didn't work consistently between platforms when passed a flag to indicate repeated timeouts. I solved this by using a different flag, and I got the results I wanted. Generally, working around a quirk for one platform doesn't affect the others negatively, so it is still fairly easy to find a solution that works for all platforms without resorting to excessive #ifdefs.

At the time of this writing, wxWindows is available on the Internet at http://www.aiai.ed.ac.uk/~jacs/wxwin.html. However, in good conscience I must introduce a word of warning with respect to this toolkit and others, both free and commercial. As I was finishing this article, Mr. Smart announced his decision to leave AIAI and form a company of his own. The future of wxWindows is thus rather unsure. The wxWindows development effort has been supported by users around the Internet for a few years, and there are active discussions about extending its development and lifespan. While I remain optimistic about its future, this news did come as a bit of surprise, and it might be considered a Good Thing that there are so many GUI toolkits available in case something should happen to your favorite one!

Conclusion

All of the toolkits covered here have features that go far beyond the scope of this article. My intent was to provide you with an introduction to the distinguishing capabilities of each, so that you can investigate them further on your own. Remember that even if you don't need cross-platform portability, you can use any one of these libraries as a wrapper for your favorite GUI, and still have access to whatever odd features your particular system might have that the toolkit doesn't encapsulate.

Whichever toolkit you choose to investigate, be sure to download the associated documentation and experiment with the example programs. Be warned that while binary distributions are often available, most of the toolkits discussed here come in source form, and while they generally compile right out of the box, you may have to do some nudging to get them up and running. Expect anywhere from 20 minutes to two hours of compilation time (no kidding) to build the libraries, and as always, Read The Fine Manuals.

There are plenty of resources available for platform-independent GUI (PIGUI) programming, as well as GUI programming in general. The PIGUI Frequently Asked Questions list can be FTP'd from rtfm.mit.edu in the directory/pub/ usenet-by-hierarchy/comp/answers/portable-GUI-software. The April 1996 issue of Sys Admin magazine had a nice summary of GUI development systems, and the April 1996 issue of UNIX Review had an in-depth article on CORBA. The August 1996 CUJ featured a review of V by Mr. Wampler himself! (I had not been aware of this until I was nearly finished with this article.)

As a closing note, I want to comment on the possible anxiety that such a varied and complex landscape of free toolkits might inspire. Someone might ask, "Well, if I take the time to learn one of these toolkits, what if I end up having to use MFC or XVT later on anyway?" and give up in frustration. I wouldn't worry about wasting time no matter which toolkit you choose. There is a saying that "everything that rises must converge," and as GUI and other programming disciplines advance and grow, the differences between competing implementations are more likely to decrease than increase. Once you've learned the prevailing paradigms and familiarized yourself with one toolkit, learning other toolkits should take only a fraction of the time compared to the first. Good luck, and have fun! o

Jamie Guinan has a BS in Computer Science from Suffolk University in Boston. He is currently working as a software engineer in the medical imaging division of a large corporation. He enjoys spending time with his wife and cats. He can be reached via e-mail at [email protected].


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.