QML概念及框架--继承JavaScript
QML推荐使用属性绑定和现有的QML元素来创建界面。为了允许执行更高级的行为,QML紧密集成了必要的JavaScript代码。QML中提供的JavaScript环境比在网页浏览器中的更严格。在QML中不可以添加或者修改JavaScript全局对象的成员,因为这样做可能会使一个没有经过声明的变量。在QML中会抛出一个异常,所以所有的局部变量都应该明确的声明。除了标准的JavaScript属性,在QML全局对象中还包含了一些很有用的函数,可以用来简化创建界面以及和QML环境进行交互。
1. 内联JavaScript
较小的JavaScript函数可以和其他QML声明一起写在QML组件中。这些内联函数会像一般的函数一样添加到QML元素中:
import QtQuick 2.4
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))
}
}
像一般函数一样,在一个QML组件根元素中的内联函数可以在组件外被调用。如果不想被被外部调用,那么这个函数可以添加到一个根元素以外的其他元素中,或者编写进一个外部的JavaScript文件中。
2. 分离的JavaScript文件
大块的JavaScript代码需要写在一个独立的文件中,这些文件可以通过使用import语句导入QML文件中,就像导入QML模块一样。
前面代码中国的factorial()函数可以移动到一个外部名为“factorial.js”的文件中,然后进行访问:
import QtQuick 2.4
import "factorial.js" as MathFunctions
Item {
MouseArea {
anchors.fill: parent
onClicked: console.log(MathFunctions.factorial(10))
}
}
相对和绝对的JavaScript路径都可以被导入。如果脚本文件不可访问,那么将发生错误。如果JavaScript需要从赢网络资源中获取,那么组件的状态会被设置为Loading,直到脚本被下载完毕。被导入的JavaScript文件总是使用as关键在来进行限定,每一个JavaScript文件的限定符必须是唯一的,在限定符合JavaScript文件之间是一对一映射的。
3. 代码隐藏(Code-Behind)实施文件
大多数的JavaScript文件被导入一个QML是有状态的,它们经常作为该QML文件的逻辑实现。在这种情况下,为了使QML组件的实例有正确的行为,每个实例都需要JavaScript对象和状态的一个独立备份。导入一个JavaScript文件时的默认行为死为每一个QML组件实例提供一个唯一的、独立的备份。JavaScript代码和QML组件实例运行在相同的范围,因此可以访问和操作对象和声明的属性。
一个QML中对JS文件中的值作了修改,再次调用时,这个值发生了变化。。。。。。
4. 无状态的JavaScript库
一些JavaScript文件的行为更像库文件,它们提供了一组无状态的辅助函数来提供输入和计算输出,但是从来不直接操作QML组件实例。如果每一个QML组件实例都有一个这些库的拷贝,那么就会造成浪费。JavaScript程序员可以使用一个pragma来致命一个特定的文件是一个没有状态的库。
.pragma library
function factorial(a) {
a = parseInt(a)
if(a <= 0) {
return 1;
} else {
return a * factorial(a -1)
}
}
这个prama声明必须出现在除了注释以外的所有JavaScript代码以前。虽然QML值可以作为函数的参数进行传递。不过这些共享的、无状态的库文件不能直接访问QML组件实例对象或属性。
5. 从其他JavaScript文件进行导入
如果一个JavaScript文件需要使用定义在其他JavaScript文件中的函数,可以通过使用Qt.include()函数来导入其他的文件。这样会将其他文件中的所有函数导入到当前文件的命名空间中。例子:
import QtQuick 2.4
import "script.js" as MyScript
Item {
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: {
MyScript.showCalculations(10)
console.log("Call factorial() from QML: ", MyScript.factorial(10))
}
}
}
script.js文件的内容:
Qt.include("factorial.js")
function showCalculations(value) {
console.log("Call factorial() from script.js: ", factorial(value))
}
factorial.js文件的内容:
function factorial(a) {
a = parseInt(a)
if(a <= 0) {
return 1;
} else {
return a * factorial(a -1)
}
}
注意,调用Qt.include()将会从factorial.js导入所有的函数到MyScript命名空间,这就意味着QML组件可以直接使用MyScript.factorial()来访问factorial()函数。
6. 在启动时运行JavaScript
有时需要在应用程序(或者组件实例)启动时运行一些命令代码,但如果仅仅包含启动脚本作为全局代码,因为QML环境还没有完全建立起来,所以这样会有严重的局限,例如一些对象可能还没有被创建或者一些属性绑定还没有被运行。后面讲述的QML JavaScript限制一段中涵盖了全局脚本代码的确切限制。QMLComponent元素提供了一个附加的onCompleted属性可以用来在QML环境完全建立后切换到启动脚本代码的执行。
Rectangle {
function startFunction(){
//...startup code
}
Component.onCompleted: startupFunction();
}
任何在QML文件中的元素,包含嵌套的元素和嵌套的QML组件实例,都可以使用这个附加属性。如果有多个onCompleted()处理器在启动时执行,它们会以未定义的顺序依次执行。类似的,Component::onDestruction附加属性会在组件销毁时触发。
7. 属性赋值与属性绑定
当同时使用QML和JavaScript时,区分QML属性绑定和JavaScript赋值是很重要的。在QML中,使用“属性:值”语法来创建一个属性绑定。
Rectangle {
width: otherItem.width //属性绑定,Rectangle的值会随otherItem.width的更改而更新
Component.onCompleted: {
width: otherItem.width //属性赋值,不会调用QML的属性绑定
}
}
8. 在JavaScript中接收QML信号
要接收一个QML信号,可以使用信号的connect()函数将它关联到一个JavaScript函数上。
Item {
id: item
width: 200; height: 200
MouseArea {
id: mouseArea
anchors.fill: parent
}
Component.onCompleted: {
mouseArea.clicked.connect(MyScript.jsFunction) //MouseArea的信号关联到script.js中的jsFunction()上
}
}
9. QML JavaScript限制
QML执行标准的JavaScript代码,会有下面的限制:
(1) JavaScript代码不能修改全局对象
在QML中,全局对象是一个常量,现有的属性不能够被修改和删除,也不能够创建新的属性。大多数的JavaScript程序并不是有意修改全局对象的,然而,JavaScript自动生成一个为声明的变量是全局对象的隐式修改,在QML中是非法的:
//a在作用链中不存在
//非法修改未声明的变量
a = 1;
for(var ii = 1; i < 10; ++ii)
a = a * ii
console.log("Result: " + a)
它可以简单修改为合法的代码:
a = 1;
for(var ii = 1; i < 10; ++ii)
a = a * ii
console.log("Result: " + a);
无论隐式的或者显式的对全局对象的修改都会导致一个异常
(2) 全局代码运行在一个缩小的范围
在启动时,如果QML文件包含一个外部的JavaScript文件和“全局”代码,它会只包含该外部文件和这个全局对象的范围内执行。也就是说,它不会像通常那样访问QML对象和属性。全局代码只访问脚本中的局部变量是允许的。
//有效的全局代码
var colors = ["red", "blue", "green", "orange", "purple"]
非法的全局代码--“rootObject”变量未定义
var initialPosition = {rootObject.x, rootObject.y}
存在此限制是因为QML环境尚未被完全建立。要宰环境完全启动后运行后运动代码。
(3) 目前在QML中this值是未定义的,要引用任何元素,可以使用其id。
Item {
width: 200; height: 200
function mouseAreaClicked(area) {
console.log("Clicked in area at: " + area.x + "," + area.y)
}
//因为this未定义,所以下面的代码不会工作
MouseArea{
height: 50; width: 200
onClicked: mouseAreaClicked(this)
}
//这样可以将area2传递给函数
MouseArea {
id: area2
y: 50; height: 50; width: 200
onClicked: mouseAreaClicked(area2)
}
}
from: https://blog.csdn.net/u012419303/article/details/45896263