前言
Qt Declarative UI 传得沸沸扬扬,却很少有中文资料介绍这是一个什么样的技术,以及如何使用它。偶尔能搜到几篇也是掐头去尾的,让人摸不着头脑。 CuteQt 网友英狐奉献的三篇文章很有参考价值,把我带入了门。我翻译的这个入门教程来自于 Qt 官方文档,更多的是语法性的介绍。
QML 是什么?
QML 是一种描诉性的脚本语言,文件格式以 .qml 结尾。语法格式非常像 CSS( 参考后文具体例子 ) ,但又支持 javacript 形式的编程控制。我个人认为它结合了 QtDesigner UI 和 QtScript 的有点。 QtDesigner 可以设计出 .ui 界面文件 , 但是不支持和 Qt 原生 C++ 代码的交互。 QtScript 可以和 Qt 原生代码进行交互,但是有一个缺点,如果要在脚本中创建一个继承于 QObject 的图形对象非常不方便,只能在 Qt 代码中创建图形对象,然后从 QtScript 中进行访问。而 QML 可以在脚本里创建图形对象,并且支持各种图形特效,以及状态机等,同时又能跟 Qt 写的 C++ 代码进行方便的交互,使用起来非常方便。
如何使用?
在 Qt /C ++文件中通过 QDeclarativeView 加载,就像使用 UiLoader 加载 .ui 文件一样。不过本文不会去介绍如何在 Qt C ++中使用 QML ,而是把重点放在 QML 的语法上,不过别担心看不到 .qml 文件的效果。 Qt 提供了一个工具 QML Viewer 可以查看 .qml 文件生成的效果,该程序在 Qt 的 bin 目录下,应用名字叫 qml(Windows 下叫 qml.exe) 。所以你在看到别人提供的 .qml 文件时,你可以用下面命令 qml filename.qml 查看 .qml 的执行结果,咱们的第一个 Hello,World 生成界面如下
开始 QML 吧
上面的 Hello,World 源代码如下
1 import Qt 4.7
2
3 Rectangle {
4 id: page
5 width: 500; height: 200
6 color: “lightgray”
7
8 Text {
9 id: helloText
10 text: “Hello world!”
11 font.pointSize: 24; font.bold: true
12 y: 30; anchors.horizontalCenter: page.horizontalCenter
13 }
14 }
< 分行解释 >
第 1 行是 Qt QML 的统一用法,指明当前 QML 会使用 Qt-4.7 里已经定义好的类型,比如第 3 行的 Rectangle 和第 8 行的 Text 。
第 3 行开始至文章结束处则定义了一个矩形的图形区域对象,第 4 行则申明了该矩形区域对象的 id 为” page ”可以被其它对象识别并通过该 id 访问其成员属性,另外几个属性 width/height/color 则很好理解,语法跟 CSS 类似,可以写在一行中用分号” ; ”隔开。
第 8 行至第 12 行则是一个文本对象,看它代码的嵌套结构可以知道它是内嵌于 Rectangle 的。 Text 的属性除了 anchors 其它几个都能顾名思义。 anchors 描诉的是当前对象的位置和其它对象的相对关系,这里看到其水平中心位置在“ page “的水平中心位置。如果相对 anchors 了解更多,请参考锚的解释。
以上就是 Hello,World 的全部代码,将其存为 hellowordl.qml ,那么只要执行 qml hellowrold.qml 就能看到文章前头的界面了。
更多对象
在上面的代码中我们用到了 Rectangle 和 Text ,那么我还有哪些对象以及这些对象有哪些属性呢。那么请访问 QML-Item 类 ,Item 类是 QML 最基础的类,通过查看它的继承类以及这些继承类可用的属性,你可以添加更多你感兴趣的内容。
好吧 , Happy QML 。
在上一篇文章里我们使用了最基础的 QML 类型实现了文字 Hello,World 的显示。这篇文章中将会增加颜色选项面板,用户可以给 Hello,World 设置不同的颜色, 选项面板由 6 个颜色小块组成,它们唯一的区别就是颜色不一样。那么我们就可以用组件 (Component) 来实现一个颜色块,然后在需要的时候使用这个组件就可以了。组件其实和其它编程语言中的宏,函数,类,结构体等功能差不多,就是代码复用。组件由一个单独的 QML 文件名组成,文件名总是以大写字母开头,要使用该组件的时候直接使用该文件名就可以了。关于如何定义自己的组件,请访问 Defining new Components 。我们为一个颜色块定义了一个 Cell.qml 文件,然后使用 Cell 作为一个去访问它。
Cell.qml 的内容
import Qt 4.7
Item {
id: container
property alias cellColor: rectangle.color
signal clicked(color cellColor)
width: 40; height: 25
Rectangle {
id: rectangle
border.color: "white"
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
onClicked: container.clicked(container.cellColor)
}
}
< 代码详解 >
Item {
id: container
property alias cellColor: rectangle.color
signal clicked(color cellColor)
width: 40; height: 25
Item 是最常使用的 QML 类型,一般用作其它类型的容器,可以理解成最顶级的父类,功能类似于 QtGui 中的 QWidget 。用一个属性别名访问其内嵌对象 rectangle 的 color 属性。在其它文件中可以用 Cell 对象的 cellColor 获得 rectangle 的 color 值。
signal clicked(color cellColor) 则为对象定义了一个信号,在代码的其它部分可以发出这个信号。
Rectangle {
id: rectangle
border.color: “white”
anchors.fill: parent
}
这一部分没有特别好说的,在 Item 中内嵌了一个 id 为 rectangle 白边框的矩形区域,大小占满整个 Item 。
MouseArea {
anchors.fill: parent
onClicked: container.clicked(container.cellColor)
}
MouseArea 则为 Item 增加了一块鼠标响应区,看它的 anchors 知道,在整个 Item 区域内都是鼠标活动区,都能侦听到鼠标事件。 onClicked 那一行则相当于为鼠标单击事件增加了一个处理行为,这里是发出了一个 clicked() 的信号。这个信号正是我们在 Item 里定义的那个 signal 。
Cell.qml 写完了,再来看看程序的主文件。
main.qml 的内容
import Qt 4.7
Rectangle {
id: page
width: 500; height: 200
color: "lightgray"
Text {
id: helloText
text: "Hello world!"
y: 30
anchors.horizontalCenter: page.horizontalCenter
font.pointSize: 24; font.bold: true
}
Grid {
id: colorPicker
x: 4; anchors.bottom: page.bottom; anchors.bottomMargin: 4
rows: 2; columns: 3; spacing: 3
Cell { cellColor: "red"; onClicked: helloText.color = cellColor }
Cell { cellColor: "green"; onClicked: helloText.color = cellColor }
Cell { cellColor: "blue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "yellow"; onClicked: helloText.color = cellColor }
Cell { cellColor: "steelblue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "black"; onClicked: helloText.color = cellColor }
}
}
这里在原来的基础上增加了一个 Grid 网格。 x 坐标是 4 ,底部挨着 page 的底部,所以我们看到的是在左下角。
新增的 6 个 Cell ,名字和 Cell.qml 是一样的。通过 cellColor 属性将颜色传给了每个颜色块。
当 Cell 接收到 onClicked 事件的时候 , 关联的代码回去修改 Hello,World 上的颜色。细心的朋友可能会注意到 Cell 只是定义了 clicked() 的信号,并没有定义 onClicked() 啊,是的这就是 Component 的语法规则了。如果你在 Cell.qml 里定义的是 plicked(), 那么你在 main.qml 中引用的时候就该用 onPlicked() 了。
经过前面两个教程,文字也能显示,也能处理鼠标事件了,接着来点动画。
这个教程实现了当鼠标按住的时候, Hello,World 从顶部到底部的一个旋转过程,并带有颜色渐变的效果。
完整的源代码 main.qml
import Qt 4.7
Rectangle {
id: page
width: 500; height: 200
color: "lightgray"
Text {
id: helloText
text: "Hello World!"
y: 30
anchors.horizontalCenter: page.horizontalCenter
font.pointSize: 24; font.bold: true
MouseArea { id: mouseArea; anchors.fill: parent }
states: State {
name: "down"; when: mouseArea.pressed == true
PropertyChanges { target: helloText; y: 160; rotation: 180; color: "red" }
}
transitions: Transition {
from: ""; to: "down"; reversible: true
ParallelAnimation {
NumberAnimation { properties: "y,rotation"; duration: 500; easing.type: Easing.InOutQuad }
ColorAnimation { duration: 500 }
}
}
}
Grid {
id: colorPicker
x: 4; anchors.bottom: page.bottom; anchors.bottomMargin: 4
rows: 2; columns: 3; spacing: 3
Cell { cellColor: "red"; onClicked: helloText.color = cellColor }
Cell { cellColor: "green"; onClicked: helloText.color = cellColor }
Cell { cellColor: "blue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "yellow"; onClicked: helloText.color = cellColor }
Cell { cellColor: "steelblue"; onClicked: helloText.color = cellColor }
Cell { cellColor: "black"; onClicked: helloText.color = cellColor }
}
}
除了这个 main.qml 之外,还有一个 Cell.qml 也是需要的。下面来看一看比起上面的代码增加出来的内容。
Text{
...
states: State {
name: "down"; when: mouseArea.pressed == true
PropertyChanges { target: helloText; y: 160; rotation: 180; color: "red" }
}
transitions: Transition {
from: ""; to: "down"; reversible: true
ParallelAnimation {
NumberAnimation { properties: "y,rotation"; duration: 500; easing.type: Easing.InOutQuad }
ColorAnimation { duration: 500 }
}
}
...
}
states 内嵌于 Text 之中,可以为 Text 元素添加多个状态,现在的这个例子只增加了一个状态。该状态的名” down ” , 然后由“ when ”指定了什么时候触发这个状态。 PropertyChanges 则指定了哪个元素的哪些属性会发生什么样的变化。例子中 PropertyChanges 利用 “ target ”指定了 id 为” helloText ”的元素会发生变化,包括其 y,rotation,color 等属性。
transitions 是用于增加动画效果的(如果把 transitions 这一段代码删去, Hello,World 的文字也会发生变化 , 但是看不到中间动画渐变效果 ) 。同样可以看到 transitions 是复数形式,意味着可以添加多个动画过程。“ from ”和” to ”指明了当前的动画作用于哪两个状态变化之间。 “ from ”和” to ”的参数名来自于 State 中的” name ”属性。
ParalleAnimation 则指定了有多个 有多个动画并行发生。 NumberAnimation 用于 qreal 类型的属性变化 ,ColorAnimation 则用于颜色变化。