事件处理过程

每一个wxEvtHandler的派生类,如frame,button,…等,都会在其内部维护一事件表,用来告诉wxWidgets事件和事件处理过程的对应关系。所有继承自wxWindow的窗口类,以及应用程类都是wxEvtHandler的派生类。

在wxWdigets中实现事件表,有以下3种方式:

  1. 通过DECLARE_EVENT_TABLEBEGIN_EVENT_TABLE,END_EVENT_TABLE实现事件表
  2. 通过wxEvtHandlerBind实现事件表
  3. 通过wxEvtHandlerConnect动态实现事件表

静态事件表

要创建一个静态事件表(意味着它在编译期间创建的),你需要下面几个步骤:

  1. 定义一个直接或者间接继承自wxEvtHandler的类
  2. 为每一个你想要处理的事件定义一个处理函数
  3. 在这个类中使用DECLARE_EVENT_TABLE声明事件
  4. 在.cpp文件中使用BEGIN_EVENT_TABLEEND_EVENT_TABLE实现一个事件表
  5. 在事件表的实现中增加事件宏,来实现从事件到事件处理的映射

所有的事件处理函数拥有相同的形式:

  1. 返回值为void
  2. 不是虚函数
  3. 只有一个事件对象作为参数,这个事件对象参数的类型是随着这个处理函数要处理的事件的变化而变化的。

通过宏定义实现事件表

通过DECLARE_EVENT_TABLEBEGIN_EVENT_TABLE,END_EVENT_TABLE3个宏实现事件表

  1. 在类定义.h文件中声明事件表

    MainFrame.h
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class MainFrame : public wxFrame
    {
    public:
    MainFrame();
    void OnExit(wxCommandEvent &event);

    private:
    void OnHello(wxCommandEvent &event);
    void OnAbout(wxCommandEvent &event);

    DECLARE_EVENT_TABLE(); // 声明事件表
    };

  2. 在类的实现.cpp文件中定义事件表

    MainFrame.cpp
    1
    2
    3
    4
    // 该事件表是直接在.cpp文件中定义,要要写在类的实现中去了哦
    BEGIN_EVENT_TABLE(MainFrame, wxFrame)
    EVT_MENU(wxID_ABOUT, MainFrame::OnAbout) // 定义了wxID_ABOUT菜单事件,映射到MainFrame的OnAbout函数上
    END_EVENT_TABLE()

wxEvtHandlerBind实现事件表

wxFramewxEvtHandler类的子类,所以也可以直接调用Bind方法来邦定事件表

MainFrame.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
MainFrame::MainFrame()
: wxFrame(NULL, wxID_ANY, "Hello World")
{
SetIcon(wxICON(sample));
wxMenu *menuFile = new wxMenu;
menuFile->Append(ID_Hello, "&Hello...\tCtrl-H",
"Help string shown in status bar for this menu item");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT);
wxMenu *menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
wxMenuBar *menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");

Bind(wxEVT_MENU, &MainFrame::OnHello, this, ID_Hello); // 将ID_Hello菜单点击事件,映射到MainFrame的OnHello处理函数上
}

动态事件表

动态事件表,也就是说在运行期改变事件表的映射关系。使用这种事件映射方法的原因,可能是你想在程序运行的不同时刻使用不同的映射关系。或者因为你使用的那种语言(如python)不支持静态映射。通过动态事件表你甚至可以单独的将事件表中的某一条目在运行期间打开或关闭。除此之外,动态事件处理还允许你在不同类之间共享事件函数。
和动态事件处理相关的API有两个wxEvtHandler::ConnectwxEvtHandler::Disconnect,大多数情况下你不需要手动调用wxEvtHandler::Disconnect函数,这个函数将在窗口初释放时自动调用。

HelloWorld.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "MainFrame.h"

class MainApp : public wxApp
{
public:
virtual bool OnInit();

private:
void OnExit(wxCommandEvent &event);
};

wxIMPLEMENT_APP(MainApp);

bool MainApp::OnInit()
{
MainFrame *frame = new MainFrame();
// 同一控件的同一事件,后连接的先被调用
frame->Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::OnExit));
frame->Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainApp::OnExit));
frame->Show(true);
return true;
}

void MainApp::OnExit(wxCommandEvent &event)
{
wxMessageBox(_("你点击了退出菜单!"));
event.Skip(); // 该消息继续在事件循环中传递
}
示例源码
MainFrame.h
MainFrame.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <wx/wx.h>

class MainFrame : public wxFrame
{
public:
MainFrame();
void OnExit(wxCommandEvent &event);

private:
void OnHello(wxCommandEvent &event);
void OnAbout(wxCommandEvent &event);

DECLARE_EVENT_TABLE();
};

enum
{
ID_Hello = 1
};
MainFrame.cpp
MainFrame.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "MainFrame.h"
#include "sample.xpm"

BEGIN_EVENT_TABLE(MainFrame, wxFrame)
EVT_MENU(wxID_ABOUT, MainFrame::OnAbout)
END_EVENT_TABLE()

MainFrame::MainFrame()
: wxFrame(NULL, wxID_ANY, "Hello World")
{
// SetIcon(wxIcon(sample_xpm));
SetIcon(wxICON(sample));
wxMenu *menuFile = new wxMenu;
menuFile->Append(ID_Hello, "&Hello...\tCtrl-H",
"Help string shown in status bar for this menu item");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT);
wxMenu *menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
wxMenuBar *menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");

Bind(wxEVT_MENU, &MainFrame::OnHello, this, ID_Hello);
// Bind(wxEVT_MENU, &MainFrame::OnAbout, this, wxID_ABOUT);
// Bind(wxEVT_MENU, &MainFrame::OnExit, this, wxID_EXIT);
}

void MainFrame::OnExit(wxCommandEvent &event)
{
wxLogMessage("The Quit menu is clicked!");
Close(true);
}

void MainFrame::OnAbout(wxCommandEvent &event)
{
wxMessageBox("This is a wxWidgets Hello World example",
"About Hello World", wxOK | wxICON_INFORMATION);
}

void MainFrame::OnHello(wxCommandEvent &event)
{
wxLogMessage("Hello world from wxWidgets!");
}
HelloWorld.cpp
HelloWorld.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include "MainFrame.h"

class MainApp : public wxApp
{
public:
virtual bool OnInit();

private:
void OnExit(wxCommandEvent &event);
};

wxIMPLEMENT_APP(MainApp);

bool MainApp::OnInit()
{
MainFrame *frame = new MainFrame();
// 同一控件的同一事件,后连接的先被调用
frame->Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::OnExit));
frame->Connect(wxID_EXIT, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainApp::OnExit));
frame->Show(true);
return true;
}

void MainApp::OnExit(wxCommandEvent &event)
{
wxMessageBox(_("你点击了退出菜单!"));
event.Skip(); // 该消息继续在事件循环中传递
}