一、事件介绍
与其他GUI框架一样,wxWidgets
的控制流是基于事件控制的,即 wxWidgets
的操作大部分都是在事件里面执行的。
事件整体有两种,一种直接与用户操作挂钩的事件(例如鼠标、按钮事件),还有一种是不直接与用户操作对应的事件(例如定时器、Socket事件)。
二、注册方式
- 通过事件表进行事件的注册
- 通过
Bind
函数进行事件绑定 - 通过
Connect
进行事件绑定(它需要手动用Disconnect
解绑事件)
三、触发顺序
同一线程下按照以下顺序(测试了几千次,包括触发频率极高的鼠标移动事件,目前没见到有特例):
Bind
绑定的函数Connect
绑定的函数- 事件表注册的函数
注意,如果一个事件同时被这三种方式绑定,如果在优先触发的函数中没有对事件执行 Skip()
函数,则会后面绑定的函数将会不起作用。例如,我使用 Bind
绑定了 fun1()
,使用事件表注册了 fun2()
,如果 fun1()
的事件对象没有执行 Skip()
,则 fun2()
永远也不会被这个事件的事件表所触发。
四、事件类型
事件类型主要用于 Bind
和 Connect
,在事件表并不适用。以下是示例:
Bind:
MyFrame::MyFrame(...)
{
Bind(wxEVT_MOTION, &MyFrame::onMotion, this);
}
void MyFrame::onMotion(wxMouseEvent &WXUNUSED(e))
{
printf("函数被触发\n");
fflush(stdout);
}
Connect:
MyFrame::MyFrame(...)
{
Connect(wxEVT_MOTION, (wxObjectEventFunction)&MyFrame::onMotion);
}
MyFrame::~MyFrame()
{
Disconnect(wxEVT_MOTION, (wxObjectEventFunction)&MyFrame::onMotion); // 这句别忘了
}
void MyFrame::onMotion(wxMouseEvent &WXUNUSED(e))
{
wxPoint pos = e.GetPosition(); // 获取当前鼠标的坐标
printf("鼠标在坐标 (%d,%d) 处\n", pos.x, pos.y);
fflush(stdout);
}
上述两个例子中,wxEVT_MOTION
就是一个事件类型,它表示的是鼠标移动事件,如果把该事件绑定到一个函数中,那么当鼠标移动时就会调用该函数。
五、Bind的多种使用方式
1. 绑定成员函数
Bind(wxEVT_MOTION, &MyFrame::OnMotion, this);
2. 绑定普通函数
Bind(wxEVT_MOTION, OnMotion);
3. 绑定lambda
Bind(wxEVT_MOTION, []{ print("hello"); });
4. 绑定函数对象
std::function fun = [] { print("hello"); };
Bind(wxEVT_MOTION, []{ print("hello"); });
void OnMotion(wxMouseEvent &e)
{
printf("hello\n");
fflush(stdout);
e.Skip();
}
...
// 某个成员函数内部...
std::function fun = OnMotion;
Bind(wxEVT_MOTION, fun);
5. 绑定 std::bind
void onMotion(wxMouseEvent &e, int i)
{
printf("flag is %d", i);
fflush(stdout);
e.Skip();
}
...
// 某个成员函数内部...
int myFlag = 10;
Bind(wxEVT_MOTION, std::bind(onMotion2, std::placeholders::_1, myFlag));
以上列举的是常用的方法,可能还有其他的一些使用方式,但我目前没用上,有意者欢迎留言补充。
六、事件表的使用方式
事件表的写法与MFC有点相似。首先,要在类声明中添加这句代码:
wxDECLARE_EVENT_TABLE();
推荐添加到类声明的最后,这样可以避免搞乱作用域。
然后在类声明之后(我习惯放在源文件中)添加以下代码:
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
// 此处注册事件
wxEND_EVENT_TABLE()
事件表便添加成功。现在,添加一个事件:
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_SIZE(MyFrame::OnSize)
wxEND_EVENT_TABLE()
这个事件在窗口size改变时被触发。EVT_SIZE
宏是窗口size的事件宏,它只接受一个参数。当然有接受两个参数的事件宏,例如 EVT_MENU
,它把菜单项与事件函数连接了起来,用法如下:
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(ID_MENU_ABOUT, MyFrame::OnAbout)
wxEND_EVENT_TABLE()
至于事件宏能不能传入普通函数,我目前没成功过,想尝试的小伙伴可以试试~~
事件篇Ⅰ 至此完毕,欢迎大家指正!还请大家点点赞,给我点动力~~
上一篇:【wxWidgets 教程】HelloWorld 程序详细介绍(二)
下一篇:【wxWidgets 教程】事件篇Ⅱ(四)