一、介绍
前面介绍在扩展QML元素时,使用JavaScript编写添加新的函数,并只属于定义它的元素。然而,应用程序的逻辑都是和界面程序分开的。为了能够使用这些函数,需要将他们导入到新的QML文档中。JavaScript可以直接被写在QML文件中或者保存在一个独立的js文件里面(这个是个更好的选择)。应用程序也可以使用QML全局对象提供的服务
二、QML全局对象
QML提供了全局的JavaScript对象——Qt。它在QML的任意部分都可以使用。例如前面带有MouseArea的例子中我们使用过acceptedButtons: Qt.LeftButton | Qt.RightButton。QML的全局对象提供了大量的函数,如创建QML模型(Qt.rect(...),Qt.rgba(...),Qt.point(...)等),其他的常用操作(Qt.playtSound(...),Qt.openUrlExternally(...),Qt.md5(...))。同时也提供了动态QML对象的创建,AJAX和本地数据访问接口
三、在QML中使用JavaScript
在QML中使用JavaScript有如下一些限制和特点:①JavaScript不能用于为全局对象添加新的成员,②在声明变量时,可以省略“var”关键字。下面介绍两种方法使用JavaScript
1、Inline JavaScript
Item {
function factorial(a){
a = parseInt(a);
if(a <= 0)
return 1;
else
return a * factorial(a - 1)
}
MouseArea {
anchors.fill: parent
onClicked: console.log(factorial(10))
}
}
2、独立的JavaScript文件
import "factorial.js" as MathFunctions
Item {
MouseArea {
anchors.fill: parent
onClicked: console.log(MathFunctions.factorial(10))
}
}
如果有很多JavaScript代码,建议把JS代码写到单独的文件中。相对或者绝对的路径的JavaScript的URLs都是可以被加载的(对于相对路径来讲,是根据与QML文档本地的相对位置转化的)
四、QML域(Scope)
当创建了QML组件实例后,QML自动会为它生成一个域(chain scope),用于JavaScript的执行和属性绑定。
注意:同一个组件的不同实例可以有不同的域
当系统解析某个引用的时候,作用域的搜索是按照特定的顺序执行的。
JavaScript variables, functions and property bindingds, attached properties or enumerations
五、QML域——元素的类型
元素类型是当访问属性和枚举值的时候使用。导入定义好的元素类型列表,如果所需要的类型没有找到,会有警告消息发出。
示例如下:
Text {
id: root
scale: root.PathView.scale // Attached property access
horizontalAlignment: Text.AlignLeft // Enumeration access
}
六、QML域——本地域
每个QML组件都有一个本地域,控件中的子控件也有自己的本地域,而且,绝大多数的变量都是从本地域进行解析的。
本地域的搜索顺序:
IDs
Script methods
Scope object
Root object
示例一:
Rectangle { // Local scope component for binding 1
id: root
property string text
Button {
text: root.text // binding 1
}
ListView {
delegate: Component { // Local scope component for binding 2
Rectangle {
width: ListView.view.width // binding 3
}
}
}
}
示例二:
Rectangle { // Local scope component for binding 3
id: root
property string text
Text {
text: root.text // binding 3
}
}
在组件内部的脚本中,搜索的顺序与属性类似,比如:JavaScript的函数调用一定是调用最近定义的那个函数
示例如下:
Item {
function getValue() {return 10;} // Method 1
Rectangle {
function getValue() {return 11;} // Method 2
function getValue2() {return parent.getValue();} // Method 3
x: getValue() // Resolves to Method 2, set to 11
y: getValue2() // Resolves to Method 3, set to 10
}
}
域object就是包含某段代码或者绑定的块
Item {
Rectangle { // Scope object for Binding 1
width: height * 2 // Binding 1 - height is a property of Rectangle
}
Text { // Scope object for Binding 2
font.pixelSize: parent.height * 0.7 // Binding 2 - parent is a property of Text
}
}
ListView {
delegate: Rectangle {
id: root
width: ListView.view.width // Binding 1
Text {
width: ListView.view.width // Binding 2 - possibility not the same value as in Binding 1
}// Should probably be: root.ListView.view.width
}
}
七、QML脚本限制
1、在JavaScript不能添加新的成员到QML全局对象中,这是由于JavaScript处理未定义变量的方法,在无意间可能违背了这个原则
2、在加载的时候,如有QML引用了一段外部的脚本文件,而这个文件里又有一段全局的代码,那么这段代码的执行的域将会是受限制的(执行的域只包含全局对象和引入的脚本文件),这个时候,不能保证所有的QML对象都已经被正确初始化(所以全局的代码不能像平时那样正常的访问到QML对象及其属性)。
八、启动脚本
某些时候,我们需要在应用开始的时候或者当一个控件初始化的时候运行一段初始化代码,将这段代码放在外部的脚本文件中,并不是一个好的解决方案。因为当这段代码执行时,并非所有的QML的域都已经被完全初始化了(参考上面所说的“QML脚本限制”)。最好的解决方案是采用Component元素的onCompleted这个attached属性,它会在整个控件完全初始化后被调用。
示例如下:
Rectangle {
function startupFunction() {
//start up code
}
Component.onCompleted: startupFunction()
}