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, &currentSender);
 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, &currentSender, 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, &currentSender, 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 }

至此,我們的猜想得到證實。分析完畢。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM