有时,我们会想让程序处理我们自己的事件,在wxWidgets中如何实现自定义事件呢?
如果你要使用自定义的事件,你需要下面的几个步骤:
- 从一个合适的事件类派生一个你自己的事件类,声明动态类型信息并且实现一个Clone函数,按照你自己的意愿增加新的数据成员和函数成员.如果你希望这个事件在窗口继承关系之间传递,你应该使用的wxCommandEvent派生类,如果你希望这个事件的处理函数可以调用Veto(译者注:某些事件可以调用这个函数来阻止后续可能对这个事件进行的任何操作(如果有的话),最典型的例子是关闭窗口事件wxEVT_CLOSE),你应该使用 wxNotifyEvent的派生类.
- 为这个事件类的处理函数定义类型.
- 定义一个你的事件类支持的事件类型的表。这个表应该定义在你的头文件中。用BEGIN_DECLARE_EVENT_TYPES()宏和END_DECLARE_EVENT_TYPES()宏包含起来。其中的每一个支持的事件的声明应该使用DECLARE_EVENT_TABLE (name, integer)格式的宏.然后在你的.cpp文件中使用DEFINE_EVENT_TYPE(name)来实现这个事件类.
- 为每个你的事件类支持的事件定义一个事件映射宏。
我们还是通过例子来让上面这段绕口的话显的更生动一些。假设我们想实现一个事件,此事件的功能是当wxApp的OnInit()执行完毕后,给MainFrame对象发送一个事件,通知MainFrame对象,应用程序初始化完成。
①首先,我们从wxCommandEvent派生一个wxStudyEvent事件类
②我们实现virtual wxEvent* Clone() const { return new wxStudyEvent(*this);}
③在此事件类中定义我们想要实现的接口方法
④定义一个事件回调函数指针
⑤通过BEGIN_DECLARE_EVENT_TYPES()声明事件类型
⑥定义事件处理宏,如#define EVT_APP_ONINIT_OK(fn)
⑦在cpp文件中,我们通过DEFINE_LOCAL_EVENT_TYPE(wxEVT_APP_ONINIT_OK)来实现我们的事件处理
以下是代码部份:
#ifndef SDK_EVENTS_H
#define SDK_EVENTS_H
#include < wx / event .h >
class MainFrame;
class Plugin;
class wxStudyEvent : public wxCommandEvent
... {
public:
wxStudyEvent(wxEventType cmdType = wxEVT_NULL,int id=0,MainFrame* pMainFrame = NULL,Plugin* plugin=NULL)
:wxCommandEvent(cmdType,id),
m_pMainFrame(pMainFrame),
m_plugin(plugin)
...{}
wxStudyEvent(const wxStudyEvent& event)
:wxCommandEvent(event),
m_pMainFrame(event.m_pMainFrame),
m_plugin(event.m_plugin)
...{}
virtual wxEvent* Clone() const ...{ return new wxStudyEvent(*this);}
public:
void SetMainFrame(MainFrame* pMainFrame) ...{ m_pMainFrame = pMainFrame;}
MainFrame* GetMainFrame() const ...{ return m_pMainFrame;}
void SetPlubin(Plugin* plugin) ...{ m_plugin = plugin;}
Plugin* GetPlugin() const ...{ return m_plugin;}
void SetEventMsg(const wxString& msg) ...{ m_strEventMsg = msg;}
wxString GetEventMsg() const ...{ return m_strEventMsg;}
protected:
MainFrame* m_pMainFrame;
Plugin* m_plugin;
wxString m_strEventMsg;
private:
DECLARE_DYNAMIC_CLASS(wxStudyEvent)
} ;
// 定义事件回调方法指针
typedef void (wxEvtHandler:: * wxStudyEventFunction)(wxStudyEvent & );
// 声明事件类型
BEGIN_DECLARE_EVENT_TYPES()
// DECLARE_EVENT_TYPE(wxEVT_APP_ONINIT_OK,evtID_APP_ONINIT_OK)
DECLARE_LOCAL_EVENT_TYPE(wxEVT_APP_ONINIT_OK,evtID_APP_ONINIT_OK)
// DECLARE_EXPORTED_EVENT_TYPE(EVTIMPORT,wxEVT_APP_ONINIT_OK,evtID_APP_ONINIT_OK)
END_DECLARE_EVENT_TYPES()
/**/ /*
#define DECLARE_ST_EVENT_TYPE(type) extern const wxEventType type;
#define DEFINE_ST_EVENT_TYPE(type) const wxEventType type = wxNewEventType();
DECLARE_ST_EVENT_TYPE(wxEVT_APP_ONINIT_OK)
*/
#define EVT_APP_ONINIT_OK(fn)
DECLARE_EVENT_TABLE_ENTRY(
wxEVT_APP_ONINIT_OK,
- 1 ,
- 1 ,
(wxObjectEventFunction)(wxEventFunction)(wxStudyEventFunction) & fn,
(wxObject * )NULL),
#endif
// wxStudyEvent.cpp
#include " sdk_events.h "
IMPLEMENT_DYNAMIC_CLASS(wxStudyEvent,wxCommandEvent)
// DEFINE_ST_EVENT_TYPE(wxEVT_APP_ONINIT_OK)
// DEFINE_EVENT_TYPE(wxEVT_APP_ONINIT_OK)
DEFINE_LOCAL_EVENT_TYPE(wxEVT_APP_ONINIT_OK)
对于宏:
#define EVT_APP_ONINIT_OK(fn)/
DECLARE_EVENT_TABLE_ENTRY(/
wxEVT_APP_ONINIT_OK,/
-1,/
-1,/
(wxObjectEventFunction)(wxEventFunction)(wxStudyEventFunction)&fn,/
(wxObject*)NULL),
这个宏的作用其实是把一个五元组放入到一个数组中,所以这段代码的语法看上去是奇怪了一些,这个五元组的意义分别解释如下:
- 事件类型:一个事件类可以处理多种事件类型,但是在我们的例子中,只处理了一个事件类型,所以就只有一个事件映射宏的记录。这个事件类型必须要和事件处理函数所有处理的事件的类型一致。
- 传递给事件映射宏的标识符:只有当事件的标识符和事件表中的标识符一致的时候,相应的事件处理函数才会被调用。
- 第二标识符。在这里-1表示没有第二标识符。
- 事件处理函数。如果没有类型的强制转换,一些编译器会报错,这也就是我们要定义事件处理函数类型的原因。
- 用户数据,通常都是NULL;
在实际中,我们可以通过类似以下的代码进行调用:
#define WX_MAIN_APP_H
// MainApp.h
#include < wx / app.h >
class MainFrame;
class wxStudyEvent;
class MainApp : public wxApp
... {
public:
MainApp();
public:
virtual bool OnInit();
private:
MainFrame* InitFrame();
void OnInitOK(wxStudyEvent& event);
private:
DECLARE_EVENT_TABLE();
} ;
DECLARE_APP(MainApp)
#endif
// MainApp.cpp
#include " mainApp.h "
#include " MainFrame.h "
#include " sdk/sdk_events.h "
#include < wx / msgdlg.h >
IMPLEMENT_APP(MainApp)
BEGIN_EVENT_TABLE(MainApp,wxApp)
EVT_APP_ONINIT_OK(MainApp::OnInitOK)
END_EVENT_TABLE()
MainApp::MainApp()
... {
}
bool MainApp::OnInit()
... {
MainFrame* pMainFrm = InitFrame();
wxStudyEvent event(wxEVT_APP_ONINIT_OK,0,pMainFrm);
event.SetEventMsg(_T("wxWidgets自定义事件学习"));
event.SetEventObject(this);
pMainFrm->GetEventHandler()->ProcessEvent(event);
return true;
}
MainFrame * MainApp::InitFrame()
... {
MainFrame* pMainFrm = new MainFrame(_T("MainFrame"));
pMainFrm->Show(true);
return pMainFrm;
}
void MainApp::OnInitOK(wxStudyEvent & event )
... {
wxMessageBox(_T("在MainApp中处理事件->") + event.GetEventMsg());
}
// MainFrame.h
#ifndef MAIN_FRAME_H
#define MAIN_FRAME_H
#include < wx / frame.h >
// #include <wx/wx.h>
class wxStudyEvent;
class MainFrame : public wxFrame
... {
public:
MainFrame(const wxString& strTitle);
private:
void InitCtrl();
private://event
void OnAbout(wxCommandEvent& event);
void OnQuit(wxCommandEvent& event);
//测试动态库
void OnShowMessage(wxCommandEvent& event);
void OnAppInitOK(wxStudyEvent& event);
private:
DECLARE_EVENT_TABLE()
} ;
#endif
// MainFrame.cpp
#include " MainFrame.h "
#include < wx / menu.h >
// #include <wx/string.h>
#include < wx / msgdlg.h >
#include < wx / dynlib.h >
#include < wx / filefn.h >
#include < wx / filename.h >
#include < wx / toolbar.h >
#include " maindef.h "
#include " sdk/sdk_events.h "
// EVENT DECLARE
BEGIN_EVENT_TABLE(MainFrame,wxFrame)
EVT_MENU(wxID_EXIT,MainFrame::OnQuit)
EVT_MENU(wxID_ABOUT,MainFrame::OnAbout)
EVT_MENU(wxID_TEST_DLL,MainFrame::OnShowMessage)
EVT_APP_ONINIT_OK(MainFrame::OnAppInitOK)
END_EVENT_TABLE()
// END EVENT DECLARE
MainFrame::MainFrame( const wxString & strTitle)
:wxFrame(NULL,wxID_ANY,strTitle)
... {
InitCtrl();
}
void MainFrame::InitCtrl()
... {
wxMenu* pMenu = new wxMenu();
pMenu->Append(wxID_EXIT,_T("Exit"));
pMenu->Append(wxID_ABOUT,_T("About"));
pMenu->Append(wxID_TEST_DLL,_T("测试动态库"));
wxMenuBar* pMenuBar = new wxMenuBar();
pMenuBar->Append(pMenu,_T("File"));
SetMenuBar(pMenuBar);
/**//*wxToolBar(wxWindow* parent, wxWindowID id,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxTB_HORIZONTAL | wxNO_BORDER,
const wxString& name = wxPanelNameStr)
*/
wxToolBar* pMainToolBar = CreateToolBar( wxTB_DOCKABLE|wxTB_HORIZONTAL, 6000 );
pMainToolBar->AddTool( 6001, wxT("tool"), wxNullBitmap, wxNullBitmap, wxITEM_NORMAL, wxT(""), wxT("") );
//pMainToolBar->Realize();
//wxToolBar* pPluginBar = new wxToolBar(this,-1);
}
/**/ /*
* 退出程序
* @param wxCommandEvent& event
*/
void MainFrame::OnQuit(wxCommandEvent & event )
... {
Close();
}
void MainFrame::OnAbout(wxCommandEvent & event )
... {
wxString strMsg(_T("wxWidgets study."));
wxString strCaption(_T("关于"));
wxMessageBox(strMsg, strCaption, wxOK | wxICON_INFORMATION,this);
//wxMessageBox("Quit program?", "Confirm",wxYES_NO | wxCANCEL, this);
}
void MainFrame::OnShowMessage(wxCommandEvent & event )
... {
//wxString dir(wxGetWorkingDirectory());
//wxMessageBox(dir,_T("dir"),wxOK);
wxChar name[MAX_PATH] = ...{0};
GetModuleFileName(0L, name, MAX_PATH);
//wxMessageBox(name,_T("dir"),wxOK);
wxFileName fname(name);
wxString strPath(fname.GetPath(wxPATH_GET_VOLUME));
wxDynamicLibrary lib;
wxString libfile(strPath << _T("/plugins/msgplugin/msgplugin.dll"));
lib.Load(libfile);
if(!lib.IsLoaded())
...{
wxMessageBox(libfile << _T(" load error."),_T("Error"),wxOK | wxICON_ERROR);
return;
}
typedef int (*ShowMessageProc)(const wxString&);
ShowMessageProc ShowMessage = (ShowMessageProc)lib.GetSymbol(_T("ShowMessage"));
if(NULL == ShowMessage)
...{
wxMessageBox(_T("don't call the method: ShowMessag"), _T("Error"),
wxOK | wxICON_ERROR);
return;
}
ShowMessage(_T("call from ") + libfile);
}
void MainFrame::OnAppInitOK(wxStudyEvent & event )
... {
wxMessageBox(_T("在MainFrame中处理事件->") + event.GetEventMsg());
event.Skip();
}
在MainApp的OnInit()方法中,有一段如下代码:
event .SetEventMsg(_T( " wxWidgets自定义事件学习 " ));
event .SetEventObject( this );
pMainFrm -> GetEventHandler() -> ProcessEvent( event );
就是将事件wxStudyEvent发送到MainFrame对象的,在MainFrame对象中,我们在事件表中如下处理:
EVT_APP_ONINIT_OK(MainFrame::OnAppInitOK)
在MainFrame::OnAppInitOK()方法中,我们的代码如下:
wxMessageBox(_T("在MainFrame中处理事件->") + event.GetEventMsg());
event.Skip();
这理的event.Skip()方法是将此事件向MainFrame的父级传递,我们在MainApp中也有类似的定义,此时MainApp中的事件处理也会被触发,如果我们不使用event.Skip(),则事件在MainFrame中被处理后就停止发送了。当然这里调用Skip,只是为了加深理解wxWidgets的事件处理机制,在实际应用中,视具体需求而定。