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\n", (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 !=
>
{
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-]; 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++模板是一种非常强大的工具,合理使用,可以使代码得到很大的优化。本来,想要通过自己写的一个简单例子来进一步说明的,不过考虑这个例子已经比较长了,所以,写在下一篇随笔上面了。

 

描述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. 【BZOJ3527】【ZJOI2014】力

    "FFT还不是随手写?"我终于能说这样的话了இwஇ 原题: 给出n个数qi,给出Fj的定义如下: 令Ei=Fi/qi,求Ei.   FFT嘛,直接推公式 然后就变成俩卷积了,FFT ...

  2. 黑马-Spring(IOC&DI) AOP

    IOC(控制翻转) 概念 把对象的创建.初始化.销毁等工作交给spring容器来做 案例 环境 步骤 1.  写一个HelloWorld类 2.  写一个配置文件   把hello类放到spring容 ...

  3. sqler sql 转rest api 源码解析(三) rest协议

    rest 服务说明 rest 协议主要是将配置文件中的宏暴露为rest 接口,使用了labstack/echo web 框架,同时基于context 模型 进行宏管理对象的共享,同时进行了一些中间件的 ...

  4. Zabbix-2.4-安装-3

      zabbix自动注册简单介绍---创建自动注册的actions   怎么把1000台机器加zabbix里 zabbix提供了网络发现和自动注册2个功能,发现有机器连过来,给它加个模板 如果是win ...

  5. 一个类似 Twitter 雪花算法 的 连续序号 ID 产生器 SeqIDGenerator

    项目地址 :     https://github.com/kelin-xycs/SeqIDGenerator 今天 QQ 群 里有网友问起产生唯一 ID 的方法 有哪些,  讨论了各种方法 . 有网 ...

  6. oracle之 ORA-12557: TNS: 协议适配器不可加载

    操作系统:windows 7数据库版本: 11.2.0.1问题描述:直接通过 sqlplus sys/oracle@10.10.100.109:1521/ysxt as sysdba 可以登录,但是通 ...

  7. java 连接 hiveserver2 例子

    启动了  hiveserver2 以后才能使用 程序连接 .目前的 使用的 是  server2 版本.和以前的版本驱动包名不同: package hadoop; import java.sql.Co ...

  8. hdfs 例子

    package hadoop; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; ...

  9. ubuntu忘记登录密码解决方法

    1.重启系统,长按Shift键,直到出现下面菜单.选择recovery mode(恢复模式).2.接下来会进入如下界面,选择Drop to root shell prompt ,也就是获取root权限 ...

  10. apache的MultipartEntityBuilder文件上传

    本文讲解多文件上传方法,不比较上传有几种方法和效率,而是定向分析apache的httpmime包的MultipartEntityBuilder类,源码包:httpmime-4.5.2.jar 一.常用 ...