第二章:初识Qt
第二章:初识Qt
Qt 和 Qt Quick
本书涉及使用Qt开发应用的各个方面。聚焦于Qt Quick技术,也描述了使用C++ 编写后端和为Qt Quick编写扩展的必要信息。
本章会呈现一个关于Qt 6的概览,它展示了可供开发者使用的不同应用模式,以及一个展示性应用,作为即将学习内容的初步预览。此外,本章旨在提供 Qt 的广泛概述以及如何与 Qt 公司的取得联系。
Qt 6 摘要
多年前,Qt 5的发布引入了以声名方式来编写华丽用户界面的方法,很大程度上改变了我们的习惯。
Qt 6 将是 Qt 5 的延续,不会对大多数用户造成干扰。 Qt 为用户带来哪些价值?
- 跨平台属性
- 扩展性
- 世界级的API和文档
- 可维护、稳定、兼容性
- 吸引众多开发者的生态
Qt 6 针对新的市场需求调整了产品,同时又紧守用户价值。
桌面市场是Qt的根基,大多数用户首次接触Qt时主是通过桌面开发,Qt的各种开 发工具和Qt的成功即根植于桌面市场。
从对性能要求较高的高端类桌面设备到对低端微控制器设备,Qt 6 预期将在嵌入式和连网类设备市场有较高的增长。其中带触摸屏的设备将呈指数级增长。很多这类设备需要有简单功能,但用户界面必须经过打磨且流畅。
另一方面,需要更复杂的 2D/3D 集成用户界面。这些具有基于 2D 元素的界面的 3D 内容将很常见,增强现实和虚拟现实的使用也将如此。
连网类设备的增长和对流畅的用户界面的更高要求需要一个更简单的工作流程来创建应用程序和设备端。将用户体验设计器集成到开发流程中是Qt 6的系统目标之一。
Qt 6带给我们什么?
- 下一代QML 语言
- 下一代显示特性
- 集成和统一的工具
- 增强 Qt的 C++ API
- 组件市场
Qt 的构建模块
Qt 6由大量模块组成。通常,一个模块是开发者使用的一个库文件。有些模块是运行Qt平台必须的, 这些模块组成 Qt 必备模块 ;另外的模块是可选的,它们组成了 Qt可选(插件)模块 。多数开发者不需要用到后者,但知道这些插件模块也是好的,因为这为解决一些有挑战性的问题提供了帮助。
Qt 模块
Qt必备模块 对于任何支持Qt 的平台都是必须的。 他们为使用Qt Quick 2开发现代Qt 6应用提供了基础。详细的Qt 模块列表详见 Qt文档-模块列表
核心必备模块
使用QML编程所需要的Qt 必备的最小集如下:
- Qt Core - 被其它模块引用的核心非图形类.
- Qt D-BUS - 实现在Linux上通过D-BUS协议来进行进程间通讯的相关类.
- Qt GUI - 图形用户界面(GUI)的基础类。包括OpenGL.
- Qt Network - 简化网络编程,提高移植性的相关类.
- Qt QML - QML和JavaScript的相关类.
- Qt Quick - 构建动态用户界面的声明式框架类.
- Qt Quick Controls - 提供轻量的QML类,以创建桌面、嵌入式、和移动设备的性能出色的用户界面。这些类使用了简单格式的结构而使得效率较高.
- Qt Quick Layouts - 用于在用户界面上布局排列基于Qt Quick 2 的控件.
- Qt Quick Test - 为QML应用配备的单元测试框架,以JavaScript函数的形式编写测试用例.
- Qt Test - 与Qt应用程序和库文件的单元测试相关的类.
- Qt Widgets - 用C++ 组件扩展Qt GUI 的相关类
Qt 可选模块
除了必备模块,Qt 还为特定场合提供了其它模块。很多模块具有完整的功能特性并与后端匹配,或者仅应用于特定平台。请熟悉以下列表中的模块,详见Qt Documentation add-on list。
- Network: Qt Bluetooth / Qt Network Authorization
- UI Components: Qt Quick 3D / Qt Quick Timeline / Qt Charts / Qt Data Visualization / Qt Lottie Animation / Qt Virtual Keyboard
- Graphics: Qt 3D / Qt Image Formats / Qt OpenGL / Qt Shader Tools / Qt SVG / Qt Wayland Compositor
- Helper: Qt 5 Core Compatibility APIs / Qt Concurrent / Qt Help / Qt Print Support / Qt Quick Widgets / Qt SCXML / Qt SQL / Qt State Machine / Qt UI Tools / Qt XML
提示
这些组件并非发行版的一部分,每个模块的状态不同,这依赖于有多少活跃开发者为其贡献代码以及模块的测试是否充分。
支持的平台
Qt 支持包括桌面和嵌入式的众多平台,通过抽象Qt应用,现在比以往更加容易将应用程序部署到所需的平台上。
在不同平台上测试Qt 6是很耗时的工作,Qt 工程选择引用一个平台子集来构建对应的平台的程序。这些平台是通过系统和彻底的测试了,以确保最佳质量。但是,请记住,没有代码是没有错误的。
Qt 工程
这里的工程,是指Qt生态的开源、完善工程,引自**Qt Wiki** :
"Qt Wiki 是一个对 Qt 感兴趣的精英共享社区。任何有共同兴趣的人都可以加入社区,参与其决策过程,并为 Qt 的发展做出贡献。"
Qt Wiki 是 Qt 用户和贡献者分享他们感悟的地方。它构成了其他用户贡献的基础。最大的贡献者是 Qt 公司,它也拥有对 Qt 的商业权利。
对于公司来讲,Qt 既有具有开源的一面和商业的一面。商业方面适用于不能或不会遵守开源许可证的公司。如果没有商业方面,这些公司将无法使用 Qt, Qt 公司也不会为 Qt 项目贡献如此多的代码。
全球有许多公司在各种平台上使用 Qt 进行咨询和产品开发。有很多开源项目和开源开发者都依赖 Qt 作为他们的主要开发库。成为这个充满活力的社区的一员并使用这些很棒的工具和库感觉很好。它会让你成为一个更好的自已吗?可能吧 :-)
Qt 6 简介
Qt Quick
Qt Quick 是在Qt 6 中使用的一系列用户界面技术总称。它是从Qt 5中引入并扩展到Qt 6版本。Qt Quick 自身是几个技术的集合:
- QML - 用户界面标记语言
- JavaScript - 动态脚本语言
- Qt C++ - 高可移植性的C++ 增强扩展库
与 HTML 类似,QML 是一种标记语言。它由标记组成,在 Qt Quick 中称为类型,用大括号括起来:Item {}。它是为创建用户界面、方便开发人员快速阅读代码而从头设计的。用户界面可以使用JavaScript代码进一步强化其特性。Qt Quick可以使用Qt C++ 轻松扩展自己的原生功能。简而言之,声明式用户界面被称为前端,原生部分被称为后端。这使得应用程序的密集计算和本地操作与用户界面部分分开。
在一个典型的项目中,前端是用QML/JavaScript开发的。后端代码,即与系统的接口和做繁重工作的代码,是用Qt C++ 开发的。这使得更多面向设计的开发者和功能型开发者之间有了自然的分离。通常,后端使用Qt Test(Qt单元测试框架)进行测试,并输出给前端开发者使用。
用户界面设计
我们先用Qt Quick创建一个简单的用户界面,通过它了解QML语言的某些方面用法。最后将会实现一个带有旋转叶片的纸质风车。本节内容里没有Qt Creator IDE的详细操作,后面章节中涉及。不过这并不妨碍你理解整个开发过程。如果你想按本文介绍的实际操作,要注意在选择工程类型时选择Qt Quick UI Prototype
。
我们先从一个叫做main.qml
的空文件开始。我们所有的QML文件的后缀都是.qml
。作为一种标记语言(像HTML),一个QML文档需要有一个而且只有一个根类型。在我们的例子中,这是一个Image
类型,其宽度和高度是基于背景图像的尺寸。
- import QtQuick
- Image {
- id: root
- source: "images/background.png"
- }
由于 QML 不限制根类型的类型选择,我们使用 Image
类型作为根类型,并将源属性设置为我们的背景图像。
提示
每种类型都有属性。例如,图像具有width
和height
属性,width
与height
都包含一个像素数。它还具有其他属性,例如source
。由于Image
类型的大小是从图片大小自动推导出来的,所以我们不需要自己设置宽度和高度属性。
大多数标准类型位于 QtQuick
模块中,该模块由 .qml
文件开头的 import
语句提供。
id
是一个特殊且可选的属性,它包含一个标识符,可用于在文档中的其他位置引用其关联类型。重要提示: id
属性在设置后无法更改,也无法在运行时设置。使用 root
作为 root-type 的 id 是本书中使用的约定,以使在较大的 QML 文档中引用最顶层的类型是可预测的(即,不假思索地使用root
来引用最顶层类型)。
前景元素,代表用户界面中的杆和风车,作为单独的图像包含在内。
![](./images/1644919354753.png)
我们想要将杆在水平方向上居中,但在垂直方向上向底部偏移,同时把风车放在背景的中间。
这个入门示例仅用到了
Image
类型,随着学习的深入,会创建由各种不同类型组成的复杂用户界面。
- Image {
- id: root
- ...
- Image {
- id: pole
- anchors.horizontalCenter: parent.horizontalCenter
- anchors.bottom: parent.bottom
- source: "images/pole.png"
- }
- Image {
- id: wheel
- anchors.centerIn: parent
- source: "images/pinwheel.png"
- }
- ...
- }
这里使用了相对复杂的属性 anchor
(锚)。锚定允许指定元素和父控件及同级控件间要对位置关系。例如,锚定在其它类型的中间(anchors.centerIn:parent
)。界面类型元素间共有以下锚点:left, right, top, bottom, centerIn, fill, verticalCenter and horizontalCenter
。当然了,当多个锚点共同使用时,必须要确保锚点有意义。例如,将一个元素的left
锚定到另一个元素的top
,这就没有意义了。
对于风车来说,锚定只需要最简单的锚点。
提示
有些场景,需要将元素从中间略微偏离一点,可以通过anchors.horizontalCenterOffset
和anchors.verticalCenterOffset
实现。类似的调整同样适用于其它锚点。可以通过Qt文档来查看所有锚点的详细介绍。
提示
将图片类型作为一种root
类型,验证了这种声明式语言的重要理念:对界面可视元素的声明描述是有层级顺序(z-order)的。最顶层的元素(背景图)最先被画出来,子元素接着在其上面被画出。
如何给这个示例加点用户交互,让他能动呢?思路如下:当用户在某处点击鼠标时,风车旋转。
这里,我们使用 MouseArea
来覆盖整个根类型的区域。
Image {
id: root
...
MouseArea {
anchors.fill: parent
onClicked: wheel.rotation += 90
}
...
}
当用户在鼠标区域点击鼠标时,会触发信号。可以通过重写 onClicked
函数来接管响应信号。信号被接管后,一旦信号发出,则相应的函数会被调用。如此,则当鼠标区域内的鼠标被点击时,id
为wheel
(假设是风车图片) 的类型元素应该旋转90度。
提示
通过在信号名前加on
,即'on+SingleName' 的形式,这个规则对每个信号都有效。其实,所有属性的值发生变化时,都会触发信号,对于这些信号,其响应函数命名规则为:on${property}Changed
例如,当 width
属性值变化时,可以通过 onWidthChanged:print(width)
函数响应到其发出的信号。
对于本章的例子来说,现在当用户点击鼠标时,风车已经能旋转了。但其转得一跳一跳的,缺少流畅的动画效果。想要达到此效果,可以通过使用animation。一个 animation 定义了一个属性值在一段时间内如何变化。这就要用到 Animation
类型的 Behavior
属性,对于属性值的每一个更改,Behavior
为属性指定一个动画。换句话说,每当属性改变时,动画就会运行。这只是在QML中实现动画的一种方式。
- Image {
- id: root
- Image {
- id: wheel
- Behavior on rotation {
- NumberAnimation {
- duration: 250
- }
- }
- }
- }
现在,当轮子的rotation属性改变时,它将使用NumberAnimation
动画,持续时间为250毫秒。所以每一个90度的转弯需要250毫秒,产生一个平滑的转弯。
提示
你实际上不会看到车轮模糊。这只是表示旋转。(在assets文件夹中有一个模糊的轮子,如果你想用它做实验的话。)
现在,这个转轮看起来好多了,而且表现得很好,同时也让我们对Qt Quick编程的基本原理有了一个非常简短的了解。