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

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

  1. class wxEvtHandler : public wxObject,
  2. public wxTrackable
  3. {
  4. // These functions are used for old, untyped, event handlers and don't
  5. // check that the type of the function passed to them actually matches the
  6. // type of the event. They also only allow connecting events to methods of
  7. // wxEvtHandler-derived classes.
  8. //
  9. // The template Bind() methods below are safer and allow connecting
  10. // events to arbitrary functions or functors -- but require compiler
  11. // support for templates.
  12. //
  13. // Dynamic association of a member function handler with the event handler,
  14. // winid and event type
  15.  
  16. void Connect(int winid,
  17. int lastId,
  18. wxEventType eventType,
  19. wxObjectEventFunction func,
  20. wxObject *userData = NULL,
  21. wxEvtHandler *eventSink = NULL)
  22. {
  23. DoBind(winid, lastId, eventType,
  24. wxNewEventFunctor(eventType, func, eventSink),
  25. userData);
  26. }
  27.  
  28. bool Disconnect(int winid,
  29. int lastId,
  30. wxEventType eventType,
  31. wxObjectEventFunction func = NULL,
  32. wxObject* userData = NULL,
  33. wxEvtHandler* eventSink = NULL)
  34. {
  35. return DoUnbind(winid, lastId, eventType,
  36. wxMakeEventFunctor(eventType, func, eventSink),
  37. userData);
  38. }
  39.  
  40. // Bind a method of a class (called on the specified handler which must
  41. // be convertible to this class) object to an event:
  42. template <typename EventTag, typename Class, typename EventArg, typename EventHandler>
  43. void Bind(const EventTag &eventType,
  44. void (Class::*method)(EventArg &),
  45. EventHandler *handler,
  46. int winid = wxID_ANY,
  47. int lastId = wxID_ANY,
  48. wxObject* userData = NULL)
  49. {
  50. DoBind(winid, lastId, eventType,
  51. wxNewEventFunctor(eventType, method, handler),
  52. userData);
  53. }
  54.  
  55. template <typename EventTag, typename Class, typename EventArg, typename EventHandler>
  56. bool Unbind(const EventTag &eventType,
  57. void (Class::*method)(EventArg&),
  58. EventHandler *handler,
  59. int winid = wxID_ANY,
  60. int lastId = wxID_ANY,
  61. wxObject* userData = NULL)
  62. {
  63. return DoUnbind(winid, lastId, eventType,
  64. wxMakeEventFunctor(eventType, method, handler),
  65. userData);
  66. }
  67.  
  68. void wxEvtHandler::DoBind(int id,
  69. int lastId,
  70. wxEventType eventType,
  71. wxEventFunctor *func,
  72. wxObject *userData)
  73. {
  74. wxDynamicEventTableEntry *entry =
  75. new wxDynamicEventTableEntry(eventType, id, lastId, func, userData);
  76.  
  77. // Check
  78. // ...
  79.  
  80. if (!m_dynamicEvents)
  81. m_dynamicEvents = new DynamicEvents;
  82.  
  83. // We prefer to push back the entry here and then iterate over the vector
  84. // in reverse direction in GetNextDynamicEntry() as it's more efficient
  85. // than inserting the element at the front.
  86. m_dynamicEvents->push_back(entry);
  87.  
  88. // 该函数的以下部分可忽略
  89. // Make sure we get to know when a sink is destroyed
  90. wxEvtHandler *eventSink = func->GetEvtHandler();
  91. if ( eventSink && eventSink != this )
  92. {
  93. wxEventConnectionRef *evtConnRef = FindRefInTrackerList(eventSink);
  94. if ( evtConnRef )
  95. evtConnRef->IncRef( );
  96. else
  97. new wxEventConnectionRef(this, eventSink);
  98. }
  99. }
  100. };

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

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

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

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

  1. struct wxDynamicEventTableEntry : public wxEventTableEntryBase
  2. {
  3. wxDynamicEventTableEntry(int evType, int winid, int idLast,
  4. wxEventFunctor* fn, wxObject *data)
  5. : wxEventTableEntryBase(winid, idLast, fn, data),
  6. m_eventType(evType)
  7. { }
  8.  
  9. // not a reference here as we can't keep a reference to a temporary int
  10. // created to wrap the constant value typically passed to Connect() - nor
  11. // do we need it
  12. int m_eventType;
  13.  
  14. private:
  15. wxDynamicEventTableEntry& operator=(const wxDynamicEventTableEntry&) = delete;
  16. };

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

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

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

  1. template <typename EventTag, typename Class, typename EventArg, typename EventHandler>
  2. inline wxEventFunctorMethod<EventTag, Class, EventArg, EventHandler> *
  3. wxNewEventFunctor(const EventTag&, void (Class::*method)(EventArg&), EventHandler *handler)
  4. {
  5. return new wxEventFunctorMethod<EventTag, Class, EventArg, EventHandler>
  6. (method, handler);
  7. }

  1. 以及wxEventFunctorMethod的实现:
  1. // functor forwarding the event to a method of the given object
  2. //
  3. template <typename EventTag, typename Class, typename EventArg, typename EventHandler>
  4. class wxEventFunctorMethod :
  5. public wxEventFunctor,
  6. private wxPrivate::HandlerImpl
  7. <
  8. Class,
  9. EventArg,
  10. wxIsPublicyDerived<Class, wxEvtHandler>::value !=
  11. >
  12. {
  13. public:
  14. void operator()(wxEvtHandler* handler, wxEvent& event) override
  15. {
  16. Class *realHandler = m_handler;
  17. if (!realHandler)
  18. {
  19. realHandler = this->ConvertFromEvtHandler(handler);
  20.  
  21. // this is not supposed to happen but check for it nevertheless
  22. wxCHECK_RET(realHandler, "invalid event handler");
  23. }
  24.  
  25. // the real (run-time) type of event is EventClass and we check in
  26. // the ctor that EventClass can be converted to EventArg, so this cast
  27. // is always valid
  28. (realHandler->*method)(static_cast<EventArg&>(event));
  29. }
  30. };

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

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

  1. // 下面简略摘抄一下,调用处理事件(wxEvent)的函数的位置:
  2. bool wxEvtHandler::SearchDynamicEventTable(wxEvent& event)
  3. {
  4. DynamicEvents& dynamicEvents = *m_dynamicEvents;
  5.  
  6. bool needToPruneDeleted = false;
  7.  
  8. for (size_t n = dynamicEvents.size(); n; n--)
  9. {
  10. wxDynamicEventTableEntry* const entry = dynamicEvents[n-];
  11.  
  12. if (!entry)
  13. {
  14. needToPruneDeleted = true;
  15. continue;
  16. }
  17.  
  18. if (event.GetEventType() == entry->m_eventType)
  19. {
  20. wxEvtHandler *handler = entry->m_fn->GetEvtHandler();
  21. if (!handler)
  22. handler = this;
  23. if (ProcessEventIfMatched(*entry, handler, event))
  24. {
  25. return true;
  26. }
  27. }
  28. }
  29.  
  30. // 以下省略
  31. };
  1. bool wxEvtHandler::ProcessEventIfMatched(const wxEventTableEntryBase& entry,
  2. wxEvtHandler *handler,
  3. wxEvent& event)
  4. {
  5. int tableId1 = entry.m_id,
  6. tableId2 = entry.m_lastId;
  7.  
  8. // match only if the event type is the same and the id is either -1 in
  9. // the event table (meaning "any") or the event id matches the id
  10. // specified in the event table either exactly or by falling into
  11. // the range between first and last
  12. if ((tableId1 == wxID_ANY) ||
  13. (tableId2 == wxID_ANY && tableId1 == event.GetId()) ||
  14. (tableId2 != wxID_ANY &&
  15. (event.GetId() >= tableId1 && event.GetId() <= tableId2)))
  16. {
  17. event.Skip(false);
  18. event.m_callbackUserData = entry.m_callbackUserData;
  19.  
  20. #if wxUSE_EXCEPTIONS
  21. if (wxTheApp)
  22. {
  23. // call the handler via wxApp method which allows the user to catch
  24. // any exceptions which may be throw by any handler in the program
  25. // in one place
  26. wxTheApp->CallEventHandler(handler, *entry.m_fn, event);
  27. }
  28. else
  29. #endif // wxUSE_EXCEPTIONS
  30. {
  31. (*entry.m_fn)(handler, event);
  32. }
  33.  
  34. if (!event.GetSkipped())
  35. return true;
  36. }
  37.  
  38. return false;
  39. }
  1. // 下面再查看一下wxAppConsoleBase的CallEventHandler函数
  2. void wxAppConsoleBase::CallEventHandler(wxEvtHandler *handler,
  3. wxEventFunctor& functor,
  4. wxEvent& event) const
  5. {
  6. // If the functor holds a method then, for backward compatibility, call
  7. // HandleEvent()
  8. wxEventFunction eventFunction = functor.GetEvtMethod();
  9.  
  10. if (eventFunction)
  11. HandleEvent(handler, eventFunction, event);
  12. else
  13. functor(handler, event);
  14. }
  1. void wxAppConsoleBase::HandleEvent(wxEvtHandler* handler,
  2. wxEventFunction func,
  3. wxEvent& event) const
  4. {
  5. // by default, simply call the handler
  6. (handler->*func)(event);
  7. }

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

  1.  

描述wxWidgets中事件处理的类型转化的更多相关文章

  1. pandas将字段中的字符类型转化为时间类型,并设置为索引

    假设目前已经引入了 pandas,同时也拥有 pandas 的 DataFrame 类型数据. import pandas as pd 数据集如下 df.head(3) date open close ...

  2. SpringMVC09异常处理和类型转化器

    public class User { private String name; private Integer age; public String getName() { return name; ...

  3. JS 强制类型转化

    在Js中, 强制类型转化分为两种情况: 一种是引用类型转化基本类型, 如数组转化成数字:一种是两种不同基本类型之间的转化,如字符串转化为数字.你不能将基本类型转化成引用类型,比如,不可能把数字转化为数 ...

  4. PHP之类型转化

    类型转化的判别 PHP在变量定义中不需要(或者不支持)明确的类型定义:变量类型是根据使用该变量的上下文所决定的, 也就是说,如果把一个string值付给变量$var,$var就成了一个string,如 ...

  5. C++中的显式类型转化

    类型转化也许大家并不陌生,int i; float j; j = (float)i; i = (int)j; 像这样的显式转化其实很常见,强制类型转换可能会丢失部分数据,所以如果不加(int)做强制转 ...

  6. javascript中的隐式类型转化

    javascript中的隐式类型转化 #隐式转换 ## "+" 字符串和数字 如果某个操作数是字符串或者能够通过以下步骤转换为字符串的话,+将进行拼接操作. 如果其中一个操作数是对 ...

  7. Java中byte、short、char、int、long运算时自动类型转化问题

    -------------------------------------------------------------------------------------------------- ★ ...

  8. java中值得类型转化

    在Java编程过程,基本数据类型(boolean除外)的可以相互转化.其中: (1)低容量小的类型自动转换为容量大的数据类型:数据类型按容量大小排序为: byte,short,char->int ...

  9. 类型转化 - js中的骚操作

    Number Number() 把字符串数字转化成数字类型,布尔类型也可以转化 parseInt parseInt() 字符串数字转化成数字类型,当布尔类型不可以(NaN),但该函数可以把数字开头的数 ...

随机推荐

  1. PS学习之制作音乐视屏

    素材: 新建画布 插入图片素材 调整和画布一样大小 喜欢彩色的 可以加照片滤镜 喜欢黑白的可以加黑白滤镜 也可以添加自己喜欢的文字 在窗口中选择时间轴 创建视屏时间轴 图中标记得就是每秒能播放30张 ...

  2. 《DSP using MATLAB》Problem 6.5

    代码: %% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ %% Output In ...

  3. Centos7部署ntp服务器同步时间以及直接将本地时间同步为北京时间

    一.查看配置 查看时区列表: timedatectl list-timezones|grep Asia 查看当前时间: date 查看当前设置: [root@localhost ~]# timedat ...

  4. Singer 学习十三 发现模式

    发现模式 发现模式提供了一种描述tap 支持数据流的方式,使用了json schema 做为描述数据的结构以及每个数据流的 类型,发现模式的实现依赖tap 的数据源,有些taps 将硬编码每个流的模式 ...

  5. 堆的操作(make_heap,push_heap,pop_heap,sort_heap,is_heap)

    堆不是一中sort ranges,堆中的元素不会以递增方式排列,内部以树状形式排列,该结构以每个结点小于等于父节点构成,优先队列就是以堆来实现 make_heap //版本一:用operator &l ...

  6. LambdaAOP

    项目地址 :  https://github.com/kelin-xycs/LambdaAOP LambdaAOP 一个 用 C# 实现的 使用 Lambda 表达式 的 AOP 这是 一个 用 C# ...

  7. ES9新特性

    这篇文章主要介绍ES2018(ES9)的新特性, 以及使用方法 JS是一门跨平台的语言, ES6也就是ECMAScript 2015 花费了5年的时间敲定, 是一次非常大的改版, 之后每年都有一个小版 ...

  8. MySQL 数据类型对比:char 与 varchar;varchar 与 text;datetime 与 timestamp;blob 与 text;

    char 与 varchar char(n) 若存入字符数小于n,则以空格补于其后,查询之时再将空格去掉.所以 char 类型存储的字符串末尾不能有空格,varchar 不限于此. char(n) 固 ...

  9. 解决web项目存在多个log4j.properties配置文件,导致日志级别配置不生效问题

    java开启log4j的debug模式 -Dlog4j.debug=true tomcat启动debug模式: linux打开catalina.sh导入: export JAVA_OPTS=" ...

  10. java小程序(课堂作业02)

    1,三种方法计算组合数 ①设计思路:第一种方法就是通过阶乘公式然后运用公式计算出组合数,第二种通过公式推导出cnk=n/(n-k)cnk-1,然后然后从ckk 开始运算到cnk,第三种方法就是通过递归 ...