QML 和 的最佳实践Qt Quick
尽管 QML 和Qt Quick 具有诸多优点,但在某些情况下它们也可能具有挑战性。以下各节详细介绍了一些最佳实践,它们将帮助你在开发应用程序时取得更好的效果。
自定义用户界面控件
在当今世界,流畅、现代的用户界面是任何应用程序成功的关键,而这正是 QML 对设计者或开发者的意义所在。Qt 提供了创建流畅、现代外观的用户界面所需的最基本用户界面控件。建议在创建自己的自定义 UI 控件之前,先浏览一下 UI 控件列表。
除了Qt Quick 本身提供的这些基本 UI 控件外,Qt Quick Controls 还提供了丰富的 UI 控件集。这些控件可满足最常见的使用情况,无需做任何更改,并提供更多的自定义选项。尤其是,Qt Quick Controls 提供了符合最新用户界面设计趋势的样式选项。如果这些用户界面控件不能满足您的应用程序需求,建议您创建一个自定义控件。
您可以在Qt Design Studio 中设计用户界面时使用这些控件。此外,它还提供了基于时间线的动画、视觉效果、布局和用于应用程序原型开发的实时预览。
相关信息
编码约定
请参阅QML 编码约定。
捆绑应用程序资源
大多数应用程序都依赖图片和图标等资源来提供丰富的用户体验。无论目标操作系统如何,向应用程序提供这些资源通常都是一项挑战。大多数流行的操作系统都采用了更严格的安全策略,限制对文件系统的访问,从而增加了加载这些资源的难度。作为替代方案,Qt 提供了自己的资源系统,该系统内置于应用程序二进制文件中,无论目标操作系统如何,都能访问应用程序的资源。
例如,请看下面的项目目录结构:
MyModule ├── images │ ├── image1.png │ └── image2.png ├── CMakeLists.txt └── main.qml
您可以按以下方式将此结构表示为CMake QML 模块:
qt_add_qml_module(my_module URI MyModule VERSION 1.0 QML_FILES main.qml RESOURCES images/image1.png images/image2.png # ... )
QML_FILES
下列出的所有 QML 文件都会自动提前编译。
您应将 QML 文件与带有 qt_add_qml_module 的 CMakeLists.txt 放在同一目录下。否则,它们的隐式导入将与它们所属的QML 模块不同。这是经常出错的原因。
相关信息
用户界面与业务逻辑分离
大多数应用程序开发人员希望实现的关键目标之一是创建一个可维护的应用程序。实现这一目标的方法之一就是将用户界面与业务逻辑分开。以下是应用程序的用户界面应使用 QML 编写的几个原因:
- 声明式语言非常适合定义用户界面。
- QML 代码比 C++ 少啰嗦,而且不是强类型的,因此编写起来更简单。这也使它成为一种极好的原型语言,这在与设计师合作时至关重要。
- 在 QML 中可以轻松使用 JavaScript 来响应事件。
作为一种强类型语言,C++ 最适合用于应用程序的业务逻辑。通常,此类代码会执行复杂计算或数据处理等任务,而 C++ 的速度比 QML 更快。
Qt Qml 提供了多种方法将 QML 和 C++ 代码集成到应用程序中。一个典型的用例是在用户界面中显示数据列表。如果数据集是静态的、简单的和/或较小的,用 QML 编写的模型就足够了。
下面的代码段演示了用 QML 编写模型的示例:
model: [ "Item 1", "Item 2", "Item 3" ] model: 10
对于大型或经常修改的动态数据集,请使用C++。
从 C++ 向 QML 公开数据
重构 QML 比重构 C++ 要容易得多,因此为了使维护无忧,我们应尽可能让 C++ 类型不被 QML 知晓。这可以通过把 C++ 类型的引用 "推入 "QML 来实现。
这可以通过使用所需的属性并通过QQmlApplicationEngine::setInitialProperties 设置它们来实现。也可以创建一个或多个singletons ,返回 C++ 方面想提供给 QML 的所有数据。
采用这种方法,即使将来需要重构 QML,C++ 也不会改变。
有关选择正确方法将 C++ 类型暴露给 QML 的快速指南,请参阅《选择 C++ 和 QML 之间正确的集成方法》(Choosing the Correct Integration Method Between C++ and QML)。
相关信息
使用Qt Design Studio
Qt Design Studio 使用扩展名为.ui.qml的用户界面文件,可将用户界面的可视化部分与您在.qml文件中实现的用户界面逻辑分开。只能在 的 视图中编辑用户界面文件。如果使用其他工具添加 不支持的代码,则会显示错误信息。修复错误后,就能再次对用户界面文件进行可视化编辑。通常情况下,应将不支持的代码移到Qt Design Studio 2D Qt Design Studio .qml文件中。
相关信息
使用Qt Quick 视图
在模型中存储状态
请参见Avoid Storing State in Delegates 。
使用Qt Quick 布局
Qt XML 提供了Qt Quick Layouts(布局),用于在布局中直观地排列Qt Quick 项目。与项目定位器不同,Qt Quick Layouts 还可以在调整窗口大小时调整其子项目的大小。虽然Qt Quick 布局通常是大多数用例的理想选择,但在使用时必须考虑以下注意事项:
注意事项
注意事项
- 不要为提供 implicitWidth 和 implicitHeight 的项目定义首选尺寸,除非它们的隐式尺寸不能令人满意。
- 不要在布局的直接子项上使用锚点。相反,请使用
Layout.preferredWidth
和Layout.preferredHeight
:RowLayout { id: layout anchors.fill: parent spacing: 6 Rectangle { color: 'orange' Layout.fillWidth: true Layout.minimumWidth: 50 Layout.preferredWidth: 100 Layout.maximumWidth: 300 Layout.minimumHeight: 150 Text { anchors.centerIn: parent text: parent.width + 'x' + parent.height } } Rectangle { color: 'plum' Layout.fillWidth: true Layout.minimumWidth: 100 Layout.preferredWidth: 200 Layout.preferredHeight: 100 Text { anchors.centerIn: parent text: parent.width + 'x' + parent.height } } }
注意: 布局和锚点都是需要占用更多内存和实例化时间的对象类型。如果只需绑定 x、y、宽度和高度属性即可,则应避免使用布局和锚点(尤其是在列表和表格委托以及控件样式中)。
相关信息
类型安全
在 QML 中声明属性时,使用 "var "类型既简单又方便:
property var name property var size property var optionsMenu
然而,这种方法有几个缺点:
- 如果分配了一个错误类型的值,报告的错误将指向属性声明的位置,而不是属性被分配的位置。这将使追踪错误变得更加困难,从而减慢开发进程。
- 静态分析无法捕捉到上述错误。
- 对于读者来说,属性的实际底层类型并不总是一目了然的。
因此,应尽可能使用实际类型:
property string name
property int size
property MyMenu optionsMenu
属性更改信号
优先使用显式交互信号,而不是值更改信号,以避免出现微妙的错误。
使用valueChanged
可能会导致事件级联,在这种情况下,值会因为某种原因被舍入或规范化而不断变化。
而单独使用显式交互信号则可以避免这类问题。
例如,Slider 有这些类似的信号:moved 和valueChanged
。
Slider { value: someValueFromBackend onValueChanged: pushToBackend(value) // or onMoved: pushToBackend(value) }
这两种情况看起来很相似,您可能希望使用valueChanged
。
开发人员经常会忽略这样一个事实,即Slider 可以自动改变其值,例如,由于箝位到最小值/最大值或四舍五入。在这种情况下,就会发出valueChanged
信号。如果使用valueChanged
信号,您可能会注意到它会在意想不到的时刻发出。
为避免可能出现的问题,请使用交互信号:当用户与控件交互时发出的信号。在本例中,如果使用moved 信号,只有当用户更改控件时才会触发槽。
性能
有关 QML 和Qt Quick 性能的信息,请参阅QML 性能考虑因素和建议。
声明式绑定优于命令式分配
在 QML 中,可以使用命令式 JavaScript 代码来执行任务,如响应输入事件、通过网络发送数据等。命令式代码在 QML 中占有重要地位,但也要注意何时不能使用它。
例如,请看下面的命令式赋值:
Rectangle { Component.onCompleted: color = "red" }
它有以下缺点:
- 速度慢。color 属性将首先用默认构造值进行评估,然后再用 "red "进行评估。
- 它会将可能在构建时发现的错误延迟到运行时,从而减慢开发进程。
- 它会覆盖已存在的声明式绑定。大多数情况下这是有意为之,但有时也可能是无意为之。更多信息,请参阅 "调试覆盖绑定"。
- 干扰工具;例如,Qt Quick Designer 不支持 JavaScript。
可以将代码重写为声明绑定:
Rectangle { color: "red" }
不要在委托中存储状态
不要在委托中存储状态。这里的问题是,委托会多次创建和销毁,因此保存的状态会丢失。
// Wrong approach: ListView { // ... delegate: Button { // ... property bool someStateProperty onClicked: someStateProperty = true } }
相反,应将状态存储在委托之外。例如,在模型中。当委托被销毁时,保存的状态不会丢失。
// Right approach: ListView { // ... delegate: Button { // ... onClicked: model.someStateProperty = true } }
使面向用户的字符串可翻译
建议从一开始就使面向用户的字符串可翻译。请参阅为翻译编写源代码。
ToolButton { id: selectionToolButton // ... icon.source: "qrc:/images/selection.png" Tooltip.Text: qsTr("Select pixels within an area and move them") onClicked: canvas.tool = ImageCanvas.SelectionTool }
不要自定义本地样式
本地样式(Windows 和 macOS 样式)不支持自定义。请确保不要自定义本地样式。
// Wrong approach: import QtQuick.Controls.Windows // Don't customize a native style Button { background: Rectangle { /*...*/ } }
建议将自定义控件建立在所有平台都可用的单一样式之上,例如基本样式、融合样式、想象样式、材质样式、通用样式。这样做可以保证无论使用哪种样式运行应用程序,控件的外观都是一样的。要了解如何使用不同的样式,请参阅 Qt Quick Controls 中的 "使用样式"。或者,你也可以创建自己的样式。
// Right approach: import QtQuick.Controls.Basic // You can customize a commonly available style Button { background: Rectangle { /*...*/ } }
工具和实用程序
要了解如何使用 QML 和Qt Quick ,请参阅Qt Quick Tools and Utilities(工具和实用程序)。
场景图
有关Qt Quick 场景图的信息,请参阅Qt Quick 场景图。
可扩展的用户界面
随着显示分辨率的提高,可扩展的应用程序用户界面变得越来越重要。实现这一目标的方法之一是为不同的屏幕分辨率维护多个用户界面副本,并根据可用分辨率加载相应的副本。虽然这种方法效果不错,但却增加了维护开销。
Qt 为这一问题提供了更好的解决方案,并建议应用程序开发人员遵循以下提示:
- 使用锚点或Qt Quick Layouts 模块来布局可视化项目。
- 不要为可视化项目指定明确的宽度和高度。
- 为应用程序支持的每种显示分辨率提供用户界面资源,如图像和图标。Qt Quick Controls 图库示例很好地证明了这一点,它为
@2x
、@3x
和@4x
分辨率提供了qt-logo.png
,使应用程序能够满足高分辨率显示的需要。只要显式启用了高 DPI 缩放功能,Qt 就会自动选择适合给定显示屏的图像。 - 对小图标使用 SVG 图像。虽然较大的 SVG 在渲染时可能会比较慢,但较小的 SVG 也能很好地工作。矢量图像可以避免像位图图像那样需要提供多个版本的图像。
- 使用基于字体的图标,例如 Font Awesome。这些图标可缩放至任何显示分辨率,还可以着色。Qt Quick Controls 文本编辑器示例很好地展示了这一点。
有了这些,您的应用程序的用户界面就可以根据所提供的显示分辨率进行缩放。
相关信息
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.