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 }

至此,我们的猜想得到证实。分析完毕。

 

posted @ 2016-03-15 13:28  lfsblack  阅读(6369)  评论(0编辑  收藏  举报