杰瑞科技汇

wxwidgets教程从哪开始学?

wxWidgets 完整教程:从入门到精通

目录

  1. 第一部分:wxWidgets 简介与环境搭建

    wxwidgets教程从哪开始学?-图1
    (图片来源网络,侵删)
    • 1 什么是 wxWidgets?
    • 2 为什么选择 wxWidgets?
    • 3 开发环境搭建 (Windows, Linux, macOS)
    • 4 你的第一个 wxWidgets 程序:Hello, World!
  2. 第二部分:核心概念与基本控件

    • 1 应用程序框架:wxAppwxFrame
    • 2 基本控件:wxButton, wxStaticText, wxTextCtrl
    • 3 布局管理器:wxBoxSizer
    • 4 事件处理:EVT_BUTTON 和宏
  3. 第三部分:进阶控件与布局

    • 1 列表控件:wxListBox, wxCheckListBox, wxListCtrl
    • 2 文本输入控件:wxTextCtrl, wxComboBox, wxChoice
    • 3 菜单、工具栏和状态栏
    • 4 对话框:wxMessageBox, wxFileDialog, wxTextEntryDialog
  4. 第四部分:高级主题

    • 1 自定义事件
    • 2 多线程与 GUI
    • 3 文档/视图架构
    • 4 使用 XRC 进行界面设计
  5. 第五部分:实战项目

    wxwidgets教程从哪开始学?-图2
    (图片来源网络,侵删)
    • 1 项目构思:一个简单的记事本
    • 2 项目实现步骤
  6. 第六部分:资源与进阶学习


第一部分: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++ 的桌面开发” 工作负载。

wxwidgets教程从哪开始学?-图3
(图片来源网络,侵删)

步骤 2:下载 wxWidgets 访问 wxWidgets 官网 下载最新稳定版的源码(wxWidgets-3.2.2.zip),并解压到一个不含空格和中文的路径,D:\dev\wxWidgets-3.2.2

步骤 3:编译 wxWidgets 库 这是最关键的一步。

  1. 打开 x64 Native Tools Command Prompt for VS 2025 命令行工具。

  2. 进入 wxWidgets 源码目录下的 build\msw 目录:

    cd D:\dev\wxWidgets-3.2.2\build\msw
  3. 运行 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 项目

  1. 创建一个新的 C++ 空项目
  2. 右键点击项目 -> 属性
  3. 配置:选择 所有配置
  4. C/C++ -> 常规 -> 附加包含目录: 添加 D:\dev\wxWidgets-3.2.2\includeD:\dev\wxWidgets-3.2.2\lib\vc_lib\mswud (注意 mswud 代表 MSW, Unicode, Debug)。
  5. 链接器 -> 常规 -> 附加库目录: 添加 D:\dev\wxWidgets-3.2.2\lib\vc_lib
  6. 链接器 -> 输入 -> 附加依赖项: 添加 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 应用程序框架:wxAppwxFrame

  • 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 自定义事件

当内置事件无法满足需求时,可以创建自己的事件类型。

  1. 定义事件 ID: 在 wx/defs.h 中定义一个新的 wxEventType
  2. 声明事件类: 继承自 wxEvent
  3. 声明事件宏: wxDECLARE_EVENT
  4. 实现事件宏: wxDEFINE_EVENT
  5. 触发事件: 使用 wxPostEventProcessEvent
  6. 绑定事件: 使用 BindEVT_CUSTOM

这是一个比较复杂的话题,通常在大型应用中才需要。

2 多线程与 GUI

GUI 操作必须在主线程(UI 线程)中执行! 任何耗时操作(如网络请求、文件读写、复杂计算)都应该放在工作线程中,以防止界面卡死。

工作线程需要通过 wxQueueEventwxPostEvent 将请求发送回主线程,由主线程来更新界面。

// 主线程
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)来设计界面,而程序员则专注于业务逻辑。

优点

  • 界面与代码分离,易于修改。
  • 可以在不重新编译程序的情况下更换界面。

第五部分:实战项目:一个简单的记事本

让我们用学到的知识来构建一个简单的记事本程序。

功能

  1. 创建一个文本编辑区域。
  2. 有 "文件" 菜单,包含 "新建"、"打开"、"保存" 和 "退出"。
  3. 实现对应的文件操作功能。

实现步骤

  1. 创建主框架和菜单: 参考 3.3 节创建菜单栏。

  2. 添加文本编辑区: 使用 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);
  3. 实现文件操作: 为菜单项绑定事件处理函数。

    // 在 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 - 最权威、最全面的资料。
  • 官方 WikiwxWidgets Wiki - 包含大量教程、技巧和示例链接。
  • 示例代码wxWidgets/samples/ 目录下有海量的示例程序,是学习高级功能的最佳途径。
  • 社区论坛wxWidgets Forum - 在这里可以提问和交流。
  • 第三方工具
    • wxFormBuilder: 可视化的界面设计器,可以生成 XRC 文件或 C++ 代码。
    • CodeLite: 一个优秀的 C/C++ IDE,对 wxWidgets 提供了非常好的集成支持。

祝你 wxWidgets 学习愉快!

分享:
扫描分享到社交APP
上一篇
下一篇