欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qt之消息事件机制

程序员文章站 2022-07-14 17:42:43
...
 我们知道WinSDK中,简单Windows程序里的WinMain函数主要就这么几件事:
    1、窗体注册;
    2、消息处理函数;
    3、等待和消息处理循环
  QApplication::exec()只做了两件事:设定根对象和调用QCoreApplication::exec()。
  QCoreApplication::exec()函数的代码如下,按惯例关键部分用颜色或追加注释。
int QCoreApplication::exec()
{
    if (!QCoreApplicationPrivate::checkInstance("exec"))
        return -1;

    QThreadData *threadData = self->d_func()->threadData;
    if (threadData != QThreadData::current()) {
        qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
        return -1;
    }
    if (!threadData->eventLoops.isEmpty()) {
        qWarning("QCoreApplication::exec: The event loop is already running");
        return -1;
    }

    // 从这里开始是事件处理循环开始前准备

    threadData->quitNow = false;
    QEventLoop eventLoop;
    self->d_func()->in_exec = true;
    self->d_func()->aboutToQuitEmitted = false;
    int returnCode = eventLoop.exec();  // 事件处理主体

    // 从这里开始是事件处理循环后处理(通常为App退出)
    threadData->quitNow = false;
    if (self) {
        self->d_func()->in_exec = false;
        if (!self->d_func()->aboutToQuitEmitted)
            emit self->aboutToQuit();
        self->d_func()->aboutToQuitEmitted = true;
        sendPostedEvents(0, QEvent::DeferredDelete);
    }

    return returnCode;
}
     我们先看QEventLoop::exec()的声明:int exec(ProcessEventsFlags flags = AllEvents);
   对于上面eventLoop.exec();  这种调用形式,意思说使用AllEvents(就是0x00值)标记
   接着看QEventLoop::exec()的定义:
int QEventLoop::exec(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (d->threadData->quitNow)
        return -1;

    if (d->inExec) {
        qWarning("QEventLoop::exec: instance %p has already called exec()", this);
        return -1;
    }
    d->inExec = true;
    d->exit = false;
    ++d->threadData->loopLevel;
    d->threadData->eventLoops.push(this);

    // remove posted quit events when entering a new event loop
    QCoreApplication *app = QCoreApplication::instance();
    if (app && app->thread() == thread())
        QCoreApplication::removePostedEvents(app, QEvent::Quit);

#if defined(QT_NO_EXCEPTIONS)
    while (!d->exit)
        processEvents(flags | WaitForMoreEvents | EventLoopExec);
#else
    try {
        while (!d->exit)  // 如果exit变量没有设定为True的话,会一直循环处理

            // flags被设定为:AllEvents、WaitForMoreEvents、EventLoopExec
            processEvents(flags | WaitForMoreEvents | EventLoopExec);
    } catch (...) {
        qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
                 "exceptions from an event handler is not supported in Qt. You must\n"
                 "reimplement QApplication::notify() and catch all exceptions there.\n");

        // copied from below
        QEventLoop *eventLoop = d->threadData->eventLoops.pop();
        Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
        Q_UNUSED(eventLoop); // --release warning
        d->inExec = false;
        --d->threadData->loopLevel;

        throw;
    }
#endif

    // copied above
    QEventLoop *eventLoop = d->threadData->eventLoops.pop();
    Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
    Q_UNUSED(eventLoop); // --release warning
    d->inExec = false;
    --d->threadData->loopLevel;

    return d->returnCode;
}

   继续深入看QEventLoop::processEvents()的定义

bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (!d->threadData->eventDispatcher)
        return false;
    if (flags & DeferredDeletion)
        QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);

     // 根据d->threadData指向对象的不同,调用不同的Dispatcher
    return d->threadData->eventDispatcher->processEvents(flags); 
}

    这里,用单步跟踪我们可以知道实际调用的是QGuiEventDispatcherWin32::processEvents(),看其实现代码:

bool QGuiEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    if (!QEventDispatcherWin32::processEvents(flags))
        return false;

    if (configRequests)                        // any pending configs?
        qWinProcessConfigRequests();

    return true;
}

    继续深入九层地狱,看QEventDispatcherWin32::processEvents()的定义:

bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
    Q_D(QEventDispatcherWin32);

    if (!d->internalHwnd)
        createInternalHwnd();

    d->interrupt = false;
    emit awake();  // emit在Win32平台下实际就是空的

    bool canWait;
    bool retVal = false;
    do {
        QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);  //这句没有深入分析

        DWORD waitRet = 0;
        HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
        QVarLengthArray<MSG> processedTimers;
        while (!d->interrupt) {
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);

            MSG msg;
            bool haveMessage;

            // 使用用户输入事件并且该事件队列不为空

            if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
                // process queued user input events
                haveMessage = true;
                msg = d->queuedUserInputEvents.takeFirst();
            } 

            // 使用Socket通知事件并且该事件队列不为空

           else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
                // process queued socket events
                haveMessage = true;
                msg = d->queuedSocketEvents.takeFirst();
            } else {

                // 所有其他情况,Peek一下Windows的消息
                haveMessage = winPeekMessage(&msg, 0, 0, 0, PM_REMOVE);

                // 根据消息种类放置到相应消息队列,备后面处理使用
                if (haveMessage && (flags & QEventLoop::ExcludeUserInputEvents)
                    && ((msg.message >= WM_KEYFIRST
                         && msg.message <= WM_KEYLAST)
                        || (msg.message >= WM_MOUSEFIRST
                            && msg.message <= WM_MOUSELAST)
                        || msg.message == WM_MOUSEWHEEL)) {
                    // queue user input events for later processing
                    haveMessage = false;
                    d->queuedUserInputEvents.append(msg);
                }
                if (haveMessage && (flags & QEventLoop::ExcludeSocketNotifiers)
                    && (msg.message == WM_USER && msg.hwnd == d->internalHwnd)) {
                    // queue socket events for later processing
                    haveMessage = false;
                    d->queuedSocketEvents.append(msg);
                }
            }
            if (!haveMessage) {
                // no message - check for signalled objects  // 没有消息的情况下,等待事件通知
                for (int i=0; i<(int)nCount; i++)
                    pHandles[i] = d->winEventNotifierList.at(i)->handle();
                waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
                if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
                    // a new message has arrived, process it
                    continue;
                }
            }
            if (haveMessage) {

                // 定时事件的处理
                if (msg.message == WM_TIMER) {
                    // avoid live-lock by keeping track of the timers we've already sent
                    bool found = false;
                    for (int i = 0; !found && i < processedTimers.count(); ++i) {
                        const MSG processed = processedTimers.constData()[i];
                        found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
                    }
                    if (found)
                        continue;
                    processedTimers.append(msg);
                } else if (msg.message == WM_QUIT) {

                    // 退出事件的处理
                    if (QCoreApplication::instance())
                        QCoreApplication::instance()->quit();
                    return false;
                }

                if (!filterEvent(&msg)) {

                    // 如果没有被[消息过滤器]过滤掉,那么就派发该消息
                    TranslateMessage(&msg);
                    QT_WA({
                        DispatchMessage(&msg);
                    } , {
                        DispatchMessageA(&msg);
                    });
                }
            } else if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
            } else {
                // nothing todo so break
                break;
            }
            retVal = true;
        }

        // still nothing - wait for message or signalled objects
        QThreadData *data = d->threadData;
        canWait = (!retVal
                   && data->canWait
                   && !d->interrupt
                   && (flags & QEventLoop::WaitForMoreEvents));
        if (canWait) {
            DWORD nCount = d->winEventNotifierList.count();
            Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
            for (int i=0; i<(int)nCount; i++)
                pHandles[i] = d->winEventNotifierList.at(i)->handle();

            emit aboutToBlock();
            waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE);
            emit awake();
            if (waitRet >= WAIT_OBJECT_0 && waitRet < WAIT_OBJECT_0 + nCount) {
                d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
                retVal = true;
            }
        }
    } while (canWait);

    return retVal;
}

   至此,一个完整的路径似乎分析完毕了,等等,好像不太对。前面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类的定义:

class Q_CORE_EXPORT QEventLoop : public QObject
{
    Q_OBJECT
    Q_DECLARE_PRIVATE(QEventLoop)

public:
    explicit QEventLoop(QObject *parent = 0);
    ~QEventLoop();

    enum ProcessEventsFlag {
        AllEvents = 0x00,
        ExcludeUserInputEvents = 0x01,
        ExcludeSocketNotifiers = 0x02,
        WaitForMoreEvents = 0x04,
#ifdef QT3_SUPPORT
        ExcludeUserInput = ExcludeUserInputEvents,
        WaitForMore = WaitForMoreEvents,
#endif
 X11ExcludeTimers = 0x08
#ifdef QT_DEPRECATED
 , DeferredDeletion = 0x10
#endif
        , EventLoopExec = 0x20
        , DialogExec = 0x40
    };
    Q_DECLARE_FLAGS(ProcessEventsFlags, ProcessEventsFlag)

    bool processEvents(ProcessEventsFlags flags = AllEvents);
    void processEvents(ProcessEventsFlags flags, int maximumTime);

    int exec(ProcessEventsFlags flags = AllEvents);
    void exit(int returnCode = 0);
    bool isRunning() const;

    void wakeUp();

public Q_SLOTS:
    void quit();
};

   果然看到了我们想看的东西,把QEventLoop::exit(int returnCode)函数代码找到:

void QEventLoop::exit(int returnCode)
{
    Q_D(QEventLoop);
    if (!d->threadData->eventDispatcher)
        return;

    d->returnCode = returnCode;
    d->exit = true;
    d->threadData->eventDispatcher->interrupt();
}

   添加一个断点,运行程序点击退出按钮之后,我们可以得到下面的堆栈列表:

QtCored4.dll!QEventLoop::exit(int returnCode=0x00000000)  行284 C++
  QtCored4.dll!QCoreApplication::exit(int returnCode=0x00000000)  行926 + 0xc 字节 C++
  QtCored4.dll!QCoreApplication::quit()  行1468 + 0x7 字节 C++
  QtCored4.dll!QCoreApplication::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000002, void * * _a=0x0012bd28)  行84 C++
  QtGuid4.dll!QApplication::qt_metacall(QMetaObject::Call _c=InvokeMetaMethod, int _id=0x00000006, void * * _a=0x0012bd28)  行96 + 0x15 字节 C++
  QtCored4.dll!QMetaObject::activate(QObject * sender=0x0012ff40, int from_signal_index=0x0000001d, int to_signal_index=0x0000001e, void * * argv=0x0012bd28)  行3104 + 0x2b 字节 C++
  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++
  QtGuid4.dll!QAbstractButton::clicked(bool _t1=false)  行198 + 0x17 字节 C++
  QtGuid4.dll!QAbstractButtonPrivate::emitClicked()  行545 C++
  QtGuid4.dll!QAbstractButtonPrivate::click()  行537 C++
  QtGuid4.dll!QAbstractButton::mouseReleaseEvent(QMouseEvent * e=0x0012c450)  行1116 C++
  QtGuid4.dll!QWidget::event(QEvent * event=0x0012c450)  行7555 C++
  QtGuid4.dll!QAbstractButton::event(QEvent * e=0x0012c450)  行1078 C++
  QtGuid4.dll!QPushButton::event(QEvent * e=0x0012c450)  行663 C++
  QtGuid4.dll!QApplicationPrivate::notify_helper(QObject * receiver=0x0012ff40, QEvent * e=0x0012c450)  行4065 + 0x11 字节 C++
  QtGuid4.dll!QApplication::notify(QObject * receiver=0x0012ff40, QEvent * e=0x0012c450)  行3767 + 0x2f 字节 C++
  QtCored4.dll!QCoreApplication::notifyInternal(QObject * receiver=0x0012ff40, QEvent * event=0x0012c450)  行610 + 0x15 字节 C++
  QtCored4.dll!QCoreApplication::sendSpontaneousEvent(QObject * receiver=0x0012ff40, QEvent * event=0x0012c450)  行216 + 0x38 字节 C++
  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++
  QtGuid4.dll!QETWidget::translateMouseEvent(const tagMSG & msg={...})  行3269 + 0x28 字节 C++
  QtGuid4.dll!QtWndProc(HWND__ * hwnd=0x00010840, unsigned int message=0x00000202, unsigned int wParam=0x00000000, long lParam=0x000c0064)  行1667 + 0xc 字节 C++
  user32.dll!77e2b6e3()

    QEventDispatcherWin32::processEvents()派发消息之后,QtWndProc()获得该消息。我们看看QtWndProc()的具体处理:

1.根据hwnd获取QWidget指针:

 widget = (QETWidget*)QWidget::find(hwnd);

2.处理事件:

result = widget->translateMouseEvent(msg);        // mouse event

   获取的widget其实就是QPushButton对象的指针;事件的处理实体在QETWidget::translateMouseEvent(),该函数又是一个超长代码的实现,其处理是事件压缩之类Mouse事件的周边处理,之后是调用:

QApplicationPrivate::sendMouseEvent(),

   我们看其实现:

bool QApplicationPrivate::sendMouseEvent(QWidget *receiver, QMouseEvent *event,
                                         QWidget *alienWidget, QWidget *nativeWidget,
                                         QWidget **buttonDown, QPointer<QWidget> &lastMouseReceiver)
{
    Q_ASSERT(receiver);
    Q_ASSERT(event);
    Q_ASSERT(nativeWidget);
    Q_ASSERT(buttonDown);

    if (alienWidget && !isAlien(alienWidget))
        alienWidget = 0;

    QPointer<QWidget> receiverGuard = receiver;
    QPointer<QWidget> nativeGuard = nativeWidget;
    QPointer<QWidget> alienGuard = alienWidget;
    QPointer<QWidget> activePopupWidget = qApp->activePopupWidget();

    const bool graphicsWidget = nativeWidget->testAttribute(Qt::WA_DontShowOnScreen);

    if (*buttonDown) {

        // 如果是按钮&是图片窗体的话,注册该窗体使得最后一个按钮释放的时候接受leave事件
        if (!graphicsWidget) {
            // Register the widget that shall receive a leave event
            // after the last button is released.
            if ((alienWidget || !receiver->internalWinId()) && !leaveAfterRelease && !QWidget::mouseGrabber())
                leaveAfterRelease = *buttonDown;
            if (event->type() == QEvent::MouseButtonRelease && !event->buttons())
                *buttonDown = 0;
        }
    } else if (lastMouseReceiver) {
        // Dispatch enter/leave if we move:
        // 1) from an alien widget to another alien widget or
        //    from a native widget to an alien widget (first OR case)
        // 2) from an alien widget to a native widget (second OR case)
        if ((alienWidget && alienWidget != lastMouseReceiver)
            || (isAlien(lastMouseReceiver) && !alienWidget)) {
            if (activePopupWidget) {
                if (!QWidget::mouseGrabber())
                    dispatchEnterLeave(alienWidget ? alienWidget : nativeWidget, lastMouseReceiver);
            } else {
                dispatchEnterLeave(receiver, lastMouseReceiver);
            }

        }
    }

#ifdef ALIEN_DEBUG
    qDebug() << "QApplicationPrivate::sendMouseEvent: receiver:" << receiver
             << "pos:" << event->pos() << "alien" << alienWidget << "button down"
             << *buttonDown << "last" << lastMouseReceiver << "leave after release"
             << leaveAfterRelease;
#endif

    // We need this quard in case someone opens a modal dialog / popup. If that's the case
    // leaveAfterRelease is set to null, but we shall not update lastMouseReceiver.
    const bool wasLeaveAfterRelease = leaveAfterRelease != 0;
    bool result = QApplication::sendSpontaneousEvent(receiver, event);

    if (!graphicsWidget && leaveAfterRelease && event->type() == QEvent::MouseButtonRelease
        && !event->buttons() && QWidget::mouseGrabber() != leaveAfterRelease) {
        // Dispatch enter/leave if:
        // 1) the mouse grabber is an alien widget
        // 2) the button is released on an alien widget

        QWidget *enter = 0;
        if (nativeGuard)
            enter = alienGuard ? alienWidget : nativeWidget;
        else // The receiver is typically deleted on mouse release with drag'n'drop.
            enter = QApplication::widgetAt(event->globalPos());

        dispatchEnterLeave(enter, leaveAfterRelease);
        leaveAfterRelease = 0;
        lastMouseReceiver = enter;
    } else if (!wasLeaveAfterRelease) {
        if (activePopupWidget) {
            if (!QWidget::mouseGrabber())
                lastMouseReceiver = alienGuard ? alienWidget : (nativeGuard ? nativeWidget : 0);
        } else {
            lastMouseReceiver = receiverGuard ? receiver : QApplication::widgetAt(event->globalPos());
        }
    }

    return result;
}

  根据单步跟踪(也可以通过代码分析)知道QApplication::sendSpontaneousEvent()调用的是QCoreApplication::sendSpontaneousEvent(),该函数判断self指针(this指针?)是否为空,不为空则调用notifyInternal()函数;也就是调用QCoreApplication::notifyInternal(),参数为receiver(也就是根据hwnd获取的QPushButton对象指针),和event。我们看其实现代码:

bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)
{
    // Make it possible for Qt Jambi and QSA to hook into events even
    // though QApplication is subclassed...
    bool result = false;
    void *cbdata[] = { receiver, event, &result };

    // 检查CallBack列表中是否有QInternal::EventNotifyCallback类型,有的话则调用之

    // 该CallBack的返回必须为True,否则此处会继续往下运行
    if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
        return result;
    }

    // Qt enforces the rule that events can only be sent to objects in
    // the current thread, so receiver->d_func()->threadData is
    // equivalent to QThreadData::current(), just without the function
    // call overhead.
    QObjectPrivate *d = receiver->d_func();
    QThreadData *threadData = d->threadData;
    ++threadData->loopLevel;

#ifdef QT_JAMBI_BUILD
    int deleteWatch = 0;
    int *oldDeleteWatch = QObjectPrivate::setDeleteWatch(d, &deleteWatch);

    bool inEvent = d->inEventHandler;
    d->inEventHandler = true;
#endif

#if defined(QT_NO_EXCEPTIONS)
    bool returnValue = notify(receiver, event);
#else
    bool returnValue;
    try {
        returnValue = notify(receiver, event);  // QCoreApplication中这是一个虚函数
    } catch(...) {
        --threadData->loopLevel;
        throw;
    }
#endif

#ifdef QT_JAMBI_BUILD
    // Restore the previous state if the object was not deleted..
    if (!deleteWatch) {
        d->inEventHandler = inEvent;
    }
    QObjectPrivate::resetDeleteWatch(d, oldDeleteWatch, deleteWatch);
#endif
    --threadData->loopLevel;
    return returnValue;
}

    从上面的分析,我们继续看QApplication::notify()的实现:

bool QApplication::notify(QObject *receiver, QEvent *e)
{
    Q_D(QApplication);

    ……
    bool res = false;
    if (!receiver->isWidgetType()) {
        res = d->notify_helper(receiver, e);
    } else switch (e->type()) {
    case QEvent::ShortcutOverride:
    case QEvent::KeyPress:
    case QEvent::KeyRelease:

        ……
        break;
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease:
    case QEvent::MouseButtonDblClick:
    case QEvent::MouseMove:
        {
            QWidget* w = static_cast<QWidget *>(receiver);

            QMouseEvent* mouse = static_cast<QMouseEvent*>(e);
            QPoint relpos = mouse->pos();

            if (e->spontaneous()) {

                if (e->type() == QEvent::MouseButtonPress) {
                    QWidget *fw = w;
                    while (fw) {
                        if (fw->isEnabled()
                            && QApplicationPrivate::shouldSetFocus(fw, Qt::ClickFocus)) {
                            fw->setFocus(Qt::MouseFocusReason);
                            break;
                        }
                        if (fw->isWindow())
                            break;
                        fw = fw->parentWidget();
                    }
                }

                // ### Qt 5 These dynamic tool tips should be an OPT-IN feature. Some platforms
                // …… 这里将来Qt5版本的实现描述。
                if (e->type() == QEvent::MouseMove && mouse->buttons() == 0) {
                    d->toolTipWidget = w;
                    d->toolTipPos = relpos;
                    d->toolTipGlobalPos = mouse->globalPos();
                    d->toolTipWakeUp.start(d->toolTipFallAsleep.isActive()?20:700, this);
                }
            }

            bool eventAccepted = mouse->isAccepted();

            QPointer<QWidget> pw = w;
            while (w) {
                QMouseEvent me(mouse->type(), relpos, mouse->globalPos(), mouse->button(), mouse->buttons(),
                               mouse->modifiers());
                me.spont = mouse->spontaneous();
                // throw away any mouse-tracking-only mouse events
                if (!w->hasMouseTracking()
                    && mouse->type() == QEvent::MouseMove && mouse->buttons() == 0) {
                    // but still send them through all application event filters (normally done by notify_helper)
                    for (int i = 0; i < d->eventFilters.size(); ++i) {
                        register QObject *obj = d->eventFilters.at(i);
                        if (!obj)
                            continue;
                        if (obj->d_func()->threadData != w->d_func()->threadData) {
                            qWarning("QApplication: Object event filter cannot be in a different thread.");
                            continue;
                        }
                        if (obj->eventFilter(w, w == receiver ? mouse : &me))
                            break;
                    }
                    res = true;
                } else {
                    w->setAttribute(Qt::WA_NoMouseReplay, false);
                    res = d->notify_helper(w, w == receiver ? mouse : &me);
                    e->spont = false;
                }
                eventAccepted = (w == receiver ? mouse : &me)->isAccepted();
                if (res && eventAccepted)
                    break;
                if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
                    break;
                relpos += w->pos();
                w = w->parentWidget();
            }

            mouse->setAccepted(eventAccepted);

            if (e->type() == QEvent::MouseMove) {
                if (!pw)
                    break;

                w = static_cast<QWidget *>(receiver);
                relpos = mouse->pos();
                QPoint diff = relpos - w->mapFromGlobal(d->hoverGlobalPos);
                while (w) {
                    if (w->testAttribute(Qt::WA_Hover) &&
                        (!qApp->activePopupWidget() || qApp->activePopupWidget() == w->window())) {
                        QHoverEvent he(QEvent::HoverMove, relpos, relpos - diff);
                        d->notify_helper(w, &he);
                    }
                    if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))
                        break;
                    relpos += w->pos();
                    w = w->parentWidget();
                }
            }

            d->hoverGlobalPos = mouse->globalPos();
        }
        break;

    ……
    default:
        res = d->notify_helper(receiver, e);
        break;
    }

    return res;
}

   其中res = d->notify_helper(w, w == receiver ? mouse : &me);调用的是QApplicationPrivate::notify_helper(),我们看其具体实现:

bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
    // send to all application event filters
    if (sendThroughApplicationEventFilters(receiver, e))
        return true;

    if (receiver->isWidgetType()) {
        QWidget *widget = static_cast<QWidget *>(receiver);

#if !defined(Q_OS_WINCE) || (defined(GWES_ICONCURS) && !defined(QT_NO_CURSOR))
        // toggle HasMouse widget state on enter and leave
        if ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) &&
            (!qApp->activePopupWidget() || qApp->activePopupWidget() == widget->window()))
            widget->setAttribute(Qt::WA_UnderMouse, true);
        else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave)
            widget->setAttribute(Qt::WA_UnderMouse, false);
#endif

        if (QLayout *layout=widget->d_func()->layout) {
            layout->widgetEvent(e);
        }
    }

    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, e))
        return true;

    // deliver the event
    bool consumed = receiver->event(e);  // 调用QPushButton::event()
    e->spont = false;
    return consumed;
}

   继续看QPushButton::event()的实现:

bool QPushButton::event(QEvent *e)
{
    Q_D(QPushButton);
    if (e->type() == QEvent::ParentChange) {
        if (QDialog *dialog = d->dialogParent()) {
            if (d->defaultButton)
                dialog->d_func()->setMainDefault(this);
        }
    } else if (e->type() == QEvent::StyleChange
#ifdef Q_WS_MAC
               || e->type() == QEvent::MacSizeChange
#endif
               ) {
  d->resetLayoutItemMargins();
  updateGeometry();
    }
    return QAbstractButton::event(e);
}

    我们进一步看QAbstractButton::event的实现,忽略次要处理,主要是调用QWidget::event(e);

  QWidget::event(QEvent *event)主体是根据参数event的类型switch分支处理各种事件,我们回头看QETWidget::translateMouseEvent(const MSG &msg)中,event对象生成时候的语句:

QMouseEvent e(type, pos, globalPos, Qt::MouseButton(button),
                       Qt::MouseButtons(state & Qt::MouseButtonMask),
                      Qt::KeyboardModifiers(state & Qt::KeyboardModifierMask));

    根据查找,这里type内容是MouseButtonPress。同样忽略其他无关代码,我们看QWidget::event()的代码:

bool QWidget::event(QEvent *event)
{
    Q_D(QWidget);
    ……

    switch (event->type()) {
    case QEvent::MouseMove:
        mouseMoveEvent((QMouseEvent*)event);
        break;

    case QEvent::MouseButtonPress:
        // Don't reset input context here. Whether reset or not is
        // a responsibility of input method. reset() will be
        // called by mouseHandler() of input method if necessary
        // via mousePressEvent() of text widgets.
        mousePressEvent((QMouseEvent*)event);
        break;
    case QEvent::MouseButtonRelease:
        mouseReleaseEvent((QMouseEvent*)event);
        break;
      ……

    default:
        return QObject::event(event);
    }
    return true;
}

   我们看该事件处理的具体代码:

void QAbstractButton::mousePressEvent(QMouseEvent *e)
{
    Q_D(QAbstractButton);
    if (e->button() != Qt::LeftButton) {
        e->ignore();
        return;
    }
    if (hitButton(e->pos())) { // <-- 根据鼠标点击点的位置匹配按钮
        setDown(true);
        repaint(); //flush paint event before invoking potentially expensive operation
        QApplication::flush();
        d->emitPressed();
        e->accept();
    } else {
        e->ignore();
    }
}

    在MouseButtonProcess事件处理之后,就是前面注册的Release事件。

   目前还有两个疑问:一个是自定义的信号(SIGNAL)如何跟Windows的消息关联的;另一个是信号和槽(SLOT)是如何关联的。根据前面的分析,对第一个问题的猜测是在event()函数里增加相应处理;对第二个问题,应该有一个函数指针表使之关联。带着这些问题我们接着分析mouseReleaseEvent()。

void QAbstractButton::mouseReleaseEvent(QMouseEvent *e)
{
    Q_D(QAbstractButton);
    if (e->button() != Qt::LeftButton) {
        e->ignore();
        return;
    }

    if (!d->down) {
        e->ignore();
        return;
    }

    if (hitButton(e->pos())) {
        d->repeatTimer.stop();
        d->click();   // 调用QAbstractButtonPrivate::click()
        e->accept();
    } else {
        setDown(false);
        e->ignore();
    }
}

   进一步看QAbstractButtonPrivate::click()的实现代码:

void QAbstractButtonPrivate::click()
{
    Q_Q(QAbstractButton);

    down = false;
    blockRefresh = true;
    bool changeState = true;
    if (checked && queryCheckedButton() == q) {
        // the checked button of an exclusive or autoexclusive group cannot be unchecked
#ifndef QT_NO_BUTTONGROUP
        if (group ? group->d_func()->exclusive : autoExclusive)
#else
        if (autoExclusive)
#endif
            changeState = false;
    }

    QPointer<QAbstractButton> guard(q);
    if (changeState) {
        q->nextCheckState();
        if (!guard)
            return;
    }
    blockRefresh = false;
    refresh();
    q->repaint(); //flush paint event before invoking potentially expensive operation
    QApplication::flush();
    if (guard)
        emitReleased();
    if (guard)
        emitClicked();
}

   主要就是调用emitReleased()和emitClicked(),我们看其中QAbstractButtonPrivate::emitClicked()的实现

void QAbstractButtonPrivate::emitClicked()
{
    Q_Q(QAbstractButton);
    QPointer<QAbstractButton> guard(q);
    emit q->clicked(checked);   // 这里调用的是QAbstractButton::clicked()
#ifndef QT_NO_BUTTONGROUP
    if (guard && group) {
        emit group->buttonClicked(group->id(q));
        if (guard && group)
            emit group->buttonClicked(q);
    }
#endif
}

   上面调用的函数实体,在moc_QAbstractButton.cpp里可以看到。这个文件实际是由QT的MOC工具自动产生的。

void QAbstractButton::clicked(bool _t1)
{
     void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
     QMetaObject::activate(this, &staticMetaObject, 2, 3, _a); // 和SLOT的调用关系应该是这里实现的。
}

   我们先来看看QMetaObject类的定义:

struct Q_CORE_EXPORT QMetaObject
{

       ……

    struct { // private data
        const QMetaObject *superdata;
        const char *stringdata;
        const uint *data;
        const void *extradata;
    } d;
};

   再看QMetaObjectPrivate的定义:

struct QMetaObjectPrivate
{
    int revision;
    int className;
    int classInfoCount, classInfoData;
    int methodCount, methodData;
    int propertyCount, propertyData;
    int enumeratorCount, enumeratorData;
    int constructorCount, constructorData;
};

   实际上QMetaObject::d.data指向的就是QMetaObjectPrivate结构体 我们看看staticMetaObject对象的定义:(同样在moc_QAbstractButton.cpp文件中)

const QMetaObject QAbstractButton::staticMetaObject = {
     { &QWidget::staticMetaObject, qt_meta_stringdata_QAbstractButton,
       qt_meta_data_QAbstractButton, 0 }
};

    这是一个常对象,除设定父类的staticMetaObject外,还设定了两个全局变量:qt_meta_stringdata_QAbstractButton和qt_meta_data_QAbstractButton。

    所有的奥秘都在这两个变量里面了。根据前面分析qt_meta_data_QAbstractButton实际是QMetaObjectPrivate结构。

static const uint qt_meta_data_QAbstractButton[] = {

 // content:
       2,       // revision
       0,       // classname
       0,    0, // classinfo
      11,   12, // methods
      11,   67, // properties
       0,    0, // enums/sets
       0,    0, // constructors

 // signals: signature, parameters, type, tag, flags

      17,   16,   16,   16, 0x05,
// 17 -- 指的是stringdata中No.17字节开始的字符串,结合下面定义实际就是pressed()
      27,   16,   16,   16, 0x05, // released()
      46,   38,   16,   16, 0x05, // clicked(bool)
      60,   16,   16,   16, 0x25, // clicked()
      70,   38,   16,   16, 0x05, // toggled(bool)

 // slots: signature, parameters, type, tag, flags
      89,   84,   16,   16, 0x0a, // setIconSize(QSize)
     113,  108,   16,   16, 0x0a,
     131,   16,   16,   16, 0x2a,
     146,   16,   16,   16, 0x0a,
     154,   16,   16,   16, 0x0a,
     163,   16,   16,   16, 0x0a,

 // properties: name, type, flags
     188,  180, 0x0a095103, // text
     199,  193, 0x45095103, // icon
     210,  204, 0x15095103,
     232,  219, 0x4c095103,
     246,  241, 0x01095103,
      38,  241, 0x01595103,
     256,  241, 0x01095103,
     267,  241, 0x01095103,
     285,  281, 0x02095103,
     301,  281, 0x02095103,
     320,  241, 0x01094103,

 // properties: notify_signal_id
       0,
       0,
       0,
       0,
       0,
       4,
       0,
       0,
       0,
       0,
       0,

       0        // eod
};

static const char qt_meta_stringdata_QAbstractButton[] = {
    "QAbstractButton\0\0pressed()\0released()\0"
    "checked\0clicked(bool)\0clicked()\0"
    "toggled(bool)\0size\0setIconSize(QSize)\0"
    "msec\0animateClick(int)\0animateClick()\0"
    "click()\0toggle()\0setChecked(bool)\0"
    "QString\0text\0QIcon\0icon\0QSize\0iconSize\0"
    "QKeySequence\0shortcut\0bool\0checkable\0"
    "autoRepeat\0autoExclusive\0int\0"
    "autoRepeatDelay\0autoRepeatInterval\0"
    "down\0"
};

   我们接着看QMetaObject::activate()的代码:

void QMetaObject::activate(QObject *sender, const QMetaObject *m,
                           int from_local_signal_index, int to_local_signal_index, void **argv)
{
    int offset = m->methodOffset(); // 指向qt_meta_data_QAbstractButton[27]字节,也就是clicked(bool)
    int from_signal_index = offset + from_local_signal_index; // 27 + 2 = 29
    int to_signal_index = offset + to_local_signal_index; // 27 + 3 = 30
    if (to_signal_index < 32
        && !qt_signal_spy_callback_set.signal_begin_callback
        && !qt_signal_spy_callback_set.signal_end_callback) {
        uint signal_mask = (1 << (to_signal_index + 1)) - 1;
        signal_mask ^= (1 << from_signal_index) - 1;

        // sender指的是QPushButton,下面指向的是:QPushButtonPrivate::connectedSignals
        if ((sender->d_func()->connectedSignals & signal_mask) == 0)
            // nothing connected to these signals, and no spy
            return;
    }
    activate(sender, from_signal_index, to_signal_index, argv);
}

   可以看到,在判断本信号是否连接有槽之后,就调用了activate的重载函数:

void QMetaObject::activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv)
{
    if (sender->d_func()->blockSig)
        return;

    void *empty_argv[] = { 0 };
    if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
        qt_signal_spy_callback_set.signal_begin_callback(sender, from_signal_index,
                                                         argv ? argv : empty_argv);
    }

    QMutexLocker locker(&sender->d_func()->threadData->mutex);
    QThreadData *currentThreadData = QThreadData::current();

    QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;
    if (!connectionLists) {
        if (qt_signal_spy_callback_set.signal_end_callback != 0)
            qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
        return;
    }
    ++connectionLists->inUse;

    // emit signals in the following order: from_signal_index <= signals <= to_signal_index, signal < 0
    for (int signal = from_signal_index;
         (signal >= from_signal_index && signal <= to_signal_index) || (signal == -2);
         (signal == to_signal_index ? signal = -2 : ++signal))
    {
        if (signal >= connectionLists->count()) {
            signal = to_signal_index;
            continue;
        }
        int count = connectionLists->at(signal).count();

     //  就是在这里获取信号接收的槽函数指针的。

     // connectionLists里的数据,猜测是由QObject::connect()填进去的。
     for (int i = 0; i < count; ++i) {
          const QObjectPrivate::Connection *c = &connectionLists->at(signal)[i];
            if (!c->receiver)
                continue;

            QObject * const receiver = c->receiver;

            // determine if this connection should be sent immediately or
            // put into the event queue
            if ((c->connectionType == Qt::AutoConnection
                 && (currentThreadData != sender->d_func()->threadData
                     || receiver->d_func()->threadData != sender->d_func()->threadData))
                || (c->connectionType == Qt::QueuedConnection)) {
                queued_activate(sender, signal, *c, argv);
                continue;
            } else if (c->connectionType == Qt::BlockingQueuedConnection) {
                blocking_activate(sender, signal, *c, argv);
                continue;
            }

            const int method = c->method;
            QObjectPrivate::Sender currentSender;
            currentSender.sender = sender;
            currentSender.signal = signal < 0 ? from_signal_index : signal;
            currentSender.ref = 1;
            QObjectPrivate::Sender *previousSender = 0;
            if (currentThreadData == receiver->d_func()->threadData)
                previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender);
            locker.unlock();

            if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
                qt_signal_spy_callback_set.slot_begin_callback(receiver,
                                                               method,
                                                               argv ? argv : empty_argv);
            }

#if defined(QT_NO_EXCEPTIONS)
            receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
#else
            try {

                // 在我们的分析中,连接的槽是QApplication::quit(),qt_metacall在哪定义的呢?
                receiver->qt_metacall(QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
            } catch (...) {
                locker.relock();

                QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);

                --connectionLists->inUse;
                Q_ASSERT(connectionLists->inUse >= 0);
                if (connectionLists->orphaned && !connectionLists->inUse)
                    delete connectionLists;
                throw;
            }
#endif

            locker.relock();

            if (qt_signal_spy_callback_set.slot_end_callback != 0)
                qt_signal_spy_callback_set.slot_end_callback(receiver, method);

            QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);

            if (connectionLists->orphaned)
                break;
        }

        if (connectionLists->orphaned)
            break;
    }

    --connectionLists->inUse;
    Q_ASSERT(connectionLists->inUse >= 0);
    if (connectionLists->orphaned) {
        if (!connectionLists->inUse)
            delete connectionLists;
    } else {
        sender->d_func()->cleanConnectionLists();
    }

    locker.unlock();

    if (qt_signal_spy_callback_set.signal_end_callback != 0)
        qt_signal_spy_callback_set.signal_end_callback(sender, from_signal_index);
}

   单步跟踪,receiver->qt_metacall();实际调用的是QApplication::qt_metacall(),根据调用参数实现不同的函数调用。在moc_QApplication.cpp中定义,是由MOC工具自动产生的代码。至此,信号与槽的关联分析完毕。

   明天接着分析QObject::connect()如何把相关数据填入connectionLists。自定义信号如何与windows消息关联在明天分析完毕之后再来证实。

   在继续分析之前,我们回头看看test.cpp的main()函数:

int main( int argc, char **argv )
{
 QApplication a( argc, argv );
 QPushButton quit( "Quit", 0 );
 quit.resize( 75, 30 );
 quit.setFont( QFont( "Times", 18, QFont::Bold ) );
 QObject::connect( &quit, SIGNAL(clicked()), &a, SLOT(quit()) );
 quit.show();
 return a.exec();
}   

   根据我们猜测,就是上面红色部分语句把消息处理函数指针填入了connectionLists。

   首先我们看看SIGNAL宏和SLOT宏的定义:

 # define METHOD(a)   "0"#a
 # define SLOT(a)     "1"#a
 # define SIGNAL(a)   "2"#a

   使用的是宏转义,SIGNAL(clicked())被展开成"2clicked()"(字符串);SLOT(quit())被展开成"1quit()"。

   再看QObject::connect()的声明:

 static bool connect(const QObject *sender, const char *signal, 
                            const QObject *receiver,const char *member,                     
                            Qt::ConnectionType = Qt::AutoConnection );

   上面的调用语句展开之后就是:

 QObject::connect(&quit, "2clicked()", &a, "1quit()");

   然后看QObject::connect()的定义:

bool QObject::connect(const QObject *sender, const char *signal,
                      const QObject *receiver, const char *method,
                      Qt::ConnectionType type)
{
    {
        const void *cbdata[] = { sender, signal, receiver, method, &type };
        if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
            return true;
    }

    if (type == Qt::AutoCompatConnection) {
        type = Qt::AutoConnection;
   }

    if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
        qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
                 sender ? sender->metaObject()->className() : "(null)",
                 (signal && *signal) ? signal+1 : "(null)",
                 receiver ? receiver->metaObject()->className() : "(null)",
                 (method && *method) ? method+1 : "(null)");
        return false;
    }
    QByteArray tmp_signal_name;

    // 检查signal是否以2开头

    if (!check_signal_macro(sender, signal, "connect", "bind"))
        return false;
    const QMetaObject *smeta = sender->metaObject();
    const char *signal_arg = signal;
    ++signal; //skip code

    // 获得signal的函数编号
    int signal_index = smeta->indexOfSignal(signal);
    if (signal_index < 0) {
        // check for normalized signatures
        tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
        signal = tmp_signal_name.constData() + 1;

        signal_index = smeta->indexOfSignal(signal);
        if (signal_index < 0) {
            err_method_notfound(sender, signal_arg, "connect");
            err_info_about_objects("connect", sender, receiver);
            return false;
        }
    }

    QByteArray tmp_method_name;
    int membcode = extract_code(method);

    // 检查receiver是否以1开头

    if (!check_method_code(membcode, receiver, method, "connect"))
        return false;
    const char *method_arg = method;
    ++method; // skip code

     // 获得receiver的函数编号

    const QMetaObject *rmeta = receiver->metaObject();
    int method_index = -1;
    switch (membcode) {
    case QSLOT_CODE:
        method_index = rmeta->indexOfSlot(method);
        break;
    case QSIGNAL_CODE:
        method_index = rmeta->indexOfSignal(method);
        break;
    }
    if (method_index < 0) {
        // check for normalized methods
        tmp_method_name = QMetaObject::normalizedSignature(method);
        method = tmp_method_name.constData();
        switch (membcode) {
        case QSLOT_CODE:
            method_index = rmeta->indexOfSlot(method);
            break;
        case QSIGNAL_CODE:
            method_index = rmeta->indexOfSignal(method);
            break;
        }
    }

    if (method_index < 0) {
        err_method_notfound(receiver, method_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return false;
    }
    if (!QMetaObject::checkConnectArgs(signal, method)) {
        qWarning("QObject::connect: Incompatible sender/receiver arguments"
                 "\n        %s::%s --> %s::%s",
                 sender->metaObject()->className(), signal,
                 receiver->metaObject()->className(), method);
        return false;
    }

    int *types = 0;
    if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
            && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes())))
        return false;

    QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
    const_cast<QObject*>(sender)->connectNotify(signal - 1);
    return true;
}

    我们先看QInternal::activateCallbacks()的实现:

bool QInternal::activateCallbacks(Callback cb, void **parameters)
{
    Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");

    QInternal_CallBackTable *cbt = global_callback_table();
    if (cbt && cb < cbt->callbacks.size()) {
        QList<qInternalCallback> callbacks = cbt->callbacks[cb];
        bool ret = false;
        for (int i=0; i<callbacks.size(); ++i)
            ret |= (callbacks.at(i))(parameters);
        return ret;
    }
    return false;
}

   这是优先处理回调函数(钩子函数),在我们这里的应用中没有回调,所以可以忽略。接着看QMetaObject::connect()的实现:

bool QMetaObject::connect(const QObject *sender, int signal_index,
                          const QObject *receiver, int method_index, int type, int *types)
{
    QObject *s = const_cast<QObject *>(sender);
    QObject *r = const_cast<QObject *>(receiver);

    QOrderedMutexLocker locker(&s->d_func()->threadData->mutex,
                               &r->d_func()->threadData->mutex);

    QObjectPrivate::Connection c = { r, method_index, type, Q_BASIC_ATOMIC_INITIALIZER(types) };
    s->d_func()->addConnection(signal_index, &c);
    r->d_func()->refSender(s, signal_index);

    if (signal_index < 0)
        sender->d_func()->connectedSignals = ~0u;
    else if (signal_index < 32)
        sender->d_func()->connectedSignals |= (1 << signal_index);

    return true;
}

   s->d_func()指向的是QPushButtonPrivate指针,QPushButtonPrivate没有addConnection()成员实际调用的是其基类成员,

   s->d_func()->addConnection()调用的是QObjectPrivate::addConnection()。进一步看其实现:

void QObjectPrivate::addConnection(int signal, Connection *c)
{
    if (!connectionLists)
        connectionLists = new QObjectConnectionListVector();
    if (signal >= connectionLists->count())
        connectionLists->resize(signal + 1);

    ConnectionList &connectionList = (*connectionLists)[signal];
    connectionList.append(*c);

    cleanConnectionLists();
}

   这里填入了发送消息的SIGNAL的函数指针!我们接着看r->d_func()->refSender(s, signal_index);其中r指向的是QApplication对象(a),所以r->d_func()是QApplicationPrivate对象指针,同样因其本身没有refSender()成员函数,调用的是其基类QObjectPrivate::refSender()。我们看其实现:

void QObjectPrivate::refSender(QObject *sender, int signal)
{
    for (int i = 0; i < senders.count(); ++i) {
        Sender &s = senders[i];
        if (s.sender == sender && s.signal == signal) {
            ++s.ref;
            return;
        }
    }

    Sender s = { sender, signal, 1 };
    senders.append(s);
}
   至此,我们的猜想得到证实。分析完毕。