【wxWidgets 教程】事件篇Ⅳ(六)

参考文档:
https://docs.wxwidgets.org/3.2/overview_events.html

本篇文章的内容在大多数情况下都不怎么用到,大家可根据自己的兴趣酌情阅读

一、自定义事件

我们先来最简单的使用 wxCommandEvent 事件类来创建一个自定义事件。先上代码:

#include <wx/wx.h>

// 声明 MY_EVENT 事件类型(注意这里是“声明”)
wxDECLARE_EVENT(MY_EVENT, wxCommandEvent);

// 定义 MY_EVENT 事件类型
wxDEFINE_EVENT(MY_EVENT, wxCommandEvent);


class MyFrame : public wxFrame
{
public:
    explicit MyFrame(const wxString &title)
      : wxFrame(nullptr, wxID_ANY, title, wxDefaultPosition, wxSize(400, 250))
    {
        new wxButton(this, wxID_ANY, "Click me!", wxDefaultPosition, wxDefaultSize);
        wxFrame::Bind(wxEVT_BUTTON, [this](wxCommandEvent &WXUNUSED(e)) {
            // 这里我们使用按钮按下来发送自定义事件,在实际使用过程中,大家应该在适当的地方进行事件发送
            SendEvent();
        }, wxID_ANY);

        wxFrame::Bind(MY_EVENT, &MyFrame::OnMyEvent, this);
    }

    // 发送事件
    void SendEvent()
    {
        wxCommandEvent event(MY_EVENT, GetId());
        event.SetEventObject(this);

        // 设置事件对象中的文本
        event.SetString("Hello, this is my event");

        // send事件
        ProcessWindowEvent(event);
    }

    void OnMyEvent(wxCommandEvent &e) // NOLINT
    {
        wxMessageBox(e.GetString(), "Information", wxOK | wxICON_INFORMATION);
        e.Skip(); // 这里是为了让 Bind 和 事件表 都能触发此事件
    }

wxDECLARE_EVENT_TABLE();
};

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_COMMAND (wxID_ANY, MY_EVENT, MyFrame::OnMyEvent)
wxEND_EVENT_TABLE()


class MyApp : public wxApp
{
public:
    bool OnInit() override
    {
        auto *frame = new MyFrame("My Event Example");
        frame->Show(true);
        return true;
    }
};

IMPLEMENT_APP(MyApp) // NOLINT

先给出结果图:
My Event Example

如无意外的话,对话框会弹出两次,因为我同时使用了 Bind事件表 进行 MY_EVENT 事件的绑定。

先看这两句:

// 声明 MY_EVENT 事件类型(注意这里是“声明”)
wxDECLARE_EVENT(MY_EVENT, wxCommandEvent);

// 定义 MY_EVENT 事件类型
wxDEFINE_EVENT(MY_EVENT, wxCommandEvent);

wxDECLARE_EVENT 用于声明 MY_EVENT 事件。要注意的是,这个声明尽量放在头文件中,并且能被那些使用到这个事件的源文件包含到,否则会找不到这个自定义事件。

wxDEFINE_EVENT 用于定义 MY_EVENT 事件,它必须放在一个源文件中,否则会导致重定义的问题。

事件声明定义完后,就可以开始用了。使用步骤分了两步:发送处理

● 发送

在示例中,我们定义了一个事件发送函数 SendEvent(),函数体如下:

void SendEvent()
{
    wxCommandEvent event(MY_EVENT, GetId());
    event.SetEventObject(this);

    // 设置事件对象中的文本
    event.SetString("Hello, this is my event");

    // send事件
    ProcessWindowEvent(event);
}

首先,我们需要定义一个 wxCommandEvent 事件对象,然后给事件对象添加一些数据,最后,我们使用 wxWindow::ProcessWindowEvent() 函数把事件send出去。

● 处理

处理 MY_EVENT 事件,我使用了两种方法,直接上代码:

wxFrame::Bind(MY_EVENT, &MyFrame::OnMyEvent, this);

...

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_COMMAND (wxID_ANY, MY_EVENT, MyFrame::OnMyEvent)
wxEND_EVENT_TABLE()

这里插入一段关于C++的常识:在构造函数中使用虚函数,最好要养成在函数前带上函数作用域(这里指的是类名),因为在构造期间,自身的虚函数表还没构建完毕,不加上作用域,有些时候可能会有意想不到的效果。那这里的 Bind 函数是虚函数吗?……不是😁,我就是习惯性加上了。

一目了然,这里就不多说了,不懂的往回看我往期的教程~~

二、自定义事件类

一些情况下,我们需要定义自己的事件类。例如,当我们需要从一个地方向另一个地方发送更复杂的数据时,这时候 wxCommandEvent 提供的数据存储方式就非常有限了,定义一个自己的事件类,我们可以更顺畅地处理我们的数据。除了定义事件类之外,如果还需要使用事件表来处理这种类型的事件,我们还要定义自己的事件表宏。

首先,我们需要定义一个事件类:

class MyCommandEvent : public wxCommandEvent
{
public:
    MyCommandEvent(wxEventType eventType, int id) :
      wxCommandEvent(eventType, id)
    {
        // 初始化你的事件数据
    }

    // 必须提供一个克隆方法
    wxEvent* Clone() const override { return new MyCommandEvent(*this); }

    // 可以添加一些特定的访问器,例如:
    // void SetMyData(int data) { m_data = data; }
    // int GetMyData() const { return m_data; }

private:
    // 可以添加一些特定的数据,例如:
    // int m_data;
};

接着,我们添加一个新的事件类型,这一步跟第一节中的步骤类似:

wxDECLARE_EVENT(myEVT_COMMAND, MyCommandEvent);

如果我们只是想在 Bind() 中使用这个事件类以及事件类型,就可以在这里打住了,直接跳过下面这步。如果我们还想让这个事件类和事件类型能在事件表中使用,我们需要加上以下代码:

// 指定事件处理函数类型
//(照搬就行了,想要知道什么意思,去看“wx/event.h”头文件,
// 里面的默认事件的函数类型处理大部分都是这个格式的,从4200多行开始看起),
// `MyCommandEvent` 这是我自己定义的事件类名,大家用的时候改成自己的。
typedef void (wxEvtHandler::*MyCommandEventFunction)(MyCommandEvent&);
#define MyCommandEventHandler(func) wxEVENT_HANDLER_CAST(MyCommandEventFunction, func)

// 为新的事件类型提供一个事件表宏。
// `wx__DECLARE_EVT1` 也就是指定一个ID;`wx__DECLARE_EVT2` 指定两个ID(first、last);
// 还有 `wx__DECLARE_EVT0`,不指定ID。
// 从 `MyCommandEvent` 构造函数看,我们应该指定一个ID,也就是应该使用 `wx__DECLARE_EVT1`。
#define EVT_MYCOMMAND(id, fn) \
    wx__DECLARE_EVT1(myEVT_COMMAND, id, MyCommandEventHandler(fn))

注释解释和扩展得够清楚了吧?继续继续~

接下来,我们对事件类型进行定义

wxDEFINE_EVENT(myEVT_COMMAND, MyCommandEvent);

好了,事件类和事件类型都创建完成了,最后就是使用了。上面步骤看不懂的小伙伴,直接来看完整代码吧(不过我把多余的注释给删了):

#include <wx/wx.h>

class MyCommandEvent : public wxCommandEvent
{
public:
    MyCommandEvent(wxEventType eventType, int id) :
      wxCommandEvent(eventType, id)
    {
    }

    [[nodiscard]] // C++17 才支持,意思就是函数返回值不能被忽略,否则编译器发出警告
    wxEvent *Clone() const override { return new MyCommandEvent(*this); }
};

wxDECLARE_EVENT(myEVT_COMMAND, MyCommandEvent);

typedef void (wxEvtHandler::*MyCommandEventFunction)(MyCommandEvent &);
#define MyCommandEventHandler(func) wxEVENT_HANDLER_CAST(MyCommandEventFunction, func)

#define EVT_MYCOMMAND(id, fn) \
    wx__DECLARE_EVT1(myEVT_COMMAND, id, MyCommandEventHandler(fn))

wxDEFINE_EVENT(myEVT_COMMAND, MyCommandEvent);


class MyFrame : public wxFrame
{
public:
    explicit MyFrame(const wxString &title)
      : wxFrame(nullptr, wxID_ANY, title, wxDefaultPosition, wxSize(400, 250))
    {
        new wxButton(this, wxID_ANY, "Click me!", wxDefaultPosition, wxDefaultSize);
        wxFrame::Bind(wxEVT_BUTTON, [this](wxCommandEvent &WXUNUSED(e)) {
            SendEvent();  // 模拟发送事件
        }, wxID_ANY);

        wxFrame::Bind(myEVT_COMMAND, &MyFrame::OnMyEvent, this);  // 绑定自定义事件类型
    }

    void SendEvent()
    {
        MyCommandEvent event(myEVT_COMMAND, GetId());  // 使用自定义的事件类
        event.SetEventObject(this);

        event.SetString("Hello, this is my event class");  // 模拟事件类传递内容

        ProcessWindowEvent(event);
    }

    void OnMyEvent(MyCommandEvent &e) // NOLINT
    {
        wxMessageBox(e.GetString(), "Information", wxOK | wxICON_INFORMATION);
        e.Skip(); // 这里是为了让 Bind 和 事件表 都能触发此事件
    }

wxDECLARE_EVENT_TABLE();
};

wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_MYCOMMAND(wxID_ANY, MyFrame::OnMyEvent) // 使用自定义的事件表宏
wxEND_EVENT_TABLE()


class MyApp : public wxApp
{
public:
    bool OnInit() override
    {
        auto *frame = new MyFrame("My Event Class Example");
        frame->Show(true);
        return true;
    }
};

IMPLEMENT_APP(MyApp) // NOLINT

效果图(如无意外,对话框会弹出两次):
My Event Example


事件篇Ⅳ 至此完毕,欢迎大家指正!还请大家点点赞,给我点动力~~

上一篇:【wxWidgets 教程】事件篇Ⅲ(五)
下一篇:【wxWidgets 教程】工具类篇:wxString(七)

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xiao_Ley

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值