本文共 7382 字,大约阅读时间需要 24 分钟。
消息是GUI程序的核心,所有的操作行为均通过消息传递。
使用静态EventTable将事件号和处理代码绑定起来,用法示例:
// 声明class debugWXFrame: public wxFrame{ DECLARE_EVENT_TABLE()};// 实现BEGIN_EVENT_TABLE(debugWXFrame,wxFrame) EVT_MENU(ID_MenuUser, debugWXFrame::OnCheckMenu)END_EVENT_TABLE()
先看下定义, wxDECLARE_EVENT_TABLE
用于在当前类中声明一些数据,大部分都是静态数据,另外提供了GetEventTable
来访问这个表;
#define DECLARE_EVENT_TABLE() wxDECLARE_EVENT_TABLE();#define wxDECLARE_EVENT_TABLE() \ private: \ static const wxEventTableEntry sm_eventTableEntries[]; \ protected: \ static const wxEventTable sm_eventTable; \ virtual const wxEventTable* GetEventTable() const; \ static wxEventHashTable sm_eventHashTable; \ virtual wxEventHashTable& GetEventHashTable() const
下面是实现,用于初始化这些静态变量,所有的消息定义都是位于wxBEGIN_EVENT_TABLE
和END_EVENT_TABLE
之间:
sm_eventTableEntries
保存消息映射表,也就是消息ID和消息处理函数关系;GetEventTable
方法供调用者使用;sm_eventHashTable
用于保存HashTable,查找速率会提升;#define BEGIN_EVENT_TABLE(a,b) wxBEGIN_EVENT_TABLE(a,b)#define END_EVENT_TABLE() wxEND_EVENT_TABLE()#define wxBEGIN_EVENT_TABLE(theClass, baseClass) \ const wxEventTable theClass::sm_eventTable = \ { &baseClass::sm_eventTable, &theClass::sm_eventTableEntries[0] }; \ const wxEventTable *theClass::GetEventTable() const \ { return &theClass::sm_eventTable; } \ wxEventHashTable theClass::sm_eventHashTable(theClass::sm_eventTable); \ wxEventHashTable &theClass::GetEventHashTable() const \ { return theClass::sm_eventHashTable; } \ const wxEventTableEntry theClass::sm_eventTableEntries[] = { \#define wxEND_EVENT_TABLE() \ wxDECLARE_EVENT_TABLE_TERMINATOR() };
位于wxBEGIN_EVENT_TABLE
和END_EVENT_TABLE
之间的是消息处理项,比如上面的EVT_MENU(ID_MenuUser, debugWXFrame::OnCheckMenu)
就是一项,我们来展开这项:
// 用户使用这个宏来定义事件映射#define EVT_MENU(winid, func) wx__DECLARE_EVT1(wxEVT_MENU, winid, wxCommandEventHandler(func))// 事件映射内部宏#define wx__DECLARE_EVT2(evt, id1, id2, fn) \ wxDECLARE_EVENT_TABLE_ENTRY(evt, id1, id2, fn, NULL),#define wx__DECLARE_EVT1(evt, id, fn) \ wx__DECLARE_EVT2(evt, id, wxID_ANY, fn)// 根据传递进来的参数创建一个wxEventTableEntry对象#define wxDECLARE_EVENT_TABLE_ENTRY(type, winid, idLast, fn, obj) \ wxEventTableEntry(type, winid, idLast, wxNewEventTableFunctor(type, fn), obj)
那么EVT_MENU(ID_MenuUser, debugWXFrame::OnCheckMenu)
展开后就得到:
wxEventTableEntry(wxEVT_MENU, winid, wxID_ANY, wxNewEventTableFunctor(wxEVT_MENU, wxCommandEventHandler(fn)), NULL)
wxNewEventTableFunctor
用于实例化一个wxObjectEventFunctor
对象,wxCommandEventHandler
用于强转消息处理函数,传递给wxNewEventTableFunctor
使用。
#define wxCommandEventHandler(func) \ wxEVENT_HANDLER_CAST(wxCommandEventFunction, func)#define wxEVENT_HANDLER_CAST( functype, func ) \ ( wxObjectEventFunction )( wxEventFunction )wxStaticCastEvent( functype, &func )inline wxObjectEventFunctor *wxNewEventTableFunctor(const wxEventType& WXUNUSED(evtType), wxObjectEventFunction method){ return new wxObjectEventFunctor(method, NULL);}
这样就可以静态创建多个wxEventTableEntry
对象,加入到sm_eventTableEntries
中。
消息处理过程,wxEvtHandler::TryHereOnly
会调用静态表查询处理:
bool wxEvtHandler::TryHereOnly(wxEvent& event){ // Then static per-class event tables if ( GetEventHashTable().HandleEvent(event, this) ) return true;}
上面GetEventHashTable
方法将返回一个wxEventHashTable
对象,随后调用这个对象的HandleEvent
方法:
wxEventHashTable
根据消息类型将Table分段,这样可以快速查找到指定类型的table;wxEventTableEntry
调用ProcessEventIfMatchesId
bool wxEventHashTable::HandleEvent(wxEvent &event, wxEvtHandler *self){ // Find all entries for the given event type. wxEventType eventType = event.GetEventType(); const EventTypeTablePointer eTTnode = m_eventTypeTable[eventType % m_size]; if (eTTnode && eTTnode->eventType == eventType) { const wxEventTableEntryPointerArray& eventEntryTable = eTTnode->eventEntryTable; const size_t count = eventEntryTable.GetCount(); for (size_t n = 0; n < count; n++) { const wxEventTableEntry& entry = *eventEntryTable[n]; if ( wxEvtHandler::ProcessEventIfMatchesId(entry, self, event) ) return true; } } return false;}
wxEvtHandler::ProcessEventIfMatchesId
处理流程如下:
wxEventTableEntryBase
匹配m_fn
执行,这里m_fn
也是经过了好几层的封装,有兴趣的继续跟踪。bool wxEvtHandler::ProcessEventIfMatchesId(const wxEventTableEntryBase& entry, wxEvtHandler *handler, wxEvent& event){ int tableId1 = entry.m_id, tableId2 = entry.m_lastId; // match only if the event type is the same and the id is either -1 in // the event table (meaning "any") or the event id matches the id // specified in the event table either exactly or by falling into // range between first and last if ((tableId1 == wxID_ANY) || (tableId2 == wxID_ANY && tableId1 == event.GetId()) || (tableId2 != wxID_ANY && (event.GetId() >= tableId1 && event.GetId() <= tableId2))) { event.Skip(false); event.m_callbackUserData = entry.m_callbackUserData; (*entry.m_fn)(handler, event); if (!event.GetSkipped()) return true; } return false;}
动态映射表项的增加和删除是通过wxEvtHandler::DoBind
和wxEvtHandler::DoUnbind
执行的,wxWidgets提供了多种接口执行这个操作。
需要注意的是:如果传递进wxEvtHandler
对象,则在处理的时候调用用户指定的wxEvtHandler
对象进行处理;如果未指定,则直接使用当前的wxEvtHandler
对象进行处理。
提供类似功能的还有Connect
接口,但是Connect
用起来比较复杂,建议后续代码全部使用Bind接口
,这里不做赘述。
Bind
接口是模板函数,提供三种接口:
wxNewEventFunctor
进行封装,不建议使用;三种接口声明如下:
templatevoid Bind(const EventTag& eventType, void (*function)(EventArg &), int winid = wxID_ANY, int lastId = wxID_ANY, wxObject *userData = NULL) template void Bind(const EventTag& eventType, const Functor &functor, int winid = wxID_ANY, int lastId = wxID_ANY, wxObject *userData = NULL) template void Bind(const EventTag &eventType, void (Class::*method)(EventArg &), EventHandler *handler, int winid = wxID_ANY, int lastId = wxID_ANY, wxObject *userData = NULL)
与静态消息入口相同,在wxEvtHandler::TryHereOnly
得到调用:
bool wxEvtHandler::TryHereOnly(wxEvent& event){ // Handle per-instance dynamic event tables first if ( m_dynamicEvents && SearchDynamicEventTable(event) ) return true;}
动态表查找比较简单,动态消息表存在m_dynamicEvents
链表中,只要出去链表的每一项逐个调用ProcessEventIfMatchesId
就可以了。
bool wxEvtHandler::SearchDynamicEventTable( wxEvent& event ){ wxList::compatibility_iterator node = m_dynamicEvents->GetFirst(); while (node) { wxDynamicEventTableEntry *entry = (wxDynamicEventTableEntry*)node->GetData(); node = node->GetNext(); if ( event.GetEventType() == entry->m_eventType ) { wxEvtHandler *handler = entry->m_fn->GetEvtHandler(); if ( !handler ) handler = this; if ( ProcessEventIfMatchesId(*entry, handler, event) ) return true; } } return false;}
转载地址:http://soyfz.baihongyu.com/