本篇博客通过一个详细的UI例子来逐个点介绍Qt Quick的基本用法,尽量通过简单例子来覆盖全部主要知识点,阅读时请对照代码来逐个理解各个知识点
Qt Quick基本用法
- Demo使用技巧:由于以下代码集中了大量知识点,同时显示会特别混乱,大家只要通过visible: false隐藏无关元素即可
- 注释:Qml和Javascript一样,通过双斜杠添加注释
- 给元素设置id:每个节点都可以设置一个id属性,其它元素可能通过id关联这个节点,参照node01
- 向DOM树中添加节点:参照node02
- 导入类库:Window,Text等元素都来自Qt Quick类库,需要导入后才能使用,参照首行import语句
- Qml基本类型:Qml提供了int,real,string,bool,list等类型,不需要导入任何类库就能使用
- Qt Quick内置类型:Qt Quick提供了date,point,rect,size,基本控件等类型,导入Qt Quick模块即可使用
- Javascript类型:Qml可以像Javascript一样,直接通过var来声明一个对象,它没有具体类名,但可以通过变量名直接访问,参照node03和node04,node03声明了一个innerObject对象,node04可以直接引用这个对象的值
- 执行Javascript代码:在onCompleted,onClicked等方法内,可以执行Javascript代码,参照node04
- 属性引用:一个元素,可以直接使用其它元素的属性值,或拿来进行运算,这个功能非常强大,比如我们想要高为宽的一半时,只需写出【height: width * 0.5】即可
- 通过锚布局定位元素:锚布局允许通过锚点,边距,填充,居中等方式组合来定位元素位置,参照node05
- 设置元素点击区域:通过给元素添加MouseArea子节点,可以为元素添加点击事件,并且可以控制点击区域范围,参照node06
- 设置字体:通过给Text元素添加font子节点,就可以设置Text的字体,当font的属性很简单只有一个的时候,也可以简写成【font.bold: true】这种格式,参照node07
- 设置边框:边框的使用和字体同理,参照node08
- 图片控件:通过Image元素可以显示图片,参照node09
- 设置鼠标滑入滑出事件:给MouseArea设置onEntered,onExited事件方法即可,参照node10
- 自定义组件:由于篇幅较长,稍后我们开一篇专门的博客来讲解
- 编码规范:由于一个Qml文件中可能包含诸多内容,为了方便阅读,应当有一个书写规范,一般按照id,自定义属性,属性赋值,事件声明,状态声明,动画声明,自定义函数的顺序来编写
- 定义动画:Qt内置了AnchorAnimation,ColorAnimation,NumberAnimation,PathAnimation,PropertyAnimation,RotationAnimation等常用动画,非常前面,如node11所示,是一个简单的颜色渐变动画
- 定义复杂动画:上面介绍的是一个非常简单的动画,控件一创建便执行,实际大多情景比这个要复杂,动画改变的属性可能不止一个,动画往往需要指定在状态变化时执行,这时单纯的Animation已经不再能满足我们的需求,我们需要配合State和Transition来实现这些复杂动画,参照node12
- 数组简写:在Qml中数组通过[ ]来表示,但是当数组只有一个元素时,也可以省略[ ]
- 加载Javascript代码:Qml可以直接将Javascript加载为一个模块对象,直接进行使用,在这一点上,Qt Quick做得比原生Javascript更加先进,参照node13和import语句
- 可视基类:所有的Qt Quick可视化元素,即控件节点,都继承自Item,参照node14,像x,y,z,width,height这些基本属性,都是继承自Item的
- 控件堆叠顺序:当一个元素中包含多个子元素时,可以通过z值来控制元素的显示顺序,z值较大的将显示在上面,遮挡z值较小的元素,默认的z值为0,z值相同的情况下,排在布局靠后的元素显示在上层,参照node14
- 坐标转换:同一个点相对于不同控件的坐标是不同的,比如对于一个Window-Item-Rectangle的三级Dom结构,点相对于Window,Item,Rectangle的坐标都是不一样的,除非这三级节点的左上角都是对齐的,要获取一个点相对于某个元素的坐标位置,可以参考node15
- 界面布局:内容不难,但由于布局有很多种,还有新版旧版之分,篇幅较长,接着我们开一篇专门博客来讲解
- 显示富文本:Text可以显示复杂带样式的文本,如加粗,倾斜,彩色,链接,序号等,通过【textFormat: Text.StyledText】或【textFormat: Text.RichText】即可启用对应的功能,StyledText仅支持基本的简单文本标签,RichText支持HTML4标签
- 使用文本编辑框:TextInput是一个文本编辑控件,但是它没有任何默认样式,甚至连基本的边框都没有,需要用户自己定义,参照node16
- 更多高级控件和属性:Qt每个控件都包含了诸多属性,每一个都可以写成一幅长篇博客,但是不管对于新手还是老手来说,这都不是一个明智之举,这里只讲解核心知识点和思路,以便大家可以快速上手进行Qt项目开发,具体细节可以用到时再探究,死背属性意义不大,以后换一种语言,换一种框架,可能又是新的属性和用法,没太大复用价值
Demo源码
//main.qml
import QtQuick 2.12
import QtQuick.Window 2.12
import "script/math.js" as MathScript
//窗口节点
Window {
id: node01
visible: true
width: 640
height: 480
title: "Qt Quick Demo"
//测试基本使用
Text {
id: node02
visible: false
text: "Node 02"
}
//测试Javascript对象
Text {
property var innerObject: ({
"title": "Node 04"
})
id: node03
visible: false
text: "Node 03"
}
//测试Javascript代码
Text {
id: node04
visible: false
text: node03.innerObject.title
Component.onCompleted: {
var jsObject = {
"id": 1,
"name": "Javascript Object"
}
console.log(node03.text)
console.log(node04.text)
console.log(jsObject.name)
}
}
//使用矩形区域测试锚布局
Rectangle {
id: node05
visible: false
width: 400
height: 400
radius: 5
color: "red"
anchors.left: parent.left
anchors.top: parent.top
anchors.leftMargin: 10
anchors.topMargin: 10
}
//测试点击区域
Rectangle {
id: node06
visible: false
width: 400
height: 400
color: "red"
anchors.centerIn: parent
//只能点击下半区域
MouseArea {
anchors.fill: parent
anchors.topMargin: parent.height * 0.5
onClicked: {
console.debug("Rectangle Clicked")
}
}
}
//测试字体
Text {
id: node07
visible: false
text: "Node 07"
font {
pointSize: 30
bold: true
italic: true
}
}
//测试边框
Rectangle {
id: node08
visible: false
width: 200
height: 200
color: "yellow"
border {
color: "red"
width: 2
}
}
//测试图片
Image {
id: node09
visible: false
source: "images/1.png"
width: parent.width
height: parent.height
fillMode: Image.Stretch
}
//测试鼠标悬浮事件
Rectangle {
id: node10
visible: false
width: 200
height: width
color: "yellow"
radius: 100
border.color: "red"
border.width: 2
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: {
parent.border.width = 4
}
onExited: {
parent.border.width = 2
}
}
}
//测试基本动画
Rectangle {
id: node11
visible: false
width: 200
height: width
radius: 100
//控件创建时,执行一个颜色渐变动画
ColorAnimation on color {
from: "yellow"
to: "orange"
duration: 3000
}
}
//测试复杂动画
Rectangle {
id: node12
visible: false
width: 200
height: width
radius: 100
state: "A"
//点击时切换到状态B
MouseArea {
anchors.fill: parent
onClicked: {
node12.state = "B"
}
}
//声明两种状态,指定状态值
states: [
State {
name: "A"
PropertyChanges {
target: node12
color: "yellow"
}
PropertyChanges {
target: node12.border
width: 2
color: "green"
}
},
State {
name: "B"
PropertyChanges {
target: node12
color: "orange"
}
PropertyChanges {
target: node12.border
width: 5
color: "blue"
}
}
]
//定义从A状态切换到B状态时的动画
transitions: Transition {
from: "A"
to: "B"
ColorAnimation {
target: node12
duration: 3000
}
NumberAnimation {
target: node12.border
property: "width"
duration: 3000
}
}
}
//测试加载Javascript文件
Rectangle {
id: node13
visible: false
width: 200
height: width
radius: 100
color: "yellow"
Component.onCompleted: {
console.debug("MathScript", MathScript.sum(1, 1))
}
}
//测试Item
Item {
id: node14
visible: false
anchors.fill: parent
Rectangle {
width: 100
height: 100
x: 100
y: 100
z: 1
color: "red"
}
Rectangle {
width: 100
height: 100
x: 150
y: 150
z: -1
color: "lightblue"
}
}
//获取控件坐标
Rectangle {
id: node15
visible: false
anchors.fill: parent
color: "yellow"
Rectangle {
id: node15Rect
width: 100
height: 100
color: "red"
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: 10
anchors.leftMargin: 10
Component.onCompleted: {
//将node15Rect中的点,转化为在node15中的坐标
console.debug(node15.mapFromItem(node15Rect, 0, 0))
//将node15中的点,转化为在node15Rect中的坐标
console.debug(node15.mapToItem(node15Rect, 10, 10))
//同理,可将Item中的坐标与Window中的坐标进行转换
//由于Window不继承自Item,需要通过Window.contentItem来获得根节点
console.debug(node01.contentItem.mapFromItem(node15Rect, 0, 0))
console.debug(node01.contentItem.mapToItem(node15Rect, 10, 10))
}
}
}
//测试文本编辑框
Rectangle {
id: node16
visible: true
anchors.fill: parent
anchors.margins: 100
color: "yellow"
TextInput {
id: node16Input
anchors.fill: parent
text: "Hello World"
color: "red"
font.pointSize: 50
focus: true
clip: true //文本裁剪,可以防止文本超出parent显示
}
}
}
//math.js
function sum (a, b) {
return a + b;
}