QML是一种用来定义用户交互界面的声明式语言。它将用户交互界面拆分成很多个小的独立单元,通过这些独立单元的组合来构成各种各样的复杂界面。同时通过JavaScript和C++我们可以丰富QML程序的业务逻辑功能。这里有点像HTML+JavaScript,但是区别是QML只是用来描述界面的而不是一个文本文件。
QML属性
在QML文件中,我们通过元素的属性来定义元素的外观,简单的例子如下所示
Text {
//元素的ID用来查找该元素
id: labelID
// 元素的水平位置和垂直位置
x: 24; y: 16
//元素的高度是宽度的两倍
height: 2 * width
//自定义属性times的值是24
property int times: 24
//给属性times起一个别名叫做anotherTimes
property alias anotherTimes: thisLabel.times
//给控件添加文本内容并关联自定义属性
text: "Greetings " + times
//设置字体类型和字体大小
font.family: "Ubuntu"
font.pixelSize: 24
//Tab扩展关联下一个焦点元素
KeyNavigation.tab: otherLabel
//高度变化的时候输出日志
onHeightChanged: console.log('height:', height)
//元素获得焦点
focus: true
//通过是否获得焦点来修改字体的颜色
color: focus?"red":"black"
}
有一点需要注意,QML控件只能引用QML文档内部的元素。虽然QML提供了一种叫做dynamic-scoping的机制,通过该机制我们可以引用之前加载的QML文档中还没有被覆盖的ID,这有点像C++中的全局变量。但是在实际应用中,这个引用依赖于程序文件的加载顺序,很不稳定,所以不建议使用该机制。如果你真的想引用外部元素,最好通过定义根元素属性的方式导出该元素。
JavaScript脚本
我们可以将QML元素和JS脚本写到一起,通过QML元素的变化触发JS脚本的执行:
Text {
id: label
x: 24; y: 24
//定义属性spacePresses
property int spacePresses: 0
text: "Space pressed: " + spacePresses + " times"
//文本变化的时候输出日志
onTextChanged: console.log("text changed to:", text)
focus: true
//空格按下的时候执行JS函数
Keys.onSpacePressed: {
increment()
}
Keys.onEscapePressed: {
label.text = ''
}
//JS函数
function increment() {
spacePresses = spacePresses + 1
}
}
基本元素
QML元素可以分为可见元素和不可见元素,可见元素如Rectange有尺寸属性,显示的时候以占据屏幕的一块尺寸。不可见元素如Timer往往提供一种通用功能用来操纵可视化的元素。
Item元素
Item元素是其他可视化元素的根元素,它不可见,但是定义了所有可视化元素的基本属性。
Geometry(尺寸属性)
位置属性(x,y) 尺寸属性(width,height),层次属性(z)
Layout handling(布局属性)
锚定属性(anchor) 间隙属性(margin)
Key handing(按键绑定属性)
按键事件(key\KeyNavigation) 焦点属性(focus)
Transformation(形状变化的属性)
缩放(scale) rotate(旋转) transform/transformOrigin(移动)
Visual(可视化属性)
透明度(opacity) visible(可见性) clip(是否裁剪) smooth(平滑性)
State(状态属性)
通过定义状态和状态间的切换流程,实现各种各样的动画
在QML编程过程中,Item通常会用来做其他元素的容器,有点像HTML中的div 元素.
其他的基本元素
Window {
visible: true
width: 640
height: 480
title: qsTr("Basic Elements")
/**************************矩形元素*******************/
Rectangle {
id: rect1
x: 12; y: 12
width: 76; height: 96
//定义背景颜色
color: "#00FFFF"
}
Rectangle {
id: rect2
x: 112; y: 12
width: 76; height: 96
//定义边框颜色和宽度
border.color: "#00FFFF"
border.width: 4
//定义圆角
radius: 8
}
//通过渐变色来填充矩形
Rectangle {
id: rect3
x: 200; y: 12
//不设置宽度和高度的话,矩形不可见
width: 60; height: 60
gradient: Gradient {
//定义起点和终点的色值,实现颜色渐变
//QML不支持有角度的渐变,最好使用预先定义好的图像
GradientStop { position: 0.0; color: "lightsteelblue" }
GradientStop { position: 1.0; color: "slategray" }
}
border.color: "slategray"
}
/**************************Text元素*******************/
Text {
//如果没有指定宽度的话,Text的初始宽度依赖于文本和字体
//如果没有指定文本内容宽度控件不可见,宽度为0
x:270; y:12
text: "QML Text Label"
//背景颜色,字体样式和颜色
color: "#303030"
font.family: "Ubuntu"
font.pixelSize: 20
width: 240; height: 120
//文本居中对齐
elide: Text.ElideMiddle
//下沉的样式
style: Text.Sunken
styleColor: '#FF4444'
//垂直对齐对齐到顶部
verticalAlignment: Text.AlignTop
}
/**************************图像元素*******************/
Image {
x: 12; y: 130
//图像的地址可以是一个网络地址
source: "qrc:/transform.png"
//指定图像的填充模式
//采用PreserveAspectCrop填充模式,应该将clip开启
//防止填充超出了控件的范围
fillMode: Image.PreserveAspectCrop
clip: true
}
/**************************鼠标元素*******************/
Rectangle {
id: rect4
x: 270; y: 130
width: 76; height: 96
color: "lightsteelblue"
MouseArea {
id: area
width: parent.width
height: parent.height
//点击鼠标区域隐藏矩形
onClicked: rect4.visible = !rect4.visible
}
}
}
显示效果如下图所示:
实现自定义控件
QML提供了很多种实现自定义UI元素的方式,最简单的方式就是通过QML文件创建一个基于文件的UI元素.我们定义一个名为MyButton.qml文件用来定义我们自定义的按钮。按钮的文件内容如下:
import QtQuick 2.8
//MyButton.qml
//自定义的UI元素要基于Item
Item
{
id: root
width: 116; height: 26
//导出外部可以访问的元素
property alias text: label.text
signal clicked
//可以鼠标点击的按钮
Rectangle {
id: button
x: 12; y: 12
width: 116; height: 26
color: "#4D9CF8"
border.color: "slategrey"
Text {
id:label
anchors.centerIn: parent
text: "点击这里"
}
MouseArea {
anchors.fill: parent
onClicked: {
status.text = "按钮被点击了"
}
}
}
//显示需点击的内容
Text {
id: status
x: 12; y: 76
width: 116; height: 26
text: "等待中..."
horizontalAlignment: Text.AlignHCenter
}
}
定义了可复用的控件元素之后,我们就可以在别的地方调用该UI元素了,调用方法如下:
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Basic Elements")
MyButton{
x:30; y:30
width: 200;height: 400
text:"测试点击"
}
}
显示效果如下图所示:
元素姿态变化
姿态变化就是控制元素的UI属性。通常我们可以移动、旋转、缩放一个QML元素,下面以一个例子说明如何控制一个图像元素的几何属性:
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Basic Elements")
Image {
id: root
x:130
y:100
width: 50; height: 50
source: "qrc:/transform.png"
//开启抗锯齿,提升缩放的时候的图片质量
antialiasing: true
MouseArea{
anchors.fill: parent
// 每次点击的时候修改图像的旋转角度.尺寸和x坐标
onClicked: {
root.rotation += 15;
root.scale += 0.1;
root.x += 5;
}
}
}
}
控制效果如下图所示:
布局元素
Qt-Widget中有各种布局:水平布局、垂直布局、网格布局,在QML中也有对应的元素,通过各种布局,我们可以按照一定的规律调整元素的位置。除了常见的布局元素之外,QML还有一个比较特殊的流布局(Flow)。该布局将一个元素添加到一个流中,流的方向通过布局方向(layoutDirection)来确定,它可以是从上向下,也可以是从左到右。当元素添加到流布局中之后,它会自动的根据布局的行列数进行调整位置,为了保证流布局的正常工作,我们需要指定布局和宽度和高度。常见布局的用例如下:
Window {
visible: true
width: 640
height: 480
title: qsTr("Layout Elements")
//垂直布局
Column {
id: column
x:20; y:20
//垂直的间隙为8
spacing: 8
Rectangle {
color:"#FF0000";
width: 48; height: 48
border.color: Qt.lighter(color)
}
Rectangle{
width: 96
height: 48
color: "#00FF00"
border.color: Qt.lighter(color)
}
Rectangle{
width: 48
height: 48
color: "#0000FF"
border.color: Qt.lighter(color)
}
}
//水平布局
Row {
id: row
x:130; y:20
// 水平的间隙为8
spacing: 8
Rectangle {
color:"#4D9CF8";
width: 48; height: 48
border.color: Qt.lighter(color)
}
Rectangle{
width: 48
height: 48
color: "#CCCCCC"
border.color: Qt.lighter(color)
}
Rectangle{
width: 48
height: 48
color: "#0D88FF"
border.color: Qt.lighter(color)
}
}
//网格布局
Grid {
id: grid
rows: 2
columns: 2
x:320 ;y:20
spacing: 10
Rectangle {
color:"#4D9CF8";
width: 48; height: 48
border.color: Qt.lighter(color)
}
Rectangle {
color:"#4D9CF8";
width: 48; height: 48
border.color: Qt.lighter(color)
}
Rectangle {
color:"#4D9CF8";
width: 48; height: 48
border.color: Qt.lighter(color)
}
Rectangle {
color:"#4D9CF8";
width: 48; height: 48
border.color: Qt.lighter(color)
}
}
//流布局
Flow {
x:450 ;y:20
width: 50
height: 200
anchors.margins: 20
spacing: 20
layoutDirection: Flow.TopToBottom
Rectangle {
color:"#4D9CF8";
width: 48; height: 48
Text{
anchors.fill: parent
text:"flow"
}
}
Rectangle {
color:"#4D9CF8";
width: 48; height: 48
Text{
anchors.fill: parent
text:"flow"
}
}
Rectangle {
color:"#4D9CF8";
width: 48; height: 48
Text{
anchors.fill: parent
text:"flow"
}
}
}
}
运行效果如下图所示:
定位元素
QML允许用户使用定位锚的方法来对控件进行布局。锚是QML可视化元素的一种基本属性,通过使用锚我们可以实现元素之间的相对定位。一个元素包括6个主要锚线,分别是:
top、bottom、left、right、horizontalCenter、verticalCenter,如下图所示:
下面介绍一下锚定位的使用方法:
1.元素填充定位
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Anchors")
Rectangle{
id:rect1
x:30;y:30
width: 96
height: 96
color: "#4D9CF8"
}
Rectangle{
anchors.fill:rect1
anchors.margins: 15
color: "#CCCCCC"
}
}
2.元素左右定位
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Anchors")
//主矩形
Rectangle{
id:rect1
x:300;y:30
width: 96
height: 96
color: "#4D9CF8"
}
//对齐矩形的右边
Rectangle{
y: 30
width: 48
height: 48
anchors.left: rect1.right
anchors.leftMargin: 20
color: "#CCCCCC"
}
//对齐矩形的左边
Rectangle{
y: 30
width: 48
height: 48
anchors.right: rect1.left
anchors.rightMargin:20
color: "#CCCCCC"
}
}
对齐效果如下图所示:
3.元素上下定位
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Anchors")
//主矩形
Rectangle{
id:rect1
x:300;y:230
width: 96
height: 96
color: "#4D9CF8"
}
//对齐矩形的下边
Rectangle{
x:300
width: 48
height: 48
anchors.top: rect1.bottom
anchors.topMargin:20
color: "#CCCCCC"
}
//对齐矩形的上边
Rectangle{
x:300
width: 48
height: 48
anchors.bottom: rect1.top
anchors.bottomMargin:20
color: "#CCCCCC"
}
}
对齐效果如下图所示:
4.中心对齐
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 20
height: 280
title: qsTr("Anchors")
//主矩形
Rectangle{
id: blue1
width: 48; height: 24
y: 8
anchors.horizontalCenter: parent.horizontalCenter
color:"#4D9CF8"
}
Rectangle{
id: blue2
width: 72; height: 24
//对齐blue1的下边,且和blue1水平居中对齐
anchors.top: blue1.bottom
anchors.topMargin: 4
anchors.horizontalCenter: blue1.horizontalCenter
color:"#00FF00"
}
Rectangle{
//blue3位于blue2的中心
id: blue3
width: 30; height: 30
anchors.centerIn: blue2
color:"#CCCCCC"
}
}
对齐效果如下图所示:
5.中心偏移定位
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 320
height: 480
title: qsTr("Anchors")
//主矩形
Rectangle{
id: blue1
width: 100; height: 100
y: 8
anchors.horizontalCenter: parent.horizontalCenter
color:"#4D9CF8"
}
Rectangle{
width: 48
height: 48
anchors.horizontalCenter: blue1.horizontalCenter
anchors.horizontalCenterOffset: -12
anchors.verticalCenter: blue1.verticalCenter
color:"#CCCCCC"
}
}
定位效果如下图所示:
输入元素
我们可以使用TextInput元素来实现文本的输入,该元素支持多种输入文本限制策略包括:
validator、inputMask、echMode.下面的例子说明了控件的使用方法:
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 120
height: 180
title: qsTr("Anchors")
Rectangle {
width: 100
height: 80
color: "#4D9CF8"
TextInput {
id: input1
x: 8; y: 8
width: 96; height: 40
focus: true
text: "文本输入1"
color: "#FFFFFF"
//支持Tab键在两个控件之间来回切换
KeyNavigation.tab: input2
}
TextInput {
id: input2
x: 8; y: 36
width: 96; height: 40
text: "文本输入2"
color: "#FFFFFF"
KeyNavigation.tab: input1
}
}
}
显示效果如下所示:
FocusScope元素
使用FocusScope元素作为根元素的时候,如果整个FocusScope元素获得焦点的时候,元素的最后一个包含focus:true属性的元素会接收到对应的焦点。通过这种方法在切换焦点的时候,我们就可以指定复杂控件中的默认焦点元素了。
Window {
visible: true
width: 640
height: 480
title: qsTr("Anchors")
FocusScope {
x: 0; y:0
width: 200
height: 200
TextInput {
id: input1
x:20;y:20
width: 96
height: 48
color: "#4D9CF8"
anchors.margins: 4
}
TextInput {
id: input2
y:20
anchors.left: input1.right
width: 96
height: 48
color: "#4D9C00"
anchors.margins: 4
focus: true
}
Rectangle {
id:rectangle
y:20
anchors.left: input2.right
width: 48
height: 48
color: "lightsteelblue"
border.color: "gray"
}
}
}
TextEdit元素
TextEdit元素和TextInput元素非常像,主要区别是TextInput是单行的而TextEdit是多行的,并且TextEdit不支持文本限制策略而TextInput支持文本限制策略,这样一来我们就不可以在TextEdit中使用正则表达式等工具限制用户的输入了。用例Demo如下:
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("TextEdit")
Rectangle {
width: 136
height: 120
color: "linen"
TextEdit {
id: input
x: 8; y: 8
width: 120; height: 104
focus: true
text: "Text Edit"
}
}
}
效果如下图所示:
按键元素
通过添加按键响应元素,我们可以根据按键响应执行对应的操作,使用用例如下所示:
import QtQuick 2.8
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Basic Elements")
Image {
id: root
x:130
y:100
width: 50; height: 50
source: "qrc:/transform.png"
//通过按键事件我们可以控制图片的位置和尺寸
antialiasing: true
focus: true
Keys.onLeftPressed: root.x -= 8
Keys.onRightPressed: root.x += 8
Keys.onUpPressed: root.y -= 8
Keys.onDownPressed: root.y += 8
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Plus:
root.scale += 0.2
break;
case Qt.Key_Minus:
root.scale -= 0.2
break;
}
}
}
}