QML 和 Qt Quick 的最佳实践

列出了使用QML和Qt Quick的最佳实践。

尽管QML和Qt Quick提供了许多好处,但在某些情况下它们可能会带来挑战。以下部分详细阐述了一些最佳实践,这些实践将帮助您在开发应用程序时获得更好的结果。

自定义UI控件

流畅和现代的UI是当今世界任何应用程序成功的关键,这就是QML对设计师或开发者如此有意义的地方。Qt提供了创建流畅和现代外观UI所需的最基本的UI控件。建议在创建自己的自定义UI控件之前,先浏览此UI控件列表。

除了Qt Quick本身提供的基本UI控件外,Qt Quick Controls还提供了一套丰富的UI控件。它们无需任何更改即可满足最常见的用例,并通过其自定义选项提供了更多可能性。特别是,Qt Quick Controls提供了符合最新UI设计趋势的样式选项。如果这些UI控件不能满足您的应用程序需求,才建议创建自定义控件。

您可以在Qt Design Studio中设计UI时使用控件。此外,它还提供了基于时间轴的动画、视觉效果、布局以及用于原型应用程序的实时预览。

编码规范

参见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模块不同。这是一个常见的错误来源。

相关信息

  • Qt 资源系统

将用户界面与业务逻辑分离

大多数应用程序开发者希望实现的关键目标之一是创建一个可维护的应用程序。实现这一目标的方法之一是将用户界面与业务逻辑分离。以下是为什么应用程序的UI应该用QML编写的几个原因:

  • 声明式语言非常适合定义用户界面。

  • QML代码编写更简单,因为它比C++更简洁,并且不是强类型的。这也使得它成为一种优秀的原型设计语言,这种特性在与设计师合作时尤为重要。

  • JavaScript 可以轻松地在 QML 中用于响应事件。

作为一种强类型语言,C++ 最适合用于应用程序的业务逻辑。通常,这类代码执行诸如复杂计算或数据处理等任务,这些任务在 C++ 中比在 QML 中更快。

Qt 提供了多种方法将 QML 和 C++ 代码集成到应用程序中。一个典型的用例是在用户界面中显示数据列表。如果数据集是静态的、简单的和/或小的,用 QML 编写的模型可能就足够了。

以下代码片段展示了用QML编写的模型示例:

使用 C++ 处理大型或频繁修改的动态数据集。

将数据从C++暴露到QML

重构QML比重构C++要容易得多,因此为了使维护变得轻松,我们应该尽可能让C++类型不了解QML。这可以通过将C++类型的引用“推入”QML来实现。

这可以通过使用必需的属性并通过QQmlApplicationEngine::setInitialProperties设置它们来完成。也可以创建一个或多个单例,这些单例将返回C++端希望提供给QML的所有数据。

采用这种方法,如果将来需要重构QML,C++代码将保持不变。

有关如何选择正确方法将C++类型暴露给QML的快速指南,请参阅选择C++和QML之间的正确集成方法。

相关信息

  • 使用C++编写QML扩展教程

  • 聊天应用教程

使用Qt Design Studio

Qt Design Studio 使用文件扩展名为 .ui.qml 的 UI 文件,将 UI 的可视部分与您在 .qml 文件中实现的 UI 逻辑分开。您应该仅在 Qt Design Studio 的 2D 视图中编辑 UI 文件。如果您使用其他工具添加了 Qt Design Studio 不支持的代码,它会显示错误消息。修复这些错误以再次启用 UI 文件的可视编辑。通常,您应该将不支持的代码移动到 .qml 文件中。

相关信息

  • Qt Design Studio: UI 文件

使用Qt Quick布局

Qt 提供了 Qt Quick 布局来在布局中视觉上排列 Qt Quick 项目。与替代方案项目定位器不同,Qt Quick 布局还可以在窗口调整大小时调整其子项的大小。尽管 Qt Quick 布局在大多数用例中通常是首选,但在使用它们时必须考虑以下注意事项禁忌

Dos

  • 使用锚点宽度高度属性来指定布局相对于其非布局父项的大小。

  • 使用Layout附加属性来设置布局直接子元素的大小和对齐属性。

不要做的事

  • 不要为提供implicitWidth和implicitHeight的项目定义首选大小,除非它们的隐式大小不令人满意。

  • 不要在布局的直接子项上使用锚点。相反,使用 Layout.preferredWidthLayout.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

性能

有关QML和Qt Quick性能的信息,请参阅QML性能考虑和建议。

优先使用声明式绑定而非命令式赋值

在QML中,可以使用命令式JavaScript代码来执行任务,例如响应输入事件、通过网络发送数据等。命令式代码在QML中占有重要地位,但同样重要的是要意识到何时不应使用它。

例如,考虑以下命令式赋值:

Rectangle {
    Component.onCompleted: color = "red"
}

这有以下缺点:

  • 它很慢。color 属性将首先使用默认构造的值进行评估,然后再使用“red”进行评估。

  • 它将可以在构建时发现的错误延迟到运行时,从而减慢开发过程。

  • 它会覆盖任何已存在的声明性绑定。在大多数情况下,这是有意为之的,但有时也可能是无意的。有关更多信息,请参阅调试绑定覆盖。

  • 它干扰了工具;例如,Qt Quick Designer 不支持 JavaScript。

代码可以重写为声明式绑定:

Rectangle {
    color: "red"
}

不要在委托中存储状态

不要在委托中存储状态。这里的问题是委托会被多次创建和销毁,因此保存的状态将会丢失。

相反,将状态存储在委托之外。例如,存储在模型中。当委托被销毁时,保存的状态不会丢失。

使面向用户的字符串可翻译

建议从一开始就使面向用户的字符串可翻译。请参阅编写用于翻译的源代码。

不要自定义原生样式

原生样式(Windows 和 macOS 样式)不支持自定义。请确保不要自定义原生样式。

相反,建议始终在所有平台上可用的单一样式基础上自定义控件,例如基本样式、融合样式、想象样式、材质样式、通用样式。通过这样做,您可以确保无论应用程序使用哪种样式,它看起来都是一样的。要了解如何使用不同的样式,请参阅在Qt Quick Controls中使用样式。或者,您可以创建自己的样式。

工具和实用程序

有关使使用QML和Qt Quick更轻松的有用工具和实用程序的信息,请参阅Qt Quick Tools and Utilities

场景图

有关Qt Quick的场景图信息,请参阅Qt Quick Scene Graph

可扩展的用户界面

随着显示分辨率的提高,可扩展的应用程序用户界面变得越来越重要。实现这一目标的方法之一是为不同的屏幕分辨率维护多个用户界面的副本,并根据可用的分辨率加载适当的副本。尽管这种方法效果很好,但它增加了维护的开销。

Qt 为这个问题提供了一个更好的解决方案,并建议应用程序开发者遵循以下提示:

  • 使用锚点或Qt Quick布局模块来布局视觉项。

  • 不要为视觉项目指定明确的宽度和高度。

  • 为应用程序支持的每种显示分辨率提供UI资源,如图像和图标。Qt Quick Controls 示例通过为 qt-logo.png 提供 @2x@3x@4x 分辨率的资源,展示了这一点,使应用程序能够适应高分辨率显示。如果明确启用了高DPI缩放功能,Qt会自动选择适合给定显示的图像。

  • 使用SVG图像作为小图标。虽然较大的SVG渲染速度可能较慢,但小的SVG效果很好。矢量图像避免了需要提供多个版本的图像,这是位图图像所必需的。

  • 使用基于字体的图标,例如 Font Awesome。这些图标可以缩放到任何显示分辨率,并且还可以进行着色。Qt Quick Controls 文本编辑器示例很好地展示了这一点。

有了这个设置,你的应用程序的用户界面应该会根据提供的显示分辨率进行缩放。

../_images/qtquickcontrols-gallery-welcome.png

相关信息

  • 画廊示例

  • 文本编辑器示例

  • Font Awesome

  • 可扩展性

  • 高DPI