zoukankan      html  css  js  c++  java
  • 描述wxWidgets中事件处理的类型转化

    wxWidgets是一个比较常用的UI界面库,我曾经试着使用wxWidgets写一个UI编辑工具,在此期间,学习了一些wxWidgets的知识。我对wxWidgets的绑定(Bind)比较好奇,想知道,wxWidgets是如何知道,我Bind的函数,是需要什么参数,所以查看了一些源代码,这里,将了解的知识写出来:

    首先,给出绑定的相关源代码:

    class wxEvtHandler : public wxObject, 
                public wxTrackable
    {
        // These functions are used for old, untyped, event handlers and don't
        // check that the type of the function passed to them actually matches the 
        // type of the event. They also only allow connecting events to methods of
        // wxEvtHandler-derived classes.
        //
        // The template Bind() methods below are safer and allow connecting
        // events to arbitrary functions or functors -- but require compiler
        // support for templates.
        //
        // Dynamic association of a member function handler with the event handler,
        // winid and event type
    
        void Connect(int winid,
            int lastId,
            wxEventType eventType,
            wxObjectEventFunction func,
            wxObject *userData = NULL,
            wxEvtHandler *eventSink = NULL)
        {
        DoBind(winid, lastId, eventType, 
            wxNewEventFunctor(eventType, func, eventSink),
            userData);
        }
    
        bool Disconnect(int winid,
                int lastId,
                wxEventType eventType,
                wxObjectEventFunction func = NULL,
                wxObject* userData = NULL,
                wxEvtHandler* eventSink = NULL)
        {
        return DoUnbind(winid, lastId, eventType,
            wxMakeEventFunctor(eventType, func, eventSink),
            userData);
        }
    
    
        // Bind a method of a class (called on the specified handler which must
        // be convertible to this class) object to an event:
        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)
        {
        DoBind(winid, lastId, eventType,
                wxNewEventFunctor(eventType, method, handler),
                userData);
        }
    
        template <typename EventTag, typename Class, typename EventArg, typename EventHandler>
        bool Unbind(const EventTag &eventType,
            void (Class::*method)(EventArg&),
            EventHandler *handler,
            int winid = wxID_ANY,
            int lastId = wxID_ANY,
            wxObject* userData = NULL)
        {
        return DoUnbind(winid, lastId, eventType,
                    wxMakeEventFunctor(eventType, method, handler),
                    userData);
        }
    
        void wxEvtHandler::DoBind(int id, 
                      int lastId, 
                      wxEventType eventType,
                      wxEventFunctor *func,
                      wxObject *userData)
        {
        wxDynamicEventTableEntry *entry = 
            new wxDynamicEventTableEntry(eventType, id, lastId, func, userData);
    
        // Check 
        // ...
        
        if (!m_dynamicEvents)
            m_dynamicEvents = new DynamicEvents;
    
        // We prefer to push back the entry here and then iterate over the vector
        // in reverse direction in GetNextDynamicEntry() as it's more efficient
        // than inserting the element at the front.
        m_dynamicEvents->push_back(entry);
    
        // 该函数的以下部分可忽略
        // Make sure we get to know when a sink is destroyed
        wxEvtHandler *eventSink = func->GetEvtHandler();
        if ( eventSink && eventSink != this )
        {
            wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
            if ( evtConnRef )
            evtConnRef->IncRef( );
            else
            new wxEventConnectionRef(this, eventSink);
        }
        }
    };

    我只贴出一部分,与我描述相关的内容,当然,很多时候会有些多余,不过,对于不太懂的地方,可以略过,对于比较重要的地方,我会比较详细的说明。wxWidgets使用的是通过模板来记录类型,然后,再将类型还原回来。

    这里,我随便从wxWidgets中贴出一个Bind使用:

    MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
    : wxFrame((wxFrame *)NULL, -1, title, pos, size)
    {
        wxPanel* mainPane = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS);
        mainPane->Bind(wxEVT_CHAR_HOOK, &MyFrame::OnKeyDown, this);
    }
    void MyFrame::OnKeyDown(wxKeyEvent& event)
    {
        wxMessageBox(wxString::Format("KeyDown: %i
    ", (int)event.GetKeyCode()));
        event.Skip();
    }

    根据wxWidgets源代码,可以知道wxEVT_CHAR_HOOK是const wxEventTypeTag<wxKeyEvent>,从Bind函数,可以知道,我们可以从两处知道OnKeyDown函数的参数是wxKeyEvent,一个是传入的参数wxEVT_CHAR_HOOK,一个是OnKeyDown。根据传参可以知道,实现只会和DoBind的wxEventType eventType,以及wxEventFunctor *func有关。wxDynamicEventTableEntry的构造函数,使用了eventType,以及func作为参数,那么类型转化会与wxDynamicEventTableEntry的实现有关吗?下面参考wxDynamicEventTableEntry的实现:

    struct wxDynamicEventTableEntry : public wxEventTableEntryBase
    {
        wxDynamicEventTableEntry(int evType, int winid, int idLast,
            wxEventFunctor* fn, wxObject *data)
        : wxEventTableEntryBase(winid, idLast, fn, data),
          m_eventType(evType)
        { }
    
        // not a reference here as we can't keep a reference to a temporary int
        // created to wrap the constant value typically passed to Connect() - nor
        // do we need it
        int m_eventType;
    
    private:
        wxDynamicEventTableEntry& operator=(const wxDynamicEventTableEntry&) = delete;
    };

    注意,这里evtType的类型是int,再向上看,其实DoBind第三个参数wxEventType实际类型就是int,也就是说,携带参数类型是wxKeyEvent的参数只有传入DoBind的wxEventFunctor *func。参看wxDynamicEventTableEntry的实现,这个结构甚至不是模板,所以不会携带类型信息。那么唯一的希望,就只有

    wxNewEventFunctor(eventType, func, eventSink)构造的func指针所指示的对象,在DoBind中的wxEvtHandler *eventSink = func->GetEvtHandler();,eventSink并不携带类型信息。

    现在我们要查看wxNewEventFunctor函数的实现:

    template <typename EventTag, typename Class, typename EventArg, typename EventHandler>
    inline wxEventFunctorMethod<EventTag, Class, EventArg, EventHandler> *
        wxNewEventFunctor(const EventTag&, void (Class::*method)(EventArg&), EventHandler *handler)
    {
        return new wxEventFunctorMethod<EventTag, Class, EventArg, EventHandler>
        (method, handler);
    }

    以及wxEventFunctorMethod的实现:
    // functor forwarding the event to a method of the given object
    //
    template <typename EventTag, typename Class, typename EventArg, typename EventHandler>
    class wxEventFunctorMethod : 
        public wxEventFunctor,
        private wxPrivate::HandlerImpl
        <
         Class,
         EventArg,
         wxIsPublicyDerived<Class, wxEvtHandler>::value != 0
        >
    {
        public:
        void operator()(wxEvtHandler* handler, wxEvent& event) override
        {
            Class *realHandler = m_handler;
            if (!realHandler)
            {
            realHandler = this->ConvertFromEvtHandler(handler);
    
            // this is not supposed to happen but check for it nevertheless
            wxCHECK_RET(realHandler, "invalid event handler");
            }
    
            // the real (run-time) type of event is EventClass and we check in
            // the ctor that EventClass can be converted to EventArg, so this cast
            // is always valid
            (realHandler->*method)(static_cast<EventArg&>(event));
        }
    };

    请认真看一下上面的static_cast<EventArg&>,正是这里,wxWiidgets将event类型转变为了正确的函数需要的类型。

    下面贴出,wxWidgets调用上述函数的地方:

    // 下面简略摘抄一下,调用处理事件(wxEvent)的函数的位置:
    bool wxEvtHandler::SearchDynamicEventTable(wxEvent& event)
    {
        DynamicEvents& dynamicEvents = *m_dynamicEvents;
    
        bool needToPruneDeleted = false;
    
        for (size_t n = dynamicEvents.size(); n; n--)
        {
        wxDynamicEventTableEntry* const entry = dynamicEvents[n-1];
    
        if (!entry)
        {
            needToPruneDeleted = true;
            continue;
        }
    
        if (event.GetEventType() == entry->m_eventType)
        {
            wxEvtHandler *handler = entry->m_fn->GetEvtHandler();
            if (!handler)
            handler = this;
            if (ProcessEventIfMatched(*entry, handler, event))
            {
            return true;
            }
        }
        }
    
        // 以下省略
    };
    bool wxEvtHandler::ProcessEventIfMatched(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 
        // the 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;
    
    #if wxUSE_EXCEPTIONS
        if (wxTheApp)
        {
            // call the handler via wxApp method which allows the user to catch
            // any exceptions which may be throw by any handler in the program
            // in one place
            wxTheApp->CallEventHandler(handler, *entry.m_fn, event);
        }
        else
    #endif    // wxUSE_EXCEPTIONS
        {
            (*entry.m_fn)(handler, event);
        }
    
        if (!event.GetSkipped())
            return true;
        }
    
        return false;
    }
    // 下面再查看一下wxAppConsoleBase的CallEventHandler函数
    void wxAppConsoleBase::CallEventHandler(wxEvtHandler *handler,
                        wxEventFunctor& functor,
                        wxEvent& event) const
    {
        // If the functor holds a method then, for backward compatibility, call
        // HandleEvent()
        wxEventFunction eventFunction = functor.GetEvtMethod();
    
        if (eventFunction)
        HandleEvent(handler, eventFunction, event);
        else
        functor(handler, event);
    }
    void wxAppConsoleBase::HandleEvent(wxEvtHandler* handler, 
                       wxEventFunction func,
                       wxEvent& event) const
    {
        // by default, simply call the handler
        (handler->*func)(event);
    }

    到这里,应该说完了wxWidgets中处理事件类型中最主要的部分,这个也是C++模板用来保证类型安全的一个具体应用。C++模板是一种非常强大的工具,合理使用,可以使代码得到很大的优化。本来,想要通过自己写的一个简单例子来进一步说明的,不过考虑这个例子已经比较长了,所以,写在下一篇随笔上面了。

     
  • 相关阅读:
    【lua实战摸索】在b.lua调用a.lua的函数
    【VScode】使用VScode 来写markdown ① 时序图
    修改虚拟机的ip地址
    PyCharm 导包提示 unresolved reference
    报错:Request failed with status code 413
    安装cuda和cudnn
    delphi 10.2报错:connot resolve unit name Forms
    运行delphi 10.2时出现的错误:Error setting debug exception hook
    已知两点坐标和半径,求圆心
    电脑蓝屏,出现BitLocker 恢复
  • 原文地址:https://www.cnblogs.com/albizzia/p/8997890.html
Copyright © 2011-2022 走看看