Cross-platform C++ GUI development using Qt

Barry R Smith

School of Crystallography, Birkbeck, University of London, Malet St, London, WC1E 7HX, UK.
E-mail:
b.smith@mail.cryst.bbk.ac.uk - WWW: http://people.cryst.bbk.ac.uk/~ubcg05s/

The GUI (graphical user interface) has taken up a central role in modern software development. GUIs can range from highly complex layouts, down to just a few buttons and menus controlling a simple application. I'm sure we've all seen badly designed GUIs, but done right they can vastly improve the user's experience. Whether you're planning to write the next killer application in your field, or perhaps just want to put a friendly face on top of a command-line driven program, which tool should you use for the job? Your decision might naturally be swayed by which programming languages you already know, or are prepared to learn. Part of the decision will also depend on which operating systems you want your software to run on. Platforms such as Microsoft Windows, Unix/Linux and Mac OS X do not share common system libraries for drawing to the screen, traditionally forcing programmers to maintain more than one set of source code or support just one platform. Learning any new language or graphics toolkit can require a serious investment of time and effort. In this article I will try to put the case for Qt, a widely used cross-platform C++ GUI toolkit. Qt is the flagship product of a Norwegian software company called Trolltech (http://www.trolltech.com/). Trolltech was founded in 1994, whilst Qt development dates back to 1992.

Nowadays there is a wide choice of software out there for the budding GUI designer — which is a good thing. For Windows-only development, Visual Basic or MFC have long been popular. However, like many programmers, I am keen on multi-platform development, including Linux and the various flavors of Unix (IRIX, Solaris, Tru64, AIX, and others). Under Unix, the traditional way of creating a GUI is via the Motif toolkit. However, rather than signing-up to any of these solutions, maybe your time is better spent working with a cross-platform toolkit?

Qt, wxWindows, Fltk and FOX - to name but four - are all cross-platform C++ GUI toolkits you might use (see http://www.atai.org/guitool/ for a better list). Recently I've done work using Fltk, and hence I do not use Qt exclusively. Since I am not a Java programmer myself, I will conveniently avoid the C++ versus Java debate. I'm not here to criticize other tools: I've seen great things done with Java, Python, Fltk et al., but for this article I will concentrate on the pros and cons of Qt itself, and why I enjoy using it. I will discuss pricing and licensing later, but as a side note I should stress the need to always read (and understand!) all software licenses relevant to your project, be they Qt or anything else.

Using Qt, you can support Windows, Unix/Linux and Mac OS X whilst maintaining a single set of C++ source code, and avoid platform-specific libraries such as MFC or Motif. When you compile your application, Qt uses the target platform's high-performance drawing functions; its classes are not inefficient 'wrappers' around other libraries' functions. Qt is a highly object-oriented (OO) library, encouraging good OO design in the programmer. It generates efficient code, with conservative use of memory. The end result looks and feels just like the user expects from their chosen operating system and desktop environment. This 'look and feel' is important — you don't want your application's fonts, colours, widget style or overall behavior to seem out of place on the user's desktop. Equally, in the commercial world you don't want to look less professional when compared to your competitor's product. Even if you are targeting just one platform right now, its sensible to keep your options open rather than become locked into that platform, and locking out potential users or customers.

I think you will find the Qt API (application programming interface) to be amongst the best around. Actually, I'll stick my head on the block and say you won't find better. The Qt widget-set (i.e. set of classes) is huge, but I'll try to convince you that it is very easy to use.

[sample Qt widgets]

Figure 1: Sample Qt widgets shown in Unix (left) and Windows (right). Top: a group of checkboxes (QCheckBox). Bottom: a progress bar (QProgressBar).

Figure 1 shows two simple widgets and how they appear on typical Unix and Windows systems. You can probably guess the purpose of most Qt classes just from reading their names: common widgets include QPushButton, QPopupMenu, QMainWindow, QCheckBox, QDialog, QProgressBar, and so on. Most user-interface classes derive from a QWidget base class. Layout is important in any GUI, e.g. arranging widgets into rows, or defining which areas will expand when the user stretches a dialog box or window. Classes such as QHBoxLayout and QGrid are for this purpose. QTable goes beyond basic layout, providing a powerful spreadsheet-style grid of editable cells. Other specialized classes include QCanvas, which is ideal for highly optimized 2D display of sprites, lines and polygons, also providing collision detection and double-buffering. For 3D graphics, the industry-standard OpenGL can be used via the QGLWidget class, but you will need to understand the OpenGL API first since Qt does not provide much help here (perhaps disappointingly for those wanting a quick route to 3D). The display of editable text, with rich text formatting or HTML is commonly required in modern software. These are easily implemented using QTextEdit and QTextBrowser respectively. The latest version of Qt (3.1) brings some useful improvements, such as a new class for syntax highlighting.

Qt is not just about buttons and other graphical building-blocks. QString is Qt's own string class, with a rich feature set including internationalization, regular expression matching and all the functions you would expect from using strings from other libraries. In order to overcome cross-platform STL issues, Trolltech also decided to reimplement a number of other STL-style classes, such as the object containers QMap, QPair and QValueList. Network classes exist for programming client/server applications, i.e. using FTP or HTTP. Database classes let you integrate SQL searches, but this is an area I've yet to explore myself. XML is becoming widely accepted as a standard data format, and is suitably well-supported in Qt.

As you've probably noticed, 'wizards' seem to be turning up all over the place in software these days. Like most things, when done well they are very effective. Qt's QWizard class is excellent for creating these. Nowadays, users also expect a 'Drag and Drop' (DnD) interface, especially on Windows platforms, e.g. to load files or perform other operations. Qt has classes such as QDragObject to add this functionality. Learning to use these techniques can improve the usability of your software immensely.

Qt also takes care of other cross-platform issues that you might encounter. With QDate and QFile you won't need to worry about how the target platforms deal with dates, times, and file systems.

More advanced programmers will be interested in Qt's thread support, MDI (multi-document interface), plug-ins, and scripting possibilities. I've also not mentioned that Qt supports some embedded operating systems, such as those used in handheld Linux devices.

As a scientific programmer, perhaps the most obvious feature that seems lacking from Qt is purpose-built graph and chart classes. Sure, you can write your own or download third-party widget-sets such as Qwt (which is admittedly rather good), but it would be nice to see properly supported 2D and 3D graph classes, even if not part of the core Qt library (its important to keep GUI libraries from becoming too bloated with 'specialist' classes).

Many companies, large and small, are using Qt. You will find well-known names such as NASA listed as Qt users — see the TrollTech website for more case studies and articles. Probably the most well-known application written using Qt is KDE (http://www.kde.org/), the desktop environment that comes with many Linux distributions. Whether you use KDE or not, its an excellent example of what Qt can do, and testimony to the quality of it’s classes. Since this is a crystallography newsletter, what better way to see Qt in action than to download CCDC's free crystal structure viewer, Mercury (http://www.ccdc.cam.ac.uk/prods/mercury/). It is available for Windows, Linux, Solaris and IRIX operating systems. Mercury makes use of the OpenGL module in Qt for it's high-quality molecular graphics display. I feel I should also point out, having worked at CCDC and seen the huge amount of effort that goes into such a project, that there is of course a lot of non-GUI related work required when creating a powerful scientific application such as Mercury.

[Mercury screenshot]

Figure 2: Mercury screenshot (Windows version).

If you think about it, a user-interface is really about letting the user control an application and be productive in what they want to do. Buttons get pressed, menu items get selected, text gets entered, things happen. Many widgets will be involved during this, and must react to events. Hence, the GUI is not a collection of static graphics, but an active communicating set of items. So, how does the programmer control this? Qt implements its own unique way, called 'Signals and Slots'. At this point I will introduce a test application to demonstrate some real C++/Qt code. As an exercise, I recently decided to write a simple protein structure viewer. The idea was to spend no more that a day on the project, from start to finish. Figure 3 shows the result. The same code compiles on Windows (Developer Studio) and Linux (GCC) without modification, and as you can see, the application inherits each platform's native look and feel. I restricted the program to drawing the protein backbone only, hence the name Bbone.

[Linux protein visualiser]  [Windows protein visualizer]

Figure 3: Screenshots of the Linux (left) and Windows (right) versions of a simple protein visualiser application written using Qt.

The simple GUI utilises QButton and QSlider widgets to let the user manipulate the molecule by rotation and translation. Returning to the idea of signals and slots, let's look at the code for implementing one of the 'sliders' in Bbone:

// Create our OpenGL molecule in a frame (implementation not shown here).

molgl = new GLBox( frame, "glbox");

// Create slider for x rotation.

QSlider* xsl = new QSlider ( 0, 360, 60, 0, QSlider::Vertical, this, "xsl" );

xsl->setTickmarks( QSlider::Left );

connect( xsl, SIGNAL( valueChanged(int) ), molgl, SLOT( setXRotation(int) ) );

First, lets concentrate on the instantiation of the QSlider. Widget constructor arguments vary, but usually require at least a pointer to a parent widget. This makes for simplified memory handling, since deletion of a parent widget automatically calls the destructor of any child widgets it knows about. The extra arguments in the QSlider constructor let us create a slider from 0 to 360 degrees, with 60 steps, and start it at zero. The connect statement then defines what we want the slider to do when it is activated by the user's mouse, i.e. the molecule must rotate around the x axis. The code takes the form of two object pointers, plus a signal from the first and a slot from the second, thus establishing the connection. Built-in classes already have many signals and slots available, such as the clicked() signal emitted by a QPushButton, or you can write your own. Slots are really just normal member functions, but with the added ability to 'listen' for signals.

A nice consequence of Trolltech's signal and slot design is that it keeps widgets as independent as possible. Signals need not be connected to any slots, or may be connected to many. Slots may listen for multiple signals. This makes for rapid GUI development, and easy maintenance of code. There is a small CPU overhead for this flexibility, but except in very specific situations it is not noticeable. One valid criticism leveled at the signals and slots approach is that it requires a preprocessing step before compiling. In the code snippets above, the keywords connect, SIGNAL and SLOT are not real C++, but instead a preprocessor spots these definitions and creates the appropriate code for them, somewhat behind the programmer’s back. However, for the C++ purist this can feel like a rather non-standard way to solve the problem. For everyone else, it’s a clear, clean, robust way to get your widgets talking to eachother.

Now we have our three sliders, this is a good time to look at how we can arrange them in a nice layout. If we want them to appear stacked one above the other, we can use QVBoxLayout, as shown in the code below.

// Arrange the sliders on top of each other

QVBoxLayout* vlayout = new QVBoxLayout( 5, "sliderbox");

vlayout->addWidget( xsl );

vlayout->addWidget( ysl );

vlayout->addWidget( zsl );

I'll briefly summarize the other parts of the program. The list of amino acids is done via a QTextBrowser widget, which will automatically show scrollbars if required. For the balls-and-sticks display I used OpenGL via Qt's QGLWidget class - admittedly not a trivial task. Adding 'File' and 'Help' menus and a QStatusBar finishes off the application. Of course, the program needs more work to be genuinely useful — the Mercury developers need not worry quite yet! Also, as I eluded to earlier, there is always plenty of non-GUI work to do. Reliably parsing the PDB file turned out to be rather tricky to write, but I could of course employ some third-party code to improve this.

Qt is not a completely free product. You probably guessed there had to be a catch somewhere. The licensing is a little complicated, but boils down to the following: Qt is free for developing open-source (GPL) projects under Unix and Linux. Closed-source, or internal company projects, require a license. Currently, Qt3 for Windows or Mac OS X is only available by purchasing a license. There are a few caveats to this. Firstly, academic users can obtain a 60% discount. For purely teaching purposes, free site licenses are also available. As described on Trolltech's website, staff at colleges and universities are finding that Qt is ideal for teaching the fundamentals of object-oriented design and C++ programming. At one stage Trolltech did release a free non-commercial version of Qt for Windows. This was great for the shareware community but unfortunately led to mis-use by commercial organisations, and hence Trolltech has not (at the time of writing) released Qt3 this way, although one can still use Qt2.3 under that license. It is difficult to criticise Trolltech too much for this pricing structure (although many shareware developers do) since academics get a hefty discount, and the commercial funding has in-part led to Qt being as good as it is, and assures Qt will be around in years to come. The latter point is rather relevant in the recent dot-com doom and gloom.

Personally, one of the biggest compliments I can give to Trolltech is for their documentation and support. The API documentation is amongst the best I've seen for any product, and is now like an old friend when I look it up. This is an important consideration when choosing a GUI toolkit. For relatively small projects you might not encounter major problems, but if committing significant time and effort to a piece of software (and potentially much longer in subsequent maintenance and support) there is a real chance you will hit against complex problems that need solving fast. Qt's documentation is extensive, from numerous examples and tutorials, to clear yet detailed descriptions of all classes and functions. Other topics, such as Signals and Slots or Drag and Drop are covered in individual articles. Another valuable source of reference is the Qt users' mailing list, which has searchable archives via the Trolltech website. The list is very active, and there's a good chance your problem is solved by going there. For paying customers, direct E-mail support is available, and on the few times I've had to use it I've received prompt and useful replies, often from the actual programmer responsible for maintaining the relevant piece of code (try that with Microsoft!).

Many GUI toolkits and software development environments include some kind of WYSIWYG application to visually create a user-interface. All versions of Qt come with Designer (naturally, also written in C++/Qt). Over the years this has developed into a powerful tool, allowing the programmer to drag and drop widgets onto a central form, quickly building and previewing your application. It also provides a simple way to connect signals and slots, by visually drawing lines between them. Even if you don't use Designer for your entire GUI, it is particularly useful for something like a 'Preferences' dialog box which can contain lots of laborious layout which probably won't be much fun to do manually. Designer uses XML ('.ui' files) to store it's layouts, and these are only converted (via another preprocessor) to real C++ classes at compile-time. Since the classes get regenerated from the '.ui' files every time you compile, any custom functionality must be added to derived classes, but this works surprisingly well.

[Qt designer]

Figure 4: Screenshot of Qt Designer, a visual GUI builder. It allows you to lay out widgets, connect signals and slots, and preview the results immediately. (image taken from the Qt website)

I'll finish by mentioning a couple of other related tools. One particular job that can create headaches for even the best programmer is the production and maintenance of Makefiles (or the equivalent files required for other compilers). Luckily, Trolltech have created a separate program called Qmake (previously named Tmake) to solve this problem. Instead of maintaining Makefiles you keep a more generic project file, which Qmake then analyses to create the Makefile, taking care of any platform-specific libraries, paths or other details. Although Qmake was primarily designed for use with Qt it can in fact be used with any C++ project. In a professional software development environment, the use of source-control software such as CVS can be combined with Qmake to set up a fully automated build process.

The second tool I'd like to recommend is Doxygen, a free (GPL) program which creates documentation directly from your source code. It is not produced by Trolltech, but they use Doxygen to generate their Qt class documentation web pages. It works by extracting text comments from the source, and its a good idea to spend a few minutes learning the special formatting it looks for, so that you can keep your code well-documented from the outset. Current and future users of your code will thank you! As well as nicely-formatted HTML output, it can also generate Latex and Windows Help files. Another good feature is its ability to draw class hierarchies and simple UML relationships, all automatically. See http://www.doxygen.org/ for more information.

So, should you be using Qt? If you want to learn C++ and OO methods, Qt is a great place to start. For small projects, requiring relatively simple user interfaces, one can argue that almost any of the previously listed toolkits or programming languages will produce satisfactory results. Comparing the pricing structure of Qt to some of the free options may understandably lead you to take another route if you're using Windows rather than Linux. Recently I made such a decision for a small project I was working on (the Fltk-based project mentioned in the introduction). For commercial software companies, it's likely that the added support that comes with a license easily outweighs the cost issue. I think the strength of Qt lies in larger application development, where you require a fully-fledged GUI, powerful underlying features, and robust, well-documented classes. In that case, you can't go wrong with Qt.