基本语法
QML是类似于HTML的标记型语言,有与HTML类似的树形节点结构,同时又包含类似JS的函数语法。一个基础的QML文档类似这个样子
import QtQuick 2.7
import QtQuick.Controls 2.0
import QtQuick.Layouts 1.0
ApplicationWindow {
objectName: "root"
visible: true
width: 1280
height: 720
maximumHeight: this.height
minimumHeight: this.height
maximumWidth: this.width
minimumWidth: this.width
title: qsTr("Security Check")
font.family: "Source Han Sans CN Normal"
Image{
x: 0
y: 0
property int _test: 8
property var list: []
}
}
其中 ApplicationWindow
就是整个界面窗口的根节点,可以通过在C++文件中使用
QObject* rootObject;
rootObject = engine.rootObjects().first();
得到指向根节点的指针,便于信号槽的链接和函数的调用(后文)
声明一个元素
在QML中,一个元素类似这样
Image{
property a: x
property b: y
}
每一种元素有预定义的属性,具体每个类型的元素有什么属性,可以参考All QML Types.
动态增加一个元素
在QML中,动态增加元素是基于一个模版(Component)的,所有需要动态添加的对象,都需要先存在一个模版元素中。
例如动态添加一个Image元素,可以这样构建:
Component{
id:create
Image {
id: tourist
source: sprites[Math.floor((Math.random()*3))]
width: 20
height: 20
property int x_axid: 1010
property int y_axid: 120
x:x_axid
y:y_axid
Behavior on x{
SmoothedAnimation{ duration: 500 }
}
Behavior on y{
SmoothedAnimation{ duration: 500 }
}
}
虽然整个QML文档中不允许同时有多个id相同的元素(类似HTML),但是给动态创建的内容id属性却是合法的。
当需要创建这个图形对象时,就可以调用 create.createObject(parentNode)
这个QML函数来在parentNode节点下创建一个Image元素,该函数返回创建的元素的指针。
需要重复创建多个相同的对象时,可以将创建的object存入一个列表中,便于管理,例如这样:
var obj = create.createObject(image,{});//从模版生成元素
list.push(obj)//将元素加入列表
绑定自定义属性
每个QML元素类型除了预定义的属性(例如x,y,opacity)以外,也可以添加一些我们自定义的属性。
给元素添加绑定属性类似这样:
Image{
id: img
x: 0
y: 0
property int maxNum: 15//绑定一个整形属性
property var list: []//绑定一个列表
}
访问一个节点的属性类似这样
img.maxNum = 15//赋值操作
绑定属性有很多应用,例如在对象的动画中,为了避免对x,y值重复操作,可以使用绑定属性来避免冲突(后文)
使用数据模型
在QML中,有一些预制好的数据模型,例如 ListModel.
使用 ListModel, 可以随意创建基于 ListItem 的列表,每个元素是一个 ListItem,元素内部又可以包含许多属性,类似C 中的结构体数组
一个典型的 ListModel 类似这样:
ListModel {
id:cpStateList
ListElement {
cpIndex:0
cpState: "空闲"
cpCurrentTime:0
cpNeedTime:0
}
ListElement {
cpIndex:1
cpState: "空闲"
cpCurrentTime:0
cpNeedTime:0
}
ListElement {
cpIndex:2
cpState: "空闲"
cpCurrentTime:0
cpNeedTime:0
}
ListElement {
cpIndex:3
cpState: "空闲"
cpCurrentTime:0
cpNeedTime:0
}
}
这样,当我们需要进行数据绑定时,可以将一个列表中的数据和 ListModel 中的 ListItem 一一绑定,当需要操作数据时,使用这样的方法:
var obj = cpStateList.get(Index)//得到在ListModel中编号为Index的元素
obj.xxx = ???//对xxx属性进行赋值
ListModel还有一个重要的应用,就是在一些预置的 View 中作为数据源,渲染到对应的模版上。
例如,一个 GridView 可以方便的构造一个网格视图,每一个网格都是一个基于delegate(代表性模版)的结构,结构中的数据会自动调用对应的 model 内部的数据。
一个典型的 GridView 类似这样:
GridView {
id: cpStateBoard
x: 56
y: 13
width: 894
height: 84
cellHeight: height
cellWidth: width/8
delegate: Item {
Column {
spacing: 30
Text{
x:0
y:10
text:cpState
font.family: "Source Han Sans CN Normal"
}
Text {
x: 0
y: 30
text: cpCurrentTime + '/' + cpNeedTime
opacity: cpState == "关闭" ? 0 : 1
font.family: "Source Han Sans CN Normal"
//anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
model: ListModel {
id:cpStateList
ListElement {
cpIndex:0
cpState: "空闲"
cpCurrentTime:0
cpNeedTime:0
}
ListElement {
cpIndex:1
cpState: "空闲"
cpCurrentTime:0
cpNeedTime:0
}
}
当使用的model内部有两个数据时,就会渲染出两个网格有两个 Text元素,每个网格内部布局和delegate一致,分别使用了ListElement中的两个数据。通过对model的操作,一个网格就可以动态的增删并且保持统一的格式。
动画的使用
给一个元素添加动画主要有几种形式:
- 在元素中定义一个完整的动画,需要运动时调用
- 给某个属性绑定插值,当某个属性发生改变时自动进行补间
比较常用的是第二种。例如,我希望这个图像运动时能够平滑的移动,可以这样:
Rectangle {
id: details
opacity: 0
Behavior on opacity{
NumberAnimation{
duration: 200
}
}
Behavior on x{
SmoothedAnimation{
duration: 100
}
}
Behavior on y{
SmoothedAnimation{
duration: 100
}
}
}
这样,当x、y值发生改变时,就自动进行一个 SmmoothedAnimation ,经历时长100ms
这个方法也可以绑定到opacity属性上,实现淡入淡出的效果。
需要注意的是,当一个元素在运动过程中,如果检查他的x,y属性,会发现是当前运动中的值,而不是最终值。所以一定避免在运动过程中进行类似 item.x -= 100
这样的赋值,否则最终的位置将不是第一次赋值-100,而是运动到某个时刻的位置-100.
为了避免这样的情况,也可以使用绑定属性的方法,例如(注意x,y属性):
Image {
id: tourist
source: sprites[Math.floor((Math.random()*3))]
width: 20
height: 20
property int x_axid: 1010
property int y_axid: 120
x:x_axid
y:y_axid
Behavior on x{
SmoothedAnimation{ duration: 500 }
}
Behavior on y{
SmoothedAnimation{ duration: 500 }
}
}
每次需要改变位置时,改变x_axid/y_axid的值,而x,y属性会随之改变,同时进行运动动画,但是运动过程中,进行 item.x_axid -= 100
,改变的仍然是最终值-100.
QML进阶
最后我想说,QML和HTML类似之处太多,甚至当在QML中不懂怎么写时,试试用JavaScript的写法,说不定能用,例如QML中也有Math.floor()、Number()、String()等等一众函数,QML中的变量也是var(偶尔又是int,傻傻分不清),总之可以多借鉴。