QT分析之消息事件机制
2016-03-15 13:28
519 查看
原文地址:http://blog.163.com/net_worm/blog/static/127702419201001432028526/
上回我们分析到QPushButton的初始化,知道了Windows的窗口注册和消息处理函数QtWndProc。
跳过test.cpp中的其他语句,我们先分析最后一行代码a.exec()语句。
我们知道WinSDK中,简单Windows程序里的WinMain函数主要就这么几件事:
1、窗体注册;2、消息处理函数;3、等待和消息处理循环
QApplication::exec()只做了两件事:设定根对象和调用QCoreApplication::exec()。
QCoreApplication::exec()函数的代码如下,按惯例关键部分用颜色或追加注释。
我们先看QEventLoop::exec()的声明:int exec(ProcessEventsFlags flags = AllEvents);
对于上面eventLoop.exec(); 这种调用形式,意思说使用AllEvents(就是0x00值)标记
接着看QEventLoop::exec()的定义:
继续深入看QEventLoop::processEvents()的定义
在这里,用单步跟踪我们可以知道实际调用的是QGuiEventDispatcherWin32::processEvents(),看其实现代码:
继续深入九层地狱,看QEventDispatcherWin32::processEvents()的定义:
至此,一个完整的路径似乎分析完毕了,等等,好像不太对。前面QtWndProc没有用上!
昨天分析到QApplication::exec()的具体实现,其中还留有一些疑问:一是QEventLoop::exec()里面和QEventDispatcherWin32::processEvents()的while循环退出条件的分析;另一个是QtWndProc()消息处理函数与QEventDispatcherWin32::processEvents()的关系。
到目前我们都是从上至下几乎都是直接看代码的方式(静态),今天换种方式用实际运行到代码的方式分析。我们知道test.cpp例子程序运行的时候,出来一个button,点击按钮之后退出。我们看QEventLoop::exec()里面的while循环:while (!d->exit) 。在规范设计中,功能模块都是封闭的,也就是说d->exit的值一定会在QEventLoop类的某个地方被赋值成True。
为证实我们的猜想,细看QEventLoop类的定义:
果然看到了我们想看的东西,把QEventLoop::exit(int returnCode)函数代码找到:
添加一个断点,运行程序点击退出按钮之后,我们可以得到下面的堆栈列表:
[下面的框架可能不正确和/或缺失,没有为 user32.dll 加载符号]
这里我们清晰的看到了消息的传递路径,而且也看到了Clicked()和quit()的调用前后次序
我们继续昨天之分析,QEventDispatcherWin32::processEvents()派发消息之后,QtWndProc()获得该消息。我们看看QtWndProc()的具体处理:(简略)
1、根据hwnd获取QWidget指针:
2、处理事件:
获取的widget其实就是QPushButton对象的指针;事件的处理实体在QETWidget::translateMouseEvent(),该函数又是一个超长代码的实现,其处理是事件压缩之类Mouse事件的周边处理,之后是调用:
我们看其实现:
根据单步跟踪(也可以通过代码分析)知道QApplication::sendSpontaneousEvent()调用的是QCoreApplication::sendSpontaneousEvent(),该函数判断self指针(this指针?)是否为空,不为空则调用notifyInternal()函数;也就是调用QCoreApplication::notifyInternal(),参数为receiver(也就是根据hwnd获取的QPushButton对象指针),和event。我们看其实现代码:
从上面的分析,我们继续看QApplication::notify()的实现:(为方便查看,大部分无关代码删除了)
其中res = d->notify_helper(w, w == receiver ? mouse : &me);调用的是QApplicationPrivate::notify_helper(),我们看其具体实现:
继续看QPushButton::event()的实现:
我们进一步看QAbstractButton::event的实现,忽略次要处理,主要是调用QWidget::event(e);
QWidget::event(QEvent *event)主体是根据参数event的类型switch分支处理各种事件,我们回头看QETWidget::translateMouseEvent(const MSG &msg)中,event对象生成时候的语句:
根据查找,这里type内容是MouseButtonPress。同样忽略其他无关代码,我们看QWidget::event()的代码:
我们看该事件处理的具体代码:
在MouseButtonProcess事件处理之后,就是前面注册的Release事件(前面粉色部分代码)。
目前还有两个疑问:一个是自定义的信号(SIGNAL)如何跟Windows的消息关联的;另一个是信号和槽(SLOT)是如何关联的。根据前面的分析,对第一个问题的猜测是在event()函数里增加相应处理;对第二个问题,应该有一个函数指针表使之关联。带着这些问题我们接着分析mouseReleaseEvent()。
进一步看QAbstractButtonPrivate::click()的实现代码:
主要就是调用emitReleased()和emitClicked(),我们看其中QAbstractButtonPrivate::emitClicked()的实现
上面调用的函数实体,在moc_QAbstractButton.cpp里可以看到。这个文件实际是由QT的MOC工具自动产生的。
我们先来看看QMetaObject类的定义:
再看QMetaObjectPrivate的定义:
实际上QMetaObject::d.data指向的就是QMetaObjectPrivate结构体 我们看看staticMetaObject对象的定义:(同样在moc_QAbstractButton.cpp文件中)
这是一个常对象,除设定父类的staticMetaObject外,还设定了两个全局变量:qt_meta_stringdata_QAbstractButton和qt_meta_data_QAbstractButton。
所有的奥秘都在这两个变量里面了。根据前面分析qt_meta_data_QAbstractButton实际是QMetaObjectPrivate结构。
我们接着看QMetaObject::activate()的代码:
可以看到,在判断本信号是否连接有槽之后,就调用了activate的重载函数:
单步跟踪,receiver->qt_metacall();实际调用的是QApplication::qt_metacall(),根据调用参数实现不同的函数调用。在moc_QApplication.cpp中定义,是由MOC工具自动产生的代码。至此,信号与槽的关联分析完毕。
明天接着分析QObject::connect()如何把相关数据填入connectionLists。自定义信号如何与windows消息关联在明天分析完毕之后再来证实。
在继续分析之前,我们回头看看test.cpp的main()函数:
根据我们猜测,就是上面红色部分语句把消息处理函数指针填入了connectionLists。
首先我们看看SIGNAL宏和SLOT宏的定义:(另外一种定义是为DEBUG用的,可忽略)
使用的是宏转义,SIGNAL(clicked())被展开成"2clicked()"(字符串);SLOT(quit())被展开成"1quit()"。
再看QObject::connect()的声明:
上面的调用语句展开之后就是:
然后看QObject::connect()的定义:
用红色标记出来的三个主要调用,我们先看QInternal::activateCallbacks()的实现:
这是优先处理回调函数(钩子函数),在我们这里的应用中没有回调,所以可以忽略。
接着看QMetaObject::connect()的实现:
s->d_func()指向的是QPushButtonPrivate指针,QPushButtonPrivate没有addConnection()成员实际调用的是其基类成员,
s->d_func()->addConnection()调用的是QObjectPrivate::addConnection()。进一步看其实现:
这里填入了发送消息的SIGNAL的函数指针!我们接着看r->d_func()->refSender(s, signal_index);其中r指向的是QApplication对象(a),所以r->d_func()是QApplicationPrivate对象指针,同样因其本身没有refSender()成员函数,调用的是其基类QObjectPrivate::refSender()。我们看其实现:
至此,我们的猜想得到证实。分析完毕。
上回我们分析到QPushButton的初始化,知道了Windows的窗口注册和消息处理函数QtWndProc。
跳过test.cpp中的其他语句,我们先分析最后一行代码a.exec()语句。
我们知道WinSDK中,简单Windows程序里的WinMain函数主要就这么几件事:
1、窗体注册;2、消息处理函数;3、等待和消息处理循环
QApplication::exec()只做了两件事:设定根对象和调用QCoreApplication::exec()。
QCoreApplication::exec()函数的代码如下,按惯例关键部分用颜色或追加注释。
int QCoreApplication::exec() { if (!QCoreApplicationPrivate::checkInstance("exec")) return -1; QThreadData *threadData = self->d_func()->threadData; if (threadData != QThreadData::current()) { qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className()); return -1; } if (!threadData->eventLoops.isEmpty()) { qWarning("QCoreApplication::exec: The event loop is already running"); return -1; } // 从这里开始是事件处理循环开始前准备 threadData->quitNow = false; QEventLoop eventLoop; self->d_func()->in_exec = true; self->d_func()->aboutToQuitEmitted = false; int returnCode = eventLoop.exec(); // 事件处理主体 // 从这里开始是事件处理循环后处理(通常为App退出) threadData->quitNow = false; if (self) { self->d_func()->in_exec = false; if (!self->d_func()->aboutToQuitEmitted) emit self->aboutToQuit(); self->d_func()->aboutToQuitEmitted = true; sendPostedEvents(0, QEvent::DeferredDelete); } return returnCode; }
我们先看QEventLoop::exec()的声明:int exec(ProcessEventsFlags flags = AllEvents);
对于上面eventLoop.exec(); 这种调用形式,意思说使用AllEvents(就是0x00值)标记
接着看QEventLoop::exec()的定义:
int QEventLoop::exec(ProcessEventsFlags flags) { Q_D(QEventLoop); if (d->threadData->quitNow) return -1; if (d->inExec) { qWarning("QEventLoop::exec: instance %p has already called exec()", this); return -1; } d->inExec = true; d->exit = false; ++d->threadData->loopLevel; d->threadData->eventLoops.push(this); // remove posted quit events when entering a new event loop QCoreApplication *app = QCoreApplication::instance(); if (app && app->thread() == thread()) QCoreApplication::removePostedEvents(app, QEvent::Quit); #if defined(QT_NO_EXCEPTIONS) while (!d->exit) processEvents(flags | WaitForMoreEvents | EventLoopExec); #else try { while (!d->exit) // 如果exit变量没有设定为True的话,会一直循环处理 // flags被设定为:AllEvents、WaitForMoreEvents、EventLoopExec processEvents(flags | WaitForMoreEvents | EventLoopExec); } catch (...) { qWarning("Qt has caught an exception thrown from an event handler. Throwing\n" "exceptions from an event handler is not supported in Qt. You must\n" "reimplement QApplication::notify() and catch all exceptions there.\n"); // copied from below QEventLoop *eventLoop = d->threadData->eventLoops.pop(); Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); Q_UNUSED(eventLoop); // --release warning d->inExec = false; --d->threadData->loopLevel; throw; } #endif // copied above QEventLoop *eventLoop = d->threadData->eventLoops.pop(); Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error"); Q_UNUSED(eventLoop); // --release warning d->inExec = false; --d->threadData->loopLevel; return d->returnCode; }
继续深入看QEventLoop::processEvents()的定义
bool QEventLoop::processEvents(ProcessEventsFlags flags) { Q_D(QEventLoop); if (!d->threadData->eventDispatcher) return false; if (flags & DeferredDeletion) QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); // 根据d->threadData指向对象的不同,调用不同的Dispatcher return d->threadData->eventDispatcher->processEvents(flags); }
在这里,用单步跟踪我们可以知道实际调用的是QGuiEventDispatcherWin32::processEvents(),看其实现代码:
bool QGuiEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) { if (!QEventDispatcherWin32::processEvents(flags)) return false; if (configRequests) // any pending configs? qWinProcessConfigRequests(); return true; }
继续深入九层地狱,看QEventDispatcherWin32::processEvents()的定义:
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) { Q_D(QEventDispatcherWin32); if (!d->internalHwnd) createInternalHwnd(); d->interrupt = false; emit awake(); // emit在Win32平台下实际就是空的 bool canWait; bool retVal = false; do { QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData); //这句没有深入分析 DWORD waitRet = 0; HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1]; QVarLengthArray<MSG> processedTimers; while (!d->interrupt) { DWORD nCount = d->winEventNotifierList.count(); Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); MSG msg; bool haveMessage; // 使用用户输入事件并且该事件队列不为空 if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) { // process queued user input events haveMessage = true; msg = d->queuedUserInputEvents.takeFirst(); } // 使用Socket通知事件并且该事件队列不为空 else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) { // process queued socket events haveMessage = true; msg = d->queuedSocketEvents.takeFirst(); } else { // 所有其他情况,Peek一下Windows的消息 haveMessage = winPeekMessage(&msg, 0, 0, 0, PM_REMOVE); // 根据消息种类放置到相应消息队列,备后面处理使用 if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents) && ((msg.message >= WM_KEYFIRST && msg.message <= WM_KEYLAST) || (msg.message >= WM_MOUSEFIRST && msg.message <= WM_MOUSELAST) || msg.message == WM_MOUSEWHEEL)) { // queue user input events for later processing haveMessage = false; d->queuedUserInputEvents.append(msg); } if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers) && (msg.message == WM_USER && msg.hwnd == d->internalHwnd)) { // queue socket events for later processing haveMessage = false; d->queuedSocketEvents.append(msg); } } if (!haveMessage) { // no message - check for signalled objects // 没有消息的情况下,等待事件通知 for (int i=0; i<(int)nCount; i++) pHandles[i] = d->winEventNotifierList.at(i)->handle(); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE); if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) { // a new message has arrived, process it continue; } } if (haveMessage) { // 定时事件的处理 if (msg.message == WM_TIMER) { // avoid live-lock by keeping track of the timers we've already sent bool found = false; for (int i = 0; !found && i < processedTimers.count(); ++i) { const MSG processed = processedTimers.constData()[i]; found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam); } if (found) continue; processedTimers.append(msg); } else if (msg.message == WM_QUIT) { // 退出事件的处理 if (QCoreApplication::instance()) QCoreApplication::instance()->quit(); return false; } if (!filterEvent(&msg)) { // 如果没有被[消息过滤器]过滤掉,那么就派发该消息 TranslateMessage(&msg); QT_WA({ DispatchMessage(&msg); } , { DispatchMessageA(&msg); }); } } else if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) { d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); } else { // nothing todo so break break; } retVal = true; } // still nothing - wait for message or signalled objects QThreadData *data = d->threadData; canWait = (!retVal && data->canWait && !d->interrupt && (flags & QEventLoop::WaitForMoreEvents)); if (canWait) { DWORD nCount = d->winEventNotifierList.count(); Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); for (int i=0; i<(int)nCount; i++) pHandles[i] = d->winEventNotifierList.at(i)->handle(); emit aboutToBlock(); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE); emit awake(); if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) { d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); retVal = true; } } } while (canWait); return retVal; }
至此,一个完整的路径似乎分析完毕了,等等,好像不太对。前面QtWndProc没有用上!
昨天分析到QApplication::exec()的具体实现,其中还留有一些疑问:一是QEventLoop::exec()里面和QEventDispatcherWin32::processEvents()的while循环退出条件的分析;另一个是QtWndProc()消息处理函数与QEventDispatcherWin32::processEvents()的关系。
到目前我们都是从上至下几乎都是直接看代码的方式(静态),今天换种方式用实际运行到代码的方式分析。我们知道test.cpp例子程序运行的时候,出来一个button,点击按钮之后退出。我们看QEventLoop::exec()里面的while循环:while (!d->exit) 。在规范设计中,功能模块都是封闭的,也就是说d->exit的值一定会在QEventLoop类的某个地方被赋值成True。
为证实我们的猜想,细看QEventLoop类的定义:
class Q_CORE_EXPORT QEventLoop : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QEventLoop) public: explicit QEventLoop(QObject *parent = 0); ~QEventLoop(); enum ProcessEventsFlag { AllEvents = 0x00, ExcludeUserInputEvents = 0x01, ExcludeSocketNotifiers = 0x02, WaitForMoreEvents = 0x04, #ifdef QT3_SUPPORT ExcludeUserInput = ExcludeUserInputEvents, WaitForMore = WaitForMoreEvents, #endif X11ExcludeTimers = 0x08 #ifdef QT_DEPRECATED , DeferredDeletion = 0x10 #endif , EventLoopExec = 0x20 , DialogExec = 0x40 }; Q_DECLARE_FLAGS(ProcessEventsFlags, ProcessEventsFlag) bool processEvents(ProcessEventsFlags flags = AllEvents); void processEvents(ProcessEventsFlags flags, int maximumTime); int exec(ProcessEventsFlags flags = AllEvents); void exit(int returnCode = 0); bool isRunning() const; void wakeUp(); public Q_SLOTS: void quit(); };
果然看到了我们想看的东西,把QEventLoop::exit(int returnCode)函数代码找到:
void QEventLoop::exit(int returnCode) { Q_D(QEventLoop); if (!d->threadData->eventDispatcher) return; d->returnCode = returnCode; d->exit = true; d->threadData->eventDispatcher->interrupt(); }
添加一个断点,运行程序点击退出按钮之后,我们可以得到下面的堆栈列表:
QtCored4.dll!QEventLoop::exit(int returnCode=0x00000000) 行284 C++ QtCored4.dll!QCoreApplication::exit(int returnCode=0x00000000) 行926 + 0xc 字节 C++ QtCored4.dll!QCoreApplication::quit() 行1468 + 0x7 字节 C++ QtCored4.dll!QCoreApplication::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000002, void * * _a=0x0012bd28) 行84 C++ QtGuid4.dll!QApplication::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000006, void * * _a=0x0012bd28) 行96 + 0x15 字节 C++ QtCored4.dll!QMetaObject::activate(QObject * sender=0x0012ff40, int from_signal_index=0x0000001d, int to_signal_index=0x0000001e, void * * argv=0x0012bd28) 行3104 + 0x2b 字节 C++ QtCored4.dll!QMetaObject::activate(QObject * sender=0x0012ff40, const QMetaObject * m=0x6590d328, int from_local_signal_index=0x00000002, int to_local_signal_index=0x00000003, void * * argv=0x0012bd28) 行3198 + 0x15 字节 C++ QtGuid4.dll!QAbstractButton::clicked(bool _t1=false) 行198 + 0x17 字节 C++ QtGuid4.dll!QAbstractButtonPrivate::emitClicked() 行545 C++ QtGuid4.dll!QAbstractButtonPrivate::click() 行537 C++ QtGuid4.dll!QAbstractButton::mouseReleaseEvent(QMouseEvent * e=0x0012c450) 行1116 C++ QtGuid4.dll!QWidget::event(QEvent * event=0x0012c450) 行7555 C++ QtGuid4.dll!QAbstractButton::event(QEvent * e=0x0012c450) 行1078 C++ QtGuid4.dll!QPushButton::event(QEvent * e=0x0012c450) 行663 C++ QtGuid4.dll!QApplicationPrivate::notify_helper(QObject * receiver=0x0012ff40, QEvent * e=0x0012c450) 行4065 + 0x11 字节 C++ QtGuid4.dll!QApplication::notify(QObject * receiver=0x0012ff40, QEvent * e=0x0012c450) 行3767 + 0x2f 字节 C++ QtCored4.dll!QCoreApplication::notifyInternal(QObject * receiver=0x0012ff40, QEvent * event=0x0012c450) 行610 + 0x15 字节 C++ QtCored4.dll!QCoreApplication::sendSpontaneousEvent(QObject * receiver=0x0012ff40, QEvent * event=0x0012c450) 行216 + 0x38 字节 C++ QtGuid4.dll!QApplicationPrivate::sendMouseEvent(QWidget * receiver=0x0012ff40, QMouseEvent * event=0x0012c450, QWidget * alienWidget=0x00000000, QWidget * nativeWidget=0x0012ff40, QWidget * * buttonDown=0x65af67d4, QPointer<QWidget> & lastMouseReceiver={...}) 行2924 + 0xe 字节 C++ QtGuid4.dll!QETWidget::translateMouseEvent(const tagMSG & msg={...}) 行3269 + 0x28 字节 C++ QtGuid4.dll!QtWndProc(HWND__ * hwnd=0x00010840, unsigned int message=0x00000202, unsigned int wParam=0x00000000, long lParam=0x000c0064) 行1667 + 0xc 字节 C++ user32.dll!77e2b6e3()
[下面的框架可能不正确和/或缺失,没有为 user32.dll 加载符号]
user32.dll!77e2b874() user32.dll!77e2b82a() user32.dll!77e2ba92() user32.dll!77e2bad0() QtCored4.dll!QEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行751 + 0x17 字节 C++ QtGuid4.dll!QGuiEventDispatcherWin32::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行1182 + 0x15 字节 C++ QtCored4.dll!QEventLoop::processEvents(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行150 C++ QtCored4.dll!QEventLoop::exec(QFlags<enum QEventLoop::ProcessEventsFlag> flags={...}) 行201 + 0x2d 字节 C++ QtCored4.dll!QCoreApplication::exec() 行888 + 0x15 字节 C++ QtGuid4.dll!QApplication::exec() 行3526 C++ test.exe!main(int argc=0x00000001, char * * argv=0x00ba7040) 行14 + 0x6 字节 C++ test.exe!__tmainCRTStartup() 行582 + 0x19 字节 C test.exe!mainCRTStartup() 行399 C kernel32.dll!7c82f23b()
这里我们清晰的看到了消息的传递路径,而且也看到了Clicked()和quit()的调用前后次序
我们继续昨天之分析,QEventDispatcherWin32::processEvents()派发消息之后,QtWndProc()获得该消息。我们看看QtWndProc()的具体处理:(简略)
1、根据hwnd获取QWidget指针:
widget = (QETWidget*)QWidget::find(hwnd);
2、处理事件:
result = widget->translateMouseEvent(msg); // mouse event
获取的widget其实就是QPushButton对象的指针;事件的处理实体在QETWidget::translateMouseEvent(),该函数又是一个超长代码的实现,其处理是事件压缩之类Mouse事件的周边处理,之后是调用:
QApplicationPrivate::sendMouseEvent(),
我们看其实现:
bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event, QWidget *alienWidget, QWidget *nativeWidget, QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver) { Q_ASSERT(receiver); Q_ASSERT(event); Q_ASSERT(nativeWidget); Q_ASSERT(buttonDown); if (alienWidget && !isAlien(alienWidget)) alienWidget = 0; QPointer<QWidget> receiverGuard = receiver; QPointer<QWidget> nativeGuard = nativeWidget; QPointer<QWidget> alienGuard = alienWidget; QPointer<QWidget> activePopupWidget = qApp->activePopupWidget(); const bool graphicsWidget = nativeWidget->testAttribute(Qt::WA_DontShowOnScreen); if (*buttonDown) { // 如果是按钮&是图片窗体的话,注册该窗体使得最后一个按钮释放的时候接受leave事件 if (!graphicsWidget) { // Register the widget that shall receive a leave event // after the last button is released. if ((alienWidget || !receiver->internalWinId()) && !leaveAfterRelease && !QWidget::mouseGrabber()) leaveAfterRelease = *buttonDown; if (event->type() == QEvent::MouseButtonRelease && !event->buttons()) *buttonDown = 0; } } else if (lastMouseReceiver) { // Dispatch enter/leave if we move: // 1) from an alien widget to another alien widget or // from a native widget to an alien widget (first OR case) // 2) from an alien widget to a native widget (second OR case) if ((alienWidget && alienWidget != lastMouseReceiver) || (isAlien(lastMouseReceiver) && !alienWidget)) { if (activePopupWidget) { if (!QWidget::mouseGrabber()) dispatchEnterLeave(alienWidget ? alienWidget : nativeWidget, lastMouseReceiver); } else { dispatchEnterLeave(receiver, lastMouseReceiver); } } } #ifdef ALIEN_DEBUG qDebug() << "QApplicationPrivate::sendMouseEvent: receiver:" << receiver << "pos:" << event->pos() << "alien" << alienWidget << "button down" << *buttonDown << "last" << lastMouseReceiver << "leave after release" << leaveAfterRelease; #endif // We need this quard in case someone opens a modal dialog / popup. If that's the case // leaveAfterRelease is set to null, but we shall not update lastMouseReceiver. const bool wasLeaveAfterRelease = leaveAfterRelease != 0; bool result = QApplication::sendSpontaneousEvent(receiver, event); if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease && !event->buttons() && QWidget::mouseGrabber() != leaveAfterRelease) { // Dispatch enter/leave if: // 1) the mouse grabber is an alien widget // 2) the button is released on an alien widget QWidget *enter = 0; if (nativeGuard) enter = alienGuard ? alienWidget : nativeWidget; else // The receiver is typically deleted on mouse release with drag'n'drop. enter = QApplication::widgetAt(event->globalPos()); dispatchEnterLeave(enter, leaveAfterRelease); leaveAfterRelease = 0; lastMouseReceiver = enter; } else if (!wasLeaveAfterRelease) { if (activePopupWidget) { if (!QWidget::mouseGrabber()) lastMouseReceiver = alienGuard ? alienWidget : (nativeGuard ? nativeWidget : 0); } else { lastMouseReceiver = receiverGuard ? receiver : QApplication::widgetAt(event->globalPos()); } } return result; }
根据单步跟踪(也可以通过代码分析)知道QApplication::sendSpontaneousEvent()调用的是QCoreApplication::sendSpontaneousEvent(),该函数判断self指针(this指针?)是否为空,不为空则调用notifyInternal()函数;也就是调用QCoreApplication::notifyInternal(),参数为receiver(也就是根据hwnd获取的QPushButton对象指针),和event。我们看其实现代码:
bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event) { // Make it possible for Qt Jambi and QSA to hook into events even // though QApplication is subclassed... bool result = false; void *cbdata[] = { receiver, event, &result }; // 检查CallBack列表中是否有QInternal::EventNotifyCallback类型,有的话则调用之 // 该CallBack的返回必须为True,否则此处会继续往下运行 if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) { return result; } // Qt enforces the rule that events can only be sent to objects in // the current thread, so receiver->d_func()->threadData is // equivalent to QThreadData::current(), just without the function // call overhead. QObjectPrivate *d = receiver->d_func(); QThreadData *threadData = d->threadData; ++threadData->loopLevel; #ifdef QT_JAMBI_BUILD int deleteWatch = 0; int *oldDeleteWatch = QObjectPrivate::setDeleteWatch(d, &deleteWatch); bool inEvent = d->inEventHandler; d->inEventHandler = true; #endif #if defined(QT_NO_EXCEPTIONS) bool returnValue = notify(receiver, event); #else bool returnValue; try { returnValue = notify(receiver, event); // QCoreApplication中这是一个虚函数 } catch(...) { --threadData->loopLevel; throw; } #endif #ifdef QT_JAMBI_BUILD // Restore the previous state if the object was not deleted.. if (!deleteWatch) { d->inEventHandler = inEvent; } QObjectPrivate::resetDeleteWatch(d, oldDeleteWatch, deleteWatch); #endif --threadData->loopLevel; return returnValue; }
从上面的分析,我们继续看QApplication::notify()的实现:(为方便查看,大部分无关代码删除了)
bool QApplication::notify(QObject *receiver, QEvent *e) { Q_D(QApplication); …… bool res = false; if (!receiver->isWidgetType()) { res = d->notify_helper(receiver, e); } else switch (e->type()) { case QEvent::ShortcutOverride: case QEvent::KeyPress: case QEvent::KeyRelease: …… break; case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: { QWidget* w = static_cast<QWidget *>(receiver); QMouseEvent* mouse = static_cast<QMouseEvent*>(e); QPoint relpos = mouse->pos(); if (e->spontaneous()) { if (e->type() == QEvent::MouseButtonPress) { QWidget *fw = w; while (fw) { if (fw->isEnabled() && QApplicationPrivate::shouldSetFocus(fw, Qt::ClickFocus)) { fw->setFocus(Qt::MouseFocusReason); break; } if (fw->isWindow()) break; fw = fw->parentWidget(); } } // ### Qt 5 These dynamic tool tips should be an OPT-IN feature. Some platforms // …… 这里将来Qt5版本的实现描述。 if (e->type() == QEvent::MouseMove && mouse->buttons() == 0) { d->toolTipWidget = w; d->toolTipPos = relpos; d->toolTipGlobalPos = mouse->globalPos(); d->toolTipWakeUp.start(d->toolTipFallAsleep.isActive()?20:700, this); } } bool eventAccepted = mouse->isAccepted(); QPointer<QWidget> pw = w; while (w) { QMouseEvent me(mouse->type(), relpos, mouse->globalPos(), mouse->button(), mouse->buttons(), mouse->modifiers()); me.spont = mouse->spontaneous(); // throw away any mouse-tracking-only mouse events if (!w->hasMouseTracking() && mouse->type() == QEvent::MouseMove && mouse->buttons() == 0) { // but still send them through all application event filters (normally done by notify_helper) for (int i = 0; i < d->eventFilters.size(); ++i) { register QObject *obj = d->eventFilters.at(i); if (!obj) continue; if (obj->d_func()->threadData != w->d_func()->threadData) { qWarning("QApplication: Object event filter cannot be in a different thread."); continue; } if (obj->eventFilter(w, w == receiver ? mouse : &me)) break; } res = true; } else { w->setAttribute(Qt::WA_NoMouseReplay, false); res = d->notify_helper(w, w == receiver ? mouse : &me); e->spont = false; } eventAccepted = (w == receiver ? mouse : &me)->isAccepted(); if (res && eventAccepted) break; if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) break; relpos += w->pos(); w = w->parentWidget(); } mouse->setAccepted(eventAccepted); if (e->type() == QEvent::MouseMove) { if (!pw) break; w = static_cast<QWidget *>(receiver); relpos = mouse->pos(); QPoint diff = relpos - w->mapFromGlobal(d->hoverGlobalPos); while (w) { if (w->testAttribute(Qt::WA_Hover) && (!qApp->activePopupWidget() || qApp->activePopupWidget() == w->window())) { QHoverEvent he(QEvent::HoverMove, relpos, relpos - diff); d->notify_helper(w, &he); } if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation)) break; relpos += w->pos(); w = w->parentWidget(); } } d->hoverGlobalPos = mouse->globalPos(); } break; …… default: res = d->notify_helper(receiver, e); break; } return res; }
其中res = d->notify_helper(w, w == receiver ? mouse : &me);调用的是QApplicationPrivate::notify_helper(),我们看其具体实现:
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e) { // send to all application event filters if (sendThroughApplicationEventFilters(receiver, e)) return true; if (receiver->isWidgetType()) { QWidget *widget = static_cast<QWidget *>(receiver); #if !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !defined(QT_NO_CURSOR)) // toggle HasMouse widget state on enter and leave if ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) && (!qApp->activePopupWidget() || qApp->activePopupWidget() == widget->window())) widget->setAttribute(Qt::WA_UnderMouse, true); else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave) widget->setAttribute(Qt::WA_UnderMouse, false); #endif if (QLayout *layout=widget->d_func()->layout) { layout->widgetEvent(e); } } // send to all receiver event filters if (sendThroughObjectEventFilters(receiver, e)) return true; // deliver the event bool consumed = receiver->event(e); // 调用QPushButton::event() e->spont = false; return consumed; }
继续看QPushButton::event()的实现:
bool QPushButton::event(QEvent *e) { Q_D(QPushButton); if (e->type() == QEvent::ParentChange) { if (QDialog *dialog = d->dialogParent()) { if (d->defaultButton) dialog->d_func()->setMainDefault(this); } } else if (e->type() == QEvent::StyleChange #ifdef Q_WS_MAC || e->type() == QEvent::MacSizeChange #endif ) { d->resetLayoutItemMargins(); updateGeometry(); } return QAbstractButton::event(e); }
我们进一步看QAbstractButton::event的实现,忽略次要处理,主要是调用QWidget::event(e);
QWidget::event(QEvent *event)主体是根据参数event的类型switch分支处理各种事件,我们回头看QETWidget::translateMouseEvent(const MSG &msg)中,event对象生成时候的语句:
QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button), Qt::MouseButtons(state & Qt::MouseButtonMask), Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));
根据查找,这里type内容是MouseButtonPress。同样忽略其他无关代码,我们看QWidget::event()的代码:
bool QWidget::event(QEvent *event) { Q_D(QWidget); …… switch (event->type()) { case QEvent::MouseMove: mouseMoveEvent((QMouseEvent*)event); break; 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. mousePressEvent((QMouseEvent*)event); break; case QEvent::MouseButtonRelease: mouseReleaseEvent((QMouseEvent*)event); break; …… default: return QObject::event(event); } return true; }
我们看该事件处理的具体代码:
void QAbstractButton::mousePressEvent(QMouseEvent *e) { Q_D(QAbstractButton); if (e->button() != Qt::LeftButton) { e->ignore(); return; } if (hitButton(e->pos())) { // <-- 根据鼠标点击点的位置匹配按钮 setDown(true); repaint(); //flush paint event before invoking potentially expensive operation QApplication::flush(); d->emitPressed(); e->accept(); } else { e->ignore(); } }
在MouseButtonProcess事件处理之后,就是前面注册的Release事件(前面粉色部分代码)。
目前还有两个疑问:一个是自定义的信号(SIGNAL)如何跟Windows的消息关联的;另一个是信号和槽(SLOT)是如何关联的。根据前面的分析,对第一个问题的猜测是在event()函数里增加相应处理;对第二个问题,应该有一个函数指针表使之关联。带着这些问题我们接着分析mouseReleaseEvent()。
void QAbstractButton::mouseReleaseEvent(QMouseEvent *e) { Q_D(QAbstractButton); if (e->button() != Qt::LeftButton) { e->ignore(); return; } if (!d->down) { e->ignore(); return; } if (hitButton(e->pos())) { d->repeatTimer.stop(); d->click(); // 调用QAbstractButtonPrivate::click() e->accept(); } else { setDown(false); e->ignore(); } }
进一步看QAbstractButtonPrivate::click()的实现代码:
void QAbstractButtonPrivate::click() { Q_Q(QAbstractButton); down = false; blockRefresh = true; bool changeState = true; if (checked && queryCheckedButton() == q) { // the checked button of an exclusive or autoexclusive group cannot be unchecked #ifndef QT_NO_BUTTONGROUP if (group ? group->d_func()->exclusive : autoExclusive) #else if (autoExclusive) #endif changeState = false; } QPointer<QAbstractButton> guard(q); if (changeState) { q->nextCheckState(); if (!guard) return; } blockRefresh = false; refresh(); q->repaint(); //flush paint event before invoking potentially expensive operation QApplication::flush(); if (guard) emitReleased(); if (guard) emitClicked(); }
主要就是调用emitReleased()和emitClicked(),我们看其中QAbstractButtonPrivate::emitClicked()的实现
void QAbstractButtonPrivate::emitClicked() { Q_Q(QAbstractButton); QPointer<QAbstractButton> guard(q); emit q->clicked(checked); // 这里调用的是QAbstractButton::clicked() #ifndef QT_NO_BUTTONGROUP if (guard && group) { emit group->buttonClicked(group->id(q)); if (guard && group) emit group->buttonClicked(q); } #endif }
上面调用的函数实体,在moc_QAbstractButton.cpp里可以看到。这个文件实际是由QT的MOC工具自动产生的。
void QAbstractButton::clicked(bool _t1) { void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) }; QMetaObject::activate(this, &staticMetaObject, 2, 3, _a); // 和SLOT的调用关系应该是这里实现的。 }
我们先来看看QMetaObject类的定义:
struct Q_CORE_EXPORT QMetaObject { …… struct { // private data const QMetaObject *superdata; const char *stringdata; const uint *data; const void *extradata; } d; };
再看QMetaObjectPrivate的定义:
struct QMetaObjectPrivate { int revision; int className; int classInfoCount, classInfoData; int methodCount, methodData; int propertyCount, propertyData; int enumeratorCount, enumeratorData; int constructorCount, constructorData; };
实际上QMetaObject::d.data指向的就是QMetaObjectPrivate结构体 我们看看staticMetaObject对象的定义:(同样在moc_QAbstractButton.cpp文件中)
const QMetaObject QAbstractButton::staticMetaObject = { { &QWidget::staticMetaObject, qt_meta_stringdata_QAbstractButton, qt_meta_data_QAbstractButton, 0 } };
这是一个常对象,除设定父类的staticMetaObject外,还设定了两个全局变量:qt_meta_stringdata_QAbstractButton和qt_meta_data_QAbstractButton。
所有的奥秘都在这两个变量里面了。根据前面分析qt_meta_data_QAbstractButton实际是QMetaObjectPrivate结构。
static const uint qt_meta_data_QAbstractButton[] = { // content: 2, // revision 0, // classname 0, 0, // classinfo 11, 12, // methods 11, 67, // properties 0, 0, // enums/sets 0, 0, // constructors // signals: signature, parameters, type, tag, flags 17, 16, 16, 16, 0x05, // 17 -- 指的是stringdata中No.17字节开始的字符串,结合下面定义实际就是pressed() 27, 16, 16, 16, 0x05, // released() 46, 38, 16, 16, 0x05, // clicked(bool) 60, 16, 16, 16, 0x25, // clicked() 70, 38, 16, 16, 0x05, // toggled(bool) // slots: signature, parameters, type, tag, flags 89, 84, 16, 16, 0x0a, // setIconSize(QSize) 113, 108, 16, 16, 0x0a, 131, 16, 16, 16, 0x2a, 146, 16, 16, 16, 0x0a, 154, 16, 16, 16, 0x0a, 163, 16, 16, 16, 0x0a, // properties: name, type, flags 188, 180, 0x0a095103, // text 199, 193, 0x45095103, // icon 210, 204, 0x15095103, 232, 219, 0x4c095103, 246, 241, 0x01095103, 38, 241, 0x01595103, 256, 241, 0x01095103, 267, 241, 0x01095103, 285, 281, 0x02095103, 301, 281, 0x02095103, 320, 241, 0x01094103, // properties: notify_signal_id 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0 // eod }; static const char qt_meta_stringdata_QAbstractButton[] = { "QAbstractButton\0\0pressed()\0released()\0" "checked\0clicked(bool)\0clicked()\0" "toggled(bool)\0size\0setIconSize(QSize)\0" "msec\0animateClick(int)\0animateClick()\0" "click()\0toggle()\0setChecked(bool)\0" "QString\0text\0QIcon\0icon\0QSize\0iconSize\0" "QKeySequence\0shortcut\0bool\0checkable\0" "autoRepeat\0autoExclusive\0int\0" "autoRepeatDelay\0autoRepeatInterval\0" "down\0" };
我们接着看QMetaObject::activate()的代码:
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int from_local_signal_index, int to_local_signal_index, void **argv) { int offset = m->methodOffset(); // 指向qt_meta_data_QAbstractButton[27]字节,也就是clicked(bool) int from_signal_index = offset + from_local_signal_index; // 27 + 2 = 29 int to_signal_index = offset + to_local_signal_index; // 27 + 3 = 30 if (to_signal_index < 32 && !qt_signal_spy_callback_set.signal_begin_callback && !qt_signal_spy_callback_set.signal_end_callback) { uint signal_mask = (1 << (to_signal_index + 1)) - 1; signal_mask ^= (1 << from_signal_index) - 1; // sender指的是QPushButton,下面指向的是:QPushButtonPrivate::connectedSignals if ((sender->d_func()->connectedSignals & signal_mask) == 0) // nothing connected to these signals, and no spy return; } activate(sender, from_signal_index, to_signal_index, argv); }
可以看到,在判断本信号是否连接有槽之后,就调用了activate的重载函数:
void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv) { if (sender->d_func()->blockSig) return; void *empty_argv[] = { 0 }; if (qt_signal_spy_callback_set.signal_begin_callback != 0) { qt_signal_spy_callback_set.signal_begin_callback(sender, from_signal_index, argv ? argv : empty_argv); } QMutexLocker locker(&sender->d_func()->threadData->mutex); QThreadData *currentThreadData = QThreadData::current(); QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists; if (!connectionLists) { if (qt_signal_spy_callback_set.signal_end_callback != 0) qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index); return; } ++connectionLists->inUse; // emit signals in the following order: from_signal_index <= signals <= to_signal_index, signal < 0 for (int signal = from_signal_index; (signal >= from_signal_index && signal <= to_signal_index) || (signal == -2); (signal == to_signal_index ? signal = -2 : ++signal)) { if (signal >= connectionLists->count()) { signal = to_signal_index; continue; } int count = connectionLists->at(signal).count(); // 就是在这里获取信号接收的槽函数指针的。 // connectionLists里的数据,猜测是由QObject::connect()填进去的。 for (int i = 0; i < count; ++i) { const QObjectPrivate::Connection *c = &connectionLists->at(signal)[i]; 39 if (!c->receiver) 40 continue; QObject * const receiver = c->receiver; // determine if this connection should be sent immediately or // put into the event queue if ((c->connectionType == Qt::AutoConnection && (currentThreadData != sender->d_func()->threadData || receiver->d_func()->threadData != sender->d_func()->threadData)) || (c->connectionType == Qt::QueuedConnection)) { queued_activate(sender, signal, *c, argv); continue; } else if (c->connectionType == Qt::BlockingQueuedConnection) { blocking_activate(sender, signal, *c, argv); continue; } const int method = c->method; QObjectPrivate::Sender currentSender; currentSender.sender = sender; currentSender.signal = signal < 0 ? from_signal_index : signal; currentSender.ref = 1; QObjectPrivate::Sender *previousSender = 0; if (currentThreadData == receiver->d_func()->threadData) previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender); locker.unlock(); if (qt_signal_spy_callback_set.slot_begin_callback != 0) { qt_signal_spy_callback_set.slot_begin_callback(receiver, method, argv ? argv : empty_argv); } #if defined(QT_NO_EXCEPTIONS) receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); #else try { // 在我们的分析中,连接的槽是QApplication::quit(),qt_metacall在哪定义的呢? receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); } catch (...) { locker.relock(); QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); --connectionLists->inUse; Q_ASSERT(connectionLists->inUse >= 0); if (connectionLists->orphaned && !connectionLists->inUse) delete connectionLists; throw; } #endif locker.relock(); if (qt_signal_spy_callback_set.slot_end_callback != 0) qt_signal_spy_callback_set.slot_end_callback(receiver, method); QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); if (connectionLists->orphaned) break; } if (connectionLists->orphaned) break; } --connectionLists->inUse; Q_ASSERT(connectionLists->inUse >= 0); if (connectionLists->orphaned) { if (!connectionLists->inUse) delete connectionLists; } else { sender->d_func()->cleanConnectionLists(); } locker.unlock(); if (qt_signal_spy_callback_set.signal_end_callback != 0) qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index); }
单步跟踪,receiver->qt_metacall();实际调用的是QApplication::qt_metacall(),根据调用参数实现不同的函数调用。在moc_QApplication.cpp中定义,是由MOC工具自动产生的代码。至此,信号与槽的关联分析完毕。
明天接着分析QObject::connect()如何把相关数据填入connectionLists。自定义信号如何与windows消息关联在明天分析完毕之后再来证实。
在继续分析之前,我们回头看看test.cpp的main()函数:
int main( int argc, char **argv ) { QApplication a( argc, argv ); QPushButton quit( "Quit", 0 ); quit.resize( 75, 30 ); quit.setFont( QFont( "Times", 18, QFont::Bold ) ); QObject::connect( &quit, SIGNAL(clicked()), &a, SLOT(quit()) ); quit.show(); return a.exec(); }
根据我们猜测,就是上面红色部分语句把消息处理函数指针填入了connectionLists。
首先我们看看SIGNAL宏和SLOT宏的定义:(另外一种定义是为DEBUG用的,可忽略)
# define METHOD(a) "0"#a # define SLOT(a) "1"#a # define SIGNAL(a) "2"#a
使用的是宏转义,SIGNAL(clicked())被展开成"2clicked()"(字符串);SLOT(quit())被展开成"1quit()"。
再看QObject::connect()的声明:
static bool connect(const QObject *sender, const char *signal, const QObject *receiver,const char *member, Qt::ConnectionType = Qt::AutoConnection );
上面的调用语句展开之后就是:
QObject::connect(&quit, "2clicked()", &a, "1quit()");
然后看QObject::connect()的定义:
bool QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type) { { const void *cbdata[] = { sender, signal, receiver, method, &type }; if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) return true; } if (type == Qt::AutoCompatConnection) { type = Qt::AutoConnection; } if (sender == 0 || receiver == 0 || signal == 0 || method == 0) { qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", sender ? sender->metaObject()->className() : "(null)", (signal && *signal) ? signal+1 : "(null)", receiver ? receiver->metaObject()->className() : "(null)", (method && *method) ? method+1 : "(null)"); return false; } QByteArray tmp_signal_name; // 检查signal是否以2开头 if (!check_signal_macro(sender, signal, "connect", "bind")) return false; const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code // 获得signal的函数编号 int signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) { // check for normalized signatures tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); signal = tmp_signal_name.constData() + 1; signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } } QByteArray tmp_method_name; int membcode = extract_code(method); // 检查receiver是否以1开头 if (!check_method_code(membcode, receiver, method, "connect")) return false; const char *method_arg = method; ++method; // skip code // 获得receiver的函数编号 const QMetaObject *rmeta = receiver->metaObject(); int method_index = -1; switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; } if (method_index < 0) { // check for normalized methods tmp_method_name = QMetaObject::normalizedSignature(method); method = tmp_method_name.constData(); switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; } } if (method_index < 0) { err_method_notfound(receiver, method_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } if (!QMetaObject::checkConnectArgs(signal, method)) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", sender->metaObject()->className(), signal, receiver->metaObject()->className(), method); return false; } int *types = 0; if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes()))) return false; QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); const_cast<QObject*>(sender)->connectNotify(signal - 1); return true; }
用红色标记出来的三个主要调用,我们先看QInternal::activateCallbacks()的实现:
bool QInternal::activateCallbacks(Callback cb, void **parameters) { Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id"); QInternal_CallBackTable *cbt = global_callback_table(); if (cbt && cb < cbt->callbacks.size()) { QList<qInternalCallback> callbacks = cbt->callbacks[cb]; bool ret = false; for (int i=0; i<callbacks.size(); ++i) ret |= (callbacks.at(i))(parameters); return ret; } return false; }
这是优先处理回调函数(钩子函数),在我们这里的应用中没有回调,所以可以忽略。
接着看QMetaObject::connect()的实现:
bool QMetaObject::connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type, int *types) { QObject *s = const_cast<QObject *>(sender); QObject *r = const_cast<QObject *>(receiver); QOrderedMutexLocker locker(&s->d_func()->threadData->mutex, &r->d_func()->threadData->mutex); QObjectPrivate::Connection c = { r, method_index, type, Q_BASIC_ATOMIC_INITIALIZER(types) }; s->d_func()->addConnection(signal_index, &c); r->d_func()->refSender(s, signal_index); if (signal_index < 0) sender->d_func()->connectedSignals = ~0u; else if (signal_index < 32) sender->d_func()->connectedSignals |= (1 << signal_index); return true; }
s->d_func()指向的是QPushButtonPrivate指针,QPushButtonPrivate没有addConnection()成员实际调用的是其基类成员,
s->d_func()->addConnection()调用的是QObjectPrivate::addConnection()。进一步看其实现:
void QObjectPrivate::addConnection(int signal, Connection *c) { if (!connectionLists) connectionLists = new QObjectConnectionListVector(); if (signal >= connectionLists->count()) connectionLists->resize(signal + 1); ConnectionList &connectionList = (*connectionLists)[signal]; connectionList.append(*c); cleanConnectionLists(); }
这里填入了发送消息的SIGNAL的函数指针!我们接着看r->d_func()->refSender(s, signal_index);其中r指向的是QApplication对象(a),所以r->d_func()是QApplicationPrivate对象指针,同样因其本身没有refSender()成员函数,调用的是其基类QObjectPrivate::refSender()。我们看其实现:
void QObjectPrivate::refSender(QObject *sender, int signal) { for (int i = 0; i < senders.count(); ++i) { Sender &s = senders[i]; if (s.sender == sender && s.signal == signal) { ++s.ref; return; } } Sender s = { sender, signal, 1 }; senders.append(s); }
至此,我们的猜想得到证实。分析完毕。
相关文章推荐
- Qt模拟键盘按键与组合键
- 更改Qt应用程序图标的方法
- QT分析之QPushButton的初始化
- QT分析之WebKit
- Qt 实时读串口数据,并将读到的数据从网口发送出去
- 发布Qt Widgets桌面应用程序的方法
- Qt中信号和槽的问题解析
- QML中的SmoothedAnimation平滑动画
- QML之PathAnimation路径动画
- QTsocket和标准C库socket混用遇到的问题
- QML之ColorAnimation颜色动画
- Qt中如何使用Sleep函数
- QML动画之NumberAnimation数字类动画
- QML中在信号处理器中处理动画
- Windows下QT程序中调用boost库
- QT异形图形的开发
- 在 QT5 中使用 SQLITE
- QT学习之路————QT程序在开发板上旋转
- Qt制作应用插件
- 笔记:QT的模态对话框、非模态对话框及QLineEdit的验证器和补全器