zoukankan      html  css  js  c++  java
  • 3、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
    "
    32                  "exceptions from an event handler is not supported in Qt. You must
    "
    33                  "reimplement QApplication::notify() and catch all exceptions there.
    ");
    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     "QAbstractButtonpressed()released()"
    60     "checkedclicked(bool)clicked()"
    61     "toggled(bool)sizesetIconSize(QSize)"
    62     "msecanimateClick(int)animateClick()"
    63     "click()toggle()setChecked(bool)"
    64     "QStringtextQIconiconQSizeiconSize"
    65     "QKeySequenceshortcutboolcheckable"
    66     "autoRepeatautoExclusiveint"
    67     "autoRepeatDelayautoRepeatInterval"
    68     "down"
    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                  "
            %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 }
    复制代码

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

    转自:http://www.cnblogs.com/lfsblack/p/5279143.html

  • 相关阅读:
    CSUST--3.14排位周赛第四场 (全解)
    CSUST--3.7排位周赛第三场 (全解)
    android 代码功能测试junit test
    Sqlite数据库创建、删除、降级笔记
    三种进程和线程数据共享模块方法Queue》Pipe》manager
    多线程实例
    信号量
    红绿灯模型
    linshiwendang12--匈牙利
    spring--注入类型--构造方法(不常用)
  • 原文地址:https://www.cnblogs.com/liushui-sky/p/6474066.html
Copyright © 2011-2022 走看看