wxWidgets
Cross-platform GUI library for C++. Uses native APIs for each platform to achieve OS-standard appearance and feel. Lightweight, fast, and commercially usable license. Provides stable API over long periods.
GitHub Overview
wxWidgets/wxWidgets
Cross-Platform C++ GUI Library
Topics
Star History
Desktop Framework
wxWidgets
Overview
wxWidgets is a cross-platform GUI library written in C++. It provides native look and feel and runs on Windows, macOS, Linux (GTK), and many other platforms. Developed since 1992, it has a reputation for stability and compatibility.
Details
wxWidgets uses native APIs directly on each platform, enabling natural appearance and usability specific to each OS. It uses Win32 API on Windows, Cocoa on macOS, and GTK on Linux, allowing you to build truly cross-platform applications. It provides rich UI controls and layout management features, making it suitable for enterprise-level application development.
Features
- Native appearance: Native look and behavior on each platform
- Wide platform support: Windows, macOS, Linux, FreeBSD, Solaris, etc.
- Rich controls: Buttons, lists, trees, grids, menus, etc.
- Open source: Available under the wxWindows Library License
- Long-term stability: Over 30 years of development experience
- Python bindings: Also available from Python via wxPython
Architecture
- MVC support: Document/View architecture support
- Event-driven: Event-based design rather than callbacks
- Memory management: Automatic memory management and resource cleanup
- Internationalization: Unicode support and multilingual support
- Printing: High-quality printing functionality
Pros and Cons
Pros
- Native appearance that integrates naturally with the OS
- Extremely stable library with expected long-term support
- Rich documentation and sample code
- Large community and ecosystem
- Track record in enterprise applications
- Python bindings (wxPython) also available
Cons
- High learning cost with complex API
- Not suitable for modern UI/UX design
- C++ specific complexity (memory management, compiler dependencies)
- Slow response to new UI trends
- Bundle size tends to be large
Reference Pages
Code Examples
Hello World
#include <wx/wx.h>
class MyApp : public wxApp
{
public:
bool OnInit() override;
};
class MyFrame : public wxFrame
{
public:
MyFrame();
private:
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
};
wxIMPLEMENT_APP(MyApp);
bool MyApp::OnInit()
{
MyFrame *frame = new MyFrame();
frame->Show(true);
return true;
}
MyFrame::MyFrame()
: wxFrame(nullptr, wxID_ANY, "Hello wxWidgets")
{
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");
}
Windows and Controls
class MainFrame : public wxFrame
{
public:
MainFrame() : wxFrame(nullptr, wxID_ANY, "wxWidgets App")
{
// Menu bar
wxMenuBar* menuBar = new wxMenuBar();
wxMenu* fileMenu = new wxMenu();
fileMenu->Append(wxID_EXIT, "&Exit");
menuBar->Append(fileMenu, "&File");
SetMenuBar(menuBar);
// Main panel
wxPanel* panel = new wxPanel(this);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
// Controls
wxStaticText* label = new wxStaticText(panel, wxID_ANY, "Hello World!");
wxTextCtrl* textCtrl = new wxTextCtrl(panel, wxID_ANY, "");
wxButton* button = new wxButton(panel, wxID_ANY, "Click Me");
// Layout
sizer->Add(label, 0, wxALL, 5);
sizer->Add(textCtrl, 0, wxALL | wxEXPAND, 5);
sizer->Add(button, 0, wxALL, 5);
panel->SetSizer(sizer);
// Event handler
button->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
[this](wxCommandEvent&) {
wxMessageBox("Button clicked!", "Info");
});
}
};
Event Handling and Dialogs
class EventFrame : public wxFrame
{
public:
EventFrame() : wxFrame(nullptr, wxID_ANY, "Event Handling")
{
wxPanel* panel = new wxPanel(this);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
wxButton* dialogBtn = new wxButton(panel, wxID_ANY, "Show Dialog");
wxButton* fileBtn = new wxButton(panel, wxID_ANY, "Open File");
wxButton* exitBtn = new wxButton(panel, wxID_ANY, "Exit");
sizer->Add(dialogBtn, 0, wxALL, 5);
sizer->Add(fileBtn, 0, wxALL, 5);
sizer->Add(exitBtn, 0, wxALL, 5);
panel->SetSizer(sizer);
// Event binding
dialogBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &EventFrame::OnDialog, this);
fileBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &EventFrame::OnOpenFile, this);
exitBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &EventFrame::OnExit, this);
}
private:
void OnDialog(wxCommandEvent& event)
{
wxMessageDialog dlg(this, "This is a message dialog", "Information",
wxOK | wxICON_INFORMATION);
dlg.ShowModal();
}
void OnOpenFile(wxCommandEvent& event)
{
wxFileDialog dlg(this, "Choose a file", "", "",
"Text files (*.txt)|*.txt|All files (*.*)|*.*",
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dlg.ShowModal() == wxID_OK)
{
wxString path = dlg.GetPath();
wxMessageBox("Selected: " + path, "File Selected");
}
}
void OnExit(wxCommandEvent& event)
{
Close(true);
}
};
Lists and Grids
class ListFrame : public wxFrame
{
public:
ListFrame() : wxFrame(nullptr, wxID_ANY, "List and Grid")
{
wxPanel* panel = new wxPanel(this);
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
// List control
wxListCtrl* listCtrl = new wxListCtrl(panel, wxID_ANY,
wxDefaultPosition, wxDefaultSize,
wxLC_REPORT | wxLC_SINGLE_SEL);
// Add columns
listCtrl->AppendColumn("Name", wxLIST_FORMAT_LEFT, 100);
listCtrl->AppendColumn("Age", wxLIST_FORMAT_RIGHT, 60);
listCtrl->AppendColumn("City", wxLIST_FORMAT_LEFT, 120);
// Add data
long index = listCtrl->InsertItem(0, "John Doe");
listCtrl->SetItem(index, 1, "30");
listCtrl->SetItem(index, 2, "New York");
index = listCtrl->InsertItem(1, "Jane Smith");
listCtrl->SetItem(index, 1, "25");
listCtrl->SetItem(index, 2, "Los Angeles");
// Grid (requires wxGrid include)
wxGrid* grid = new wxGrid(panel, wxID_ANY);
grid->CreateGrid(5, 3);
grid->SetColLabelValue(0, "Product");
grid->SetColLabelValue(1, "Price");
grid->SetColLabelValue(2, "Stock");
// Grid data
grid->SetCellValue(0, 0, "Laptop");
grid->SetCellValue(0, 1, "$999");
grid->SetCellValue(0, 2, "10");
sizer->Add(listCtrl, 1, wxALL | wxEXPAND, 5);
sizer->Add(grid, 1, wxALL | wxEXPAND, 5);
panel->SetSizer(sizer);
}
};
Multithreading and Timers
class TimerFrame : public wxFrame
{
public:
TimerFrame() : wxFrame(nullptr, wxID_ANY, "Timer Example"),
timer(this, wxID_ANY)
{
wxPanel* panel = new wxPanel(this);
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
timeLabel = new wxStaticText(panel, wxID_ANY, "00:00:00");
wxButton* startBtn = new wxButton(panel, wxID_ANY, "Start Timer");
wxButton* stopBtn = new wxButton(panel, wxID_ANY, "Stop Timer");
sizer->Add(timeLabel, 0, wxALL | wxCENTER, 10);
sizer->Add(startBtn, 0, wxALL, 5);
sizer->Add(stopBtn, 0, wxALL, 5);
panel->SetSizer(sizer);
// Event binding
startBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &TimerFrame::OnStart, this);
stopBtn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, &TimerFrame::OnStop, this);
timer.Bind(wxEVT_TIMER, &TimerFrame::OnTimer, this);
startTime = wxDateTime::Now();
}
private:
wxTimer timer;
wxStaticText* timeLabel;
wxDateTime startTime;
void OnStart(wxCommandEvent& event)
{
startTime = wxDateTime::Now();
timer.Start(1000); // 1 second interval
}
void OnStop(wxCommandEvent& event)
{
timer.Stop();
}
void OnTimer(wxTimerEvent& event)
{
wxDateTime now = wxDateTime::Now();
wxTimeSpan diff = now - startTime;
timeLabel->SetLabel(diff.Format("%H:%M:%S"));
}
};
Custom Drawing
class DrawingPanel : public wxPanel
{
public:
DrawingPanel(wxFrame* parent) : wxPanel(parent)
{
Bind(wxEVT_PAINT, &DrawingPanel::OnPaint, this);
Bind(wxEVT_LEFT_DOWN, &DrawingPanel::OnMouseDown, this);
}
private:
std::vector<wxPoint> points;
void OnPaint(wxPaintEvent& event)
{
wxPaintDC dc(this);
// Clear background
dc.Clear();
// Gradient
wxRect rect = GetClientRect();
dc.GradientFillLinear(rect, wxColour(200, 200, 255),
wxColour(100, 100, 200), wxSOUTH);
// Draw circles and lines
dc.SetPen(wxPen(wxColour(255, 0, 0), 3));
dc.SetBrush(wxBrush(wxColour(0, 255, 0, 128)));
for (size_t i = 0; i < points.size(); ++i)
{
dc.DrawCircle(points[i], 20);
if (i > 0)
{
dc.DrawLine(points[i-1], points[i]);
}
}
// Draw text
dc.SetTextForeground(wxColour(0, 0, 0));
dc.DrawText(wxString::Format("Points: %zu", points.size()), 10, 10);
}
void OnMouseDown(wxMouseEvent& event)
{
points.push_back(event.GetPosition());
Refresh(); // Trigger redraw
}
};