• Qt中事件分发源码剖析


                       Qt中事件分发源码剖析

    Qt中事件传递顺序:

    在一个应该程序中,会进入一个事件循环,接受系统产生的事件,而且进行分发,这些都是在exec中进行的。
    以下举例说明:

    1)首先看看以下一段演示样例代码:

    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
        MouseEvent w;
        w.show();
        
        return a.exec();
    }

    2)a.exec进入事件循环,调用的是QApplication::exec();

    int QApplication::exec()
    {
        return QGuiApplication::exec();
    }

    3)QApplication::exec()调用的是QGuiApplication::exec();

    int QGuiApplication::exec()
    {
    #ifndef QT_NO_ACCESSIBILITY
        QAccessible::setRootObject(qApp);
    #endif
        return QCoreApplication::exec();
    }

    4)QGuiApplication::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();
        threadData->quitNow = false;
        if (self) {
            self->d_func()->in_exec = false;
            if (!self->d_func()->aboutToQuitEmitted)
                emit self->aboutToQuit(QPrivateSignal());
            self->d_func()->aboutToQuitEmitted = true;
            sendPostedEvents(0, QEvent::DeferredDelete);
        }
    
    
        return returnCode;
    }

    5)QCoreApplication::exec()调用eventLoop.exec()进行事件循环;

    int QEventLoop::exec(ProcessEventsFlags flags)
    {
        Q_D(QEventLoop);
        //we need to protect from race condition with QThread::exit
        QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(d->threadData->thread))->mutex);
        if (d->threadData->quitNow)
            return -1;
    
    
        if (d->inExec) {
            qWarning("QEventLoop::exec: instance %p has already called exec()", this);
            return -1;
        }
    
    
        struct LoopReference {
            QEventLoopPrivate *d;
            QMutexLocker &locker;
    
    
            bool exceptionCaught;
            LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true)
            {
                d->inExec = true;
                d->exit = false;
                ++d->threadData->loopLevel;
                d->threadData->eventLoops.push(d->q_func());
                locker.unlock();
            }
    
    
            ~LoopReference()
            {
                if (exceptionCaught) {
                    qWarning("Qt has caught an exception thrown from an event handler. Throwing
    "
                             "exceptions from an event handler is not supported in Qt. You must
    "
                             "reimplement QApplication::notify() and catch all exceptions there.
    ");
                }
                locker.relock();
                QEventLoop *eventLoop = d->threadData->eventLoops.pop();
                Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
                Q_UNUSED(eventLoop); // --release warning
                d->inExec = false;
                --d->threadData->loopLevel;
            }
        };
        LoopReference ref(d, locker);
    
    
        // remove posted quit events when entering a new event loop
        QCoreApplication *app = QCoreApplication::instance();
        if (app && app->thread() == thread())
            QCoreApplication::removePostedEvents(app, QEvent::Quit);
    
    
        while (!d->exit)
            processEvents(flags | WaitForMoreEvents | EventLoopExec);
    
    
        ref.exceptionCaught = false;
        return d->returnCode;
    }

    6)eventLoop.exec()调用QCoreApplication的processEvents进行事件分发;

    7)调用notify进行分发

    QCoreApplication::sendEvent、QCoreApplication::postEvent和QCoreApplication::sendPostedEvents都调用notify进行事件分发;

    bool QCoreApplication::notify(QObject *receiver, QEvent *event)
    {
        Q_D(QCoreApplication);
        // no events are delivered after ~QCoreApplication() has started
        if (QCoreApplicationPrivate::is_app_closing)
            return true;
    
    
        if (receiver == 0) {                        // serious error
            qWarning("QCoreApplication::notify: Unexpected null receiver");
            return true;
        }
    
    
    #ifndef QT_NO_DEBUG
        d->checkReceiverThread(receiver);
    #endif
    
    
        return receiver->isWidgetType() ?

    false : d->notify_helper(receiver, event); }

    8)notify调用notify_helper进行事件分发;

    bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
    {
        // send to all application event filters
        if (sendThroughApplicationEventFilters(receiver, event))
            return true;
        // send to all receiver event filters
        if (sendThroughObjectEventFilters(receiver, event))
            return true;
        // deliver the event
        return receiver->event(event);
    }

    9)从上面第8步的代码能够看出事件传递

    传递的顺序是:首先传递给全局的事件过滤器,再传递给目标对象的事件过滤器,终于传递给目标对象。

  • 相关阅读:
    定义扩展点,实现发布订阅机制
    JS阻止事件冒泡
    Virtualbox安装黑苹果
    外部Tomcat使用Java热部署利器JRebel
    在Windows server 2016上使用docker
    Tomcat加载web.xml文件的顺序详解
    IDEA反编译整个jar包
    java集合类的继承结构
    利用BodyTagSupport创建带标签体的自定义标签
    jquery实现简单弹出框
  • 原文地址:https://www.cnblogs.com/claireyuancy/p/6977757.html
Copyright © 2020-2023  润新知