wxWidgets源码分析(3) - 消息映射表

目录

  • 消息映射表

    • 静态消息映射表
    • 静态消息映射表处理过程
    • 动态消息映射表
    • 动态消息映射表处理过程

消息映射表

消息是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 <typename EventTag, typename EventArg>
void Bind(const EventTag& eventType,
          void (*function)(EventArg &),
          int winid = wxID_ANY,
          int lastId = wxID_ANY,
          wxObject *userData = NULL)

template <typename EventTag, typename Functor>
void Bind(const EventTag& eventType,
          const Functor &functor,
          int winid = wxID_ANY,
          int lastId = wxID_ANY,
          wxObject *userData = NULL)

template <typename EventTag, typename Class,
          typename EventArg, typename EventHandler>
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;
}

原文地址:https://www.cnblogs.com/psbec/p/9784568.html

时间: 2024-08-22 10:56:31

wxWidgets源码分析(3) - 消息映射表的相关文章

wxWidgets源码分析(4) - 消息处理过程

目录 消息处理过程 消息如何到达wxWidgets Win32消息与wxWidgets消息的转换 菜单消息处理 消息处理链(基于wxEvtHandler) 消息处理链(基于wxWindow) 总结 消息处理过程 消息如何到达wxWidgets Windows程序有其自身运行的一套规律,::SendMessage是MS提供的windows消息发送接口,用户调用这个接口后会进入到MS系统库程序,此接口指定了目标HWND和消息参数,Windows系统内部会查找指定HWND,然后通过gapfnScSen

wxWidgets源码分析(8) - MVC架构

目录 MVC架构 wxDocManager文档管理器 框架菜单命令的执行过程 SDI消息传递总结 更新视图 MVC架构 wxDocManager文档管理器 wxWidgets使用wxDocManager类来管理MVC中的文档和视图的对应关系,使用方法: 创建一个wxDocManager对象,然后向此对象中增加文档模板wxDocTemplate对象,文档模板对象中说明了文档类型和该文档对应的文档类.视图类: 将此wxDocManager对象传递给wxDocParentFrame类(SDI),这样框

源码分析 Kafka 消息发送流程(文末附流程图)

温馨提示:本文基于 Kafka 2.2.1 版本.本文主要是以源码的手段一步一步探究消息发送流程,如果对源码不感兴趣,可以直接跳到文末查看消息发送流程图与消息发送本地缓存存储结构. 从上文 初识 Kafka Producer 生产者,可以通过 KafkaProducer 的 send 方法发送消息,send 方法的声明如下: Future<RecordMetadata> send(ProducerRecord<K, V> record) Future<RecordMetada

飞鸽传书源码分析二消息机制

转载请注明出处:http://blog.csdn.net/mxway/article/details/40225725 本篇文章是在飞鸽传书2.06源码的基础的分析的. 飞鸽传书的消息大致可分为三类:普通窗口类(后面以TMainWin为例进行分析)消息,对话框类(后面以TSendDlg为例进行分析)消息,对话框控件(后面以TEditSub为例进行分析)消息.这三类消息先合后分,这三类窗口设置的消息处理函数都是TApp::WinProc,在TApp::WinProc函数中再分发给各自的消息处理函数

Akka源码分析-Remote-发消息

上一篇博客我们介绍了remote模式下Actor的创建,其实与local的创建并没有太大区别,一般情况下还是使用LocalActorRef创建了Actor.那么发消息是否意味着也是相同的呢? 既然actorOf还是委托给了LocalActorRef,那么在本地创建的Actor发消息还是跟以前一样的,那么如果如何给远程的Actor发消息呢?我们一般是通过actorSelection或者给远程Actor发送一个Identify消息,来接收对应的ActorRef,然后再发消息.我们来分析一下这两者的区

Akka源码分析-Remote-收消息

上一遍博客中,我们分析了网络链接建立的过程,一旦建立就可以正常的收发消息了.发送消息的细节不再分析,因为对于本地的actor来说这个过程相对简单,它只是创立链接然后给指定的netty网路服务发送消息就好了.接收消息就比较麻烦了,因为这对于actor来说是透明的,netty收到消息后如何把消息分发给指定的actor呢?这个分发的过程值得研究研究. 之前分析过,在监听创立的过程中,有一个对象非常关键:TcpServerHandler.它负责链接建立.消息收发等功能.TcpServerHandler继

wxWidgets源码分析(9) - wxString

目录 wxString wxString的中文字符支持 wxString与通用字符串的转换 字符集转换 wxString wxString的中文字符支持 中文字符的编码格式如下: 汉字 GBK 区位码 UTF-8 UTF-16 中 D6 D0 54 48 E4 B8 AD 4E 2D 文 CE C4 46 36 E6 96 87 65 87 不同操作系统的默认内码 Windows系统(默认GBK):41 42 d6 d0 ce c4 Linux系统(默认UTF-8):41 42 e4 b8 ad

android smack源码分析——接收消息以及如何解析消息

在android里面用的smack包其实叫做asmack,该包提供了两种不同的连接方式:socket和httpclient.该并且提供了很多操作xmpp协议的API,也方便各种不同自定义协议的扩展.我们不需要自己重新去定义一套接收机制来扩展新的协议,只需继承然后在类里处理自己的协议就可以了.而本文今天主要说两点,一点就是消息是如何接收的,另一点就是消息是如何通知事件的. 总的思路 1.使用socket连接服务器 2.将XmlPullParser的数据源关联到socket的InputStream

wxWidgets源码分析(7) - 窗口尺寸

目录 窗口尺寸 概述 窗口Size消息的处理 用户调整Size消息的处理 调整窗口大小 程序调整窗口大小 wxScrolledWindow设置窗口大小 获取TextCtrl控件最合适大小 窗口尺寸 概述 类型 说明 Size 当前窗口实际大小,通过wxWindow::SetSize()获取: Client Size 客户区大小: Best Size 最合适的大小,用户可以实现wxWindow::DoGetBestSize()方法,自定义返回最合适的大小: Best Client Size 最合适