QApplication (GUI 程序中 有且仅有一个)
QApplication 类 管理GUI程序的控制流和主设置。
QApplication 包含主事件循环。所有来自窗口系统和其他源的事件将被处理和分配。它也处理程序的初始化,析构和提供会话管理。
对于非GUI的用QCoreApplication 代替QApplication,它不依赖QtGui库。
qApp是一个全局的指针,指向QApplication的对象。
QApplication的主要职责如下:
1,初始化程序的用户桌面设置,如palette(),font(),doubleClickInterval()(鼠标双击的时间间隔),并一直监视这些属性,以防用户改变他们(得到及时的更新)。
2,处理事件,意思是它接收来自底层窗口系统的事件,并把他们分发给关联的窗口,通过sendEvent(),postEvent(),你可以把你自己的事件发给部件。
3,解析命令行参数。
4,定义程序的观感(被封装在QStyle 对象中)。通过setStyle()可以实时的改变。
5,它知道程序的窗口信息。可以通过widgetAt(),还可以得到一个窗口列表通过topLevelWidgets(),然后通过closeAllWindows()关闭所有窗口。
6,还管理鼠标操作。
7,它还提供一个复杂的会话管理。它使程序在用户退出时可以“优美”的结束,或者如果干掉一个进程如果这个进程不能保留程序之前的状态(对会话管理不了解,翻译的不准确)
由于QApplication对象做了这么多初始化操作,所以它必须在所以与用户接口有关的对象创建之前被创建。
QCoreApplication类exec()方法分析
看看这个类是怎么调用线程那些东西的。
在QtCoreApplication类的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();
self->d_func()->aboutToQuitEmitted = true;
sendPostedEvents(0, QEvent::DeferredDelete);
}
return returnCode;
我重点想知道的是,d_func()函数如何得到QThreadData对象的,这个需要进入到QCoreApplicationPrivate类当中了。
发现了这样的定义:
#define Q_DECLARE_PRIVATE(Class) /
inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } /
inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } /
friend class Class##Private;
原来该类调用了qGetPtrHelper方法,继续跟踪下去:
template <typename T> static inline T *qGetPtrHelper(T *ptr) { return ptr; }
template <typename Wrapper> static inline typename Wrapper::pointer qGetPtrHelper(const Wrapper &p) { return p.data(); }
其实d_func()是将QObjectData类的指针转换为QCoreApplicatPrivate类的指针。看下继承关系,QCoreApplicatPrivate继承自QObjectPrivate,QObjectPrivate继承自QObjectData;但是这样的转换还是会带来危险,因为本来这个指针就不是该类的,这样相当于扩大了对象的引用内存的大小,容易产生越界错误。而且这样的强转之后,也只是调用了其中来自 QObjectData的方法,这里的用意不是很明白,应该在其他地方对应用指针做了新的赋值。去找找看。
QScopedPointer d_ptr的定义来自QObject,现在找找看,是否有对该指针的赋值:
QObject::QObject(QObject *parent): d_ptr(new QObjectPrivate)
实际的引用对象就是QObjectPrivate类的对象了。
现在就可以总结一下了,整个Qt控制台应用程序框架基本明了:
我们编写的代码是:
QCoreApplication a(argc,argv);
a.exec();
通过这两行代码,Qt为我们做了如下工作:
首先创建一个用于保存整个程序运行核心数据的类对象:
QCoreApplication继承自QObject,所以,首先构造QOjbect类对象,于是有了:
D_ptr(new QObjectPrivate)
随后对QThread进行初始化:
#ifndef QT_NO_THREAD
QThread::initialize();
#endif
接下来创建事件派送器,从Windows中将消息传递至Qt,并进行分发,构造函数完成。
随后调用a.exec()方法,获取线程信息:
QThreadData *threadData = self->d_func()->threadData;
最后进入事件循环,并进行事件发送等
对exec()函数的后半部分进行分析:
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();
self->d_func()->aboutToQuitEmitted = true;
sendPostedEvents(0, QEvent::DeferredDelete);
}
该方法在程序退出之前是不返回的,eventLoop.exec()的调用会让程序进入一个事件循环,直到程序结束。
进入QEventLoop中的exec方法:
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)
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
在此调用事件循环不同平台下的事件分派器的processEvent方法进行事件分析。
当出现异常时,将当前事件放弃掉。
// 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;
在此,事件循环退出。
qcoreapplication 与qapplication的联系与区别
故事的背景是这样的,我们在写QT程序的时候或者在开始写QT程序之前总会看到这样的语句
QApplication app(argc, argv);
这是什么呢? QApplication这个类是继承QCoreApplication的,而QCoreApplication有继承
QObject的,而QObject就是QT中最基本的基类,也就是QT的根基了,这里就从QCoreApplication
说起吧,头文件中有这样的开始
classQ_CORE_EXPORTQCoreApplication : public QObject
Q_CORE_EXPORT是什么呢?如果在编写动态库时,定义DLL符号,Q_GUI_EXPORT就是导出函数或者类了,如果在应用程序中使用时,不定义Dll符号,Q_GUI_EXPORT就是导入类或者函数了,这里当然是导入了,我们写的可是命令行的,不是编写动态库,下面就是一些函数和变量的定义了,看到这里我震惊了
QCoreApplication * instance ()
定义一个指向自己的实例,啊?这是要闹哪样啊?难道要调用自己不成,实现就在类内,因为是静态的static QCoreApplication *instance() { returnself; }就是返回一个 self
static QCoreApplication *self;
是一个私有的静态成员变量,实现在类外
QCoreApplication *QCoreApplication::self = 0;
这下算是知道这个 self是做什么的了,可是这里有个疑问,连QCoreApplication自己都没被创建呢,哪来的指向 QCoreApplication的指针存在呢?这里个人的解释是静态成员的创建应该类创建之前就已经存在了,至于这个指向暂且就不考虑了,或许系统会解决这个问题吧,这里就当成是一个指针吧。
这里有必要看一下这个QCoreApplication类的构造函数
QCoreApplication::QCoreApplication(int &argc, char **argv) : QObject(*new QCoreApplicationPrivate(argc, argv))
{
init();
QCoreApplicationPrivate::eventDispatcher->startingUp();
#if defined(Q_OS_SYMBIAN) && !defined(QT_NO_LIBRARY)// Refresh factoryloader, as text codecs are requested during lib path// resolving process and won't be therefore properly loaded.// Unknown if this is symbian specific issue.
QFactoryLoader::refreshAll();
#endif#if defined(Q_OS_SYMBIAN) &&!defined(QT_NO_SYSTEMLOCALE)
d_func()->symbianInit();
#endif
}
下面看着有点晕了,这里就只看 init();这个函数,就在构造函数的下面,这里的代码更多了,
这里只写我们关注的一行
voidQCoreApplication::init(){ QCoreApplication::self = this;}
这里对 self进行了赋值,这就让这个self指向了当前这个对象,而不是什么也不指了,这个self就可以代替当前对象来使用了,当然这只能在类内,因为self是私有的,要在类外使用是不是应该定义一个共有成员函数什么的,先把疑问留在这里。
说完这个静态成员变量self还是没有解决这里为什么要闹这样的问题,原来我们在类定义的上一行看到这样的代码
#define qApp QCoreApplication::instance()
定义了一个 qApp宏,这个宏也就成了一个指针,指向的是自己,这样做又有什么用呢
当我们在主程序中定义了 QCoreApplication app(argc, argv);对象的时候完全是不需要用qApp这个宏的啊,但是如果出了主函数要用这个对象怎么办,传吗?这样比较麻烦,QT用这个指向自己的东东就是帮助我们解决这样要在主函数外使用app这个对象的而找不到对象的苦恼。好了,在函数外你就用qApp吧,这样会不会有什么问题呢?这个东西在内存吗?嘿嘿,这是静态的啊,就在内存呆着呢,大胆的去用这个指向自己的宏指针吧,只要app没析构这东东就一直在内存。
下面来说明一下QApplication,这个是从 QCoreApplication继承来的,
#define qApp (static_cast<QApplication *>(QCoreApplication::instance()))
在类定义前有一段这样的代码,这里也是取 self这个指向自己的指针但是要做一个类型转换,至于这个类型转换是否安全,我想应该是安全的,因为相当于是从基类往派生类转换,基类有的应该是对拷贝的,但是这里的static会不会对这个造成困扰还不是很清楚,总之,要转换后这个qApp才能在主函数外使用。
这里还要说明的是这个 QCoreApplication是不是单例的问题,网上有很多人认为是单例,也有很多人赞成,但是我实践了一下应该不是单例
for(int i = 0 ; i < 3 ; ++i)
{
QCoreApplication app;
}
这里这样的操作时允许的,因为之前的已经析构了,QT中说的只允许创建一个是指在一个函数内,只能创建一个,这里显然不是,单例的实现一般都是通过私有构造函数来实现的,这里的构造函数是共有的显然不是单例的节奏。