Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Build cross-platform GUIs using wxWidgets

The wxWidgets family of tools lets you use your language of choice

Noel Rappin, Senior Software Engineer, Motorola, Inc.
Noel Rappin has a Ph.D. from the Graphics, Visualization, and Usability Center at the Georgia Institute of Technology and is a senior software engineer at Motorola, Inc. He is also the co-author of wxPython in Action (Manning Publications, March 2006) and Jython Essentials (O'Reilly, March 2002).

Summary:  The wxWidgets toolkit contains powerful, cross-platform tools for graphical user interface (GUI) development. In addition to its native C++, several languages offer wrappers for use with the toolkit. Learn how to use the wxWidgets toolkit to create elegant and highly useful GUIs in your programming language of choice.

Date:  21 Sep 2006
Level:  Intermediate
Also available in:   Russian

Activity:  22128 views
Comments:  

Why use wxWidgets? Because you want to be able to write a GUI quickly and easily that runs across platforms. You also want to be able to use the programming language of your choice, and you want your GUI to look as good as this:


Figure 1. The Chandler e-mail client
The Chandler e-mail client

Figure 1 shows Chandler, a calendar and e-mail management program in development at the Open Source Application Foundation. It is being written using the wxWidgets toolkit. Although the original version of wxWidgets is implemented in C++, Chandler's creators are using Python with the wxPython toolkit as a wrapper so that the Python code interacts seamlessly with the C++ library. The wxWidgets toolkit uses native objects wherever possible; these objects are augmented with powerful custom widgets where they're needed. You can write a wxWidgets program that will run on a wide variety of platforms, and you can use a variety or programming languages to do it.

Getting started with wxWidgets

For this article, I start with the assumption that you've already gone to the wxWidgets home page and downloaded the relevant package for your platform. If not, see the Resources section for a link and download it now. I further assume that you've managed the commands or settings you need to integrate the wxWidgets library with your compiler or integrated development environment (IDE) of choice. If that process is still a mystery, however, the links in the Resources section will point you in the right direction for that information as well. With those tasks complete, you can focus on the code.

The backbone of a wxWidgets program consists of two main objects: the application and a frame object. You can, of course, have more than one frame. In addition, you need a handful of wxWidgets-specific macros somewhere in your code. Here's how the pieces fit together.

Link to the wxWidgets library

To link to the wxWidgets library, you must include it. Place the following line at the top of your header files:

#include "wx/wx.h"

The wx/wx.h header file contains all the wxWidgets definitions you're likely to need. If you're desperately concerned with performance, you can replace this file with include statements for the specific header files you're going to use.

Define the application class

Next, you must define an application class. In most simple cases, this class doesn't do much, but you will need a class of your own. A wxWidgets application inherits from the wxApp class, and the minimal definition is minimal, indeed:

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

The OnInit() function is called when your application starts -- effectively, it's your main() method.

When you've defined your application class, place the following macro somewhere in your code:

IMPLEMENT_APP(DemoApp)

You would replace DemoApp with the name of your application class. This macro creates the actual main() method that wxWidgets uses. It also creates an instance of the application object and begins the initialization process.

Define the frame class

Now, define a frame class, which will represent the main window of your application. The wxWidgets parent class is wxFrame. Listing 1 shows a brief example.


Listing 1. Example wxFrame class

class DemoFrame : public wxFrame {
public:
   DemoFrame(const wxString& title);
   void OnButtonPress(wxCommandEvent& event);

private:
   DECLARE_EVENT_TABLE()
};

To clear up the names that are likely to be unfamiliar, wxString is a wxWidgets-specific string wrapper class used for string operations throughout the wxWidgets toolkit. The toolkit declines the use of Standard Template Library (STL) classes so as not to limit wxWidgets to platforms on which STL is available. (A compile-time switch allows you to use STL as the underlying implementation, if you like.) Similarly, wxCommandEvent is one of the parent classes for events -- specifically, command events, which are higher-level events that typically relate to user actions, such as a button click or list selection. Finally, the DECLARE_EVENT_TABLE macro is required for any wxWidgets object that wants to respond to events, which definitely includes the little demo frame in this article.

Define the event table

To actually respond to events, you must define the event table within your implementation files. It's another macro, and (in our case) it looks like Listing 2.


Listing 2. The event table macro

BEGIN_EVENT_TABLE(DemoFrame, wxFrame)
   EVT_BUTTON(wxID_CLOSE,  DemoFrame::OnButtonPress)
END_EVENT_TABLE()

The BEGIN_EVENT_TABLE() macro takes two arguments: the class the event table is actually for and the immediate parent of that class. The END_EVENT_TABLE macro, as you might expect, signifies the end of the event table. In between, you can have any number of specific event macros. The example in this article has one.

The wxWidgets toolkit contains several different event macros, each corresponding to a different kind of event. In our case, EVT_BUTTON refers to a button click. The first parameter to the macro is an identifier referring to the specific button being handled. The wxID_CLOSE identifier is one of several predefined identifiers that relate to common features of an application. You use a predefined identifier here purely for convenience, although in some cases, specific identifiers are a trigger for special handling by the wxWidgets system. The second parameter to the macro is the fully qualified name of the method that should be called when the menu event is triggered. You manage all events in wxWidgets using event macros similar to the one described here.

Define your methods

At this point, it's time to start defining some methods. I show three simple methods, starting with the OnInit() startup method shown in Listing 3.


Listing 3. The OnInit() method

bool DemoApp::OnInit() {
   DemoFrame *frame = new DemoFrame("DeveloperWorks Demo");
   frame->Show(true);
   return true;
}

The first two lines of this method do something you would expect to happen at the start of a GUI program: they create and show a main window. The third line is actually most important to the rest of the application, however. Returning true is a signal to the rest of the wxWidgets engine that the initialization has completed successfully and the program can continue. In contrast, returning false would stop the application in its tracks and cause it to exit.

The OnInit() method references the constructor for DemoFrame. In that method, you add a button to your frame, as shown in Listing 4.


Listing 4. Add a button to your frame

DemoFrame::DemoFrame(const wxString& title)
      : wxFrame(NULL, wxID_ANY, title) {
   wxSizer *sizer = new wxBoxSizer(wxVERTICAL);
   this->SetSizer(sizer);
   wxButton *button = new wxButton(this, wxID_CLOSE, "Click Me");
   sizer->Add(50, 50);
   sizer->Add(button, 0, wxALL, 50);
}

Before you can add the button to the frame, however, you create a sizer. Sizers are the wxWidgets equivalent of the Java programming language's layout managers: they allow you to use predefined rules to place objects in your window rather than having to set the size and position of each widget independently. In this case, you use wxBoxSizer, which lays out its component widgets in a straight line. (A box sizer can be vertical or horizontal.) After creating the sizer, you attach it to the frame with the SetSizer() method. Then, you create the button. The parameters to the button constructor are:

  • The parent widget (in this case, the frame)
  • An integer ID
  • The label to display on the button

You don't need to explicitly add the button to the frame; merely identifying the frame as the parent container does the trick. You must explicitly add the button to the sizer, however, for the sizer's layout algorithm to be aware of it. You do that in the last line of the method, but not before adding 50 x 50 pixels of blank space at the top of the row. When you do add the button, you also tell the sizer to surround the button with a border of 50 pixels. You do that using the wxALL flag and the last argument of 50.

Define an event handler

Finally, you define the simple event handler shown in Listing 5.


Listing 5. A simple event handler

void DemoFrame::OnButtonPress(wxCommandEvent& event) {
   Close(true);
}

You can't get much simpler than that. Compile them all, and you'll see the lovely window with a single button shown in Figure 2. Click the button, and the window closes. In wxWidgets, closing the last parent frame automatically exits the application, so clicking the button also results in the application exiting completely.


Figure 2. The demo window
The demo window

This example, of course, barely scratches the surface of what's possible with wxWidgets. See the Resources section for guides to further exploration.


wxPython

While wxWidgets is a powerful toolkit, not everybody wants to deal with C++ destructors, memory management, and all the rest. Luckily, a group of talented programmers has created wrapper bindings to the wxWidgets library that are usable from other programming languages. So, even if C++ isn't your programming weapon of choice, you can still have the benefits of the wxWidgets library.

The most mature and fully developed of the wxWidgets bindings is wxPython, with which you can create wxWidgets programs using the Python programming language. Downloads are available for the Microsoft® Windows®, Mac, and Linux® platforms, and there is a fairly large and active user community. What does it look like? The Python program shown in Listing 6 creates exactly the same blank window you created earlier in C++.


Listing 6. Python blank window application

#!/usr/bin/env python

import wx

class DemoApp(wx.App):

   def OnInit(self):
      frame = DemoFrame(parent=None, id=-1, title='DeveloperWorks')
      frame.Show()
      return True

class DemoFrame(wx.Frame):

   def __init__(self, parent, id, title):
      wx.Frame.__init__(self, parent, id, title)
      sizer = wx.BoxSizer(wx.VERTICAL)
      self.SetSizer(sizer)
      button = wx.Button(self, wx.ID_CLOSE, "Click Me")
      sizer.Add((50, 50))
      sizer.Add(button, 0, wx.ALL, 50)
      self.Bind(wx.EVT_BUTTON, self.OnButtonClick)

   def OnButtonClick(self, event):
      self.Close(True)

if __name__ == "__main__":
   app = DemoApp()
   app.MainLoop()

As you can see, in a program this short there's nearly a one-to-one correspondence between the C++ API calls and the wxPython calls. In both cases, you create an application object and a frame object. You also start with an OnInit() method and define similar constructors and object handlers.

The biggest difference you can see in this particular example is in the binding of events to handlers. Where the C++ version manages this binding using the event table macros, the Python version uses the Bind() method, which takes as arguments a Python object representing the event type and the actual method to be called when the event is invoked. This structure takes advantage of Python's ability to treat methods as variables and pass them as arguments in exactly the same way a string or integer would be passed.

The advantages of wxPython over the C++ wxWidgets toolkit begin to show in a longer or more complex program. Even without digressing into a debate about the relative merits of C++ and Python as programming languages, there are still some nice features specific to the wxPython version of the toolkit that may be attractive to you. The event-handling mechanism using the Bind() methods blends more easily into the wxPython version than in wxWidgets. In the Python version, it's easier to update your handlers dynamically at run time. Some complex or compound widgets, such as a tree list control or an image radio button, are standard in the wxPython toolkit but not in the C++ version. Also, wxPython contains the Py package of developer tools, which makes adding interactive debugging to your wxPython program trivial.


wxEverythingElse

Python is not the only programming language to have a binding for accessing the wxWidgets library. Although wxPython is the most mature of the bunch, several others are worth checking out if you prefer working in a particular programming language. Let's take a quick tour through some of the other stops in wxWorld. Please note that the assessments of the stability and health of these projects is an estimate based on available materials. Many of these projects are a labor of love from one or two dedicated programmers. If you're interested in a specific language project, by all means, check it out for yourself.

wxPerl

The wxPerl binding had its last major release in June 2006. It has restarted the delivery of daily snapshots, but the available documentation is a couple of years older. The mailing list is active in the range of two or three messages a day. Binary downloads are available for Win32, Linux, and Mac OS X. In addition to the main toolkit, some extras are available, including an OpenGL wrapper and a packager for creating Mac OS X applications.

The main issue with wxPerl is translating the wxWidgets API into Perl's somewhat idiosyncratic flavor of object-oriented programming (OOP). The snippet shown in Listing 7 is somewhat similar to the frame examples shown above.


Listing 7. A wxPerl window example

package MyFrame;

use base 'Wx::Frame';

use Wx::Event qw(EVT_BUTTON);

sub new {
   my $class = shift;
   my $self = $class->SUPER::new(undef, -1, 'Trying wxPerl',
      [-1, -1], [250, 200]);
   my $sizer = Wx::BoxSizer->new(wxVERTICAL);
   my $button = Wx::Button->new($self, -1, 'Click me!', [-1, -1],
      [-1, -1]);
   EVT_BUTTON($self, $button, \&OnButtonClick);
   $sizer->Add($button);
   $self->SetSizer($sizer);
   return $self;
}

sub OnButtonClick {
   my($self, $event) = @_;
   $self->SetTitle('You Did It');
}

This code is basically a line-by-line translation of the C++ and Python code you've already seen. In this case, the wxWidgets library comes in as a Perl package, and a EVT_BUTTON function call is used, which looks similar to the macro definitions in the C++ version.

wxRuby

The wxRuby project is in something of an awkward state. There is an early tool in which the bindings to the wxWidgets API were created by hand. This tool was last released in November 2004, and since that time, development has continued intermittently on a version that uses the more powerful Simplified Wrapper and Interface Generator (SWIG) toolkit to generate the bindings between Ruby and wxWidgets. The new version was recently described in the mailing list as being ready "Any day now . . . or maybe not for several months."

One interesting thing about wxRuby is that, unlike most other wxWidgets bindings, the developers have chosen to adjust the names of wxWidgets API calls to more closely conform to Ruby naming conventions (specifically, lower_case_with_underscores rather than the wxWidgets UpperCaseWithCamelCase). So, where all the above code examples use the SetSizer() function, wxRuby calls it set_sizer(). Beyond that, again, the portions of a wxRuby program that deal with the wxWidgets API are going to be largely similar to the examples already shown.

wxWidgets world

Other wxWidgets ports are in various stages of completion or incompletion. Here's a tour of the rest of the wxWidgets landscape:

  • wxBasic is both a Basic language interpreter and a set of wxWidgets bindings. It's in the process of a coming out with a new version (the last beta release was in May 2006).
  • wxEuphoria was last released in December 2005 and is a binding for the Euphoria programming language.
  • wxJS is a JavaScript port of wxWidgets. It has been tested only on Windows systems and includes an executable to run the JavaScript scripts. It, too, was last released in May 2006. The developers have announced that the next version will support Linux.
  • wxLua is a binding for the Lua programming language. It's cross-platform, has a relatively small footprint, and its last release was March 2006.
  • The wx.NET project binds C# to wxWidgets. Its last release was July 2005.

Explore the world

The wxWidgets world has a lot to offer programmers of all stripes. The base toolkit is flexible and capable of handling most of your GUI needs, and the various language bindings place wxWidgets within the reach of most programmers. Investigating the wxWidgets toolkit in your language of choice will help you build great-looking interfaces for your own applications.


Resources

Learn

Get products and technologies

  • The main wxWidgets toolkit is available for download from the wxWidgets site.

  • You can download wxPython from the wxPython site.

  • You can get more information about and download wxPerl from Sourceforge.net.

  • For more information and to download wxRuby, visit the wxRuby wiki.

  • More information about bindings for other programming languages are available on their respective home pages:
  • Order the SEK for Linux, a two-DVD set containing the latest IBM trial software for Linux from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.

  • With IBM trial software, available for download directly from developerWorks, build your next development project on Linux.

Discuss

About the author

Noel Rappin has a Ph.D. from the Graphics, Visualization, and Usability Center at the Georgia Institute of Technology and is a senior software engineer at Motorola, Inc. He is also the co-author of wxPython in Action (Manning Publications, March 2006) and Jython Essentials (O'Reilly, March 2002).

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux, Open source
ArticleID=161166
ArticleTitle=Build cross-platform GUIs using wxWidgets
publish-date=09212006
author1-email=noelrappin@gmail.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers