QT Demo 之 window

我们开始学习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().

文档中描述了这么几点:

  1. exec()函数是一个阻塞函数,知道程序被退出(exit()函数被调用),才会退出整个main()函数
  2. exec()函数中启动消息处理,完成消息的控制和转发
  3. 在调用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进行管理和操作。

时间: 2024-11-01 11:14:30

QT Demo 之 window的相关文章

QT Demo 之 window(3) Qt.quit()

在上一章<QT Demo 之 window(2) Splash>学习中留下一个TODO没有解决,就是在示例代码中有这样的一段: MouseArea { anchors.fill: parent onClicked: Qt.quit() } 其本意就是在显示Splash Screen的时候,如果点击Splash Screen图片就会退出整个程序,但实际结果就是报了一行错误: Signal QQmlEngine::quit() emitted, but no receivers connected

QT Demo 之 window(2) Splash

在QT Demo 之 window一章我们学习了在C++层的QQuickWindow的一些知识,这一章我们重点看一下源码中的Splash.qml文件,该文件给我们演示了如何使用splash screen来显示应用的启动及界面. 关于应用的启动界面,有一个最直观也是比较常见的例子就是photoshop,下图就是PS启动时显示的界面: 在这个例子里也是先显示一个Qt的Logo,然后再进入到主应用窗口中. 源码结构 Splash.qml实现了一个自定义的window来完成Splash screen的功

QT Demo 之 window(5) window.qml

在分析了main.cpp.Splash.Qt.quit()以及ScreenInfo之后,我们终于开始了正题:window.qml. window.qml的主体结构 window.qml主体是一个QtObject,其中包含了4个子元素:palette.controlWindow.testWindow和splashWindow: QtObject { property real defaultSpacing: 10 property SystemPalette palette: SystemPale

QT Demo 之 window(4) ScreenInfo

启动程序后,跳过Splash Screen之后就会看到下面的一个界面: 我们重点看一下界面的下半部分Screen Info部分. 上面分别显示了: 屏幕名称 屏幕尺寸(即分辨率) 像素密度 逻辑像素密度 可用的屏幕尺寸 屏幕方向 原始的屏幕方向 下面我们就逐一展开每一项的实际意义. 屏幕名称 在PC上,如果只有一个屏幕就会显示为\\.\DISPLAY1,而如果是双屏幕,则会显示为\\.\DISPLAY2. 获取了屏幕的名称后,我们就可以判断当前是在哪个显示设备上进行显示,从而可以完成一些自定义的

QT Demo 之 calqlatr(2) calqlatr.qml

import QtQuick 2.0 import "content" import "content/calculator.js" as CalcEngine 同样,这次我们针对qml代码开始的最常见的import部分也不放过了,也要至少做到基本了解和使用. 在Qml中如果需要使用系统组件,必须在开始进行声明.对于自定义的组件也需要在开始的时候import进来,并且需要注意的是,系统组件直接通过名称即可,而对于自定义组件,需要使用""包起来.

QT Demo 之 threading(2) Spinner

在QT Demo 之 threading一章中我们使用到了Spinner,但是由于Spinner本身和thread部分关系不大,而是作为一个独立的自定义组件,因此我们在这一章中单独讲解. Spinner定义在threading/workerscript/Spinner.qml文件中,由一个Text和Rectangle组成: Rectangle { width: 64 height: 64 property alias value: list.currentIndex property alias

QT Demo 之 imageelements

在学习了MouseArea和Text之后,这一节开始学习image相关的知识. 和上一节QT Demo 之 text一样,imageelements的入口也是一个LauncherList,然后添加了5个子example,下面我就针对每一个子example进行详细分析. borderimage.qml 首先看到的是borderimage.qml的主体结构是由一个BorderImageSelector和Flickable组成的: Rectangle { id: page width: 320 hei

QT Demo 之 threading(3) triangle

同<QT Demo 之 threading(2) Spinner>一样,这一章也是把<QT Demo 之 threading>中的一个和threading完全独立的部分拿出来分析一下. 在中的WorkerScript.onMessage函数中有如下的调用,用来计算给定row和column下计算triangle的值: WorkerScript.onMessage = function(message) { //Calculate result (may take a while, u

qt demo pro

qt demo pro qtdemo.pri TEMPLATE = app QT_VER = $$[QT_VERSION] QT_PATH = $$[QT_INSTALL_PREFIX] #message(Qt version is $$QT_VER) #message(Qt install prefix is $$QT_PATH) QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += debug_and