Table Of Contents

Previous topic

Coding

Next topic

Python

This Page

C++ Introduction to wxWidgets

About

wxWidgets is a C++ library that provides an API for writing multi-platform programs. While the main focus is on graphical user interface development using classes such as wxWindow, wxWidgets also provides classes for file, thread, and database access among others. The three main branches of wxWidgets are wxGTK (UNIX), wxMSW (Windows), and wxMac (Mac OS). Other branches such as X11, OS/2, and embedded platforms are available but are not as mature or widespread. Programs written with wxWidgets are able to be recompiled for each supported platform with no changes to the source code.

Programs written using wxWidgets may be licensed under the GPL or the library may be dual licensed using the LGPL. This means that a closed source program using wxWidgets is possible - the wxWidgets library is then under the LGPL, with the closed source part under whatever license the author chooses.

Obtaining

The wxWidgets library is free software licensed under the GNU General Public License. It is available at the wxWidgets website. The library is distributed in source code form. There is a download available for each of the major supported branches, or the source code for all platforms may be downloaded in one archive. Choose the download that is appropriate for your situation. However, if you plan on using the library to develop on multiple platforms then the “All ports combined” archive is ideal. In this guide, the wxGTK-2.6.2.tar.bz2 archive was used.

Compiling

As this guide is hosted on a Linux Wiki, the remaining details will be focused on Linux. Provided that the development environment is set up properly on Windows and Mac OS the rest of this guide will apply. See the Tips section on working with wxWidgets on other platforms.

Extract the archive downloaded, either the single port or the archive of all ports. For end users, it is enough to use the classic ./configure && make && make install method of compiling and installing the library. However, developers typically require a variety of libraries with different configurations. For example, it is a good idea to have debug, standard, and optimized versions of the library on hand. In the case of wxWidgets, it is also good to have custom static versions for particular applications containing the features desired such as ODBC connectivity and SDL support. For an explanation of static verses shared builds and other build options see the Tips section.

The following demonstrates how to compile a build of the library while keeping the source tree clean for other builds. This build will be a static release build.

$ tar jxf wxGTK-2.6.2.tar.bz2 #extract the archive
$ cd wxGTK-2.6.2 # enter the base source directory
$ mkdir build_release_static # create the directory for this build
$ cd build_release_static # enter the build directory
$ ../configure --disable-debug --disable-shared # configure the build

Future builds with different configuration options should be made in a new directory. When the above completes, you should see something similar to the following.

Configured wxWidgets 2.6.2 for i686-pc-linux-gnu

  Which GUI toolkit should wxWidgets use?                 GTK+ 2
  Should wxWidgets be compiled into single library?       no
  Should wxWidgets be compiled in debug mode?             no
  Should wxWidgets be linked as a shared library?         no
  Should wxWidgets be compiled in Unicode mode?           no
  What level of wxWidgets compatibility should be enabled?
                                       wxWidgets 2.2      no
                                       wxWidgets 2.4      yes
  Which libraries should wxWidgets use?
                                       jpeg               sys
                                       png                sys
                                       regex              sys
                                       tiff               sys
                                       zlib               sys
                                       odbc               no
                                       expat              sys
                                       libmspack          no
                                       sdl                no
                                       gnomeprint         no

Whenever you make a new build, check the output of configure to ensure the build will be correct for what you need. More options are available and they are explained by running configure –help. All that remains to do at this point is compile with make. Note that you should not run make install unless you have configured a build you wish to make use of system wide.

Documentation

The wxWidgets API is thoroughly documented. It is available for download in a variety of formats and also online. In particular, the alphabetical class reference and alphabetical function reference are very useful. The documentation covers all of the available classes, methods for those classes, and utility functions provided by other files. Additionally, there are more in depth guides included to explain concepts such as sizers and tips to increase program portability.

Programming

As with many other toolkits, wxWidgets programs do not begin with a main function. Instead, main is implemented in the library itself and the application programmer takes over processing using the wxApp class. This is accomplished by creating a class which inherits from the wxApp class and implements the wxApp method OnInit. Typically in a small program the body of the OnInit implementation simply creates the application window, shows it, and returns true to indicate successful initialization.

Most wxWidgets programs center on two things. The first is building windows, and the second is interacting with those windows through event tables. Window construction is handled by constructing additional wxWidgets objects such as buttons and input boxes, then laying out those object using sizers. Programmers familiar with Java will equate sizers to Java constructs such as the GridBagLayout and others. Basically sizers are a way of describing where items in a window should be displayed and how their positions and sizes change with the window’s size. A more in depth discourse in sizers is available in the manual.

Event handling in wxWidgets is controlled using event tables. These tables are collections of C++ macros intended to reduce the work on the part of the programmer. This reduction is accomplished by reducing the amount of code written by the application programmer to link an event generated by an object to a function to execute when that event is generated.

Below is a sample program which demonstrates all of the above concepts. It is a skeleton program that can be compiled and run with a functioning wxWidgets library. To compile this program, save the skel.h and skel.cpp files in a directory, then execute the following commands:

$ g++ skel.cpp `/path/to/wxGTK-2.6.2/build_release_static/wx-config --cppflags` -c
$ g++ skel.o `/path/to/wxGTK-2.6.2/build_release_static/wx-config --libs` -o skel
$ ./skel

The application has a wxFrame as its top level window. A wxFrame is a window which can contain items such as a menu bar and a status bar. Another window type is the wxPanel, whose explicit purpose is to contain controls such as buttons, text boxes, and drop down menus. In the OnInit method of the application class a mainWindow (wxFrame) is declared. The constructor of the mainWindow class then creates some menus, a status bar, and a wxPanel containing a wxTextCtrl.

The last remaining note about wxWidgets specific code is that string literals are enclosed in a macro, _T(“”) or wxT(“”). This macro ensures that string literals are processed correctly on both ASCII and unicode systems. For more small wxWidgets programs, many of which are well documented, see the samples directory provided with the wxWidgets library source code.

// File: skel.h

#ifndef __SKELH__
#define __SKELH__

// Begin definition of the application class

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

// End definition of the application class

// Begin definition of the main window class

class mainWindow : public wxFrame
{
  public:
    // Constructor
    mainWindow();

    // File menu event handlers
    void OnNew(wxCommandEvent &event);
    void OnOpen(wxCommandEvent &event);
    void OnSaveAs(wxCommandEvent &event);
    void OnExit(wxCommandEvent &event);

    // Help menu event handlers
    void OnHelp(wxCommandEvent &event);
    void OnAbout(wxCommandEvent &event);

    // Window close button event handler
    void OnClose(wxCloseEvent &event);

    // Helpers
    bool Exit();

  // Macro that defines the event table for this class
  DECLARE_EVENT_TABLE()
};

// Example custom event identifiers
// enum
// {
//   SKEL_ENUM = wxID_HIGHEST+1,
//   SKEL_ENUM_2
// };

// End definition of the main window class

#endif

// End File: skel.h
// File: skel.cpp

#include <wxprec.h>

#ifdef __BORLANDC__
  #pragma hdrstop
#endif

#ifndef WX_PRECOMP
  #include
#endif

#include "skel.h"

// Begin implementation of the application class

IMPLEMENT_APP(skel)

bool skel::OnInit()
{
  // Create the main window and show it
  mainWindow *mw = new mainWindow();
  mw->Show(TRUE);
  return TRUE;
}

// End implementation of the application class

// Begin implementation of the main window class

// Macros that implement the event table for this class
BEGIN_EVENT_TABLE(mainWindow, wxFrame)

  // File menu
  EVT_MENU (wxID_NEW,           mainWindow::OnNew)
  EVT_MENU (wxID_OPEN,          mainWindow::OnOpen)
  EVT_MENU (wxID_SAVEAS,        mainWindow::OnSaveAs)
  EVT_MENU (wxID_EXIT,          mainWindow::OnExit)

  // Help menu
  EVT_MENU (wxID_HELP,          mainWindow::OnHelp)
  EVT_MENU (wxID_ABOUT,         mainWindow::OnAbout)

  // Window close button
  EVT_CLOSE(                    mainWindow::OnClose)
END_EVENT_TABLE()

mainWindow::mainWindow()
  : wxFrame(NULL, -1, _T("skel"))
{
#if wxUSE_MENUS
  // how to add a submenu
  // mainFile->Append(SOME_ID, _T("Some Menu"), some_wxMenu);

  // File menu
  wxMenu *mainFile = new wxMenu;
  mainFile->Append(wxID_NEW, _T("New    Ctrl-N"), _T("New File"));
  mainFile->Append(wxID_OPEN, _T("Open...    Ctrl-O"), _T("Open File"));
  mainFile->AppendSeparator();
  mainFile->Append(wxID_SAVEAS, _T("Save as..."),
                   _T("Save with a new filename"));
  mainFile->AppendSeparator();
  mainFile->Append(wxID_EXIT, _T("Exit    Ctrl-Q"), _T("Exit skel"));

  // Help menu
  wxMenu *mainHelp = new wxMenu;
  mainHelp->Append(wxID_HELP, _T("Online Help..."), _T("skel help"));
  mainHelp->Append(wxID_ABOUT, _T("About..."), _T("About skel"));

  // Create the main menu bar
  wxMenuBar *mainMenuBar = new wxMenuBar();
  mainMenuBar->Append(mainFile, _T("&File"));
  mainMenuBar->Append(mainHelp, _T("&Help"));

  SetMenuBar(mainMenuBar);
#endif

#if wxUSE_STATUSBAR
  CreateStatusBar();
  SetStatusText(_T("Welcome to skel"));
#endif

  // create member windows
  wxPanel *mainPanel = new wxPanel(this, -1);
  wxTextCtrl *mainTextCtrl =
    new wxTextCtrl(mainPanel, -1, _T("Sample"), wxDefaultPosition,
                   wxDefaultSize, wxTE_MULTILINE);

  // Size the panel
  wxBoxSizer *mainPanelSizer = new wxBoxSizer(wxVERTICAL);
  mainPanelSizer->Add(mainTextCtrl, 1, wxEXPAND);
  mainPanel->SetSizer(mainPanelSizer);
  mainPanelSizer->SetSizeHints(mainPanel);

  // Size the Frame
  wxBoxSizer *mainFrameSizer = new wxBoxSizer(wxVERTICAL);
  mainFrameSizer->SetMinSize(wxSize(300,200));
  mainFrameSizer->Add(mainPanel, 1, wxEXPAND);
  SetSizer(mainFrameSizer);
  mainFrameSizer->SetSizeHints(this);

  // Send the signal for all sizers to readjust themselves
  mainFrameSizer->Fit(this);
}

// File menu

void mainWindow::OnNew(wxCommandEvent &event)
{

}

void mainWindow::OnOpen(wxCommandEvent &event)
{
  wxFileDialog dlg(this, _T("Choose a file"), _T(""),
                   _T("DefaultFile"),
                   _T("All Files (*.*)|*.*|Text Files (*.txt)|*.txt"),
                   wxOPEN | wxFILE_MUST_EXIST);

  dlg.ShowModal();
}

void mainWindow::OnSaveAs(wxCommandEvent &event)
{
  wxFileDialog dlg(this, _T("Save As"), _T(""), _T("DefaultFile"),
                   _T("All Files (*.*)|*.*|Text Files (*.txt)|*.txt"),
                   wxSAVE | wxOVERWRITE_PROMPT);

  dlg.ShowModal();
}

void mainWindow::OnExit(wxCommandEvent &event)
{
  if(Exit())
    Destroy();
}

// Help menu

void mainWindow::OnHelp(wxCommandEvent &event)
{

}

void mainWindow::OnAbout(wxCommandEvent &event)
{
  wxMessageDialog dlg(this, _T("About skel"), _T("skel"), wxOK);
  dlg.CenterOnParent();
  dlg.ShowModal();
}

// Window close button

void mainWindow::OnClose(wxCloseEvent &event)
{
  if(Exit())
    Destroy();
}

bool mainWindow::Exit()
{
  wxMessageDialog dlg(this, _T("Are you sure?"), _T("Exit skel?"), wxOK | wxCANCEL);
  dlg.CenterOnParent();
  return dlg.ShowModal() == wxID_OK;
}

// End implementation of the main window class

// End File: skel.cpp

Distribution

If the application developed is distributed in source code form, simply make sure that it can be compiled with the classic ./configure && make && make install. Set your configure script up so that it is possible to point the build to a particular wx-config. This will make it easier to test with different wxWidgets builds, and provide end users a little more freedom when compiling.

When packaging binary distributions of your program there are a few things to take into consideration. One is whether you expect end users to have the wxWidgets library installed. If so, create a version of your program which links dynamically to a shared wxWidgets library. If not, compile wxWidgets statically in and require the user to have a compatible version of GTK installed. Either way, if program size is an issue upx is a must for binary compression.

Tips

Memory management has been simplified somewhat. It is rare that delete will need to be called on a wxWidgets object, especially if that object is a type of window or control. When a window is destroyed it also destroys all of its child windows.

Most users will not have wxWidgets installed as a base system library. For that reason, learning to create statically included libraries for applications is a must. Including the library statically means that a copy of wxWidgets is embedded as a literal part of the application binary. It is difficult to provide a binary which will work for everyone, therefore if the application is open source let the distribution maintainers and package creators worry about this. Let the rest compile from source with their own options.

When working on Mac OS X, install the Apple developer tools and be sure to statically compile wxWidgets into your program. Mac OS isn’t nearly the moving target Linux is for producing binaries. Be aware that version compatibility can be tricky and even small changes in the Mac OS X version number may break the binary. Additionally, end users will expect .app packages instead of just UNIX binaries and creating those requires adding more build commands to the build process. See the Mac section of the wxWidgets wiki for more information.

When working on Windows, MinGW and MSYS are very useful. Additionally, installing the MSYS developer’s toolkit will provide the GNU auto-tools. When MSYS is run through its rxvt terminal emulator, this guide may be followed on Windows in its entirety with one significant exception: to create a static library for static binaries, build wxWidgets without thread support. Otherwise the resulting library will depend on an MSYS DLL and fail to run on other systems.

Having covered all three major supported platforms, it is easy to see how standardizing on GNU tools makes sense. On the other hand, wxWidgets itself relies on ‘bakefiles’ which produce native build files for the library on whatever platform and compiler being used. Bakefiles are XML files containing generic build instructions. Using them, it is possible to then produce a UNIX makefile and a Microsoft Visual Studio project or whatever else system a user might be running.

Cross Compiling Windows Applications

Using the MingGW cross compiler and open source tools associated with the wine project it is possible to compile Windows applications on Linux. This may aid in simplifying development of wxWidgets applications. These two wxWidgets wiki articles describe the process with older versions of the software. The process has been verified to work with the newer versions: binutils 2.16.1, gcc 3.3, mingw runtime 3.9 (i386 precompiled), w32api 3.6 (i386 precompiled), and wxWidgets 2.6.2. Compilation was accomplished with GCC 3.3.6.

When you arrive at the step involving the configuration and compilation of the wxWidgets library, the configure command should look similar to the following code. Note that the archive used should be the all ports combined tar.bz2/gz and not wxMSW-version.zip, otherwise there may be permission problems with the files and UNIX compilation may not be possible.

$ mkdir build_release_static
$ cd build_release_static
$ # configure command should be all on one line
$ ../configure --prefix=/usr/local/i386-mingw32 --host=i386-mingw32
  --target=i386-mingw32 --with-msw --disable-debug --disable-shared
  --enable-monolithic --disable-threads
$ make
$ make install # don't run this without reading below

Again, do not run make install unless you have settled on a configuration to make available ‘system’ wide (system this time meaning the i386-mingw cross compiling environment). This is not as important as when dealing with a native library (wxGTK) since it will not have any impact on your Linux system. The rest of this section assumes you have settled on a configuration to install, but if not, direct your configure script to wherever the cross /path/to/build_release_static/wx-config is located.

Once compilation is finished simply point your own application’s configure script to /usr/local/i386-mingw32/bin/wx-config to produce a Windows binary (use that wx-config and i386-mingw32-g++ if not using a configure script). Testing applications can be performed using wine.