博客
关于我
wxWidgets源码分析(3) - 消息映射表
阅读量:433 次
发布时间:2019-03-06

本文共 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_TABLEEND_EVENT_TABLE之间:

  1. sm_eventTableEntries保存消息映射表,也就是消息ID和消息处理函数关系;
  2. 实现GetEventTable方法供调用者使用;
  3. 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_TABLEEND_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方法:

  1. wxEventHashTable根据消息类型将Table分段,这样可以快速查找到指定类型的table;
  2. 遍历指定类型的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处理流程如下:

  1. 检查当前收到的消息是否与本项wxEventTableEntryBase匹配
  2. 调用用户的处理函数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::DoBindwxEvtHandler::DoUnbind执行的,wxWidgets提供了多种接口执行这个操作。

需要注意的是:如果传递进wxEvtHandler对象,则在处理的时候调用用户指定的wxEvtHandler对象进行处理;如果未指定,则直接使用当前的wxEvtHandler对象进行处理。

提供类似功能的还有Connect接口,但是Connect用起来比较复杂,建议后续代码全部使用Bind接口,这里不做赘述。

Bind接口是模板函数,提供三种接口:

  1. 直接绑定全局函数,此时只有event类型和function参数必须,建议使用
  2. 绑定Funtor函数,用户需要自己用wxNewEventFunctor进行封装,不建议使用
  3. 使用类内部函数,同时需要指定类指针,建议使用

三种接口声明如下:

template 
void 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/

你可能感兴趣的文章
liteide错误: 进程无法启动--解决方法
查看>>
Java程序中的代理作用和应用场景及实现
查看>>
Java 前台后台数据传递、中文乱码解决方法
查看>>
Git报错:Permission denied (publickey)
查看>>
常见的图文布局
查看>>
Laravel - 上手实现 - 文件上传、保存到 public 目录下
查看>>
将mongo设置为windows的服务
查看>>
【Flink】Flink 底层RPC框架分析
查看>>
【集合框架】JDK1.8源码分析之LinkedList(七)
查看>>
Jenkins 集成postman 自动化运行接口测试用例
查看>>
django+appium实现UI自动化测试平台(开源部分,可定制开发)
查看>>
第七届C/C++B-方格填数 DFS
查看>>
数据结构课设--3哈夫曼编码译码系统(树应用)
查看>>
pku 1061 青蛙的约会 扩展欧几里得
查看>>
Spring Boot 2.4 配置文件将加载机制大变化
查看>>
也来玩玩 javascript对象深拷贝,浅拷贝
查看>>
【转载】Kubernetes CNI网络最强对比:Flannel、Calico、Canal和Weave
查看>>
Kubernetes实战总结 - 动态存储管理StorageClass
查看>>
wcf webHttpBinding Post 大数据量提交 ios c#客户端
查看>>
[LeetCode题解]141. 环形链表 | 快慢指针
查看>>