我们开始学习GUI编程中的一个超级重要的概念,那就是Window,不过在Qt中有很多种Windows,今天我学习的是QQuickWindow,顾名思义,就是用来显示QML场景的窗口。
在这一章,我们从cpp代码开始分析,先看一下main.cpp的代码:
int main(int argc, char* argv[]) { QGuiApplication app(argc, argv); QQmlEngine engine; QQmlComponent component(&engine); QQuickWindow::setDefaultAlphaBuffer(true); component.loadUrl(QUrl("qrc:///window/window.qml")); if ( component.isReady() ) component.create(); else qWarning() << component.errorString(); return app.exec(); }
上面的代码包含以下几个知识点:
- QGuiApplication
- QQmlEngine
- QQmlComponent
- QQuickWindow
- qWarning
下面就针对每一个知识点进行详细讲解。
QGuiApplication
先从官方文档上了解一下QGuiApplication的简述:
The QGuiApplication class manages the GUI application‘s control flow and main settings.
这句话整体说明了QGuiApplication类是用来进行GUI的控制流程和主设定相关。
QGuiApplication contains the main event loop, where all events from the window system and other sources are processed and dispatched. It also handles the application‘s initialization and finalization, and provides session management. In addition, QGuiApplication
handles most of the system-wide and application-wide settings.
这一段说明了QGuiApplication包含了主消息循环处理,用来进行系统消息和其它消息的处理和分发。并且也完成了程序的初始化和销毁,以及提供整个的会话管理。
For any GUI application using Qt, there is precisely one QGuiApplication object no matter whether the application has 0, 1, 2 or more windows at any given time. For non-GUI Qt applications, use QCoreApplication instead, as it does not depend on the Qt
GUI module. For QWidget based Qt applications, use QApplication instead, as it provides some functionality needed for creating QWidget instances.
这一段强调了一个程序只能有一个QGuiApplication对象,而且如果不是GUI程序也请不要使用QGuiApplication类。
这一章的例子中仅仅使用到了QGuiApplication的创建和exec()函数,其它的功能暂时没有使用到,那么我们就先学习这两个小部分。
QGuiApplication app(argc, argv);
为什么从程序一开始就示例化了一个QGuiApplication对象,在官方文档中有如下的说明:
Since the QGuiApplication object does so much initialization, it must be created before any other objects related to the user interface are created. QGuiApplication also deals with common command line arguments. Hence, it is usually a good idea to create
it before any interpretation or modification of argv is done in the application itself.
很显然,因为QGuiApplication做了非常非常的初始化操作,在没有创建QGuiApplication之前,创建或调用其他组件或接口都无法正常工作。而且因为QGuiApplication也包含对输入参数的处理,那么就很有必要在程序的一开始就创建QGuiApplication对象了。
return app.exec();
最后为什么要调用app.exec(),它又起什么作用呢?
Enters the main event loop and waits until exit() is called, and then returns the value that was set to exit() (which is 0 if exit() is called via quit()).
It is necessary to call this function to start event handling. The main event loop receives events from the window system and dispatches these to the application widgets.
Generally, no user interaction can take place before calling exec().
文档中描述了这么几点:
- exec()函数是一个阻塞函数,知道程序被退出(exit()函数被调用),才会退出整个main()函数
- exec()函数中启动消息处理,完成消息的控制和转发
- 在调用exec()函数之前,没有任何的UI操作会被执行(消息处理还没开始,如何响应)
从QGuiApplication的创建和exec()函数执行,却是对应了QGuiApplication完成了应用的初始化和销毁操作,也是一个app的start和end。
QQmlEngine
同样是先看一下官方文档的说明:
The QQmlEngine class provides an environment for instantiating QML components.
QQmlEngine类是用来给实例化QML组件的一个环境,从QQmlEngine的名字上也可以了解这个是在C++程序中创建一个QML的处理引擎对象,以便于实例化QML组件。
需要注意的一点是,QQmlEngine是从QJsEngine类继承过来的,因此我们在QML中加载和执行JS代码才可以正常运行。
class Q_QML_EXPORT QQmlEngine : public QJSEngine
更多的QQmlEngine的知识需要配合QQmlComponent讲解,下面就开始QQmlComponent的学习。
QQmlComponent
The QQmlComponent class encapsulates a QML component definition
从官方文档上,我们知道QQmlComponent类是用来封装一个QML组件定义的。
main.cpp中共有5处使用到了QQmlComponent,分别是:
QQmlComponent component(&engine);
根据指定的QQmlEngine创建一个空的component。
component.loadUrl(QUrl("qrc:///window/window.qml"));
从QUrl指定的文件中,加载指定的qml组件。
if ( component.isReady() )
加载外部的qml文件有可能有各种错误产生或者由于是网络上的资源需要Loading,因此QQmlComponent有以下几个枚举变量用来表示加载qml的状态:
- QQmlComponent::Null This QQmlComponent has no data. Call loadUrl() or setData() to add QML content.
- QQmlComponent::Ready This QQmlComponent is ready and create() may be called.
- QQmlComponent::Loading This QQmlComponent is loading network data.
- QQmlComponent::Error An error has occurred. Call errors() to retrieve a list of {QQmlError}{errors}.
component.create();
根据上面的QQmlComponent状态定义,当qml组件加载完毕的时候,就可以调用create来创建该qml组件的实例化对象。
qWarning() << component.errorString();
因为此处加载的是本地qml,所以不存在Loading的情况,如果不是Ready就是Null和Error这两种错误了。
所以,当加载失败的时候,会打印出错误信息。
Returns a human-readable description of any error. If no errors are present, an empty string is returned.
The string includes the file, location, and description of each error. If multiple errors are present, they are separated by a newline character.
注意,errorString()函数并没有出现了QQmlComponent的官方文档说明中,但是在QQmlComponent.h头文件中有该函数的定义,而上面的说明是我从另外一处的errorString()函数说明搬运过来的,从设计上同一个接口表达的意思应该是一致的。
如,我们把上面加载的文件改成window11.qml,则会报以下错误:
"qrc:///window/window11.qml:-1 File not found"
QQuickWindow
The QQuickWindow class provides the window for displaying a graphical QML scene
QQuickWindow provides the graphical scene management needed to interact with and display a scene of QQuickItems.
QQuickWindow类用来提供一个具体的window来显示QML组件。
注意,这里并没有具体的实例化一个QQuickWindow,而具体的window是在component通过指定QUrl加载的qml文件中定义了window,而这里这是由于业务需要设定了DefaultAlphaBuffer为true:
QQuickWindow::setDefaultAlphaBuffer(true);
void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha) [static]
useAlpha specifies whether to use alpha transparency on newly created windows.
In any application which expects to create translucent windows, it‘s necessary to set this to true before creating the first QQuickWindow, because all windows will share the same QOpenGLContext. The default value is false.
因为setDefaultAlphaBuffer()函数是一个static类型的函数,所以此处是直接通过QQuickWindow类进行调用,而且它是对于全局有效的,所以要在创建第一个QQuickWindow之前进行设定。
具体的QQuickWindow在分析qrc:///window/window.qml文件时进行讲解。
qWarning()
在main()函数中有下述的一行代码用于输出调试信息:
qWarning() << component.errorString();
qWarning()函数属于Qt的Global函数,除了qWarnning()函数还有几个类似的函数来分别输出不同的打印级别:
- qCritical(const char * message, ...)
- qDebug(const char * message, ...)
- qFatal(const char * message, ...)
- qWarning(const char * message, ...)
这几个函数都是输出调试信息使用的,而且同时兼容c语言的printf格式输出和c++语言的cout格式输出(需要include <QtDebug>),e.g.:
qWarning("f: bad argument, c == %d", c); qWarning() << "Brush:" << myQBrush << "Other value:"<< i;
如果系统默认的调试信息输出模式不满足自己项目的需要,Qt提供了一种机制可以注册自定义的消息处理函数用来处理调试信息:
To suppress the output at runtime, install your own message handler with qInstallMessageHandler().
下面就是一个完整的示例:
#include <qapplication.h> #include <stdio.h> #include <stdlib.h> void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg) { QByteArray localMsg = msg.toLocal8Bit(); switch (type) { case QtDebugMsg: fprintf(stderr, "Debug: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtWarningMsg: fprintf(stderr, "Warning: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtCriticalMsg: fprintf(stderr, "Critical: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); break; case QtFatalMsg: fprintf(stderr, "Fatal: %s (%s:%u, %s)\n", localMsg.constData(), context.file, context.line, context.function); abort(); } } int main(int argc, char **argv) { qInstallMessageHandler(myMessageOutput); QApplication app(argc, argv); ... return app.exec(); }
请注意,上面设置自定义调试信息输出控制函数是在实例化QApplication之前,这与上面讲解QGuiApplication一章提到的QApplication需要在最开始调用时不冲突的。因为在QApplication实例化的过程中也会有很多调试信息需要输出,那么要做到完全的自定义调试信息输出方式,就需要在所有的操作之前注册上自己的函数。
总结
本节学到的知识点:
- QGuiApplication
- QQmlEngine
- QQmlComponent
- QQuickWindow
- qWarning以及如何自定义调试信息输出方式
这一章我们对于Qt的Window只是了解了一个入门,从下一章开始详细分析QML Quick中如何对Window进行管理和操作。