一、导读
把焦点回到Qt应用开发中,一般情况下,Qt应用程序的本体由main.cpp
文件中的main()
函数中内容描述:
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
在上述代码中,创建了一个QApplication
实例和MainWindow
实例,MainWindow实例表示主窗体应用,QApplication
正是本文的描述对象,她是QWidget
的“地基”。QApplication
是专门的QGuiApplication
,它具有一些基于QWidget
应用程序需要的功能:处理小部件特定的初始化和销毁操作。文档中对她是这样描述的:

该类继承自QGuiApplication
类,文档中对QGuiApplication
是这样描述的:

从上图可知,QGuiApplication
继承自QCoreApplication
,又来看看QCoreApplication
类:

从上述描述可知,QApplication、QGuiApplication、QCoreApplication
这三个类是“父-子”包含关系,那么在实际开发中,该如何选择呢?
对于任何基于QWidget的GUI应用程序来说(注意是基于QWidget的),无论该应用程序在任何时间有多少个窗口,都只有一个QApplication
对象;如果不是基于QWidget
的GUI应用程序,应该使用QGuiApplication
,例如QtQuick应用,而对于不需要QWidget
或者GUI的Qt应用程序来说,应该使用QCoreApplcation
,该类不依赖于QtWidgets
库。在不需要GUI的应用程序中,使用QCoreApplication
,该类可以避免对图形用户界面所需的资源进行不必要的初始化。
二、再谈QApplication
在文本开始处贴出的代码中,main函数传入的参数argc
、argv
在创建QApplication实例的时候传了进去,因为在QApplication初始过程中需要用argv
中的argc
命令行参数构造应用程序对象,从源码角度看,在QApplication
的构造函数中会进行如下操作:

上图中,Q_D
是一个宏定义,用于创建一个指向ApplicationPrivate
的指针,定义如下:
#define Q_D(Class) Class##Private * const d = d_func()
ApplicationPrivate
类的存在用于描述QApplication
的私有数据,她的存在是为了Qt源码而设计的。回到QApplication
的构造函数中,最后会调用init()
,该函数实现如下(/qtbase/src/widgets/kernel
目录中):
void QApplicationPrivate::init()
{
#if defined(Q_OS_MACOS)
QMacAutoReleasePool pool;
#endif
//初始化QGuiApplication的私有数据。
QGuiApplicationPrivate::init();
//初始化资源。
initResources();
qt_is_gui_used = (application_type != QApplicationPrivate::Tty);
//处理命令行参数。
process_cmdline();
// Must be called before initialize()
QColormap::initialize(); //初始化QColormap
initializeWidgetPalettesFromTheme();
qt_init_tooltip_palette();
//初始化QApplication的私有数据
QApplicationPrivate::initializeWidgetFontHash();
//初始化QApplication对象,重磅函数
initialize();
eventDispatcher->startingUp();
#ifndef QT_NO_ACCESSIBILITY
// factory for accessible interfaces for widgets shipped with Qt
QAccessible::installFactory(&qAccessibleFactory);
#endif
}
从源码角度,可以清楚地看到QApplication
的构造过程和功能,主要用于初始化与GUI相关的的资源,创建QApplication
对象,有如下行为:
- (1)使用我们的桌面设置(如
palette()
、font()
和doubleClickInterval()
)来初始化应用程序。并跟踪这些属性,以防止我们全局地更改桌面,例如:通过某种控制面板。 - (2)执行事件处理,它从底层窗口系统接收事件并将它们分派到相关的小部件(可理解成一个事件中转站)。通过使用
sendEvent()
和postEvent()
,可以将自己的事件发送到小部件。 - (3)解析常用的命令行参数并相应地设置其内部状态。
- (4)定义应用程序的外观,并封装在
QStyle
对象中。当然可以在运行时使用setStyle()
进行更改。 - (5)提供了通过
translate()
创建可见字符串的本地化操作。 - (6)提供一些方便快捷的对象,便于在开发中使用,例如
desktop()
和clipboard()
。 - (7)管理应用程序的窗口。我们可以使用
widgetAt()
来询问哪个小部件位于某个位置,获取topLevelWidgets()
和closeAllWindows()
的列表等。 - (8)管理应用程序的鼠标指针处理。
在实际开发中,可以通过instance()
函数访问QApplication
对象,该函数返回一个与全局qApp指针等价的指针。(qApp引用是应用程序对象的唯一全局指针。它等价于QCoreApplication::instance()
,但转换为指向QApplication的指针,因此仅当唯一的应用程序对象是QApplication时才有效),Qt源码中qApp
定义如下:
#define qApp (static_cast<QApplication *>(QCoreApplication::instance()))
三、结尾
QApplication
就像QWidget的地基一样,GUI中的界面控件就如同“砖块”一样码在上面了。
最后,贴出参考文档中给出的一份代码,其实现背后的知识值得学习:
QCoreApplication* createApplication(int &argc, char *argv[])
{
for (int i = 1; i < argc; ++i)
if (!qstrcmp(argv[i], "-no-gui"))
return new QCoreApplication(argc, argv);
return new QApplication(argc, argv);
}
int main(int argc, char* argv[])
{
QScopedPointer<QCoreApplication> app(createApplication(argc, argv));
if (qobject_cast<QApplication *>(app.data())) {
// start GUI version...
} else {
// start non-GUI version...
}
return app->exec();
}
上述代码演示了如何动态创建适当类型的应用程序,小生从上述代码get到一招和五个知识点......,打住,再写就跑题了。
『推荐阅读』
1、
2、
3、