QML 是语言,Qt Quick、Qt Quick Controls、Qt Multimedia 等才是提供具体类型的模块。找到一个名称后,应同时确认它来自哪个模块、当前 Qt 版本是否提供、能否直接实例化,以及需要什么运行环境。本文以 Qt 6.10 为基准,保留基础类型、交互、布局、模型、动画、路径、媒体和扩展模块的查找入口。
先阅读 QML 语法与属性。下列每个 QML 代码块都是一个独立文件;除特别说明的图表程序外,可由 Qt Quick 应用或安装了相应模块的 qml 工具加载。示例使用无版本号导入,要求环境满足本文版本,不代表能够原样运行在 Qt 5。
基础显示、输入与控件
跳转到“基础显示、输入与控件”| 名称 | 模块与用途 | 使用要点 |
|---|---|---|
Rectangle | QtQuick,矩形背景、边框、圆角 | color、border、radius 控制外观;它仍是一个矩形 Item |
Text | QtQuick,文本显示 | 显式选择纯文本或富文本,注意换行、裁剪和字体大小 |
Image | QtQuick,图像显示 | source 是 URL;用 fillMode 控制缩放,检查 status 和资源路径 |
Gradient | QtQuick,渐变描述 | 与 GradientStop 配合,赋给 Rectangle.gradient 等接受它的属性 |
Label | QtQuick.Controls,带控件样式的文本 | 需要与其他 Controls 保持样式一致时使用 |
Button | QtQuick.Controls,触发动作 | 常用 text、icon、onClicked;也可定制 contentItem |
CheckBox、Switch | QtQuick.Controls,选中状态或开关 | 读写 checked;多选与开关在交互语义上有区别 |
RadioButton | QtQuick.Controls,互斥选项 | 同一父项下默认自动互斥,也可用 ButtonGroup 显式分组 |
Slider | QtQuick.Controls,范围内连续/步进选值 | from、to、value、stepSize;属性通知不带通用的 value 参数 |
SpinBox | QtQuick.Controls,整数步进输入 | 用 from、to、value;小数显示需另行转换,并非直接声明任意浮点范围 |
ComboBox | QtQuick.Controls,从模型中选择 | model、currentIndex、currentText,复杂模型还要指定角色 |
ProgressBar | QtQuick.Controls,进度反馈 | from、to、value;未知总量时使用 indeterminate |
TextField、TextArea | QtQuick.Controls,单行/多行编辑 | 根据需要设置校验器、占位文字、回显模式和滚动容器 |
MouseArea | QtQuick,鼠标按下、移动、点击、拖动 | 用显式事件参数,如 onClicked: (mouse) => { ... } |
MultiPointTouchArea | QtQuick,处理多个触点 | 提供触点列表;需要缩放/拖动时也可选 PinchHandler、DragHandler |
DropArea | QtQuick,接收拖放 | 与 Drag 附加属性或平台拖放配合;不会自动生成拖动源 |
FocusScope | QtQuick,组合组件内部焦点域 | 自身不绘制焦点边框;焦点项可用 activeFocus 决定外观 |
KeyEvent | QtQuick,键盘处理器收到的事件对象 | 从 Keys.onPressed 的参数读取 key、text、accepted,不能当普通可视类型创建 |
以下 ControlsDemo.qml 把点击、动态输入、单选、多选、开关、数值联动、进度和键盘焦点放在一个完整页面。将焦点移到输入框后,方向键和文本输入由控件处理;空白焦点域收到空格时增加计数。
import QtQuickimport QtQuick.Controlsimport QtQuick.Layoutsimport QtQml.Models
FocusScope { id: root width: 560; height: 520 focus: true property int clicks: 0 Keys.onPressed: (event) => { if (event.key === Qt.Key_Space) { clicks += 1 event.accepted = true } } Rectangle { anchors.fill: parent color: "#f4f6f8" border.color: root.activeFocus ? "steelblue" : "lightgray" } ColumnLayout { anchors.fill: parent; anchors.margins: 16 spacing: 8 Label { id: counter; text: "点击/空格次数:" + root.clicks } RowLayout { Button { text: "增加"; onClicked: root.clicks += 1 } CheckBox { id: enabledChoice; text: "允许编辑"; checked: true } Switch { id: mode; text: "夜间模式" } } RowLayout { RadioButton { id: localChoice; text: "本地"; checked: true } RadioButton { id: remoteChoice; text: "远程" } ComboBox { id: formats textRole: "name" model: ListModel { ListElement { name: "文本" } ListElement { name: "图像" } ListElement { name: "音频" } } delegate: ItemDelegate { required property string name width: formats.width text: name } } } TextField { id: titleInput Layout.fillWidth: true enabled: enabledChoice.checked placeholderText: "输入标题" } TextArea { id: bodyInput Layout.fillWidth: true; Layout.fillHeight: true enabled: enabledChoice.checked placeholderText: "输入多行内容" wrapMode: TextEdit.Wrap } RowLayout { Slider { id: slider; Layout.fillWidth: true; from: 0; to: 100; value: 25 } SpinBox { id: amount; from: 0; to: 100; value: 25 } } ProgressBar { id: progress; Layout.fillWidth: true; from: 0; to: 100; value: slider.value } Rectangle { id: tile Layout.preferredWidth: 120; Layout.preferredHeight: 44 radius: 8; color: "lightblue" Text { anchors.centerIn: parent; text: "点击色块" } MouseArea { anchors.fill: parent onClicked: (mouse) => { root.clicks += 1 tile.color = mouse.button === Qt.LeftButton ? "lightgreen" : "lightblue" } } } }}Slider 的通知处理、动态监听和取消监听详见 Connections。控件文本的自动转换不代表所有属性类型都可任意混用;不兼容赋值会产生诊断。
ComboBox 使用带 name 角色的 ListModel 时,textRole 决定选中项显示文字,delegate 决定弹出列表外观。用 ItemDelegate 可以保留菜单项点击和可访问性语义;仅把普通 Item/Rectangle 当委托,需要自行补齐相应交互。
DragDropDemo.qml 展示拖动源与接收区。释放鼠标由源 MouseArea 处理,并调用 Drag.drop();目标的完成信号是 DropArea.onDropped,不存在这里常被误写的 DropArea.onReleased。
import QtQuick
Item { id: root width: 460; height: 180 property int drops: 0 property string lastLabel: "" Rectangle { x: 260; y: 20; width: 160; height: 120 color: target.containsDrag ? "lightgreen" : "#e8edf2" Text { anchors.centerIn: parent; text: "放到这里" } DropArea { id: target anchors.fill: parent keys: ["note"] onDropped: (drop) => { root.lastLabel = drop.source.label root.drops += 1 drop.acceptProposedAction() } } } Rectangle { id: card x: 20; y: 20; width: 100; height: 60; color: "lightblue" property string label: "笔记" Drag.active: mouse.drag.active Drag.source: card Drag.keys: ["note"] Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 Text { anchors.centerIn: parent; text: card.label } MouseArea { id: mouse anchors.fill: parent drag.target: card onReleased: card.Drag.drop() } }}位置排列、尺寸布局与页面切换
跳转到“位置排列、尺寸布局与页面切换”| 名称 | 模块与用途 | 边界 |
|---|---|---|
Row、Column、Grid | QtQuick,按子项既有尺寸排列位置 | 主要安排位置,不会因为父项变大就自动把每个子项拉伸 |
RowLayout、ColumnLayout | QtQuick.Layouts,水平/垂直分配空间 | 用 Layout.minimumWidth、preferredWidth、fillWidth 等声明尺寸要求 |
GridLayout | QtQuick.Layouts,行列、跨行、跨列布局 | 用 rowSpacing、columnSpacing;Layout.rowSpan、columnSpan 控制跨度 |
StackLayout | QtQuick.Layouts,显示一个现有子页面 | 设置 currentIndex;不要假定 QML 中有 setCurrentIndex() 方法 |
StackView | QtQuick.Controls,页面栈导航 | push()、pop() 操作导航历史;与只切换既有子项的 StackLayout 用途不同 |
TabBar | QtQuick.Controls,标签选择 | 搭配 TabButton 和 StackLayout.currentIndex |
ToolBar | QtQuick.Controls,工具栏容器 | 将按钮和布局放入其内容区域 |
布局应决定其直接子项的几何尺寸,避免又给这些子项设置互相竞争的 anchors、x/y、width/height 绑定。外层布局自己可以锚定父项。ColumnLayout.layoutDirection 控制水平方向相关排列/对齐,不会把垂直顺序倒过来;需要倒序时调整模型或子项顺序。锚点规则详见 锚点与边距。
LayoutNavigation.qml 展示标签页、行列尺寸约束、网格跨列、抽屉和一个独立页面栈。按“打开详情”会创建第二页,返回后销毁该导航实例;标签页则一直是 StackLayout 的两个子项。
import QtQuickimport QtQuick.Controlsimport QtQuick.Layouts
Item { id: root width: 640; height: 420 ColumnLayout { anchors.fill: parent ToolBar { Layout.fillWidth: true RowLayout { Button { text: "导航"; onClicked: drawer.open() } Label { text: "布局与页面" } } } TabBar { id: tabs Layout.fillWidth: true TabButton { text: "表单" } TabButton { text: "页面栈" } } StackLayout { id: pages Layout.fillWidth: true; Layout.fillHeight: true currentIndex: tabs.currentIndex ColumnLayout { RowLayout { Rectangle { color: "lightblue"; Layout.preferredWidth: 100; Layout.preferredHeight: 40 } Rectangle { color: "lightgreen"; Layout.fillWidth: true; Layout.preferredHeight: 40 } } GridLayout { id: grid columns: 2; rowSpacing: 8; columnSpacing: 12 Label { text: "姓名" } TextField { Layout.fillWidth: true; placeholderText: "输入姓名" } Label { text: "城市" } TextField { Layout.fillWidth: true; placeholderText: "输入城市" } Label { text: "此行横跨两列"; Layout.columnSpan: 2 } } Item { Layout.fillHeight: true } } StackView { id: stack initialItem: Component { Button { text: "打开详情"; onClicked: stack.push(detailPage) } } } } } Component { id: detailPage Button { text: "返回"; onClicked: stack.pop() } } Drawer { id: drawer width: 180; height: root.height Column { spacing: 8 Label { text: "导航目录" } Button { text: "表单"; onClicked: { tabs.currentIndex = 0; drawer.close() } } } }}模型、视图、重复项与延迟加载
跳转到“模型、视图、重复项与延迟加载”| 名称 | 模块与用途 | 关键区别 |
|---|---|---|
ListModel | QtQml.Models,带角色的 QML 列表数据 | ListElement 声明初始项;append、setProperty、remove 等更新数据 |
ListView、GridView | QtQuick,列表/网格形式显示模型 | 由 delegate 生成可视项;视口之外的实例由视图管理,不应靠 delegate 保存业务数据 |
Repeater | QtQuick,为模型每项创建可视对象 | 通常搭配 Row/Column/Grid;它会创建所有委托,不具备 ListView 的按需实例化特点 |
Loader | QtQuick,按需创建组件 | 使用 source 或 sourceComponent;通过 active 释放/重建对象,读取 item 前检查状态 |
Timer | QtQml,事件循环中的定时触发 | interval、repeat、running 和 onTriggered;不是高精度实时调度器 |
TextMetrics | QtQuick,计算指定字体下的文本尺寸 | 设置与目标文字一致的 text、font,再读 width、height 等度量 |
ModelUtilities.qml 的两个视图共享一个模型;定时器只触发一次。Loader 创建的文本和度量对象读同一标题。委托中的 required property 明确列出视图需要注入的角色。
import QtQuickimport QtQml.Models
Item { id: root width: 520; height: 320 property string title: "模型与视图" property bool showDetails: true property int ticks: 0 ListModel { id: entries ListElement { name: "Alice"; value: 10 } ListElement { name: "Bob"; value: 20 } } ListView { id: list x: 12; y: 12; width: 220; height: 130 model: entries delegate: Text { required property string name required property int value width: ListView.view.width; height: 30 text: name + ": " + value } } GridView { id: grid x: 260; y: 12; width: 240; height: 130 cellWidth: 110; cellHeight: 50 model: entries delegate: Rectangle { required property string name width: 100; height: 40; color: "lightblue" Text { anchors.centerIn: parent; text: parent.name } } } Row { id: repeatedRow x: 12; y: 160; spacing: 8 Repeater { id: repeated model: 3 Rectangle { width: 30; height: 30; color: "coral" } } } TextMetrics { id: metrics; text: root.title; font.pixelSize: 18 } Loader { id: details x: 12; y: 220 active: root.showDetails sourceComponent: Component { Text { text: root.title; font.pixelSize: 18 } } } Timer { id: once; interval: 30; running: true; repeat: false; onTriggered: root.ticks += 1 }}需要在委托内部访问 ListView.isCurrentItem 等附加属性时,应明确附加对象所属的 delegate 根项,参见 附加属性与生命周期。
菜单、弹窗和系统选择器
跳转到“菜单、弹窗和系统选择器”| 名称 | 模块与用途 | 使用方式 |
|---|---|---|
Menu、MenuItem | QtQuick.Controls,菜单及动作项 | Menu.popup() 弹出;菜单项通过 onTriggered 处理动作 |
ContextMenu | QtQuick.Controls,自 Qt 6.9 提供的附加类型 | 使用 ContextMenu.menu: Menu { ... } 关联右键菜单,不能写成通用独立容器 |
Popup | QtQuick.Controls,通用浮层 | 通过 open/close 控制,并配置关闭策略、模态状态和内容 |
Dialog | QtQuick.Controls,含标题、内容及标准按钮的对话框 | 使用 standardButtons、onAccepted、onRejected |
FileDialog、ColorDialog | QtQuick.Dialogs,文件/颜色选择 | Qt 6 文件结果为 selectedFile;目录选择用同模块的 FolderDialog |
DialogsDemo.qml 展示菜单、确认框、文件、目录和颜色选择。系统对话框实际外观取决于平台;只有用户接受后才保存选择结果。URL 应作为 URL 交给媒体/图像 API,不能直接假定它是操作系统路径字符串。
import QtQuickimport QtQuick.Controlsimport QtQuick.Dialogs as NativeDialogs
Item { id: root width: 460; height: 280 property url chosenFile: "" property url chosenFolder: "" property color chosenColor: "lightblue" property int acceptedCount: 0 Column { spacing: 8 Button { text: "右键菜单 / 点击选择文件" onClicked: fileDialog.open() ContextMenu.menu: Menu { MenuItem { text: "选择目录"; onTriggered: folderDialog.open() } MenuItem { text: "选择颜色"; onTriggered: colorDialog.open() } } } Button { text: "确认"; onClicked: confirm.open() } Button { text: "提示浮层"; onClicked: tip.open() } Rectangle { width: 80; height: 40; color: root.chosenColor } } Dialog { id: confirm title: "保存更改" standardButtons: Dialog.Ok | Dialog.Cancel onAccepted: root.acceptedCount += 1 Label { text: "是否保存当前内容?" } } Popup { id: tip; width: 150; height: 70; Label { text: "操作提示" } } NativeDialogs.FileDialog { id: fileDialog title: "选择一个文件" fileMode: NativeDialogs.FileDialog.OpenFile onAccepted: root.chosenFile = selectedFile } NativeDialogs.FolderDialog { id: folderDialog; onAccepted: root.chosenFolder = selectedFolder } NativeDialogs.ColorDialog { id: colorDialog; onAccepted: root.chosenColor = selectedColor }}ContextMenu 的附加用法和引入版本见 Qt 6.10 ContextMenu 文档。
动画、行为、状态与过渡
跳转到“动画、行为、状态与过渡”| 名称 | 作用 | 典型参数或边界 |
|---|---|---|
Animation | 所有动画共享的抽象基类接口 | running、paused、loops、启动停止方法;本身不可直接创建 |
PropertyAnimation | 对指定属性执行插值动画 | target、property、from、to、duration、easing |
NumberAnimation | 数值属性变化 | 位置、尺寸、透明度等;透明度的属性名是 opacity |
ColorAnimation | 颜色变化 | 指定颜色属性和目标色 |
RotationAnimation | 角度变化 | direction 可选最短路径或特定旋转方向 |
SequentialAnimation | 子动画依次运行 | 内部放动画,不要把 Rectangle 放入动画序列 |
ParallelAnimation | 子动画同时运行 | 可嵌入序列,组合位移、颜色和旋转 |
PauseAnimation | 给动画序列插入等待 | duration 为等待时间,不会阻塞整个应用线程 |
Behavior | 属性变化时使用默认动画 | 如 Behavior on x;写入新值是触发条件 |
SmoothedAnimation | 在新目标值到来时平滑追踪 | 常用 velocity;适合连续更新的目标 |
SpringAnimation | 以弹簧方式趋近目标 | spring、damping 等控制响应;不等同于固定时长匀速移动 |
PathAnimation | 沿路径改变目标位置 | path、target、duration、定向设置;没有通用 yoyo 属性 |
State | 描述界面的命名配置 | 用 PropertyChanges 等声明目标值,由 Item 的 state 选择 |
Transition | 在状态之间执行动画 | from、to 和子动画;状态属性不是 State.active |
三种启动方式要分清:独立动画通常显式 start()/restart() 或设 running;NumberAnimation on x 这种属性值来源默认会运行;Behavior on x 则在 x 的目标值改变时响应。loops: Animation.Infinite 表示无限循环,反向往返可用两个方向的动画组成序列,不能凭空增加 yoyo。参见 Qt Quick 动画与过渡。
AnimationsDemo.qml 的 startAll() 同时启动四组独立示范:序列内并行变化、平滑追踪、弹簧追踪和三角形路径;另一色块通过状态切换宽度和透明度。
import QtQuick
Item { id: root width: 440; height: 350 property real goal: 20 function startAll() { movement.restart() goal = 200 travel.restart() stateBox.state = "expanded" } Rectangle { id: box; x: 20; y: 20; width: 32; height: 32; color: "red" } SequentialAnimation { id: movement ParallelAnimation { NumberAnimation { target: box; property: "x"; to: 200; duration: 120 } ColorAnimation { target: box; property: "color"; to: "blue"; duration: 120 } RotationAnimation { target: box; property: "rotation"; to: 180; duration: 120; direction: RotationAnimation.Shortest } } PauseAnimation { duration: 30 } PropertyAnimation { target: box; property: "opacity"; to: 0.5; duration: 80 } } Rectangle { id: smooth x: root.goal; y: 85; width: 32; height: 32; color: "orange" Behavior on x { SmoothedAnimation { velocity: 800 } } } Rectangle { id: springBox x: root.goal; y: 145; width: 32; height: 32; color: "green" Behavior on x { SpringAnimation { spring: 3; damping: 0.3; epsilon: 0.05 } } } Rectangle { id: runner; x: 20; y: 220; width: 16; height: 16; radius: 8; color: "purple" } PathAnimation { id: travel target: runner; duration: 360 path: Path { startX: 20; startY: 220 PathLine { x: 180; y: 220 } PathLine { x: 100; y: 300 } PathLine { x: 20; y: 220 } } } Rectangle { id: stateBox x: 260; y: 220; width: 60; height: 50; color: "steelblue" states: State { name: "expanded" PropertyChanges { target: stateBox; width: 140; opacity: 0.6 } } transitions: Transition { from: ""; to: "expanded" NumberAnimation { properties: "width,opacity"; duration: 180 } } } MouseArea { anchors.fill: parent; onClicked: root.startAll() }}频繁变化的目标适合 SmoothedAnimation/SpringAnimation;需要确定时长和终点的 UI 转场适合 NumberAnimation。动画更新跟随事件循环与帧调度,不保证每毫秒都产生一次可见帧。
还可以在运行时创建动画,但固定动画优先声明在 QML 中,避免每次点击都解析字符串。下面 DynamicAnimation.qml 保留 Qt.createQmlObject() 的动态创建方式,显式指定 target,并在停止后销毁临时动画。重复点击会先停止旧动画;动画不会永远积累在父对象下。动态字符串应来自受控代码,不应直接拼接不可信输入。
import QtQuick
Item { id: root width: 300; height: 180 property var animation: null function animateWidth() { if (animation !== null) animation.stop() const created = Qt.createQmlObject( 'import QtQuick; NumberAnimation { property: "width"; to: 200; duration: 120 }', box, "width-animation") created.target = box animation = created created.stopped.connect(function() { if (root.animation === created) root.animation = null created.destroy() }) created.start() } Rectangle { id: box width: 100; height: 100; color: "lightblue" MouseArea { anchors.fill: parent; onClicked: root.animateWidth() } }}路径、形状、渐变与视觉效果
跳转到“路径、形状、渐变与视觉效果”| 名称 | 模块与作用 | 正确关系 |
|---|---|---|
Shape | QtQuick.Shapes,绘制一个或多个形状路径 | 继承 Item,能够使用 anchors |
ShapePath | QtQuick.Shapes,路径及描边/填充样式 | 继承 Path,直接放路径段;没有再套一层 path: Path {} 的属性 |
Path | QtQuick,声明路径数据 | 供 PathAnimation、PathView、ShapePath 等使用;自身不直接绘制 |
PathLine | QtQuick,直线段 | 从当前位置连到 x,y |
PathQuad | QtQuick,二次贝塞尔曲线 | 一个控制点 controlX,controlY 和终点 |
PathCubic | QtQuick,三次贝塞尔曲线 | 两个控制点及终点 |
PathCurve | QtQuick,经过一组点的 Catmull–Rom 曲线 | 不是 PathCubic 的别名;连续段形成平滑曲线 |
PathArc | QtQuick,椭圆弧段 | radiusX,radiusY、方向和终点;半径必须与目标弧段几何相容 |
PathSvg | QtQuick,解析 SVG path 数据字符串 | path 是字符串;跨代码行时用字符串拼接,不能在普通引号内直接换行 |
LinearGradient、RadialGradient | QtQuick.Shapes,ShapePath 的填充渐变 | 赋给 fillGradient;与 Qt5Compat.GraphicalEffects 中同名类型不是同一个 API |
ShaderEffect | QtQuick,用着色器绘制效果 | Qt 6 的着色器通常用 Shader Tools 预编译为 .qsb,不是直接套用 Qt 5 内联 GLSL 字符串 |
ShapesDemo.qml 把直线、二次/三次曲线、插值曲线、圆弧和 SVG 路径放在完整的 Shape 中。第一条路径显式回到起点形成封闭轮廓;圆弧两端相距 100,radiusX、radiusY 均为 50。ShapePath 的声明式路径段与 QPainterPath/Canvas 的 moveTo()、lineTo() 方法属于不同 API,不能相互照抄。
import QtQuickimport QtQuick.Shapes
Item { id: root width: 420; height: 300 Shape { id: shapes anchors.fill: parent ShapePath { id: outline strokeColor: "navy"; strokeWidth: 2 fillGradient: LinearGradient { x1: 20; y1: 20; x2: 180; y2: 130 GradientStop { position: 0; color: "lightblue" } GradientStop { position: 1; color: "white" } } startX: 20; startY: 20 PathLine { x: 180; y: 20 } PathQuad { x: 180; y: 100; controlX: 230; controlY: 60 } PathCubic { x: 20; y: 100; control1X: 150; control1Y: 160; control2X: 50; control2Y: 40 } PathLine { x: 20; y: 20 } } ShapePath { id: arc strokeColor: "crimson"; strokeWidth: 3; fillColor: "transparent" startX: 260; startY: 20 PathArc { x: 360; y: 20; radiusX: 50; radiusY: 50; direction: PathArc.Clockwise } } ShapePath { id: curve strokeColor: "green"; strokeWidth: 3; fillColor: "transparent" startX: 20; startY: 190 PathCurve { x: 70; y: 150 } PathCurve { x: 130; y: 230 } PathCurve { x: 200; y: 190 } } ShapePath { id: svg strokeColor: "purple"; strokeWidth: 2 fillGradient: RadialGradient { centerX: 300; centerY: 200; centerRadius: 65 focalX: 300; focalY: 200 GradientStop { position: 0; color: "white" } GradientStop { position: 1; color: "orchid" } } PathSvg { path: "M 250 170 L 350 170 " + "L 330 240 L 270 240 Z" } } }}Shape 并非在所有场景都比 Canvas 快;复杂度、路径更新频率、后端和缓存方式都会影响性能。几何对象成功加载,也不等于每种 GPU 后端都已验证画面。
模糊、颜色调整和阴影可考虑 QtQuick.Effects.MultiEffect,或在确需兼容旧效果时使用 Qt5Compat.GraphicalEffects。MultiEffect 的 source、blurEnabled、shadowEnabled 等按需开启;避免同时显示源与效果而意外叠画。阴影还涉及边界、裁剪和额外纹理尺寸,不能只改一个名称就保证效果相同。参见 MultiEffect 与 ShaderEffect。
音频、视频与摄像头
跳转到“音频、视频与摄像头”| 名称 | Qt 6 中的作用 | 必须区分的对象 |
|---|---|---|
MediaPlayer | 播放本地或网络媒体,控制播放、暂停、定位与状态 | 用 audioOutput 连接 AudioOutput,用 videoOutput 连接 VideoOutput |
VideoOutput | 显示视频帧 | Qt 6 应由 MediaPlayer 或 CaptureSession 向其输出,不使用 Qt 5 的 source: player 写法 |
Video | QtMultimedia 的便捷视频组件 | Qt 6.10 仍提供,组合了播放和显示功能,不应误判为已从 Qt 6 删除 |
SoundEffect | 适合短促反馈音的低延迟播放 | source、volume、loops;无限重复枚举为 SoundEffect.Infinite |
Camera | QtMultimedia 中的真实摄像设备控制 | 通过 CaptureSession 连接 VideoOutput;权限、设备和后端支持另行处理 |
Audio | Qt 5 QtMultimedia 的旧接口 | Qt 6 使用 MediaPlayer + AudioOutput;音量设置在 AudioOutput 上 |
MediaDemo.qml 默认不访问文件、不启用摄像头。给 mediaUrl、clipUrl、effectUrl 赋可访问的 URL 后,再调用相应播放方法;用 camera.start() 启动采集前应完成平台摄像权限处理。示例中的三块输出分别属于通用播放器、便捷 Video 和摄像头采集。
import QtQuickimport QtMultimedia
Item { id: root width: 660; height: 240 property url mediaUrl: "" property url clipUrl: "" property url effectUrl: "" function playMedia() { if (mediaUrl.toString().length > 0) player.play() } function pauseMedia() { player.pause() } function playClip() { if (clipUrl.toString().length > 0) video.play() } function playEffect() { if (effectUrl.toString().length > 0) effect.play() } AudioOutput { id: audio; volume: 0.5 } MediaPlayer { id: player source: root.mediaUrl audioOutput: audio videoOutput: output } VideoOutput { id: output; x: 0; width: 210; height: 180; fillMode: VideoOutput.PreserveAspectFit } Video { id: video x: 220; width: 210; height: 180 source: root.clipUrl autoPlay: false volume: 0.5 } SoundEffect { id: effect; source: root.effectUrl; volume: 0.4; loops: SoundEffect.Infinite } Camera { id: camera; active: false } CaptureSession { id: capture; camera: camera; videoOutput: preview } VideoOutput { id: preview; x: 440; width: 210; height: 180 }}视频长宽比由 VideoOutput/Video 的填充模式处理;音量的 0~1 取值是接口刻度,不应理解为感知响度线性。文件格式和编解码器能力取决于 Qt Multimedia 后端及系统环境,不能仅凭 .mp3、.wav、.mp4 后缀保证播放。生产程序还应处理 errorOccurred、媒体状态和加载进度,短音效应等待资源就绪。MediaPlayer/Video 的播放状态用 playbackState 及其变化通知判断,不应使用拼造的 status == Video.Playing;stop() 停止播放,播放器的 position/setPosition()、Video 的 seek() 等定位接口应按所属类型和 seekable 状态使用。这里验证的是对象、属性及输出连接,未声称真实媒体、音频硬件或摄像头已运行。参见 Qt 6 Multimedia 迁移 和 Video。
图表、地图和 3D 模块
跳转到“图表、地图和 3D 模块”图表:先确认 Qt Charts 与 Qt Graphs
跳转到“图表:先确认 Qt Charts 与 Qt Graphs”ChartView 属于 QtCharts;该模块的 LineSeries、PieSeries、BarSeries、ScatterSeries 分别用于折线、饼图、分组条形图和散点图。它们不是仅导入 QtQuick 后就能创建的通用类型。Qt Graphs 也有若干同名系列,但使用 GraphsView 等另一套接口,不能只替换 import 就保证兼容。
Qt Charts 自 Qt 6.10 起弃用,新项目应评估 Qt Graphs;维护旧项目时仍需正确识别 ChartView 的写法。下面的 ChartsDemo.qml 同时展示折线和带五个数据点的条形图,必须安装 Qt Charts,并使用以 QApplication 创建的宿主程序,而不只是 QGuiApplication。这与 Qt Charts 对 Graphics View 的依赖有关。Qt Charts 概览
import QtQuickimport QtCharts
Item { id: root width: 520; height: 640 ChartView { id: chart width: parent.width; height: 320 title: "温度趋势" antialiasing: true ValueAxis { id: axisX; min: 0; max: 3; tickCount: 4 } ValueAxis { id: axisY; min: 0; max: 30 } LineSeries { id: line name: "温度" axisX: axisX; axisY: axisY XYPoint { x: 0; y: 12 } XYPoint { x: 1; y: 18 } XYPoint { x: 2; y: 16 } XYPoint { x: 3; y: 25 } } } ChartView { id: barChart y: 320; width: parent.width; height: 320 title: "分类数据" BarSeries { id: bars axisX: BarCategoryAxis { categories: ["A", "B", "C", "D", "E"] } BarSet { id: set; label: "Set 1"; values: [1, 2, 3, 4, 5] } } }}地图:提供者与网络是额外依赖
跳转到“地图:提供者与网络是额外依赖”Map 来自 QtLocation,QtPositioning 提供坐标。地图插件决定瓦片来源、支持能力和所需参数;网络、提供者服务及使用条件会影响显示。下面的 MapDemo.qml 将请求地图服务,适用于具备对应插件和网络的运行环境,不能把一个空白 Map 对象当作地图可用性证明。
import QtQuickimport QtLocationimport QtPositioning
Item { id: root width: 520; height: 320 Plugin { id: mapProvider; name: "osm" } Map { id: map anchors.fill: parent plugin: mapProvider center: QtPositioning.coordinate(39.9, 116.4) zoomLevel: 10 }}交互缩放、拖动需要另配输入处理器或使用包含所需交互的组件。不能因为设置了 center 和 zoomLevel 就假定全部鼠标操作已自动提供。Qt Location Map
3D:场景相机与硬件摄像头不同
跳转到“3D:场景相机与硬件摄像头不同”Qt Quick 3D 通过 View3D 将场景放入 Qt Quick。QtQuick3D.Camera 与 Light 是不可直接创建的基类;实际创建 PerspectiveCamera/OrthographicCamera 和 DirectionalLight/PointLight/SpotLight。这与 QtMultimedia.Camera 采集真实设备视频完全不同,Qt3D.Render 也有自己的一组同名 API。
SceneDemo.qml 显示相机、方向光和内置立方体资源。Qt Quick 3D 的相机朝局部负 Z 方向看,下面把相机放在 z=400。需要朝向目标时可调用相机的 lookAt() 方法;不要写成不存在的 lookAt: Qt.vector3d(...) 属性赋值。
import QtQuickimport QtQuick3D
Item { id: root width: 480; height: 320 View3D { id: scene anchors.fill: parent environment: SceneEnvironment { backgroundMode: SceneEnvironment.Color clearColor: "#e8edf2" } camera: camera PerspectiveCamera { id: camera; z: 400 } DirectionalLight { id: light; eulerRotation.x: -30; brightness: 1.2 } Model { id: cube source: "#Cube" eulerRotation: Qt.vector3d(20, 35, 0) materials: PrincipledMaterial { baseColor: "steelblue" } } }}Qt Quick 3D 需要合适的图形后端;仅创建场景对象不证明 GPU 渲染成功。Qt Quick 3D QML 类型
容易混淆的旧名称和非通用名称
跳转到“容易混淆的旧名称和非通用名称”以下名称保留为查找入口,但不能假定它们是 Qt 6.10 常用模块中同名且可直接创建的 QML 类型。项目可以定义自己的同名组件,因此诊断“不是一个类型”时还应检查项目 QML 模块和导入路径。
| 名称 | 应如何理解或替换 |
|---|---|
Circle、Ellipse | 圆可由正方形 Rectangle 设置 radius 绘制;椭圆用 Shape 的弧段、SVG 或 Canvas,不能把不等宽高的圆角矩形一概称作椭圆 |
Line、Polygon、Arc | 用 ShapePath + PathLine/PathArc 等描述几何;QtQuick 没有这里假定的通用同名可视类型 |
Opacity | 普通 Qt Quick Item 使用小写 opacity 属性,透明度动画用 NumberAnimation |
GestureArea | 不应视作 QtQuick 的通用手势类型;按需求选择 TapHandler、DragHandler、PinchHandler 或 MultiPointTouchArea |
DragArea | 拖动可用 DragHandler 或 MouseArea.drag;跨区域拖放用 Drag 附加属性 + DropArea |
FocusArea、FocusHandler | 不是此处假定的标准焦点组件;使用 focus、activeFocus、FocusScope、Keys 和 focus/activeFocus 属性通知 |
FocusIndicator | 焦点外观通常是控件样式或自定义组件,用 activeFocus 绑定边框/可见性;不要依赖未导入的同名类型 |
ImageButton | 使用 Button.icon 或定制 Button.contentItem,也可自定义组件;不能只导入 QtQuick 就假定存在 |
ImageView、ImageViewer | 图像基本显示用 Image;缩放、滚动、预览器由 Flickable、输入处理器等组合 |
DatePicker、TimePicker、DateTimeEdit | Qt Quick Controls 没有这里假定的三个通用控件;可用 CalendarModel/MonthGrid/DayOfWeekRow 等日期部件、Tumbler 或 SpinBox/TextField 组合,注意校验和时区;Qt Widgets 的 QDateTimeEdit 是另一套 API |
GridModel、SimpleListModel | 普通列表/网格通常用 ListModel、JavaScript 数组或 C++ QAbstractItemModel;GridView 是视图,不要求一个名叫 GridModel 的类型 |
NavigationDrawer | Qt Quick Controls 的抽屉类型名是 Drawer,导航内容由应用组合 |
TabView | 常见于旧 Qt Quick Controls 1;Qt 6 Controls 用 TabBar + StackLayout 等组合 |
PopupMenu | 使用 Qt Quick Controls Menu;Popup 是更通用的浮层基础类型 |
DropShadowEffect、GraphicsEffect | 不要与 QWidget 的 QGraphicsEffect 类体系混用;Qt Quick 可用 MultiEffect,或兼容模块中的 DropShadow 等明确类型 |
ImageFilter | 不是此处假定的统一图像滤镜类型;按需求使用 MultiEffect、ShaderEffect、兼容图形效果或 C++ 图像处理 |
FileSelector | Qt 的资源变体选择关联 C++ QQmlFileSelector/QFileSelector,不能把概念直接写成任意 FileSelector QML 对象 |
ImageProvider | 动态图像通常由 C++ QQuickImageProvider 注册到引擎,然后在 Image.source 使用 image:// URL;不是普通 QML ImageProvider 容器 |
Context3D | 不能将其视为 QtQuick 的通用 3D 场景入口;Qt Quick 3D 使用 View3D,旧 Canvas3D 等接口要按版本另查 |
Lens | 不应套用为 Qt Quick 3D 的通用实例类型;投影参数放在相应 PerspectiveCamera/OrthographicCamera 上,Qt 3D 的 CameraLens 属于另一模块 |
HeatMap | 不是这里列出的 QtCharts 通用图表类型;可按数据用着色矩形网格、图像或具体图表扩展实现,先确认库的真实类型与版本 |
SoundPlayer | QtMultimedia 使用 MediaPlayer 或 SoundEffect;若项目提供同名封装,按该项目接口使用 |
查类型时先查 Qt 6.10 QML 类型总表,再进入具体模块文档;对于旧博客示例尤其要检查模块和版本。实例成功加载、属性行为正确、实际外部资源可用和最终画面正确是不同层次的验证。