Skip to main content

Looking through wxWindows

An intro to the portable C++ and Python GUI toolkit

Summary:  Markus Neifer gives an overview of wxWindows, the portable C++ and Python GUI toolkit. He discusses the library's architecture, talks about how to deal with multi-platform file handling and the directory separation character, and touches on wxHTML, image file formats, and Unicode. He also walks you through some helpful wxWindows debugging tips and talks a bit about porting MFC applications to Linux.

Date:  01 Feb 2001
Level:  Intermediate
Activity:  3549 views

The wxWindows library, whether or not it's compiled as a Dynamic Link Library (DLL), makes it possible to have very small executives. It also offers various goodies for multi-platform development: you get an OpenGL interface and built-in support for HTML, Unicode, and internationalization. And it helps you to port applications from Windows-only MFC (Microsoft Foundation Classes) to Linux, for example. One of the main goals of wxWindows is to run on as many platforms as possible, so it supports nearly every C++ compiler available. It does not yet use all features of Standard C++ (such as namespaces, std::string class, and STL container). But Standard C++ is on the to-do list and there's already some support for the new cast syntax and std::string.

A bit of history

Julian Smart started wxWindows in 1992 at the Artificial Intelligence Applications Institute, University of Edinburgh. In 1995 Markus Holzem released his port of wxWindows to Xt, the X toolkit. After a brief period of stagnation, in May 1997 the Windows and GTK+ ports were merged and put into a CVS repository made available to all contributors to wxWindows. Towards the end of '97, Julian Smart started distributing a CD-ROM of wxWindows, including the complete source, compiler stuff, and much more.

wxWindows is now released under the GPL, with one exception: You can distribute binary executables without the source code. That_s a nice option for commercial projects. It's currently available for various flavors of UNIX and Microsoft Windows, and for Macintosh OS. Ports to OS/2 and other operating systems are under development. And now, let's get to the meaty stuff....


Platforms to go

wxWindows takes a different approach to widgets than most other multi-platform GUI libraries because it uses the native controls whenever possible. Controls that are not available, like tree controls under UNIX, are emulated. This will give the user of your application the familiar look and feel. The wxWindows library currently supports the following platforms:

  • Windows 3.1, Windows 95/98, Windows NT
  • Most UNIX variants with Motif/Lesstif
  • Most UNIX variants with GTK+
  • Mac

The wxBase library with only the non-GUI classes can also be built under UNIX/Win32 and (with some limitation) BeOS. And even if you're not compiling wxWindows as a DLL, you can get very small executables. For example, the minimal sample application compiled with Microsoft Visual C++ for the Windows platform is smaller than 400 KByte. And because of wxWindows' small executable sizes, you can usually avoid the so-called "DLL hell".

And now, onto the multi-platform goodies...


Architecture-independent types

In order to avoid architecture dependencies, the library offers various architecture-independent types and macros that handle bit-swapping with respect to the applications endianness. These are:

  • wxInt32 (32-bit signed integer)
  • wxInt16 (16-bit signed integer)
  • wxInt8 (8-bit signed integer)
  • wxUint32 (32-bit unsigned integer)
  • wxUint16 = wxWord (16-bit unsigned integer)
  • wxUint8 = wxByte (8-bit unsigned integer)

The bit-swapping macros are available for integer and unsigned integer (where xx stands for 16 or 32, BE for big endian, and LE for little endian).

  • wxINTxx_SWAP_ON_BE()
  • wxUINTxx_SWAP_ON_BE()
  • wxINTxx_SWAP_ON_LE()
  • wxUINTxx_SWAP_ON_LE()
  • wxINTxx_SWAP_ALWAYS()
  • wxUINTxx_SWAP_ALWAYS()

Usage is straightforward here, as the following example shows:

wxInt32 old_var = 0xF1F2F3F4;
wxInt32 new_var = wxINT32_SWAP_ALWAYS( old_var )

Besides these macros, wxWindows offers #defines to determine the current endianness of the machine the library was compiled on. An example of this might be:

if ( wxBYTE_ORDER == wxLITTLE_ENDIAN )
{
    // Do stuff for little endian machine...
}
else
{
    // Do stuff for big endian machine...
}


File handling

It's always difficult to write for different platforms that have different concepts of file storage. To overcome this issue, wxWindows has functions for multi-platform file handling. First let's look at some functions for the basic file operations like copy, remove, and rename.

wxString old_report = "smithers_00.doc"
wxString new_report = "my_smithers.doc";
if ( wxCopyFile( old_report, "smithers_00.bak" ) == true )
{
    if ( wxRemoveFile( old_report ) == true )
    {
        if ( wxRenameFile( new_report, old_report ) == false )
        {
            // Doh!
        }
    }
}

Another serious problem posed by writing for different platforms is the directory separation character, which you can avoid altogether with the help of the wxPathList class. wxPathList contains a list of directories to search for files. If you want to look for a file, you just pass the filename to the wxPathList class and it'll search the predefined directories.

wxPathList path_list;

// Add current working directory
path_list.Add( "." );

// Add one directory above current working directory
path_list.Add( ".." );

// Add directories from environment variable PATH to the list
path_list.AddEnvList( "PATH" );

wxString path = path_list.FindValidPath( "homer.bmp" );

Two other useful functions in wxWindows are the wxFileNameFromPath(), which strips a filename from a full path and wxPathOnly(), which strips the path from a full path.


HTML

Vaclav Slavik's wxHTML library parses and renders basic HTML. It doesn't fully implement the HTML standard, but its functionality is good enough to handle online help and can be extended using tag handlers. To display HTML you create an object of class wxHtmlWindow. You then call its methods to set the related frame to actually display the HTML and the related status bar to display messages generated by the HTML parser.

wxHtmlWindow html_window = new wxHtmlWindow( this );
html_window->SetRelatedFrame( this, "HTML : %%s" );
html_window->SetRelatedStatusBar( 0 );

After this, you can load an HTML page by using:

html_window->LoadPage( "burns.htm" );

Or you can display HTML code by using

html_window->SetPage( "<html><body>Hello, Monty!</body></html>" );


Images

The wxImage class loads various image file formats using image format handlers. You can extend wxImage to load a new image format by implementing your own image format handler. The existing image format handlers use well known libraries like Sam Leffler's libTIFF library or the Independent JPEG Group's JPEG library. There are handlers for BMP, PNG, JPEG, GIF, PCX, PNM, and TIFF. You can activate each image format handler by using the wxImage class' static method AddHandler() in your application startup code.

bool MyApp::OnInit()
{
    wxImage::AddHandler( new wxPNGHandler );
    // more ...
}

To use all existing image format handlers, simply call function wxInitAllImageHandlers() instead of the AddHandler() method shown above.

Take extra care when using toolbar bitmaps in your application on different platforms. On Windows you'll use the Windows bitmap format, but Linux bitmaps are usually pixmaps, in which case conditional compilation cannot be completely avoided. Let's have a look at some sample code.

#if defined(__WXGTK__) || defined(__WXMOTIF__)
    #include "maggie.xpm"
#endif

// more ...

void MyApp::UseBitmap()
{
    wxBitmap bitmap( wxBITMAP( maggie ));

    // more ...
}

Get the idea? All the magic is done by the wxBITMAP() macro. For Windows and OS/2 it will create a wxBitmap object using a bitmap called 'maggie' from the application resources. For all other platforms it will create a wxBitmap object using a pixmap called 'maggie_xpm'.

While a bitmap can be drawn in a device context using wxDC::DrawBitmap(), you have to use a wxImage object for image manipulations, as shown below.

wxImage* p_image = new wxImage( bitmap );
		
// Have some fun
if ( p_image->Ok() )
{
    if ( p_image->GetHeight() > 50 && p_image->GetWidth() > 50 )
    {
        unsigned char red   = p_image->GetRed( 50, 50 ); 
        unsigned char green = p_image->GetGreen( 50, 50 ); 
        unsigned char blue  = p_image->GetBlue( 50, 50 );

        // Secure but might be slow
        p_image->SetRGB( 50, 50, red, green, blue );

        // If you want fast action use a pointer...
        unsigned char* data = p_image->GetData();

        // Manipulate the data...
    }
}


Unicode and internationalization (i18n)

When developing software for the international market, you can't expect everybody to be able to read your application messages in English. But the widely used ANSI code can't handle all language symbols. (It does not, for example, cover Chinese.) And writing for both ANSI and Unicode can get a bit complex, as you can see from the example below.

#ifdef USE__UNICODE
    wchar_t wide_char = L'h';
    wchar_t const* wide_string = L"Hello, World!";
    int length = wcslen( wide_string );
#else
    char ansi_char = 'h';
    char const* ansi_string = "Hello, World!";
    int length = strlen( ansi_string );
#endif

So, using wxWindows Unicode capabilities you'll simply write the following:

wxChar wx_char = wxT( '*' );
wxString wx_string = wxT( "Hello, World!" );
int length = wx_string.Len();

The wxT() macro and the wxChar and wxString classes will take care of everything. The library uses this approach internally for all messages. There are currently translations for Czech, Danish, German, French, Italian, and Russian available, but you can compile a localized version of the library and use it with some help from the wxLocale class.


Debugging

The library offers various classes, functions, and macros to help you debug your application. If you compile the library in debug mode, the new and delete operators for class wxObject (the base class for most classes in wxWindows) have been redefined to store additional information about objects allocated on the heap. Using this information, the class wxDebugContext can be used to get detailed information about object allocation, memory leaks, overwrites, and underwrites.

// Start logging for Dump() call
wxDebugContext::SetCheckpoint();

wxString *thing = new wxString;
wxDate* date = new wxDate;

// non-object allocation
char *ordinaryNonObject = new char[1000];

// more ...

// Print number of object and non-object allocations
wxDebugContext::Dump();

// Print allocation statistics
wxDebugContext::PrintStatistics();

A call to wxDebugContext::Dump() will pop up a window containing a list of allocations. Below you'll find such a list I've created using the memcheck sample provided with wxWindows.

13:32:45: ----- Memory dump of memcheck at Tue Dec 26 13:32:45 2000 -----
13:32:45: ..\..\..\samples\memcheck\memcheck.cpp(88): 
   non-object data at $DD3DC0, size 4
13:32:45: ..\..\..\samples\memcheck\memcheck.cpp(89): 
   wxDate at $DD40D0, size 24
13:32:45: ..\..\..\samples\memcheck\memcheck.cpp(92): 
   non-object data at $DD4118, size 1000

A call to wxDebugContext::PrintStatistics() will give you the statistics you see below.

13:32:45: ----- Memory statistics of memcheck at Tue Dec 26 13:32:45 2000 -----
13:32:45: 1 objects of class wxDate, total size 24
13:32:45: 5 objects of class nonobject, total size 4256
13:32:45: 
13:32:45: Number of object items: 1
13:32:45: Number of non-object items: 5
13:32:45: Total allocated size: 4280


Porting MFC apps to Linux

The wxWindows library can also port MFC applications to Linux and other operating systems. As you can see in the following code snippet, the wxWindows string class wxString has some similarities to the MFC string class CString.

wxString s1 = "Hello, World!";
wxString s2 = "Hello";

if ( s1.IsEmpty() == false )
{
    s2.Empty();

    s2 = s1.Left( 5 );

    int pos = s1.Find( ',' );
    s2 += s1.Mid( pos, 2 );

    s2 += s1.Right( 6 );
}

wxWindows' event system also closely resembles MFC, in which message maps map event handler methods to the event system. In wxWindows, these are called event tables. The event tables macros differ very slightly from MFC's message mapping. The main differences are illustrated in the source code below.

class CButtonCtrl : public COleControl
{
// Implementation
protected:
    LRESULT OnOcmCommand( WPARAM wParam, LPARAM lParam );

    DECLARE_MESSAGE_MAP()
};

BEGIN_MESSAGE_MAP( CButtonCtrl, COleControl )
    //{{AFX_MSG_MAP( CButtonCtrl )
    ON_MESSAGE( OCM_COMMAND, OnOcmCommand )
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

class MyButton : public wxButton
{
    void OnButton( wxMouseEvent& event )

private:
    DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE( MyButton, wxButton )
    EVT_BUTTON( -1, MyButton::OnButton )
END_EVENT_TABLE()

As you can see, it's not that different. There are already quite a few people on the mailing list who have successfully ported their existing MFC applications to wxWindows (and are more than happy to help with yours:).


Standard C++

wxWindows doesn't yet use Standard C++ techniques (like std::string, STL, or namespaces) because it would drastically reduce the number of platforms on which wxWindows compiles. (Very few compilers fully support the latest features of Standard C++.) But as Standard C++ becomes more universally supported, the wxWindows development team will be integrating support for Standard C++ in its library. Let's have a look at some examples of the work they're doing.

class A {
    virtual void foo() {};
};
class B : public A {};

A* p_A = new B();
B* p_B = dynamic_cast<b*>( p_A );

If things go wrong here, p_B will contain a zero-pointer. In wxWindows, you'll find a macro-based system for run-time type information, but for the Standard C++ code above you have to use the wxDynamicCast() macro like this:

B* p_B = wxDynamicCast( p_A, B );

If the cast fails, p_B contains a zero-pointer. For implementations that support the new cast syntax, the macro expands to dynamic_cast<>. But unfortunately the macro only works for pointers and not for references. Besides the wxDynamicCast() macro, there are also the wxStaticCast() and wxConstCast() macros.

The wxWindows string class wxString provides 90% of the methods of the standard string class. Here are some examples:

wxString s1 = "Hello, World!";
wxString s2 = "Hello";

s2.erase();

for ( size_t i = 0; i < s1.length(); ++i )
    s2 += s1[i];

if ( s1.compare( s2 ) == 0 ) {
    // Strings are equal
}

Note that if you typedefwxString as std::string here, you can write for both wxWindows and Standard C++.


Documentation

The wxWindows documentation is by no means perfect at this point. While "old" classes like wxString are well documented, the description for recently implemented classes like wxGrid or more obscure classes like wxGLCanvas could be better. The main documentation, which offers a quick introduction to the library and its concepts, an alphabetical class reference, programming strategies, topic overviews, and some notes for wxHTML and wxPython, is available as HTML, WinHelp, MS HTML Help, and PDF (see Resources).

If you're new to wxWindows you should start with the topic overviews. They provide a lot of basic information and code samples about common topics like debugging, event handling, printing, etc. The main documentation also contains a few technical notes and tutorials, which provide information on topics ranging from the more common questions about learning wxWindows to compiler specific questions. You'll also get a bunch of plain text files containing installation and release notes for supported platforms. The wxWindows distribution also provides about 50 sample applications to go along with the documentation.


Support

What if you have any problems using the library and you can't find an answer in the documentation? Don't worry. In general you have two support options: free support using the mailing list, or commercial support. Unfortunately, if you need a quick answer you're probably better off using the commercial support. While the core developers always have one eye on the mailing list, they're often too busy to respond immediately. But you can usually expect an answer within a day or so. (If you're asking something unusual or complex, you should wait at least two days before reposting.) You can access the latest source from the CVS source code database at Sourceforge (see Resources).


Wrapup

You should now have a good idea of what wxWindows is all about and what it offers for multi-platform development. But of course there's always more. Take for example the current wxStudio project, which is developing a Microsoft Visual Studio like IDE using wxWindows. Or the wxCVS project, which will be a multi-platform graphical interface for the CVS system. Or wxDesigner, an RAD tool developed by Robert Roebling to build wxWindows dialogs. As you can see, the wxWindows community is growing, so take a look at it the next time you have a multi-platform project in the wings.

All product names mentioned are the trademarks or registered trademarks of their respective owners.


Resources

About the authors

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

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
ArticleID=11073
ArticleTitle=Looking through wxWindows
publish-date=02012001

My developerWorks community

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.

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).

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).

Rate a product. Write a review.

Special offers