Qml基础语法
# 02.Qml基础语法
- 2.1 QML语法基础
- 2.1.1 QML是什么
- 2.1.2 QML文件结构
- 2.2 import导入
- 2.2.1 基本语法
- 2.2.2 导入标准QML模块
- 2.2.3 导入自定义QML文件
- 2.2.4 使用别名导入模块
- 2.2.5 导入JavaScript文件
- 2.2.6 import综合示例
- 2.3 QML类型系统
- 2.3.1 基本类型
- 2.3.2 JavaScript类型
- 2.3.3 对象类型
- 2.4 对象特性
- 2.4.1 id特性
- 2.4.2 属性特性
- 2.4.3 信号和信号处理器特性
- 2.4.4 方法特性
- 2.4.5 附加属性【待补充】
- 2.4.6 枚举【待补充】
- 2.4.7 状态控制
- 2.5 集成JavaScript
- 2.5.1 在QML中用JavaScript
- 2.5.2 导入JavaScript文件
- 2.5.3 JavaScript与QML交互
- 2.5.3.1 访问QML对象属性
- 2.5.3.2 调用QML对象方法
- 2.5.3.3 信号与JavaScript交互
- 2.5.4 JavaScript模块化
- 2.5.5 JavaScript作用域
- 2.5.6 JavaScript性能优化
- 2.6 Qml常见使用
- 2.6.1 加载图片
- 2.6.2 加载字体
- 2.6.3 加载音频
- 2.6.4 加载QML组件
- 2.6.5 加载网络资源
- 2.6.6 加载本地文件
- 2.6.7 QML国际化
- 2.6.8 QML编码约定
- 2.8 应用Window
- 2.8.1 看main.qml文件
- 2.8.2 Window主要属性
- 2.8.3 自定义窗口样式
- 2.8.4 Window框架设计
- 2.8.5 Window窗口绘制
- 2.8.6 多窗口Window
# 2.1 QML语法基础
# 2.1.1 QML是什么
QML(Qt Meta-Object Language)是 Qt Quick 的核心语言,用于描述用户界面和交互逻辑。
它是一种声明式语言,语法简洁直观,类似于 JSON 和 JavaScript。
# 2.1.2 QML文件结构
一个 QML 文件通常由以下部分组成:
- 导入语句:导入所需的模块。
- 根元素:定义 UI 的根对象。
- 子元素:定义 UI 的子组件。
示例:
import QtQuick 2.15 // 导入 QtQuick 模块
Rectangle { // 根元素
width: 200
height: 100
color: "lightblue"
Text { // 子元素
text: "Hello, QML!"
anchors.centerIn: parent
}
}
2
3
4
5
6
7
8
9
10
11
12
# 2.2 import导入
# 2.2.1 基本语法
QML 的 import 语句通常遵循以下格式:
import <模块名> [版本号] [as <别名>]
- 模块名:要导入的模块或库的名称。
- 版本号(可选):指定模块的版本号,确保使用正确的版本。
- 别名(可选):为导入的模块指定一个别名,避免命名冲突或简化使用。
# 2.2.2 导入标准QML模块
QML 提供了一些标准模块,例如 QtQuick 和 QtQuick.Controls。这些模块通常包含常用的 QML 类型和组件。
import QtQuick 2.15
import QtQuick.Controls 2.15
2
QtQuick:包含基本的 QML 类型,例如Rectangle、Text、Image等。QtQuick.Controls:包含 UI 控件,例如Button、Slider、TextField等。
如果未指定版本号,QML会使用默认版本。 指定版本号可以避免因版本差异导致的兼容性问题。
# 2.2.3 导入自定义QML文件
可以导入自定义的 QML 文件或模块,通常用于组织项目中的代码。
假设有一个名为 MyComponent.qml 的文件,可以这样导入:
import "components"
"components":表示文件所在的目录路径。导入后可以直接使用 MyComponent 类型。
# 2.2.4 使用别名导入模块
通过 as 关键字为模块指定别名,避免命名冲突或简化使用。
import QtQuick.Controls 2.15 as Controls
Controls.Button {
text: "Click Me"
}
2
3
4
5
使用别名后,必须通过 Controls 前缀来访问模块中的类型。
# 2.2.5 导入JavaScript文件
QML 支持导入 JavaScript 文件,以便在 QML 中使用 JavaScript 函数。
假设有一个名为 utils.js 的文件,可以这样导入:
import "utils.js" as Utils
Rectangle {
width: Utils.calculateWidth()
}
2
3
4
5
Utils 是导入的 JavaScript 文件的别名。可以通过 Utils 调用 JavaScript 文件中的函数。
# 2.2.6 import综合示例
以下是一个综合示例,展示了多种导入方式:
import QtQuick 2.15
import QtQuick.Controls 2.15 as Controls
import "utils.js" as Utils
import "components"
Rectangle {
width: Utils.calculateWidth()
height: 200
Controls.Button {
text: "Click Me"
}
MyComponent {
id: customComponent
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 2.3 QML类型系统
# 2.3.1 基本类型
QML 提供了一些基本的内置类型,用于表示常见的数据类型和 UI 组件。
基本数据类型:
int:整数类型。double:双精度浮点数。bool:布尔类型。string:字符串类型。color:颜色类型。var:动态类型,可以存储任意值。list:列表类型,用于存储多个值。
几何类型:
point:表示一个点,包含x和y属性。size:表示一个尺寸,包含width和height属性。rect:表示一个矩形,包含x、y、width和height属性。
Rectangle {
width: 200
height: 100
color: "red"
property int count: 10
property string message: "Hello, QML!"
property color backgroundColor: Qt.rgba(0.5, 0.5, 0.5, 1.0)
}
2
3
4
5
6
7
8
9
# 2.3.2 JavaScript类型
# 2.3.3 对象类型
QML 对象通常由类型名称、属性、信号和方法组成。一个对象可以嵌套其他对象,形成层次结构。
示例:基本对象结构
Rectangle {
width: 200
height: 100
color: "blue"
Text {
text: "Hello, QML!"
anchors.centerIn: parent
}
}
2
3
4
5
6
7
8
9
10
# 2.4 对象特性
对象特性包括:属性、信号、方法、绑定、状态等。
# 2.4.1 id特性
在 QML 中,每个对象都可以通过 id 属性来标识自己。id 必须是唯一的,且在同一作用域内不能重复。例如:
Rectangle {
id: myRect
width: 100
height: 100
color: "red"
}
2
3
4
5
6
id 的作用域:id 的作用域是当前 QML 文件。也就是说,id 只能在定义它的 QML 文件中使用,不能在其他文件中直接访问。
通过 id,可以方便地引用和操作对象,实现属性绑定、信号连接、动态对象创建等功能。合理使用 id 可以大大提高 QML 代码的可读性和灵活性。
# 2.4.2 属性特性
属性是对象的核心特性,用于存储数据或配置对象的行为。每个 QML 类型都有一组内置属性,开发者也可以定义自定义属性。
内置属性
width:对象的宽度。height:对象的高度。color:对象的颜色。visible:对象是否可见。
自定义属性:可以通过 property 关键字定义自定义属性。
Rectangle {
property int customWidth: 300 //自定义属性
width: customWidth
height: 200
color: "green"
}
2
3
4
5
6
# 2.4.3 信号和信号处理器特性
信号是 QML 对象的事件机制,用于通知其他对象某些事件发生。QML 类型通常有内置信号,例如 onClicked、onCompleted 等,开发者也可以定义自定义信号。
内置信号
onClicked:鼠标点击事件。onCompleted:对象初始化完成时触发。
自定义信号,可以通过 signal 关键字定义信号。
Rectangle {
//自定义信号
signal customSignal(string message)
MouseArea {
anchors.fill: parent
onClicked: {
customSignal("Mouse clicked!")
}
}
Connections {
target: parent
onCustomSignal: console.log(message)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2.4.4 方法特性
方法是对象的行为特性,用于定义可执行的操作。可以通过 function 关键字定义方法。
示例:定义和调用方法
Rectangle {
width: 200
height: 100
color: "blue"
function changeColor(newColor) {
color = newColor
}
MouseArea {
anchors.fill: parent
onClicked: parent.changeColor("red")
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2.4.5 附加属性
# 2.4.6 枚举
# 2.4.7 状态控制
状态是 QML 对象的特性之一,用于定义对象的不同配置。通过状态,可以轻松切换对象的属性值或行为。
示例:定义和切换状态
Rectangle {
width: 200
height: 100
color: "blue"
states: [
State {
name: "redState"
PropertyChanges { target: parent; color: "red" }
},
State {
name: "greenState"
PropertyChanges { target: parent; color: "green" }
}
]
MouseArea {
anchors.fill: parent
onClicked: {
parent.state = parent.state === "redState" ? "greenState" : "redState"
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 2.5 集成JavaScript
QML 是一种声明式语言,用于构建用户界面,而 JavaScript 是一种强大的脚本语言,用于处理逻辑和动态行为。
在 QML 中,JavaScript 可以无缝集成,用于增强 QML 的功能和灵活性。通过集成 JavaScript,开发者可以实现复杂的逻辑、动态数据处理以及与 QML 对象的交互。
# 2.5.1 在QML中用JavaScript
1,QML 支持直接在属性、信号处理器和方法中编写 JavaScript 代码。示例:内联 JavaScript
Rectangle {
width: 200
height: 100
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Rectangle clicked!")
parent.color = parent.color === "blue" ? "red" : "blue"
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
在这个示例中,onClicked 信号处理器中直接使用了 JavaScript 代码。
2.定义JavaScript函数
可以在 QML 对象中定义 JavaScript 函数,用于封装逻辑。示例:定义和调用 JavaScript 函数
简单的 QML 应用程序,包含一个按钮和一个文本框。当按钮被点击时,会调用一个 JavaScript 函数来改变文本框的内容。
Rectangle {
width: 200
height: 100
color: "lightblue"
Text {
anchors.top: parent.Center
text: "Click the button"
}
Text {
id: textItem
anchors.centerIn: parent
}
Button {
anchors.bottom: parent.bottom
text: "Click Me"
onClicked: {
// JavaScript function to change the text
function changeText() {
textItem.text = "Button Clicked!" ;
}
// Call the JavaScript function
changeText();
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
3.使用JavaScript表达式
QML 属性可以绑定到 JavaScript 表达式,动态更新属性值。示例:绑定到 JavaScript 表达式
Rectangle {
width: 100
height: width * 2 // 使用 JavaScript 表达式
color: "green"
MouseArea {
anchors.fill: parent
onClicked: {
parent.width += 10 // 动态更新宽度
}
}
}
2
3
4
5
6
7
8
9
10
11
12
在这个示例中,height 属性绑定到 width * 2,并随着 width 的变化自动更新。
# 2.5.2 导入JavaScript文件
QML 支持加载外部 JavaScript 文件,便于组织代码和复用逻辑。
1.外部JavaScript文件,创建一个 .js 文件,例如 utils.js:
function calculateArea(width, height) {
return width * height;
}
function toggleColor(currentColor) {
return currentColor === "blue" ? "red" : "blue";
}
2
3
4
5
6
7
2.在qml中导入JavaScript文件。使用 import 关键字导入外部 JavaScript 文件。
import "utils.js" as Utils
Rectangle {
MouseArea {
anchors.fill: parent
onClicked: {
parent.color = Utils.toggleColor(parent.color)
console.log("Area:", Utils.calculateArea(parent.width, parent.height))
}
}
}
2
3
4
5
6
7
8
9
10
11
# 2.5.3 JavaScript与QML交互
JavaScript 可以直接访问 QML 对象的属性和方法,也可以通过信号与 QML 对象交互。
# 2.5.3.1 访问QML对象属性
JavaScript 可以读取和修改 QML 对象的属性。示例:访问属性
Rectangle {
width: 200
height: 100
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Width:", parent.width)
parent.color = "red"
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 2.5.3.2 调用QML对象方法
JavaScript 可以调用 QML 对象定义的方法。示例:调用方法
Rectangle {
width: 200
height: 100
color: "blue"
function changeColor(newColor) {
color = newColor
}
MouseArea {
anchors.fill: parent
onClicked: parent.changeColor("red")
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 2.5.3.3 信号与JavaScript交互
QML 信号可以触发 JavaScript 函数。示例:信号与 JavaScript 交互
Rectangle {
width: 200
height: 100
color: "blue"
signal colorChanged(string newColor)
MouseArea {
anchors.fill: parent
onClicked: {
parent.color = "red"
//调用信号槽
parent.colorChanged(parent.color)
}
}
Connections {
target: parent
onColorChanged: console.log("Color changed to:", newColor)
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 2.5.4 JavaScript模块化
通过外部 JavaScript 文件,可以实现模块化开发,将逻辑分离到不同的文件中。
math.js
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
2
3
4
5
6
7
color.js
function toggleColor(currentColor) {
return currentColor === "blue" ? "red" : "blue";
}
2
3
main.qml
import "math.js" as Math
import "color.js" as Color
Rectangle {
width: 200
height: 100
color: "blue"
MouseArea {
anchors.fill: parent
onClicked: {
parent.color = Color.toggleColor(parent.color)
console.log("Sum:", Math.add(5, 3))
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 2.5.5 JavaScript作用域
在 QML 中,JavaScript 的作用域分为以下几种:
- 局部作用域:函数内部定义的变量只能在函数内访问。
- 全局作用域:外部 JavaScript 文件中的函数和变量可以被 QML 全局访问。
- QML 对象作用域:QML 对象的属性和方法可以被其子对象访问。
Rectangle {
width: 200
height: 100
color: "blue"
function localFunction() {
var localVar = "I am local"
console.log(localVar)
}
MouseArea {
anchors.fill: parent
onClicked: {
localFunction()
// console.log(localVar) // 错误:localVar 不在作用域内
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 2.5.6 JavaScript性能优化
在 QML 中使用 JavaScript 时,需要注意性能问题:
- 避免复杂的计算逻辑放在属性绑定中。
- 使用
Timer或信号处理器来处理耗时操作。 - 尽量将复杂逻辑移到外部 JavaScript 文件中。
# 2.6 Qml常见使用
# 2.6.1 加载图片
使用 Image 元素加载图片。图片可以来自文件系统或应用程序资源。
从文件系统加载
Image {
source: "file:///path/to/image.png" // 绝对路径
width: 100
height: 100
}
2
3
4
5
如果图片被打包到应用程序资源中(例如 Qt 的资源系统),可以使用相对路径:
Image {
source: "images/logo.png" // 相对路径,资源文件
width: 100
height: 100
}
2
3
4
5
# 2.6.2 加载字体
使用 FontLoader 加载自定义字体。加载字体文件
import QtQuick 2.15
FontLoader {
id: customFont
source: "fonts/CustomFont.ttf" // 字体文件路径
}
Text {
text: "Hello, QML!"
font.family: customFont.name
font.pixelSize: 24
}
2
3
4
5
6
7
8
9
10
11
12
可以直接使用系统字体:
Text {
text: "Hello, QML!"
font.family: "Arial"
font.pixelSize: 24
}
2
3
4
5
# 2.6.3 加载音频
使用 MediaPlayer 和 Audio 元素加载和播放音频。
使用 MediaPlayer
import QtQuick 2.15
import QtMultimedia 5.15
MediaPlayer {
id: player
source: "audio/sound.mp3" // 音频文件路径
}
Button {
text: "Play"
onClicked: player.play()
}
2
3
4
5
6
7
8
9
10
11
12
使用 Audio
import QtQuick 2.15
import QtMultimedia 5.15
Audio {
id: audio
source: "audio/sound.mp3"
}
Button {
text: "Play"
onClicked: audio.play()
}
2
3
4
5
6
7
8
9
10
11
12
# 2.6.4 加载QML组件
使用 Loader 动态加载其他 QML 文件或组件。
加载 QML 文件
Loader {
source: "MyComponent.qml" // QML 文件路径
}
2
3
动态切换组件,可以通过绑定属性动态切换加载的组件:
Loader {
source: componentPath
}
property string componentPath: "ComponentA.qml"
Button {
text: "Switch Component"
onClicked: componentPath = "ComponentB.qml"
}
2
3
4
5
6
7
8
9
10
# 2.6.5 加载网络资源
QML 支持从网络加载资源,例如图片、音频、视频等。
加载网络图片
Image {
source: "https://example.com/image.png" // 网络图片 URL
width: 100
height: 100
}
2
3
4
5
加载网络视频
MediaPlayer {
id: player
source: "https://example.com/video.mp4" // 网络视频 URL
}
2
3
4
# 2.6.6 加载本地文件
使用 FileDialog 或 Qt.resolvedUrl 加载本地文件。
使用Qt.resolvedUrl
Image {
source: Qt.resolvedUrl("images/logo.png") // 解析相对路径
width: 100
height: 100
}
2
3
4
5
# 2.6.7 QML的国际化
# 2.6.8 QML的编码约定
# 2.8 应用Window
# 2.8.1 看main.qml文件
以下是一个简单的 ApplicationWindow 示例:
- 窗口管理:ApplicationWindow 是一个顶层窗口,负责管理窗口的显示、隐藏、关闭等行为。
- 布局与内容:通过 contentItem 属性,开发者可以定义窗口的主要内容区域。
- 集成控件:支持菜单栏 (menuBar)、工具栏 (toolBar)、状态栏 (footer) 等常见 UI 元素。
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
width: 800
height: 600
title: "My Application"
// 菜单栏
menuBar: MenuBar {
Menu {
title: "File"
MenuItem { text: "Open" }
MenuItem { text: "Save" }
MenuItem { text: "Exit" }
}
}
// 工具栏
toolBar: ToolBar {
Row {
ToolButton { text: "New" }
ToolButton { text: "Open" }
ToolButton { text: "Save" }
}
}
// 状态栏
statusBar: StatusBar {
Label { text: "Ready" }
}
// 主要内容
Label {
text: "Hello, QML!"
anchors.centerIn: parent
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 2.8.2 Window主要属性
通过上面案例,可以看到Window中常见的属性如下所示:
| 属性名 | 描述 |
|---|---|
visible | 控制窗口是否可见,默认为 false。 |
width | 窗口的宽度。 |
height | 窗口的高度。 |
title | 窗口的标题。 |
menuBar | 设置窗口的菜单栏。 |
toolBar | 设置窗口的工具栏。 |
statusBar | 设置窗口的状态栏。 |
contentItem | 窗口的主要内容区域,默认是一个 Item,可以放置其他组件。 |
常用方法
| 方法名 | 描述 |
|---|---|
show() | 显示窗口。 |
hide() | 隐藏窗口。 |
close() | 关闭窗口。 |
minimize() | 最小化窗口。 |
maximize() | 最大化窗口。 |
restore() | 恢复窗口到正常大小。 |
信号
| 信号名 | 描述 |
|---|---|
closing | 当窗口即将关闭时触发。 |
visibleChanged | 当窗口的可见性发生变化时触发。 |
# 2.8.3 自定义窗口样式
可以通过 style 属性自定义窗口的外观:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Controls.Material 2.15
ApplicationWindow {
visible: true
width: 800
height: 600
title: "Custom Style"
Material.theme: Material.Dark
Material.accent: Material.Purple
Label {
text: "Styled Application"
anchors.centerIn: parent
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 2.8.4 Window框架设计
- 提供标准窗口结构:包含标题栏、菜单栏、工具栏和状态栏等标准 UI 元素。支持窗口的最小化、最大化和关闭操作。
- 管理布局:提供
contentItem属性,用于放置应用程序的主要内容。支持嵌套其他布局组件(如RowLayout、ColumnLayout、GridLayout)。 - 集成菜单和工具栏:支持通过
menuBar和toolBar属性添加菜单和工具栏。 - 状态管理: 提供
statusBar属性,用于显示状态信息。 - 跨平台支持:在不同操作系统(如 Windows、macOS、Linux)上自动适配本地窗口样式。
# 2.8.5 Window窗口绘制
QML 窗口的绘制基于 Qt Quick 的渲染引擎,其核心流程如下:
(1) 场景图(Scene Graph)
QML 使用场景图(Scene Graph)来管理 UI 元素的渲染。场景图是一个树形结构,每个节点代表一个可视元素(如矩形、文本、图像等)。ApplicationWindow 的内容区域(contentItem)是场景图的根节点之一。
(2) 渲染循环
Qt Quick 使用渲染循环(Render Loop)来更新和绘制场景图。渲染循环会根据需要(如属性变化、动画触发)自动调度重绘。渲染循环的具体实现依赖于底层的图形 API(如 OpenGL、Vulkan、DirectX 等)。
(3) 窗口系统集成
ApplicationWindow 通过 Qt 的窗口系统模块(如 QWindow)与操作系统的窗口管理器交互。窗口的创建、显示、隐藏、关闭等操作由底层的 QWindow 处理。
(4) 绘制流程
- 布局计算:计算每个 UI 元素的位置和大小。
- 场景图更新:根据属性变化更新场景图节点。
- 渲染:将场景图渲染到窗口的帧缓冲区。
- 显示:将帧缓冲区的内容显示到屏幕上。
# 2.8.6 多窗口Window
从技术上讲,可以创建多个ApplicationWindow实例。每个ApplicationWindow都是一个独立的顶级窗口。但是,需要注意以下几个问题:
- 应用程序的生命周期管理:通常,一个QML应用程序有一个主窗口(ApplicationWindow),当主窗口关闭时,应用程序退出。如果有多个顶级窗口,我们需要决定哪个窗口的关闭会导致应用程序退出,或者我们可以设置应用程序在没有窗口时退出(通过设置Qt.quitOnLastWindowClosed属性)。
- 内存管理:每个ApplicationWindow都是一个独立的窗口,会占用系统资源。我们需要确保在不再需要窗口时销毁它们,以避免内存泄漏。
- 窗口间通信:如果多个窗口需要共享数据或相互通信,我们需要设计一个机制来实现这一点,例如使用信号和槽、单例对象或共享上下文属性。
- 用户体验:多个独立窗口可能会让用户感到混乱,尤其是当它们没有明确的关系时。我们需要确保多窗口的设计符合用户预期,并提供清晰的窗口管理(例如,通过任务栏图标或应用程序内的窗口管理菜单)。
- 全局设置:如果应用程序有全局设置(如主题、语言等),我们需要确保这些设置在所有窗口之间保持一致,并且一个窗口中的更改能够反映到其他窗口。
- 事件循环:每个窗口都有自己的事件循环,但整个应用程序只有一个主事件循环。多个窗口不会影响事件循环,但需要注意模态对话框可能会阻塞其他窗口。
- 资源竞争:如果多个窗口访问共享资源(如文件、网络连接等),需要适当的同步机制,以防止竞争条件。