Qt--QCoreApplication、QGuiApplication、QApplication

Q:当我们使用Qtcreator创建Qt Core Application、Qt Widgets Application、Qt Quick Application时,会发现main函数中所使用的QXxxApplication类不同,创建Qt Core Application时使用的是QCoreApplication,创建Qt Widgets Application时使用的是QApplication,创建Qt Quick Application时使用的是QGuiApplication,那么它们有什么区别和联系呢?

区别和联系

QCoreApplication、QGuiApplication、QApplication区别和联系:

从继承关系来看,QApplication继承自QGuiApplication,QGuiApplication继承自QCoreApplication,所以它们的功能是逐步递增的。

从所处模块来看,QCoreApplication定义在core模块中,为应用程序提供了一个非gui的事件循环;QGuiApplication定义在gui模块中,提供了额外的gui相关的设置,比如桌面设置,风格,字体,调色板,剪切板,光标;QApplication定义在widgets模块中,是QWidget相关的,能设置双击间隔,按键间隔,拖拽距离和时间,滚轮滚动行数等,能获取桌面,激活的窗口,模式控件,弹跳控件等。

具体使用哪个取决于你的应用程序,如果你的应用程序是无界面的,直接使用QCoreApplication即可,如果是gui相关,但没有使用widgets模块的就使用QGuiApplication,否则使用QApplication。

下面具体介绍下QCoreApplication中几个重要的接口

QCoreApplication

在Qt的应用程序中,在main函数中,我们一般会这么写:

int main(int argc, char* argv[]){
        QCoreApplication app(argc, argv);
        //...
        return app.exec();
}   

我们首先示例了一个QCoreApplication或其派生类的对象,这个对象是全局唯一的,qApp指针指向这个对象。QCoreApplication初始化时会获取库所在路径,并创建一个事件派遣类。看下它的构造函数:

构造函数

QCoreApplication::QCoreApplication(int &argc, char **argv
#ifndef Q_QDOC
                                   , int _internal
#endif
                                   )
#ifdef QT_NO_QOBJECT
    : d_ptr(new QCoreApplicationPrivate(argc, argv, _internal))
#else
    : QObject(*new QCoreApplicationPrivate(argc, argv, _internal))
#endif
{
    d_func()->q_ptr = this;
    d_func()->init();
#ifndef QT_NO_QOBJECT
    QCoreApplicationPrivate::eventDispatcher->startingUp();
#endif
}


void QCoreApplicationPrivate::init()
{
    Q_Q(QCoreApplication);

    initLocale();

    Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
    QCoreApplication::self = q;

    // Store app name (so it's still available after QCoreApplication is destroyed)
    if (!coreappdata()->applicationNameSet)
        coreappdata()->application = appName();

    QLoggingRegistry::instance()->init();

#ifndef QT_NO_LIBRARY
    // Reset the lib paths, so that they will be recomputed, taking the availability of argv[0]
    // into account. If necessary, recompute right away and replay the manual changes on top of the
    // new lib paths.
    QStringList *appPaths = coreappdata()->app_libpaths.take();
    QStringList *manualPaths = coreappdata()->manual_libpaths.take();
    if (appPaths) {
        if (manualPaths) {
            // Replay the delta. As paths can only be prepended to the front or removed from
            // anywhere in the list, we can just linearly scan the lists and find the items that
            // have been removed. Once the original list is exhausted we know all the remaining
            // items have been added.
            QStringList newPaths(q->libraryPaths());
            for (int i = manualPaths->length(), j = appPaths->length(); i > 0 || j > 0; qt_noop()) {
                if (--j < 0) {
                    newPaths.prepend((*manualPaths)[--i]);
                } else if (--i < 0) {
                    newPaths.removeAll((*appPaths)[j]);
                } else if ((*manualPaths)[i] != (*appPaths)[j]) {
                    newPaths.removeAll((*appPaths)[j]);
                    ++i; // try again with next item.
                }
            }
            delete manualPaths;
            coreappdata()->manual_libpaths.reset(new QStringList(newPaths));
        }
        delete appPaths;
    }
#endif

#ifndef QT_NO_QOBJECT
    // use the event dispatcher created by the app programmer (if any)
    if (!eventDispatcher)
        eventDispatcher = threadData->eventDispatcher.load();
    // otherwise we create one
    if (!eventDispatcher)
        createEventDispatcher();
    Q_ASSERT(eventDispatcher);

    if (!eventDispatcher->parent()) {
        eventDispatcher->moveToThread(threadData->thread);
        eventDispatcher->setParent(q);
    }

    threadData->eventDispatcher = eventDispatcher;
    eventDispatcherReady();
#endif

#ifdef QT_EVAL
    extern void qt_core_eval_init(QCoreApplicationPrivate::Type);
    qt_core_eval_init(application_type);
#endif

    processCommandLineArguments();

    qt_call_pre_routines();
    qt_startup_hook();
#ifndef QT_BOOTSTRAPPED
    if (Q_UNLIKELY(qtHookData[QHooks::Startup]))
        reinterpret_cast(qtHookData[QHooks::Startup])();
#endif

#ifndef QT_NO_QOBJECT
    is_app_running = true; // No longer starting up.
#endif
}

嗯,就是new了一个QCoreApplicationPrivate私有类,然后调用init函数,在init函数中最重要的一行就是createEventDispatcher();这个方法会根据不同的平台去创建不同的事件派遣类。

exec

QCoreApplication类中大部分都是static静态方法,看看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;
}

定义了一个QEventLoop 事件循环类,调用了eventLoop.exec(),转过去发现如下语句:

while (!d->exit.loadAcquire())
    processEvents(flags | WaitForMoreEvents | EventLoopExec);

这就是我们要找的应用程序事件循环了,只要exit标识为false,会一直调用processEvents处理事件

bool QEventLoop::processEvents(ProcessEventsFlags flags)
{
    Q_D(QEventLoop);
    if (!d->threadData->eventDispatcher.load())
        return false;
    return d->threadData->eventDispatcher.load()->processEvents(flags);
}

而processEvents中就是调用事件派遣类的processEvents,不同平台下有不同的实现,比如windows下是通过PeekMessage然后DispatchMessage,而glib下就是g_main_context_iteration了。

那么exit接口实现也很简单,就是将exit标识置true,就会退出事件循环。

sendEvent

再来看看sendEvent和postEvent是如何工作的

我追踪了下源码,简述下sendEvent的主要调用过程:

notify->doNotify->QCoreApplicationPrivate::notify_helper

bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{
    // send to all application event filters (only does anything in the main thread)
    if (QCoreApplication::self
            && receiver->d_func()->threadData->thread == mainThread()
            && QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event))
        return true;
    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, event))
        return true;
    // deliver the event
    return receiver->event(event);
}

通过这个函数我们就很明白Qt的事件处理过程了,先是通过应用程序对象QCoreApplication的事件过滤eventFilter,然后是接受者的事件过滤eventFilter,最后调用event,然后是细分的event,如mousePressEvent,timerEvent等。sendEvent是同步事件处理,即发送事件后会立即调用事件处理函数

postEvent

void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
    if (receiver == 0) {
        qWarning("QCoreApplication::postEvent: Unexpected null receiver");
        delete event;
        return;
    }

    QThreadData * volatile * pdata = &receiver->d_func()->threadData;
    QThreadData *data = *pdata;
    if (!data) {
        // posting during destruction? just delete the event to prevent a leak
        delete event;
        return;
    }

    // lock the post event mutex
    data->postEventList.mutex.lock();

    // if object has moved to another thread, follow it
    while (data != *pdata) {
        data->postEventList.mutex.unlock();

        data = *pdata;
        if (!data) {
            // posting during destruction? just delete the event to prevent a leak
            delete event;
            return;
        }

        data->postEventList.mutex.lock();
    }

    QMutexUnlocker locker(&data->postEventList.mutex);

    // if this is one of the compressible events, do compression
    if (receiver->d_func()->postedEvents
        && self && self->compressEvent(event, receiver, &data->postEventList)) {
        return;
    }

    if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {
        // remember the current running eventloop for DeferredDelete
        // events posted in the receiver's thread.

        // Events sent by non-Qt event handlers (such as glib) may not
        // have the scopeLevel set correctly. The scope level makes sure that
        // code like this:
        //     foo->deleteLater();
        //     qApp->processEvents(); // without passing QEvent::DeferredDelete
        // will not cause "foo" to be deleted before returning to the event loop.

        // If the scope level is 0 while loopLevel != 0, we are called from a
        // non-conformant code path, and our best guess is that the scope level
        // should be 1. (Loop level 0 is special: it means that no event loops
        // are running.)
        int loopLevel = data->loopLevel;
        int scopeLevel = data->scopeLevel;
        if (scopeLevel == 0 && loopLevel != 0)
            scopeLevel = 1;
        static_cast<QDeferredDeleteEvent *>(event)->level = loopLevel + scopeLevel;
    }

    // delete the event on exceptions to protect against memory leaks till the event is
    // properly owned in the postEventList
    QScopedPointer<QEvent> eventDeleter(event);
    data->postEventList.addEvent(QPostEvent(receiver, event, priority));
    eventDeleter.take();
    event->posted = true;
    ++receiver->d_func()->postedEvents;
    data->canWait = false;
    locker.unlock();

    QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();
    if (dispatcher)
        dispatcher->wakeUp();
}

线程数据里有个postEventList,postEvent会将事件添加addEvent到接受者所在线程的postEventList,然后唤醒wakeUp接受者线程的事件派遣类eventDispatcher,在线程的事件循环中去处理事件。所以postEvent是一个异步事件处理,post事件后不会立即得到处理,而是需要在接受者线程的事件循环获取到这个事件,然后才处理。

话外: repaint和update的区别

看到这,我们也应该明白了通常的repaint和update的区别了,repaint就是sendEvent立即引起重绘,调用paintEvent,update是postEvent到线程事件队列中,如果同一时间段有很多update事件,会合并这些update,只调用paintEvent一次。

总结

至此,我们了解了QCoreApplication、QGuiApplication、QApplication的区别和联系,知道了QCoreApplication在初始化和exec时搞了什么鬼,通过分析sendEvent和postEvent摸清了Qt的事件路由

QCoreApplication::eventFilter->接受者eventFilter->接受者event->接受者细分event->接受者父对象

你可能感兴趣的:(Qt,Qt5源码赏析)