一、介紹
前面介紹在擴展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()
}