原文地址: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 }
至此,我們的猜想得到證實。分析完畢。
