wxWidgets 完整教程:从入门到精通
目录
-
第一部分:wxWidgets 简介与环境搭建
(图片来源网络,侵删)- 1 什么是 wxWidgets?
- 2 为什么选择 wxWidgets?
- 3 开发环境搭建 (Windows, Linux, macOS)
- 4 你的第一个 wxWidgets 程序:Hello, World!
-
第二部分:核心概念与基本控件
- 1 应用程序框架:
wxApp和wxFrame - 2 基本控件:
wxButton,wxStaticText,wxTextCtrl - 3 布局管理器:
wxBoxSizer - 4 事件处理:
EVT_BUTTON和宏
- 1 应用程序框架:
-
第三部分:进阶控件与布局
- 1 列表控件:
wxListBox,wxCheckListBox,wxListCtrl - 2 文本输入控件:
wxTextCtrl,wxComboBox,wxChoice - 3 菜单、工具栏和状态栏
- 4 对话框:
wxMessageBox,wxFileDialog,wxTextEntryDialog
- 1 列表控件:
-
第四部分:高级主题
- 1 自定义事件
- 2 多线程与 GUI
- 3 文档/视图架构
- 4 使用 XRC 进行界面设计
-
第五部分:实战项目
(图片来源网络,侵删)- 1 项目构思:一个简单的记事本
- 2 项目实现步骤
-
第六部分:资源与进阶学习
第一部分:wxWidgets 简介与环境搭建
1 什么是 wxWidgets?
wxWidgets 是一个 C++ 编写的跨平台 GUI(图形用户界面)工具包,它允许你使用相同的 C++ 代码为 Windows、macOS、Linux 等多种操作系统创建原生外观的应用程序,这意味着,你的程序在 Windows 上看起来就是原生的 Windows 程序,在 macOS 上看起来就是原生的 macOS 程序,无需任何额外修改。
2 为什么选择 wxWidgets?
- 跨平台:一次编写,多平台运行。
- 原生外观:使用各操作系统的原生控件,用户体验一致。
- 免费开源:遵循宽松的许可证(LGPL),可用于商业和开源项目。
- 功能强大:提供了几乎所有常见的 GUI 控件和高级功能。
- 成熟稳定:拥有悠久的历史和庞大的社区支持。
- C++ 语言:充分利用 C++ 的强大特性和性能。
3 开发环境搭建
这里以 Windows + Visual Studio 2025 为例,这是最常见和方便的组合。
步骤 1:安装编译器 确保你安装了 Visual Studio 2025,并在安装时勾选 “使用 C++ 的桌面开发” 工作负载。

步骤 2:下载 wxWidgets
访问 wxWidgets 官网 下载最新稳定版的源码(wxWidgets-3.2.2.zip),并解压到一个不含空格和中文的路径,D:\dev\wxWidgets-3.2.2。
步骤 3:编译 wxWidgets 库 这是最关键的一步。
-
打开 x64 Native Tools Command Prompt for VS 2025 命令行工具。
-
进入 wxWidgets 源码目录下的
build\msw目录:cd D:\dev\wxWidgets-3.2.2\build\msw
-
运行
build.py脚本进行配置和编译,这个脚本会为你生成 Visual Studio 的项目文件并进行编译。python build.py --wxmsw=3.2.2 --wxpython=4.2.1 --debug=1 --unicode=1 --monolithic=1 --toolkit=msw --vs=2025 --build=64
--wxmsw=3.2.2: 指定 wxWidgets 版本。--unicode=1: 强烈建议使用 Unicode 版本。--monolithic=1: 将所有库编译成一个wx.lib,方便链接。--vs=2025: 指定 Visual Studio 版本。--build=64: 编译 64 位版本。
编译过程可能需要 10-30 分钟,请耐心等待,完成后,你会在
D:\dev\wxWidgets-3.2.2\lib\vc_lib目录下找到编译好的库文件(如wx32d.lib)。
步骤 4:配置 Visual Studio 项目
- 创建一个新的 C++ 空项目。
- 右键点击项目 -> 属性。
- 配置:选择
所有配置。 - C/C++ -> 常规 -> 附加包含目录:
添加
D:\dev\wxWidgets-3.2.2\include和D:\dev\wxWidgets-3.2.2\lib\vc_lib\mswud(注意mswud代表 MSW, Unicode, Debug)。 - 链接器 -> 常规 -> 附加库目录:
添加
D:\dev\wxWidgets-3.2.2\lib\vc_lib。 - 链接器 -> 输入 -> 附加依赖项:
添加
wx32d.lib(Debug 模式) 或wx32.lib(Release 模式)。
你的开发环境已经配置好了。
4 你的第一个 wxWidgets 程序:Hello, World!
创建一个 main.cpp 文件,粘贴以下代码:
#include <wx/wx.h>
// 1. 创建一个应用程序类,继承自 wxApp
class MyApp : public wxApp
{
public:
// 虚函数,在程序启动时被调用
virtual bool OnInit() override;
};
// 2. 创建一个窗口类,继承自 wxFrame
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
};
// 3. 实现窗口类的构造函数
MyFrame::MyFrame(const wxString& title)
: wxFrame(nullptr, wxID_ANY, title)
{
// 创建一个面板,作为窗口的子窗口
wxPanel* panel = new wxPanel(this);
// 创建一个静态文本标签
wxStaticText* helloText = new wxStaticText(panel, wxID_ANY,
wxLiteral("Hello, World!"),
wxPoint(50, 50));
// 设置窗口大小
SetSize(400, 300);
}
// 4. 实现应用程序类的 OnInit 函数
bool MyApp::OnInit()
{
// 创建主窗口
MyFrame *frame = new MyFrame("My First wxWidgets App");
// 显示窗口
frame->Show(true);
return true;
}
// 5. 这是程序的入口点,wxWidgets 宏会自动创建并运行 MyApp 实例
wxIMPLEMENT_APP(MyApp);
编译并运行:如果环境配置正确,你应该能看到一个标题为 "My First wxWidgets App" 的窗口,中间有 "Hello, World!" 的文字。
第二部分:核心概念与基本控件
1 应用程序框架:wxApp 和 wxFrame
wxApp: 这是整个应用程序的基类,每个 wxWidgets 程序有且只有一个wxApp对象,它的核心方法是OnInit(),程序启动时会调用它,通常在这里创建主窗口。wxFrame: 代表应用程序的主窗口或顶级框架,它可以有标题栏、边框、菜单栏等。
2 基本控件:wxButton, wxStaticText, wxTextCtrl
wxButton: 按钮。wxStaticText: 静态文本,用于显示信息,不能被用户编辑。wxTextCtrl: 文本框,用于单行或多行文本输入和显示。
// 在 MyFrame 构造函数中添加 wxPanel* panel = new wxPanel(this); wxButton* button = new wxButton(panel, wxID_ANY, "Click Me", wxPoint(50, 100)); wxTextCtrl* textCtrl = new wxTextCtrl(panel, wxID_ANY, "", wxPoint(50, 150), wxSize(200, -1));
3 布局管理器:wxBoxSizer
绝对定位(wxPoint)在窗口大小改变时会出现问题,wxWidgets 强烈使用布局管理器来自动调整控件位置和大小。
wxBoxSizer 是最常用的布局管理器,它可以将控件水平或垂直排列。
// 修改 MyFrame 构造函数
MyFrame::MyFrame(const wxString& title)
: wxFrame(nullptr, wxID_ANY, title)
{
wxPanel* panel = new wxPanel(this);
// 1. 创建一个垂直方向的 BoxSizer
wxBoxSizer* vbox = new wxBoxSizer(wxVERTICAL);
// 2. 创建控件
wxStaticText* helloText = new wxStaticText(panel, wxID_ANY, "Hello, World!");
wxButton* button = new wxButton(panel, wxID_ANY, "Click Me");
wxTextCtrl* textCtrl = new wxTextCtrl(panel, wxID_ANY, "Enter text here...");
// 3. 将控件添加到 sizer,并设置边距和拉伸选项
// Add(控件, 比例, 标志, 边距)
vbox->Add(helloText, 0, wxALL, 10); // 上下左右边距10,不拉伸
vbox->Add(button, 0, wxALIGN_CENTER | wxALL, 10); // 居中
vbox->Add(textCtrl, 1, wxEXPAND | wxALL, 10); // 拉伸填充剩余空间
// 4. 将 sizer 应用到 panel
panel->SetSizer(vbox);
this->SetInitialSize(); // 根据内容设置初始大小
}
4 事件处理:EVT_BUTTON 和宏
GUI 程序是事件驱动的,用户点击按钮、输入文字等都会产生事件,我们需要“连接”事件处理函数到相应的事件上。
// 在 MyFrame 类的声明中添加事件处理函数声明
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
// 声明一个处理按钮点击的函数
void OnButtonClicked(wxCommandEvent& event);
};
// 在 MyFrame 构造函数中连接事件
MyFrame::MyFrame(const wxString& title)
: wxFrame(nullptr, wxID_ANY, title)
{
// ... (sizer 和控件的创建代码) ...
button->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClicked, this); // 使用现代的 Bind 方法
}
// 实现事件处理函数
void MyFrame::OnButtonClicked(wxCommandEvent& event)
{
wxMessageBox("Button was clicked!", "Info", wxOK | wxICON_INFORMATION);
}
宏方法 (旧式但依然流行):
你可能会看到 EVT_BUTTON(wxID_ANY, MyFrame::OnButtonClicked) 这样的宏,这需要在类的声明后加上 BEGIN_EVENT_TABLE(MyFrame, wxFrame) ... END_EVENT_TABLE()。Bind 方法更现代、更灵活,推荐使用。
第三部分:进阶控件与布局
1 列表控件:wxListBox, wxCheckListBox, wxListCtrl
wxListBox: 单选列表。wxCheckListBox: 每项带复选框的列表。wxListCtrl: 功能强大的列表,支持图标和报告视图。
wxListBox* listBox = new wxListBox(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SINGLE);
listBox->Append("Item 1");
listBox->Append("Item 2");
listBox->Append("Item 3");
vbox->Add(listBox, 1, wxEXPAND | wxALL, 10);
2 文本输入控件:wxTextCtrl, wxComboBox, wxChoice
wxTextCtrl: 文本输入框。wxComboBox: 下拉列表框,可输入也可选择。wxChoice: 纯下拉选择框,不能输入。
wxComboBox* comboBox = new wxComboBox(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, 0, nullptr, wxCB_DROPDOWN);
comboBox->Append("Option A");
comboBox->Append("Option B");
vbox->Add(comboBox, 0, wxALL, 10);
3 菜单、工具栏和状态栏
-
菜单栏 (
wxMenuBar,wxMenu):wxMenuBar* menuBar = new wxMenuBar(); wxMenu* fileMenu = new wxMenu(); fileMenu->Append(wxID_NEW, "&New\tCtrl+N"); // &N 表示快捷键 Alt+N fileMenu->Append(wxID_OPEN, "&Open...\tCtrl+O"); fileMenu->AppendSeparator(); fileMenu->Append(wxID_EXIT, "E&xit\tAlt-X"); menuBar->Append(fileMenu, "&File"); this->SetMenuBar(menuBar);
-
工具栏 (
wxToolBar):wxToolBar* toolbar = CreateToolBar(); toolbar->AddTool(wxID_NEW, "New", wxArtProvider::GetBitmap(wxART_NEW)); toolbar->AddTool(wxID_OPEN, "Open", wxArtProvider::GetBitmap(wxART_FILE_OPEN)); toolbar->Realize();
-
状态栏 (
wxStatusBar):CreateStatusBar(); SetStatusText("Welcome to wxWidgets!");
4 对话框:wxMessageBox, wxFileDialog, wxTextEntryDialog
对话框用于与用户进行临时交互。
// 在按钮点击事件中调用
void MyFrame::OnButtonClicked(wxCommandEvent& event)
{
// 1. 消息框
wxMessageBox("This is a message box.", "Title", wxOK | wxCANCEL | wxICON_QUESTION);
// 2. 文件对话框
wxFileDialog openFileDialog(this, "Open File", "", "", "Text files (*.txt)|*.txt", wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openFileDialog.ShowModal() == wxID_CANCEL)
return; // 用户取消了
wxString filePath = openFileDialog.GetPath();
wxLogMessage("Selected file: %s", filePath);
// 3. 文本输入对话框
wxString text = wxGetTextFromUser("Please enter your name:", "Name Input");
if (!text.IsEmpty())
{
wxLogMessage("Hello, %s!", text);
}
}
第四部分:高级主题
1 自定义事件
当内置事件无法满足需求时,可以创建自己的事件类型。
- 定义事件 ID: 在
wx/defs.h中定义一个新的wxEventType。 - 声明事件类: 继承自
wxEvent。 - 声明事件宏:
wxDECLARE_EVENT。 - 实现事件宏:
wxDEFINE_EVENT。 - 触发事件: 使用
wxPostEvent或ProcessEvent。 - 绑定事件: 使用
Bind或EVT_CUSTOM。
这是一个比较复杂的话题,通常在大型应用中才需要。
2 多线程与 GUI
GUI 操作必须在主线程(UI 线程)中执行! 任何耗时操作(如网络请求、文件读写、复杂计算)都应该放在工作线程中,以防止界面卡死。
工作线程需要通过 wxQueueEvent 或 wxPostEvent 将请求发送回主线程,由主线程来更新界面。
// 主线程
void MyFrame::OnStartLongTask(wxCommandEvent& event)
{
// 创建并启动工作线程
wxThread* thread = new MyWorkerThread(this);
if (thread->Run() != wxTHREAD_NO_ERROR)
{
wxLogError("Could not create the worker thread!");
delete thread;
}
}
// 工作线程类
class MyWorkerThread : public wxThread
{
public:
MyWorkerThread(MyFrame* frame) : m_frame(frame) {}
ExitCode Entry() override
{
// 模拟耗时操作
for (int i = 0; i < 10; ++i)
{
wxThread::Sleep(1000); // 睡眠1秒
// 创建一个自定义事件,包含进度信息
wxCommandEvent* evt = new wxCommandEvent(wxEVT_UPDATE_PROGRESS);
evt->SetInt(i);
// 将事件发送回主线程
wxQueueEvent(m_frame, evt);
}
return (ExitCode)0;
}
private:
MyFrame* m_frame;
};
// 在 MyFrame 中定义和绑定自定义事件
wxDEFINE_EVENT(wxEVT_UPDATE_PROGRESS, wxCommandEvent);
// 在 MyFrame 构造函数中绑定
Bind(wxEVT_UPDATE_PROGRESS, &MyFrame::OnUpdateProgress, this);
// 处理自定义事件的函数
void MyFrame::OnUpdateProgress(wxCommandEvent& event)
{
int progress = event.GetInt();
// 在主线程中安全地更新界面
wxLogMessage("Progress: %d/10", progress);
}
3 文档/视图架构
这是 wxWidgets 提供的一种强大的应用程序设计模式,特别适合处理数据(文档)及其显示(视图)分离的应用程序(如文本编辑器、图像查看器),它将数据管理、显示逻辑和用户界面框架清晰地分离开来。
4 使用 XRC 进行界面设计
XRC (XML Resources) 允许你将用户界面布局和控件定义保存在一个 XML 文件中,这使得设计师可以使用可视化工具(如 wxFormBuilder)来设计界面,而程序员则专注于业务逻辑。
优点:
- 界面与代码分离,易于修改。
- 可以在不重新编译程序的情况下更换界面。
第五部分:实战项目:一个简单的记事本
让我们用学到的知识来构建一个简单的记事本程序。
功能:
- 创建一个文本编辑区域。
- 有 "文件" 菜单,包含 "新建"、"打开"、"保存" 和 "退出"。
- 实现对应的文件操作功能。
实现步骤:
-
创建主框架和菜单: 参考 3.3 节创建菜单栏。
-
添加文本编辑区: 使用
wxTextCtrl,并将其设置为多行模式 (wxTE_MULTILINE),并让它能够自动拉伸 (wxEXPAND)。// 在 MyFrame 构造函数中 wxTextCtrl* m_textCtrl = new wxTextCtrl(panel, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); vbox->Add(m_textCtrl, 1, wxEXPAND | wxALL, 5);
-
实现文件操作: 为菜单项绑定事件处理函数。
// 在 MyFrame 声明中 void OnNew(wxCommandEvent& event); void OnOpen(wxCommandEvent& event); void OnSave(wxCommandEvent& event); void OnExit(wxCommandEvent& event);
// 在 MyFrame 构造函数中连接事件 Bind(wxEVT_MENU, &MyFrame::OnNew, this, wxID_NEW); Bind(wxEVT_MENU, &MyFrame::OnOpen, this, wxID_OPEN); Bind(wxEVT_MENU, &MyFrame::OnSave, this, wxID_SAVE); Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT); // 实现事件处理函数 void MyFrame::OnNew(wxCommandEvent& event) { if (!m_textCtrl->IsEmpty() && wxMessageBox("Do you want to save the current changes?", "Confirm", wxYES_NO | wxCANCEL | wxICON_QUESTION) == wxYES) { OnSave(event); // 调用保存 } m_textCtrl->Clear(); } void MyFrame::OnOpen(wxCommandEvent& event) { wxFileDialog openFileDialog(this, "Open Text File", "", "", "Text files (*.txt)|*.txt", wxFD_OPEN | wxFD_FILE_MUST_EXIST); if (openFileDialog.ShowModal() == wxID_CANCEL) return; wxString filePath = openFileDialog.GetPath(); wxTextFile file(filePath); if (file.Open()) { m_textCtrl->Clear(); for (size_t i = 0; i < file.GetLineCount(); ++i) { m_textCtrl->AppendText(file.GetLine(i) + "\n"); } file.Close(); } else { wxMessageBox("Could not open the file.", "Error", wxOK | wxICON_ERROR); } } void MyFrame::OnSave(wxCommandEvent& event) { wxFileDialog saveFileDialog(this, "Save Text File", "", "", "Text files (*.txt)|*.txt", wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if (saveFileDialog.ShowModal() == wxID_CANCEL) return; wxString filePath = saveFileDialog.GetPath(); wxTextFile file(filePath); file.Create(); // 创建或覆盖文件 if (file.Open()) { long pos = 0; while (!m_textCtrl->GetRange(pos, pos + 1).IsEmpty()) { wxString line = m_textCtrl->GetRange(pos, m_textCtrl->GetValue().Find('\n', pos)); if (line.IsEmpty()) break; file.AddLine(line); pos = m_textCtrl->GetValue().Find('\n', pos) + 1; } file.Write(); file.Close(); } else { wxMessageBox("Could not save the file.", "Error", wxOK | wxICON_ERROR); } } void MyFrame::OnExit(wxCommandEvent& event) { Close(true); }
你已经拥有了一个功能完备的简单记事本!
第六部分:资源与进阶学习
- 官方文档:wxWidgets Official Docs - 最权威、最全面的资料。
- 官方 Wiki:wxWidgets Wiki - 包含大量教程、技巧和示例链接。
- 示例代码:
wxWidgets/samples/目录下有海量的示例程序,是学习高级功能的最佳途径。 - 社区论坛:wxWidgets Forum - 在这里可以提问和交流。
- 第三方工具:
- wxFormBuilder: 可视化的界面设计器,可以生成 XRC 文件或 C++ 代码。
- CodeLite: 一个优秀的 C/C++ IDE,对 wxWidgets 提供了非常好的集成支持。
祝你 wxWidgets 学习愉快!
