本篇文章的内容在大多数情况下都不怎么用到,大家可根据自己的兴趣酌情阅读
一、自定义事件
我们先来最简单的使用 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
先给出结果图:
如无意外的话,对话框会弹出两次,因为我同时使用了 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
效果图(如无意外,对话框会弹出两次):
事件篇Ⅳ 至此完毕,欢迎大家指正!还请大家点点赞,给我点动力~~
上一篇:【wxWidgets 教程】事件篇Ⅲ(五)
下一篇:【wxWidgets 教程】工具类篇:wxString(七)