本页内容

QML 和Qt Quick

尽管 QML 和Qt Quick 提供了诸多优势,但在某些情况下仍可能带来挑战。以下各节详细阐述了一些最佳实践,这些实践将帮助您在开发应用程序时获得更好的效果。

优先使用内置控件而非自定义 UI 控件

在当今世界,流畅且现代的用户界面是任何应用程序成功的关键,这也正是QML对设计师或开发者而言如此有价值的原因。Qt提供了创建流畅且现代用户界面所必需的最基本UI控件。建议在创建自定义UI控件之前,先浏览这份UI控件列表。

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

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

编码规范

请参阅《QML 编码规范》。

打包应用程序资源

大多数应用程序依赖于图像和图标等资源来提供丰富的用户体验。无论目标操作系统为何,让应用程序能够访问这些资源往往都是一项挑战。 大多数主流操作系统都采用了限制文件系统访问的安全策略,这使得加载这些资源变得更加困难。作为替代方案,Qt 提供了一种内置于应用程序二进制文件中的资源系统,从而能够不受目标操作系统限制地访问应用程序的资源。

例如,对于一个 C++ 项目,考虑以下目录结构:

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

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

大多数应用程序开发人员希望实现的关键目标之一是创建一个易于维护的应用程序。实现这一目标的方法之一是将用户界面(前端)与业务逻辑(后端)分离。以下是应用程序的 UI 应使用 QML 编写的一些原因:

  • 声明式语言非常适合定义用户界面。
  • QML 代码更简洁,因为它比 C++ 更简洁,且不是强类型语言。这也使其成为一种出色的原型设计语言,例如在与设计师协作时,这一特性至关重要。
  • 在 QML 中可以轻松使用 JavaScript 来响应事件。

强类型语言(如 C++)最适合处理应用程序的业务逻辑,即后端部分。通常,此类代码执行复杂计算或数据处理等任务,而强类型语言在此类任务上的执行速度比 QML 更快。

Qt 提供了多种方法,可在应用程序中将 QML 与强类型语言集成。一个典型的用例是在用户界面中显示数据列表。如果数据集是静态的、简单的且规模较小,则使用 QML 编写的模型就足够了。

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

model: [ "Item 1", "Item 2", "Item 3" ]

model: 10

对于规模较大且更具动态性的数据集,请使用C++ 等强类型语言来处理业务逻辑。

将 C++ 中的数据暴露给 QML

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

具体可通过使用required 属性,并借助 `QQmlApplicationEngine::setInitialProperties` 进行设置来实现。此外,还可以创建一个或多个单例,由其返回 C++ 端希望提供给 QML 的所有数据。

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

有关如何选择将 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 Quick 视图

在模型中存储状态

参见Avoid Storing State in Delegates 。

使用Qt Quick Layouts

Qt 提供了Qt Quick Layouts ,用于在布局中直观地排列Qt Quick 项。与替代方案——项定位器(item positioners)不同,布局定位器(Qt Quick Layouts )还能在窗口调整大小时自动调整其子项的大小。尽管在多数使用场景下,布局定位器(Qt Quick Layouts )通常是首选方案,但在使用时仍需注意以下注意事项:

应做事项

  • 使用anchors 或width 和height 属性,指定布局相对于其非布局父项的大小。
  • 使用Layout 附加属性来设置布局的直接子元素的大小和对齐属性。

禁忌

  • 除非其隐式大小不理想,否则请勿为提供 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、width 和 height 属性即可满足需求时,请避免使用它们(特别是在列表和表格委托以及控件样式中)。

类型安全

在 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 { /*...*/ }
}

相反,建议始终基于一种在所有平台上都可用的样式来定制控件,例如Basic Style、Fusion Style、Imagine Style、Material Style 或Universal Style。这样可以确保控件的外观始终一致,无论应用程序使用哪种样式运行。 要了解如何使用不同的样式,请参阅《Qt Quick Controls 》中的“使用样式”部分。此外,您还可以创建自己的样式。

// Right approach:
import QtQuick.Controls.Basic

// You can customize a commonly available style
Button {
    background: Rectangle { /*...*/ }
}

工具与实用程序

有关可简化 QML 和Qt Quick 操作的实用工具和辅助程序的信息,请参阅Qt Quick 中的“工具与辅助程序”。

场景图

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

可缩放用户界面

随着显示分辨率的提高,可缩放的应用程序用户界面变得越来越重要。实现这一目标的方法之一是维护多个适用于不同屏幕分辨率的用户界面副本,并根据可用分辨率加载相应的副本。虽然这种方法效果不错,但会增加维护开销。

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

  • 使用锚点或Qt Quick Layouts 模块来布局可视化项。
  • 不要为视觉控件显式指定宽度和高度。
  • Qt Quick Controls qt-logo.png @2x @3x @4x
  • 对于小型图标,请使用 SVG 图像。虽然较大的 SVG 图像渲染速度可能较慢,但小型 SVG 图像效果良好。与位图图像不同,矢量图像无需提供多种版本。
  • 使用基于字体的图标,例如 Font Awesome。这些图标可适应任何显示分辨率,并且支持自定义颜色。Qt Quick Controls 文本编辑器示例对此进行了很好的演示。

完成上述设置后,您的应用程序界面应能根据当前显示分辨率进行自适应调整。

带 Qt 徽标的图库示例欢迎界面

© 2026 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.