您的位置:首页 > 移动开发

Qt--QCoreApplication、QGuiApplication、QApplication

2017-11-23 10:18 323 查看
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,
4000
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<QHooks::StartupCallback>(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();
c8ff

//     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->接受者父对象
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: