wxWindows, A Linux GUI Applications Development Alternative
(Published in the C/C++ Users Journal, May 2001)

Carlos Moreno


Introduction

Graphical User Interface (GUI) is perhaps the most important feature in applications that require interaction with end-users.  Virtually all modern operating systems, including recent versions of Linux, provide users with a graphical environment that allows them to launch applications and manage resources.  Users are accostumed to these standardized facilities, and expect no less from applications.

In this article I present an introductory tutorial of wxWindows, a free C++ based toolkit for the development of multi-platform GUI applications.  The targetted platforms include Linux, MS Windows, MAC, and Sun. wxWindows also offers a class library for the development of non-GUI applications (console or embedded applications).  In this article, however, I will concentrate on the use of wxWindows to develop GUI applications for Linux.  You can find more about other benefits and facilities for other platforms at wxWindows' web site (http://www.wxwindows.org).
 

Using wxWindows

One of the main benefits of wxWindows is its ease of use.  It provides an extremely intuitive approach to develop your GUI applications, yet you can develop very complex applications, with fully-featured user interface.

Among the important classes that are part of the wxWindows library, are wxApp, wxWindow, and wxFrame.  wxApp represents the application itself.  In general, you derive from this class to create your custom application class.  The customization is done by overriding certain methods -- notably the OnInit method.  OnInit is executed when the application starts, and you use it to place initialization code that you require, including the instantiation and display of the application's main window.

Class wxWindow represents graphical components, like regular windows, dialog boxes, controls, etc.  Class wxFrame is derived from wxWindow, and it is normally used to represent the application's main frame.  As with wxApp, we don't use class wxFrame directly, but we use it as the base class to create our customized Frame class.

The following fragment of code shows an example of defining our ownapplication class and main frame class.  In the application class,the overriden OnInit method creates and shows an instance of our main frame:

    class SampleApp : public wxApp
    {
    private:
        virtual bool OnInit();
    };

    class MainFrame : public wxFrame
    {
    public:
        MainFrame (const wxString & title,
                   const wxPoint & position,
                   const wxSize & size)
            : wxFrame (NULL, -1, title, pos, size)
        {}
    };

    MainFrame * frmMain;    // could also be a private data
                            // member of class SampleApp

    bool SampleApp::OnInit()
    {
        frmMain = new MainFrame ("Sample Application",
                                 wxPoint(20,20),
                                 wxSize(200,150));
        frmMain->Show (true);
        SetTopWindow (frmMain);

        return true;
    }

In this example, creating the class MainFrame was not particularly useful,  since I didn't customize it.  I chose to do it this way to illustrate the typical approach, since the main frame class always requires some customization.

The parameters to wxFrame's constructor are as follows:  The first one is the parent, or the "container" window.  Given that this is a top-level window, its parent is set to NULL.  The second parameter is the ID, which is not required in this case.  A value of -1 indicates a "default," or "not used" setting for this ID.

In the case of wxFrame, the parameter title corresponds to the frame's titlebar text.  Notice the use of class wxString.  This class is relatively similar to the standard library's string class, at least when used in simple situations.  You can find out more about class wxString in the wxWindows documentation.

The other two parameters are position and size.  These are conveniently specified using classes wxPoint and wxSize;  wxPoint's constructor takes parameters x and y (in screen coordinates), and wxSize's constructor takes parameters width and height, in pixels.

Notice that the frame object is dynamically allocated.  It is a requirement that you use dynamic memory allocation for all of the windows (and as you will see, for most GUI objects of the wxWindows library).

By adding the appropriate include files and an entry point, we could turn this fragment into a minimally working application.  The include directives should be as shown below:

    #include <wx/wxprec.h>
 
    #ifdef __BORLANDC__
    #pragma hdrstop
    #endif
 
    #ifndef WX_PRECOMP
    #include <wx/wx.h>
    #endif

Including only <wx/wx.h> would suffice under Linux, but it is strongly advised that you always use the above, since this provide increased portability, and takes advantage of specific features of compilers on other platforms.

The starting point is provided by the IMPLEMENT_APP macro.  This macro requires only the application class' name, and it hides the definition of main, the instantiation of the application object, and all possible initializations required.  In this example you would place, at file scope, the following line:

    IMPLEMENT_APP (SampleApp)

This sample code shows an absolutely minimal application, in that theapplication does nothing useful -- it merely displays a window with "Sample Application" on its title bar.  To make something more useful, we need to first understand the notion of events, which enable the application to respond to actions from the user.
 

Understanding the Event-driven Programming Paradigm

GUI applications have a funny way of working:  they are always doing nothing (they call it idle to make us believe that they are actually working).  Their functionality is based on the notion of events.  Events are conditions usually external to the application (often associated to actions from the user, such as mouse clicks, keystrokes, etc.) that are reported to the application so that it does something in response to it.

Thus, instead of imposing the user a predefined sequence of actions, GUI applications typically present a set of graphical input and output devices, and let the user choose the sequence of data-entry and commands; the application will simply be waiting for the appropriate commands, and respond to them.

As an example, try to picture how a login authorization process would work.  The event-driven, GUI approach would be as follows:  display a dialog box containing two text boxes with labels next to them, an OK button, and a Cancel button.  The user can type the name first, or the password first, and they could come back one or several times to re-enter or edit whatever information was previously entered.

If you accept the idea that the keystrokes, text edition, etc. is handled internally by the text input devices (not too hard to accept), then you can see that the application just has to display the dialog box and enter an "idle" loop, waiting for the user to click on Ok or Cancel.  Only in these cases, the application should "wake up" and respond.  In general, after responding to the user's action, the application returns to idle.

This example is not a formal or complete description of the event-driven programming paradigm, but I hope it gives you an idea of how the approach works, in case you were not familiar with it.

When designing a GUI application, one of the key elements is to identify what events you should respond to, and set up the fragments of code that respond appropriately.  These fragments of code are called event handlers.

In wxWindows, event handlers are implemented as methods of the window class related to the event.  Events are associated to particular GUI elements in the application;  for instance, in the previous example, it is not enough for the application to know that the user clicked the mouse;  it has to know if the click was on the OK button, or if it was on the Cancel button -- these are two different events, requiring two different responses.  As the examples in the next sections will show you, we use event tables to establish the links between the events and the corresponding event handlers.  Event tables provide a practical and intuitive way to bind the various events to their corresponding event handlers.
 

Menus

Providing menus in wxWindows applications is very straigthforward.  We typically create the menus in the constructor of our frame class, and set up the event handlers for each menu selection.

The fragment of code shown below creates a menu tree with a structure as shown in figure 1:

    wxMenuBar * menuBar = new wxMenuBar;    // main menu bar
 
    wxMenu * menuFile = new wxMenu;         // menu items and submenus
    wxMenu * menuHelp = new wxMenu;
    wxMenu * menuMessages = new wxMenu;
 
    menuMessages->Append (ID_message1, "Message 1");
    menuMessages->Append (ID_message2, "Message 2");
 
    menuFile->Append (ID_messages, "Display &Message", menuMessages);
    menuFile->AppendSeparator();
    menuFile->Append (ID_exit, "E&xit\tCtrl-Q");
 
    menuHelp->Append (ID_about, "&About");
 
    menuBar->Append (menuFile, "&File");
    menuBar->Append (menuHelp, "&Help");
 
    SetMenuBar (menuBar);
 

The first four statements instantiate the menu objects.  You need one wxMenuBar object for the top-level menu, and one wxMenu object for each pop-up menu required.  You populate wxMenu objects using Append.  Each menu item is identified by a unique numeric constant, which allows you to attach the event handlers to each menu selection.

You can also use AppendSeparator to place a separator line at the corresponding position.  The second parameter to Append is a string with the caption for the corresponding menu item.  An & indicates that the following character is the accelerator key for that menu item, and a TAB character indicates that what follows is a code for the shortcut key.  You can use a third parameter to indicate that the added menu item has a sub-menu associated.

The above code only show the menus; you still need to set up the event handlers and bind them to the various menu selections, using an event table.

Event tables require a two-step procedure:  declaring it (done in the frame class' definition), and defining it (done at file scope).  The first step is done by simply placing the DECLARE_EVENT_TABLE() macro in the frame's class definition, typically in the private section:

    class MainFrame : public wxFrame
    {
        // ...
    private:
        DECLARE_EVENT_TABLE()
    };

Then, at some point in the file, we define the event table using the BEGIN_EVENT_TABLE and END_EVENT_TABLE macros.  BEGIN_EVENT_TABLE receives two parameters:  the name of the class for which it is being defined (in this case, MainFrame), and the name of the base class (in this case, wxFrame).  The base class is necessary, since some events associated to the frame may have an event handler in the base class.  The event table could look like the following:

    BEGIN_EVENT_TABLE (MainFrame, wxFrame)
        EVT_MENU (ID_message1, MainFrame::OnFileMessage1)
        EVT_MENU (ID_message2, MainFrame::OnFileMessage2)
        EVT_MENU (ID_exit,     MainFrame::OnFileExit)
        EVT_MENU (ID_about,    MainFrame::OnHelpAbout)
    END_EVENT_TABLE ()

Each entry uses the EVT_MENU macro to bind a menu event associated to the object with a particular ID, to the event handler given by the member function indicated by the second parameter.

Event handlers are defined as methods that receive a reference to a wxEvent object as parameter.  Handlers for different types of events receive specialized parameters (i.e., objects of classes derived from wxEvent).  In this example, the event handlers should be similar to:

    void OnFileMessage1 (wxCommandEvent & event);

The parameter provides information relevant to the event.  In this particular case, such information is not useful, so you ignore the parameter (you may omit the parameter name).  Examples of when this parameter may be particularly useful is the handling of certain mouse events;  the parameter in that case provides information about the coordinates of the mouse pointer, and the state of the mouse buttons and the alt, ctl, and shift keys at the time the event was produced.

Listing 1 (sample_wx.cpp) shows the complete version of this minimal sample, including the menus and a brief note on installing wxWindows and compiling the application.  I use wxMessageBox to display dialog boxes with simple messages as the response to the mouse events.  Also, notice the call to the Close method (inherited from wxFrame) in the OnFileExit event handler.  This closes the main frame, terminating the application.
 

Controls

Controls are GUI elements that provide interaction between the user and the application.  They may be used to get input from the user (text boxes, push buttons, check boxes, etc.), or to display information (static text boxes, static bitmaps, etc.).

Class wxControl represents the common functionality of controls, and is at the top of their class hierarchy.  You can use wxControl-derived classes to represent specific types of controls, as required by your application.

I will use classes wxButton, wxStaticText, and wxTextCtrl to show an example of a temperature conversion utility.  The application shows a text box with a caption, and two buttons, one to convert from Celsius to Fahrenheit, and one from Fahrenheit to Celsius.  The text box is represented by wxTextCtrl class, the caption by wxStaticText class, and the buttons by wxButton.

The constructors for the various types of controls are very similar for most regular controls.  In particular, the first five parameters are the same for many of the frequently used controls.  The first parameter is a pointer to its parent window (the window that "owns" or "contains" the control).  The second parameter is the ID that uniquely identifies the control.  This ID is useful if we need to handle events associated to that control.  Still, it is in general a good idea that you always provide a unique numeric ID for every control.  The third parameter is the caption.  For a wxButton, it specifies the caption displayed inside the button;  for a wxStaticText, it is the text that the control displays;  for a wxTextCtrl it indicates the initial contents of the text box, etc.  The remaining two parameters are position and size.

You usually declare the controls as private data members of the window class that contains them, and instantiate them in the constructor.  As with the frame and the menus, you must always allocate them dynamically.

The following fragment of code shows the modifications to the sample_wx program to create and display the controls:

    class MainFrame : public wxFrame
    {
    public:
        MainFrame (const wxString & title,
                   const wxPoint & position,
                   const wxSize & size);
 
    private:
        wxStaticText *  label;
        wxTextCtrl *    input;      // input from user
        wxStaticText *  output;     // result
        wxButton *      FtoC;
        wxButton *      CtoF;
 
        DECLARE_EVENT_TABLE()
    };
 
    MainFrame::MainFrame (const wxString & title,
                          const wxPoint & position,
                          const wxSize & size)
        : wxFrame (NULL, -1, title, position, size)
    {
        label = new wxStaticText (this, -1, "Temperature:",
                                    wxPoint(20,20), wxDefaultSize);
        input = new wxTextCtrl (this, -1, "",
                                  wxPoint(150,20), wxSize(80,20));
        output = new wxStaticText (this, -1, "Result: ",
                                     wxPoint(20,50), wxSize(150,20));
        FtoC = new wxButton (this, ID_FtoC, "F to C",
                               wxPoint(250,20), wxDefaultSize);
        CtoF = new wxButton (this, ID_FtoC, "C to F",
                             wxPoint(250,50), wxDefaultSize);
    }

The sizes and positions of the controls assume that the main frame is 350 by 90.  I used wxDefaultSize to indicate that those controls should assume their default size, according to the contents (caption) specified.

You typically use this as the first parameter in the constructors, indicating that the MainFrame object is the parent window.  The two buttons are the only controls attached to events, so they do have ID values that uniquely identify them.

Listing 2 (controls.cpp) shows the complete code for this second sample application.  There is one important difference with respect to the sample shown above.  In the sample above, I created the controls directly on the frame to simplify this initial example.  However, it is not a good idea to place controls in a wxFrame-derived object.  Listing 2 shows the recommended approach, which is placing a container window (e.g., a wxPanel) inside your frame, and use it as the container for the controls.
 
In both event handlers, the information from the text box is read with wxTextCtrl's GetValue method, which returns a wxString.  This wxString is converted to its numerical equivalent, and used to generate the output string, which is sent to the output wxStaticText using its SetLabel method.

wxString::c_str converts the string to a C-style string that atof can manipulate.  I could have also used wxString::ToDouble, which has the advantage that it notifies client code if the conversion was succesful (check the online documentation for more details).

If you want to clear the input text box after doing the conversion, you could use wxTextCtrl's SetValue method, which receives a wxString that specifies the new value for the contents of the text box:

    input->SetValue ("");   // clear input box
 

Custom Dialog Boxes

Class wxDialog provides a convenient framework for creating and using custom dialog boxes.  As with wxFrame, you should use wxDialog as the base class to create your own custom dialog classes.  wxDialog provides the basic functionality of dialog boxes, including the Show and ShowModal methods, automatic closing and returning the appropriate button code when the user clicks on OK or Cancel, etc.

In the following example, I create a dialog box that asks the user for name and password.  The first step is to create the class to represent this custom dialog box.  This procedure is relatively similar to the way that we create custom frames:

    class PasswordDialog : public wxDialog
    {
    public:
        PasswordDialog (wxWindow * parent,
                        const wxString & title = "Login",
                        const wxPoint & position = wxDefaultPosition)

        wxString get_name () const
        {
            return name->GetValue();
        }
 
        wxString get_password () const
        {
            return password->GetValue();
        }
 
    private:
        wxStaticText *   label_name;
        wxStaticText *   label_password;
        wxTextCtrl *     name;
        wxTextCtrl *     password;
        wxButton *       ok;
        wxButton *       cancel;
    };

The public member functions allow client code to get the information entered by the user (name and password).  I could have made the controls public, but this would break the encapsulation.  I always prefer keeping the data members corresponding to the controls in the private section, and provide public methods to access the controls' contents if required.

Instead of using event handlers attached to the OK and Cancel buttons, you just use pre-defined ID values when creating them.  These values(wxID_OK and wxID_CANCEL) identify them and give them the appropriate behaviour, including the fact that the dialog will close when the user clicks on either of them, and the value returned to client code will be a code identifying the button.

The dialog can be used as follows:

    MainFrame::OnLogin (wxCommandEvent &)
    {
        PasswordDlg pwdBox (this);

        if (pwdBox.ShowModal() == wxID_OK)
        {
            // use pwdBox.get_name() and pwdBox.get_password()
            // for whatever purposes as required
        }
    }

Notice that here, it is ok to declare a local dialog object, as opposed to dynamically allocate it.  You may still choose dynamic allocation, to be consistent with the other objects.  The tricky detail is that if you allocate the dialog dynamically, then you must call its Destroy method when you're done, instead of using delete.

Listings 3 and 4 (pwd_box.h and pwd_box.cpp) show the details of this custom dialog sample.  The rest of the sample application can be downloaded from CUJ's web site (files dialogs_app.h ,   dialogs_app.cpp ,   main_frame.h ,   main_frame.cpp , and Makefile).  This sample shows the recommended approach to split your projects into modules:  one pair of .h/.cpp files for the application class, and one pair for every dialog or frame class that you create.

I handle the keystroke events for both text boxes, to keep the OK button disabled as long as one of the two text boxes is empty.  Both events require the same response (check the contents of both text boxes and enable the OK button accordingly);  thus, I provide only one event handler, and attach both events to the same handler.

The password text box is given the appropriate behaviour using the extra parameters in wxTextCtrl's constructor (in particular, the style parameter).  Setting the style to wxTE_PASSWORD sets the control to show asterisks as the user types, while internally storing the actual text that the user types.

Other more convenient techniques are available for the positioning and sizing of the controls in custom dialog boxes (notably the use of sizers).  I will omit a detailed discussion and samples on the use of sizers.  You can find more information in the documentation and samples available when you download and install wxWindows, or from wxWindows'
web site.
 

Common Dialogs

wxWindows provides a handful of ready-to-use custom dialogs, for the common data entry tasks, such as browsing for a filename or multiple filenames, fonts, printer, etc.  The fragment of code below shows an example of use of the file selection dialog.  You can check the samples shipped with wxWindows (or available from their web site) to find more about this and the other common dialogs.

    MainFrame::OnFileOpen (wxCommandEvent &)
    {
        wxFileDialog dialog (this, "Open file", "", "", "*.*");

        if (dialog.ShowModal() == wxID_OK)
        {
            // Use dialog.GetPath() as required
 
            // dialog.GetFilename() and dialog.GetDirectory()
            // may also be used.
        }
    }
 

Memory Management Issues

As I already mentioned, you must always use dynamic memory allocation for objects that represent GUI elements (controls and windows).  One detail that I didn't mention is that you don't have to explicitly use delete to deallocate them (what? you didn't think that I am so irresponsible that I forgot to delete them in the samples, did you?)

Dialogs and frames keep track of the controls they own.  When you call wxDialog::Destroy, or wxFrame::Close, etc., these methods handle self-destruction.  On destruction, these objects handle destruction of the contained controls, and any other window objects that are owned by them (using delete, which is why you must always use new to allocate them).

In some cases, like with dialogs, it is ok to use allocation on the stack.  My advice is that you check the samples to see how allocation is handled for specific classes that you're not familiar with.
 

The Ultimate Proof of wxWindows' Ease of Use

In this rather anecdotic section, I present a complete application developed with wxWindows.  I am not using this application as a sample of what you can do with wxWindows, or as a tutorial to the rest of the features that I didn't discuss in this article (which are countless).  Rather, I offer this application as proof of how easy to learn and use wxWindows is.

This application was developed by a group of ten of my students in the context of a "Team Project" course.  After a three-week C++ course, I was in charge of a two-week team project, in which the students have the responsibility to develop a relatively large and complex application using C++.  I decided to have the entire group of ten students develop an Image Display utility for Linux, using wxWindows.  The goal was to develop an open source application of enough quality to be published and good enough to be actually useful.  And I think they did achieve that goal, completing the design of an application that we named Wx-View.

wxWindows' ease of use becomes obvious when you consider that ten students with zero actual programming experience learned wxWindows and completed an application that uses lots of advanced features.  All that in only two weeks, and without much prior training in wxWindows (I just introduced it the last day of the C++ course, and gave them an extra day to practice, read the online documentation and samples, etc.).

You can find more about the Wx-View project (and download it and use it) from my web site, at http://www.mochima.com/wx-view, or from wxWindows' web site, following the link to contributions.
 

The Future of wxWindows

I hope that this introduction triggers your interest in this convenient and powerful toolkit.  A lot of effort is being done to improve and increase wxWindows' capabilities.  Among the features that are relevant to Linux developers are: Support for CORBA, better support for graphics, including OpenGL, HTML and XML support.  The support for databases is also improving; more drivers are being embedded to provide support for a wider range of database engines and servers.  UNICODE, and thus better internationalization support is also on its way.

A book on wxWindows, code-named wxBook is being worked on.  This is indeed great news, since the lack of documentation is arguably one of wxWindows main weak points.  Of course, this possible lack of documentation is compensated by how easy to learn and use wxWindows is.

Commercial supplements also give a solid standing to wxWindows' future.  These supplements will include, in the near future, commercial technical support that guarantees timely solutions or alternatives to your development problems (commercial support is currently available for wxPython, a close cousin of wxWindows).

wxDesigner is also an interesting commercial supplement to wxWindows; it provides you with a graphical IDE that simplifies the design of portable, sizer-based custom dialogs.
 

Conclusion

GUI applications represent an important fraction of the end-user applications in a Linux desktop environment.  I have presented an introductory tutorial of wxWindows, a cross-platform toolkit that greatly simplifies the development of complex, fully-featured GUI applications.

Its robustness is definitely an important benefit, but its being so easy to learn and use is also one of the most important features of this toolkit.

Cross-platform development is another extremely important feature of wxWindows.  Even though the emphasis of this article was on using wxWindows to develop GUI applications on Linux, it is worth mentioning that all of the samples included in this article compile and run without any modification whatsoever under MS Windows, and surely on other platforms as well (I actually tested them only on a RedHat Linux 6.2, using g++ 2.95.2, and on Windows 2000, using Borland C++ 5.5.1).

I presented an introductory discussion on the main features, but you can find out about the many additional features and tools from the online documentation and samples.  These are shipped with wxWindows and are made available when you install it.  They are also available from wxWindows web site, following the links to the documentation and samples.
 

Acknowledgements

I would like to thank the authors of the wxWindows toolkit for kindly reviewing the article draft and making valuable suggestions.
 

References

wxWindows online documentation.  http://www.wxwindows.org