wxWidgets Cheat Sheet
Overview
wxWidgets is a free, open-source C++ library for building cross-platform graphical user interfaces. Unlike frameworks that draw their own widgets, wxWidgets uses each platform’s native GUI toolkit — Win32 on Windows, Cocoa on macOS, and GTK on Linux — resulting in applications that look and feel truly native on each operating system. The library has been actively developed since 1992, making it one of the most mature cross-platform GUI frameworks available.
wxWidgets provides an extensive set of controls, dialogs, device contexts for drawing, printing support, sockets, threads, clipboard access, drag-and-drop, and database connectivity. It supports both frame-based and dialog-based applications, MDI (multiple document interface), and custom widget development. The library is used in production by notable applications and supports C++17 and later standards. Language bindings exist for Python (wxPython), Perl, Ruby, and other languages.
Installation
# macOS
brew install wxwidgets
# Ubuntu/Debian
sudo apt install libwxgtk3.2-dev wx-common wx3.2-headers
# Fedora
sudo dnf install wxGTK3-devel
# Arch Linux
sudo pacman -S wxwidgets-gtk3
# Windows (vcpkg)
vcpkg install wxwidgets
# Build from source
git clone https://github.com/wxWidgets/wxWidgets.git
cd wxWidgets
git submodule update --init
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j$(nproc)
sudo cmake --install .
# Verify installation
wx-config --version
wx-config --cxxflags
wx-config --libs
Basic Application
// main.cpp
#include <wx/wx.h>
class MyApp : public wxApp
{
public:
bool OnInit() override;
};
class MyFrame : public wxFrame
{
public:
MyFrame();
private:
void OnHello(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
};
// Event table
enum {
ID_Hello = 1
};
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(ID_Hello, MyFrame::OnHello)
EVT_MENU(wxID_EXIT, MyFrame::OnExit)
EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
wxEND_EVENT_TABLE()
wxIMPLEMENT_APP(MyApp);
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show(true);
return true;
}
MyFrame::MyFrame()
: wxFrame(nullptr, wxID_ANY, "Hello wxWidgets", wxDefaultPosition, wxSize(800, 600))
{
// Menu bar
wxMenu* menuFile = new wxMenu;
menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", "Say hello");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT);
wxMenu* menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
// Status bar
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");
// Center on screen
Centre();
}
void MyFrame::OnHello(wxCommandEvent& event)
{
wxLogMessage("Hello from wxWidgets!");
}
void MyFrame::OnExit(wxCommandEvent& event)
{
Close(true);
}
void MyFrame::OnAbout(wxCommandEvent& event)
{
wxMessageBox("wxWidgets sample application",
"About", wxOK | wxICON_INFORMATION);
}
Common Controls
| Control | Description |
|---|---|
wxStaticText | Label / static text |
wxTextCtrl | Text input (single/multi line) |
wxButton | Push button |
wxToggleButton | Toggle button |
wxCheckBox | Checkbox |
wxRadioButton | Radio button |
wxRadioBox | Radio button group |
wxChoice / wxComboBox | Dropdown selection |
wxListBox | List selection |
wxListCtrl | Multi-column list |
wxTreeCtrl | Hierarchical tree |
wxSlider | Value slider |
wxSpinCtrl | Numeric spinner |
wxGauge | Progress bar |
wxStaticBitmap | Image display |
wxNotebook | Tabbed pages |
wxSplitterWindow | Resizable split panes |
wxGrid | Spreadsheet-like grid |
Building a Form
MyFrame::MyFrame()
: wxFrame(nullptr, wxID_ANY, "Registration", wxDefaultPosition, wxSize(400, 500))
{
wxPanel* panel = new wxPanel(this);
wxBoxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
// Title
wxStaticText* title = new wxStaticText(panel, wxID_ANY, "Registration Form");
wxFont titleFont = title->GetFont();
titleFont.SetPointSize(18);
titleFont.SetWeight(wxFONTWEIGHT_BOLD);
title->SetFont(titleFont);
mainSizer->Add(title, 0, wxALL | wxALIGN_CENTER, 15);
// Form grid
wxFlexGridSizer* formSizer = new wxFlexGridSizer(2, 10, 10);
formSizer->AddGrowableCol(1, 1);
// Name field
formSizer->Add(new wxStaticText(panel, wxID_ANY, "Name:"), 0, wxALIGN_CENTER_VERTICAL);
m_nameCtrl = new wxTextCtrl(panel, wxID_ANY);
formSizer->Add(m_nameCtrl, 1, wxEXPAND);
// Email field
formSizer->Add(new wxStaticText(panel, wxID_ANY, "Email:"), 0, wxALIGN_CENTER_VERTICAL);
m_emailCtrl = new wxTextCtrl(panel, wxID_ANY);
formSizer->Add(m_emailCtrl, 1, wxEXPAND);
// Password field
formSizer->Add(new wxStaticText(panel, wxID_ANY, "Password:"), 0, wxALIGN_CENTER_VERTICAL);
m_passwordCtrl = new wxTextCtrl(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD);
formSizer->Add(m_passwordCtrl, 1, wxEXPAND);
// Country dropdown
formSizer->Add(new wxStaticText(panel, wxID_ANY, "Country:"), 0, wxALIGN_CENTER_VERTICAL);
wxArrayString countries;
countries.Add("United States");
countries.Add("United Kingdom");
countries.Add("Germany");
countries.Add("Japan");
m_countryChoice = new wxChoice(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, countries);
m_countryChoice->SetSelection(0);
formSizer->Add(m_countryChoice, 1, wxEXPAND);
mainSizer->Add(formSizer, 0, wxEXPAND | wxALL, 15);
// Terms checkbox
m_termsCheck = new wxCheckBox(panel, wxID_ANY, "I agree to the terms");
mainSizer->Add(m_termsCheck, 0, wxLEFT | wxBOTTOM, 15);
// Buttons
wxBoxSizer* btnSizer = new wxBoxSizer(wxHORIZONTAL);
btnSizer->AddStretchSpacer();
wxButton* cancelBtn = new wxButton(panel, wxID_CANCEL, "Cancel");
wxButton* submitBtn = new wxButton(panel, wxID_OK, "Submit");
btnSizer->Add(cancelBtn, 0, wxRIGHT, 10);
btnSizer->Add(submitBtn, 0);
mainSizer->Add(btnSizer, 0, wxEXPAND | wxALL, 15);
panel->SetSizer(mainSizer);
// Bind events
submitBtn->Bind(wxEVT_BUTTON, &MyFrame::OnSubmit, this);
cancelBtn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { Close(); });
}
Sizers (Layout Management)
// wxBoxSizer - horizontal or vertical
wxBoxSizer* vSizer = new wxBoxSizer(wxVERTICAL);
vSizer->Add(ctrl1, 0, wxALL, 5); // Fixed size, 5px margin
vSizer->Add(ctrl2, 1, wxEXPAND | wxALL, 5); // Expandable
vSizer->AddStretchSpacer(); // Flexible space
// wxFlexGridSizer - flexible grid
wxFlexGridSizer* grid = new wxFlexGridSizer(2, 5, 5); // 2 cols, 5px gaps
grid->AddGrowableCol(1, 1); // Column 1 grows
grid->Add(label, 0, wxALIGN_CENTER_VERTICAL);
grid->Add(input, 1, wxEXPAND);
// wxGridBagSizer - precise grid placement
wxGridBagSizer* bag = new wxGridBagSizer(5, 5);
bag->Add(title, wxGBPosition(0, 0), wxGBSpan(1, 2), wxEXPAND);
bag->Add(label, wxGBPosition(1, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
bag->Add(input, wxGBPosition(1, 1), wxDefaultSpan, wxEXPAND);
// wxStaticBoxSizer - labeled group
wxStaticBoxSizer* group = new wxStaticBoxSizer(wxVERTICAL, panel, "Settings");
group->Add(checkbox1, 0, wxALL, 5);
group->Add(checkbox2, 0, wxALL, 5);
// Apply sizer
panel->SetSizer(mainSizer);
mainSizer->Fit(this); // Resize window to fit content
Event Handling
// Method 1: Bind (modern, preferred)
button->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClick, this);
textCtrl->Bind(wxEVT_TEXT, &MyFrame::OnTextChanged, this);
// Lambda binding
button->Bind(wxEVT_BUTTON, [this](wxCommandEvent& event) {
wxMessageBox("Button clicked!");
});
// Method 2: Event table (traditional)
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_BUTTON(ID_MyButton, MyFrame::OnButtonClick)
EVT_TEXT(ID_MyText, MyFrame::OnTextChanged)
EVT_CLOSE(MyFrame::OnClose)
EVT_SIZE(MyFrame::OnSize)
EVT_TIMER(ID_Timer, MyFrame::OnTimer)
wxEND_EVENT_TABLE()
// Common events
void MyFrame::OnButtonClick(wxCommandEvent& event) { }
void MyFrame::OnTextChanged(wxCommandEvent& event) {
wxString text = event.GetString();
}
void MyFrame::OnClose(wxCloseEvent& event) {
if (wxMessageBox("Quit?", "Confirm", wxYES_NO) == wxYES)
Destroy();
else
event.Veto();
}
void MyFrame::OnSize(wxSizeEvent& event) {
wxSize size = event.GetSize();
event.Skip(); // Allow default processing
}
Dialogs
// Message box
wxMessageBox("Operation complete!", "Success", wxOK | wxICON_INFORMATION);
int result = wxMessageBox("Save changes?", "Confirm",
wxYES_NO | wxCANCEL | wxICON_QUESTION);
if (result == wxYES) { /* save */ }
// File dialog
wxFileDialog openDialog(this, "Open File", "", "",
"Text files (*.txt)|*.txt|All files (*.*)|*.*",
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openDialog.ShowModal() == wxID_OK) {
wxString path = openDialog.GetPath();
}
// Save dialog
wxFileDialog saveDialog(this, "Save File", "", "document.txt",
"Text files (*.txt)|*.txt", wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
// Directory dialog
wxDirDialog dirDialog(this, "Choose directory", "",
wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
// Color picker
wxColourDialog colorDialog(this);
if (colorDialog.ShowModal() == wxID_OK) {
wxColour color = colorDialog.GetColourData().GetColour();
}
// Text input dialog
wxTextEntryDialog textDialog(this, "Enter value:", "Input", "default");
if (textDialog.ShowModal() == wxID_OK) {
wxString value = textDialog.GetValue();
}
// Progress dialog
wxProgressDialog progress("Processing", "Please wait...",
100, this, wxPD_AUTO_HIDE | wxPD_APP_MODAL | wxPD_CAN_ABORT);
for (int i = 0; i <= 100; i++) {
if (!progress.Update(i, wxString::Format("Step %d of 100", i)))
break; // User cancelled
wxMilliSleep(50);
}
Configuration (Build System)
# CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(myapp LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(wxWidgets REQUIRED COMPONENTS core base adv)
include(${wxWidgets_USE_FILE})
add_executable(myapp
src/main.cpp
src/mainframe.cpp
src/mainframe.h
)
target_link_libraries(myapp PRIVATE ${wxWidgets_LIBRARIES})
# Windows: hide console window
if(WIN32)
set_target_properties(myapp PROPERTIES WIN32_EXECUTABLE TRUE)
endif()
# Build with CMake
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . -j$(nproc)
# Build with wx-config directly
g++ -o myapp main.cpp $(wx-config --cxxflags --libs)
# With additional libraries
g++ -o myapp main.cpp $(wx-config --cxxflags --libs core,base,adv,aui)
Advanced Usage
// Custom drawing with wxDC
void MyPanel::OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(this);
dc.SetBackground(*wxWHITE_BRUSH);
dc.Clear();
dc.SetPen(wxPen(*wxBLUE, 2));
dc.SetBrush(*wxLIGHT_GREY_BRUSH);
dc.DrawRectangle(50, 50, 200, 100);
dc.SetFont(wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD));
dc.DrawText("Hello wxWidgets", 70, 85);
dc.SetPen(wxPen(*wxRED, 3));
dc.DrawCircle(400, 200, 50);
dc.DrawLine(0, 0, GetSize().GetWidth(), GetSize().GetHeight());
}
// Timer
class MyFrame : public wxFrame {
wxTimer m_timer;
MyFrame() : wxFrame(...), m_timer(this, wxID_ANY) {
m_timer.Start(1000); // 1 second interval
Bind(wxEVT_TIMER, &MyFrame::OnTimer, this);
}
void OnTimer(wxTimerEvent& event) {
SetStatusText(wxDateTime::Now().FormatTime());
}
};
// Threading
class WorkerThread : public wxThread {
wxEvtHandler* m_handler;
public:
WorkerThread(wxEvtHandler* handler) : wxThread(wxTHREAD_DETACHED), m_handler(handler) {}
void* Entry() override {
for (int i = 0; i <= 100; i++) {
wxThreadEvent event(wxEVT_THREAD, wxID_ANY);
event.SetInt(i);
wxQueueEvent(m_handler, event.Clone());
wxMilliSleep(100);
}
return nullptr;
}
};
// Drag and drop
class MyDropTarget : public wxFileDropTarget {
bool OnDropFiles(wxCoord x, wxCoord y, const wxArrayString& filenames) override {
for (const auto& file : filenames) {
wxLogMessage("Dropped: %s", file);
}
return true;
}
};
panel->SetDropTarget(new MyDropTarget());
Troubleshooting
| Issue | Solution |
|---|---|
| ”wxWidgets not found” | Check wx-config --prefix; set wxWidgets_ROOT_DIR in CMake |
| GTK warnings on Linux | Install libwxgtk3.2-dev; ensure GTK3 development headers installed |
| macOS dark mode issues | Update to wxWidgets 3.2+; use system colors instead of hardcoded |
| Sizer layout wrong | Call Layout() or Fit() after adding controls; check flags |
| Events not firing | Verify event binding; check event.Skip() for propagation |
| Unicode display issues | Ensure wxUSE_UNICODE is enabled (default in 3.x) |
| Static linking fails | Build wxWidgets as static: cmake -DwxBUILD_SHARED=OFF |
| High DPI blurry | Enable DPI awareness in manifest (Windows); use FromDIP() for sizes |
| Crash on destruction | Check parent-child ownership; avoid manual delete of parented widgets |
| Memory leak warnings | Use wxSizerFlags; ensure all new’d objects have parent windows |