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