【转】Qt 事件处理机制 (下篇)
转自:http://mobile.51cto.com/symbian-272816.htm
在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生、分发、接受和处理事件。
继续我们上一篇文章继续介绍,Qt 事件处理机制 (上篇) 介绍了Qt框架的事件处理机制:事件的产生、分发、接受和处理,并以视窗系统鼠标点击QWidget为例,对代码进行了剖析,向大家分析了Qt框架如何通过Event Loop处理进入处理消息队列循环,如何一步一步委派给平台相关的函数获取、打包用户输入事件交给视窗系统处理,函数调用栈如下:
main(int, char **)
QApplication::exec()
QCoreApplication::exec()
QEventLoop::exec(ProcessEventsFlags )
QEventLoop::processEvents(ProcessEventsFlags )
QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags)
本文将介绍Qt app在视窗系统回调后,事件又是怎么一步步通过QApplication分发给最终事件的接受和处理者QWidget::event, (QWidget继承Object,重载其虚函数event),以下所有的讨论都将嵌入在源码之中。
QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) bool QETWidget::translateMouseEvent(const MSG &msg)
bool QApplicationPrivate::sendMouseEvent(...)
inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)
bool QApplication::notify(QObject *receiver, QEvent *e)
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
bool QWidget::event(QEvent *event) // (续上文Section 7) Section 2-1:
QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
...
//检查message是否属于Qt可转义的鼠标事件
if (qt_is_translatable_mouse_event(message)) {
if (QApplication::activePopupWidget() != ) {
POINT curPos = msg.pt;
//取得鼠标点击坐标所在的QWidget指针,它指向我们在main创建的widget实例
QWidget* w = QApplication::widgetAt(curPos.x, curPos.y);
if (w)
widget = (QETWidget*)w;
}
if (!qt_tabletChokeMouse) {
//对,就在这里。Windows的回调函数将鼠标事件分发回给了Qt Widget
// => Section 2-2
result = widget->translateMouseEvent(msg);
...
}
// Section 2-2 $QTDIR\src\gui\kernel\qapplication_win.cpp
//该函数所在与Windows平台相关,主要职责就是把已windows格式打包的鼠标事件解包、翻译成QApplication可识别的QMouseEvent,QWidget.
bool QETWidget::translateMouseEvent(const MSG &msg)
{
//.. 这里很长的代码给以忽略
// 让我们看一下sendMouseEvent的声明
// widget是事件的接受者; e是封装好的QMouseEvent
// ==> Section 2-3
res = QApplicationPrivate::sendMouseEvent(widget, &e, alienWidget, this, &qt_button_down, qt_last_mouse_receiver);
}
// Section 2-3 $QTDIR\src\gui\kernel\qapplication.cpp
bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,
QWidget *alienWidget, QWidget *nativeWidget,
QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver,
bool spontaneous)
{
//至此与平台相关代码处理完毕
//MouseEvent默认的发送方式是spontaneous, 所以将执行sendSpontaneousEvent。 sendSpontaneousEvent() 与 sendEvent的代码实现几乎相同,
除了将QEvent的属性spontaneous标记不同。 这里是解释什么spontaneous事件:如果事件由应用程序之外产生的,比如一个系统事件。
显然MousePress事件是由视窗系统产生的一个的事件(详见上文Section ~ Section ),因此它是 spontaneous事件 if (spontaneous)
result = QApplication::sendSpontaneousEvent(receiver, event); ==〉Section -
else
result = QApplication::sendEvent(receiver, event);
} // Section 2-4 C:\Qt\4.7.1-Vs\src\corelib\kernel\qcoreapplication.h
inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{
//将event标记为自发事件
//进一步调用 2-5 QCoreApplication::notifyInternal
if (event) event->spont = true; return self ? self->notifyInternal(receiver, event) : false;
}
// Section 2-5: $QTDIR\gui\kernel\qapplication.cpp
bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)
{ // 几行代码对于Qt Jambi (QT Java绑定版本) 和QSA (QT Script for Application)的支持
...
// 以下代码主要意图为Qt强制事件只能够发送给当前线程里的对象,也就是说receiver->d_func()->threadData应该等于QThreadData::current()。
注意,跨线程的事件需要借助Event Loop来派发
QObjectPrivate *d = receiver->d_func();
QThreadData *threadData = d->threadData;
++threadData->loopLevel;
bool returnValue;
QT_TRY {
//哇,终于来到大名鼎鼎的函数QCoreApplication::nofity()了 ==> Section 2-6
returnValue = notify(receiver, event);
} QT_CATCH (...) {
--threadData->loopLevel;
QT_RETHROW;
}
}
// Section 2-6: $QTDIR\gui\kernel\qapplication.cpp
// QCoreApplication::notify和它的重载函数QApplication::notify在Qt的派发过程中起到核心的作用,Qt的官方文档时这样说的:
任何线程的任何对象的所有事件在发送时都会调用notify函数。
bool QApplication::notify(QObject *receiver, QEvent *e)
{
//代码很长,最主要的是一个大大的Switch,Case
..
switch ( e->type())
{
...
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
...
//让自己私有类(d是私有类的句柄)来进一步处理 ==> Section 2-7
res = d->notify_helper(w, w == receiver ? mouse : &me);
e->spont = false;
break;
}
...
}
// Section 2-7: $QTDIR\gui\kernel\qapplication.cpp
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
...
// 向事件过滤器发送该事件,这里介绍一下Event Filters. 事件过滤器是一个接受即将发送给目标对象所有事件的对象。
//如代码所示它开始处理事件在目标对象行动之前。过滤器的QObject::eventFilter()实现被调用,能接受或者丢弃过滤,
允许或者拒绝事件的更进一步的处理。如果所有的事件过滤器允许更进一步的事件处理,事件将被发送到目标对象本身。
如果他们中的一个停止处理,目标和任何后来的事件过滤器不能看到任何事件。
if (sendThroughObjectEventFilters(receiver, e))
return true;
// 递交事件给receiver => Section 2-8
bool consumed = receiver->event(e);
e->spont = false;
}
// Section 2-8 $QTDIR\gui\kernel\qwidget.cpp
// QApplication通过notify及其私有类notify_helper,将事件最终派发给了QObject的子类- QWidget.
bool QWidget::event(QEvent *event)
{
...
switch(event->type()) {
case QEvent::MouseButtonPress:
// Don't reset input context here. Whether reset or not is
// a responsibility of input method. reset() will be
// called by mouseHandler() of input method if necessary
// via mousePressEvent() of text widgets.
#if 0
resetInputContext();
#endif
//mousePressEvent是虚函数,QWidget的子类可以通过重载重新定义mousePress事件的行为
mousePressEvent((QMouseEvent*)event);
break;
}
【转】Qt 事件处理机制 (下篇)的更多相关文章
- Qt 事件处理机制 (下篇)
继续我们上一篇文章继续介绍,Qt 事件处理机制 (上篇) 介绍了Qt框架的事件处理机制:事件的产生.分发.接受和处理,并以视窗系统鼠标点击QWidget为例,对代码进行了剖析,向大家分析了Qt框架如何 ...
- Qt 事件处理机制 (上篇)
本篇来介绍Qt 事件处理机制 .深入了解事件处理系统对于每个学习Qt人来说非常重要,可以说,Qt是以事件驱动的UI工具集. 大家熟知Signals/Slots在多线程的实现也依赖于Qt的事件处理机制. ...
- Qt事件处理机制
研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 先给出原文 ...
- 【转】解读Qt 事件处理机制(上篇)
[转自]:http://mobile.51cto.com/symbian-272812.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生 ...
- 9、Qt 事件处理机制
原文地址:http://mobile.51cto.com/symbian-272812.htm 在Qt中,事件被封装成一个个对象,所有的事件均继承自抽象类QEvent. 接下来依次谈谈Qt中有谁来产生 ...
- Qt 事件处理机制
Qt 事件处理机制 因为这篇文章写得特别好,将Qt的事件处理机制能够阐述的清晰有条理,并且便于学习.于是就装载过来了(本文做了排版,并删减了一些冗余的东西,希望原主勿怪),以供学习之用. 简介 在Qt ...
- QT开发(十二)——QT事件处理机制
一.QT事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下和松开 ...
- 【QT学习】QT事件处理机制
GUI应用程序由 事件驱动. 键盘.鼠标.拖放.滚动.绘屏.定时事件. connect
- Qt之事件处理机制
思维导读 一.事件简介 QT程序是事件驱动的, 程序的每个动作都是由内部某个事件所触发.QT事件的发生和处理成为程序运行的主线,存在于程序整个生命周期. 常见的QT事件类型如下: 键盘事件: 按键按下 ...
随机推荐
- SVN管理工具Cornerstone之:创建分支、提交合并
创建工程的分支: 步骤: 1.选择左下角仓库repositories中的工程名->选择trunk->点击Branch->在提示框里填写分支名称create, 2.在做上角work ...
- JavaScript注意事项
var m = "false"; Boolean(m); // true而非 false ajax不能设置setRequestHeaders("Cookie", ...
- 彻底卸载 RAD Studio 2009/2010/XE+ 的步骤
重新安装 RAD 系列时,建议将上一个版本彻底卸载,彻底卸载 RAD Studio 2009/2010/XE+ 的步骤: 控制面板-->添加/删除程序中执行了卸载操作以后, 还需要做以下工作: ...
- @action 注解
================================================= 下载 注解配置 private String fileName; private String co ...
- linux下文件编码的查看与转换(转)
通常来说,Windows中默认的文件格式是GBK(gb2312),而Linux一般都是UTF-8,所以Linux下打开windows的文件会有乱码的情况.另外,有时要将文件进行编码转换,如将简体中文转 ...
- win7 清理系统
1. 先用CCleaner等第三方软件清理一下.2. 将用户文件.我的文档.我的音乐.我的视频.桌面等路径更改到非系统分区(文件也会一并移动过去)3. 将系统盘的系统还原占用空间比减小,将大部分虚拟内 ...
- baiduMap 显示所有的marker(在视野里显示所有的)
搞Android的,所以比较幸苦和累现在搞的app是关于百度地图的,因为要求要把所有覆盖物显示在一个视野...所以在网上找了很久,终于找打了方法 我引用的包是: 记录一下,其实不算很难.一个小点.翻了 ...
- Java---IO加强(2)
转换流 ★转换流功能1:充当字节流与字符流之间的桥梁 需求:模拟英文聊天程序,要求: (1) 从键盘录入英文字符,每录一行就把它转成大写输出到控制台: (2) 保存聊天记录到字节流文件. 要求1的设计 ...
- Visual Studio Code 与 Github 集成
使用Visual Studio Code进行Nodejs开发充满了便利,为了更好的进行开发工作,有必要使用Github进行代码管理. Visual Studio Code已经集成了GIT组件: htt ...
- SRM 584 DIV1
A 简单的差分约束模型 , 因为d是定值 , 所以也可以按最短路理解 , trick是不能把圈算进去. #define maxn 55 class Egalitarianism { public: i ...