良好的編程規範能夠大幅提升一個程序的可讀性、可維護性。編碼規範是你們達成一致的約定,這樣你們的代碼就能夠互相看懂,維護起來更容易,思想更暢快的交流,經驗更快的獲得傳播。javascript
QML是一種聲明方式設計的語言,用來設計應用程序的界面,包括式樣和表現行爲。在QML中,一個用戶界面被制定爲一棵樹形的對象模型而且包含了對象的屬性,這棵樹樹根對應的就是qml文件中的根組件。
JavaScript在QML中做爲一種腳本語言,對QML進行邏輯方面的編程,也能夠說QML是對JavaScript的擴展,提供了js主機環境。
QML中的JavaScript是特殊的JavaScript,是簡化的JavaScript。java
Item { id:root width:800 height:480 Rectangle { x:200 y: 60 width:200 height:160 color:「red」 visible: false } }
Rectangle組件中一些語法的聲明能夠寫成JavaScript的方式:c++
Item { id:root width:800 height:480 property bool visibleState: false property bool lightState: false Rectangle { x: parent.widht/4 y: parent.height/8 width:parent.widht/4 height:parent.height/3 color:root.lightState ? 「red」 : "green" visible: root.visibleState ? true : false } }
經過以上兩段QML代碼中Rectangle組件屬性的賦值狀況能夠看出,組件的屬性能夠直接賦值,也可經過JavaScript代碼賦值。git
統一的代碼風格,減小代碼冗餘,加強其代碼可讀性,還能夠提高工做效率,讓程序更清晰、健壯。編程
這裏的文件分爲 _*.qml_ 文件和 _*.js_ 文件。框架
因每個qml文件的使用方法可能不固定,在某些狀況下做爲程序的入口文件,文件名的首字母能夠大寫,也能夠小寫,但在另外一些狀況下可能做爲Component(控件,類)來使用,首字母必須大寫。例如:
mainScreen.qml是一個程序界面的入口文件,在後續的維護中,須要在mainScreen.qml中添加一系列的按鈕,在這種狀況下,能夠從新寫一個繼承於mainScreen的界面入口文件main.qml,此時則需從新命名mainScreen.qml爲MainScreen.qml。
爲了方便qml文件的維護,避免上述狀況的發生,qml文件的命名規則爲:函數
以大寫字母開始,遵循大駝峯的命名規則,例如:佈局
SwithcButton.qml ListButton.qml MainScreen.qml
import的意思是導入模塊,相似java語言中的import包,用來導入qml文件所要用到的內置模塊、自定義插件、控件、js文件等。
qml文件中,import模塊的順序爲:動畫
例如:ui
// ModuleA.qml import QtQuick 2.6 import QtQuick.Controls 2.1 import net.phoneyou.EasyUI 1.0 as HUI import net.phoneyou.EasyUI.Material 1.0 as HUIMaterial import net.phoneyou.Roshan.MapRender 1.0 as MapRender2D import your_project_name 1.0 import "path_to_package" import "my_component_impl.js" as MyComponent .import "utils.js" as Utils
爲了不不一樣模塊之間的命名衝突,咱們在導入模塊時可使用「as」關鍵字設置該模塊在本 qml 文件中的「別名」。
對於"as"的相關規則說明:
爲了保證 Qt 內部組件和命名空間不被污染以及保持代碼能夠順利升級--好比升級 Qt 版本,須要確保不對 Qt 模塊設置別名,而對其餘,好比HUI、Roshan以及其餘三方的或者項目內的模塊,進行「別名」處理。尤爲是那些對常見控件進行定製的模塊。
qml文件描述的實際上一個由 「UI組件節點」 組成的「樹」。而「根組件」也就是「根節點」,在qml文件有且只有一個。
根組件做爲邏輯上的頂層組件,直接承擔起了 qml 文件的全部定義,其屬性、信號、方法即爲該 qml 組件的屬性、信號、方法。
其餘組件只能做爲其子節點存在,對外是不可見的,相似 private 做用域,他們的屬性、信號、方法命名需添加雙下劃線("__")前綴,而根組件沒有任何的先後綴。
關於根組件的類型:
例如:
Rectangle { id: root /*! 鼠標按下狀態,當鼠標按下時爲true */ property bool pressed: false /*! 圖片資源 */ property alias source: photoImage.source /*! 鼠標點擊事件 */ signal clicked /*! 返回photoImage的width */ function doSomething() { return photoImage.width } width: { if (photoImage.width > 200) { photoImage.width; } else { 200; } } height: 480 color: "gray" Rectangle { id: photoRect x: 20 y: 20 width: parent.width height: 200 color: "white" Image { id: photoImage anchors.centerIn: parent } } MouseArea { anchors.fill: parent onPressed: { root.pressed = true } onClicked: { root.clicked() } } states: State { name: "selected" when: root.pressed PropertyChanges { target: root.border color: "red" } } transitions: Transition { from: "" to: "selected" ColorAnimation { target: root.border duration: 200 } } }
在聲明組件時,能夠同時聲明其「id」屬性。「id」屬性是該組件的代號,聲明該屬性的好處是,能夠被它的做用域爲:。
同一qml文件中,每一個控件均可以設置id屬性,且不能相同,id相同會有書寫錯誤提示。
具體命名規則以下:
控件默認不設置id屬性,若某個控件的id須要被使用(控件間存在關聯關係),纔要設置id。
例如控件的佈局存在相對位置的關係或控件間的屬性在邏輯上存在依賴關係等,則須要設置id,例如:
Item{ width: 200 height: 300 Button{ id: saveBtn width: 60 height: 30 text: "Save" } Button{ anchors { top: saveBtn.bottom topMargin: 10 left: saveBtn.left } width: 60 height: 30 visible: saveBtn.visible } }代碼中的Item和第二個Button沒有被引用到,沒必要設置id,這樣可避免設置過多的無用id,徒增命名的煩惱。
當子控件要使用父控件的屬性或根據父控件設置相對位置時,不要使用父控件的id,用parent代替id。例如:
Item { id: root width: 800 height: 480 Rectangle{ id: testRect anchors.centerIn: parent width: parent.width/2 height: 200 color: "red" Button{ id: testBtn width: parent.width/2 height: 40 text: "1233" highlighted: (parent.color === "red") ? false : true } } }
ListView { id:listView anchors.centerIn: parent width: 160 height: 240 model: Qt.fontFamilies() delegate: ItemDelegate { id:fontDelegate text: modelData width: parent.width highlighted: ListView.isCurrentItem onClicked:{ listView.currentIndex = index console.log("clicked:", modelData) } } }
代碼中ItemDelegate的id名稱fontDelegate,是不能在ItemDelegate以外被使用的。
在qml代碼中,爲了方便外界使用本自定義控件或者便於qml文件內部控件之間的關聯,常須要自定義一些屬性。
一個property是對象的一個屬性,能夠被賦爲靜態值或者是綁定到動態表達式上。一個property的值能夠被其它的對象讀取。通常狀況下,property屬性也能夠被其它對象修改,除非該QML類型明確指定該property屬性不能被修改。
property var __locale: Qt.locale()
自定義屬性的語法格式以下:
property <propertyType> <propertyName> : value
Rectangle { property string someText: 「 」 onSomeTextChanged: console.log("The someText will be: " + someText) }
property var someNumber: 1.5 property var someString: "aaa" property var someBool: true property var someList: [2,5,"one","two"] property var someObject: Rectangle { width: 100 height: 100 color: "red" } property var theArray: new Array() property var theDate: new Date()
另外,任何的QML對象類型均可以被用做property屬性類型。例如:
property Item someItem property Rectangle someRectangle
這對於自定義QML類型也是適用的。
property alias <name> : aliasreference
name是咱們自定以的屬性名,aliasreference是屬性別名所引用的那個屬性或對象,也就是說屬性別名能夠引用自一個單一的屬性,也能夠引用自一個複雜的對象。屬性綁定是動態的,但不是雙向的,而經過屬性別名可達到動態、雙向的效果,即修改name和aliasreference中任一個的值,與之關聯的另外一個屬性值也會隨之改變。
須要注意的是,若是屬性別名與已有的屬性名相同,就會把已有的屬性覆蓋掉,當咱們再使用這個屬性的時候,實際上使用的是咱們自定義的那個屬性,例如:
Rectangle { id: rootRect // 引用屬性 property alias color: blueRect.color // 引用對象 property alias theRect: blueRect color: "red" Rectangle { id: blueRect color: "#1234ff" } }
Rectangle { id: rootRect property alias count: nameList.count property alias currentIndex: nameList.currentIndex color: "red" ListView{ id: nameList } }
Rectangle沒有count和currentIndex屬性,則要將屬性別名與引用屬性名設置成同樣。
property alias sureBtn: testBtn property alias text: sureBtn.text Button{ id: testBtn width: 100 height: 40 text: "1233" } Component.onCompleted: console.log("text="+text)
產生的錯誤提示: Invalid alias reference. Unable to find id "sureBtn"
任何對象的定義均可以使用readonly關鍵字定義只讀屬性,使用下面的語法:
readonly property <propertyType> <propertyName> : <initialValue>
只讀屬性必須在初始化的時候指定值。一旦只讀屬性被初始化了,它就不可能再被賦值了,不管是賦值(使用」=」)仍是其它的方式。
例如,下面的Component.onCompleted代碼塊就是非法的:
Item{ id: root readonly property int chiledNum: 10 width: 100 height: 40 Component.onCompleted: root.chiledNum = 20 }
信號就是當某些事件發生的時候從對象類型中發出通知:例如,一個屬性改變,一個動畫開始或者中止,當一個圖片下載完成,或者MouseArea類型當用戶點擊的時候就會發射一個點擊信號。
當一個信號發射了,對象能夠經過signal handler被通知。一個signalhandler的定義語法爲on
例如,MouseArea對象的定義中能夠定義onClicked類型的signal handler,當MouseArea被點擊了該signal handler就會被調用,使得控制檯打印出消息:
Item{ id: root width: 100 height: 40 MouseArea{ anchors.fill: parent onClicked: console.log("Click!") } }
qml文件中,定義信號signal的語法:
signal <signalName>[([<type> <parameter name>[, ...]])]
信號通常是相關聯的事件觸發的,信號名由英文字母和_組成,已小駝峯的形式命名。
signal clicked signal hovered(string msgInfo) signal activated(real msgXPosition, real msgYPosition)
信號的調用方式:信號所屬控件id.信號
自定義的js函數,能夠帶參數,也能夠不帶出參數,且參數不用指明參數類型,由於函數的參數類型默認是萬能的var類型。
函數名由字母和_組成,且首字母必須是小寫,即以小駝峯的形式命名,例如:
function addZero(num){ if(num < 10) return "0"+num else return num }
function __factorial(a) { a = parseInt(a); if (a <= 0) return 1; else return a * factorial(a - 1); }
當一個qml文件中的js代碼較多即:js函數代碼超過80行時,應根據其分類或應用邏輯單獨做成一個js文件,再將js文件導入qml文件。
js文件的導入方法:
import "jsfile_impl.js" as Jsflie
.import "filename_imp.js" as Filename Qt.include "filename.js"`
StateGroup { states: [ State { name: "xxx" when: visible PropertyChanges { target: xxxBtn visible: true } PropertyChanges { target: yyyBtn visible: true } PropertyChanges { target: zzzBtn visible: fasle } } ] }
StateGroup { states: [ State { name: "start" when: 0 == animationState PropertyChanges { target: imageAnimation running: true } }, State { name: "pause" when: 1 == animationState PropertyChanges { target: imageAnimation paused: true } }, State { name: "stop" when: 2 == animationState PropertyChanges { target: imageAnimation running: false } } ] }
StateGroup { states: [ State { name: "color" when: bChanged PropertyChanges { target: rootRect color: "red" } }, State { name: "enabled" when: disabled PropertyChanges { target: rootRect enabled: false } } ] }
StateGroup { states: [ State { name: "xxx" when: 0 == loaderState PropertyChanges { target: xxxLoader source: "Xxx.qml" } }, State { name: "yyy" when: 1 == loaderState PropertyChanges { target: xxxLoader source: "Yyy.qml" } }, State { name: "zzz" when: 2 == loaderState PropertyChanges { target: xxxLoader source: "Zzz.qml" } } ] }
// statetest.js function getColor() { return "green"; } // test.qml import "statetest.js" as StateTest Rectangle{ id: rect width: 50; height: 50 color: "red" MouseArea { anchors.fill: parent onClicked: rect.state = "changedColor" } StateGroup { states: State { name: "changedColor" StateChangeScript{ name: "myScript" script: rect.color = StateTest.getColor() } } } }
Rectangle { id: rect width: 120; height: 120 color: "black" Rectangle { id: myRect; width: 50; height: 50; color: "red" } StateGroup { states: State { name: "reanchored" AnchorChanges { // 改變 myRect 的anchors屬性 target: myRect anchors.top: rect.top anchors.bottom: rect.bottom } PropertyChanges { target: myRect anchors.topMargin: 10 anchors.bottomMargin: 10 } } } // 鼠標事件 MouseArea { anchors.fill: parent onClicked: rect.state = "reanchored" } }
Item { width: 200 height: 100 Rectangle { id: redRect width: 100 height: 100 color: "red" } Rectangle { id: blueRect x: redRect.width width: 50 height: 50 color: "blue" states: State { name: "reparented" ParentChange { target: blueRect parent: redRect x: 10 y: 10 } } MouseArea { anchors.fill: parent onClicked: blueRect.state = "reparented" } } }
states中每一個state之間的觸發條件與PropertyChanges中的屬性之間不要有關聯,如有關聯,可能會造成死循環。
在qml中使用states時,必定要加上父層StateGroup,這樣能夠避免與原始控件中state名相同的state相沖突,產生沒法預料的問題。
能用條件運算符替代states的地方,儘可能用條件運算符,不要用states。條件運算符的執行速度比state快。
StateGroup { states: State { name: "name" when: bChanged PropertyChanges { target: root color: "red" } } }
應修改成:
color: bChanged ? "red" : "white"`
例如:
錯誤的寫法
StateGroup { states: State { name: "name" when: bChanged PropertyChanges { target: root color: "red" } PropertyChanges { target: root border.color: "red" } } }
應該修改成:
StateGroup { states: State { name: "name" when: bChanged PropertyChanges { target: root color: "red" border.color: "red" } } }
與「根組件定義」相同,可是其屬性、信號、方法命名需添加雙下劃線("__")前綴。
好比:
Item { id: root width: 800 height: 480 Item { id: subItem property bool __updateState:false signal __clicked function __doSomething(x) { return x+subItem.x } width: 400 height: 400 } }
爲了提升qml代碼的可讀性和便於後期維護,註釋是必不可少的。
文件功能註釋是對此qml大概功能的一個概述,若此qml文件還做爲控件別的qml文件使用,則還有寫出大概的使用方法。
例以下面是combobox.qml文件功能的註釋:
/*! 將一個model的數據如下拉框列表的形式顯示,經過選擇下拉框列表數據,改變當前顯示的數據 \qml ComboBox { width: 200 model: [ "Banana", "Apple", "Coconut" ] } \endqml */
註釋以「/!」開始,以「 /」 結束,包含的對文件功能的說明,和控件的使用方法,使用用例以「\qml」開始,以「\endqml」結束。
/*! ComboBox下拉框列表當前選中的index值 */ property int currentIndex: 0
這裏的其它註釋是指除文件功能註釋和自定義屬性、信號、方法註釋外的其它須要註釋的代碼。
代碼註釋格式爲:「 // xxxxxxxxx 」,寫在被註釋代碼上方,例如:
// temporarily set fillIndex to new item fillIndex = __items.length
通常屬性設置的順序爲:
id: rootRect x: 10 y: 10 width: 100 height: 100 ...
在對控進行屬性設置時,若是有同一類組的多個屬性須要設置,爲了提升代碼可讀性,則使用組的提示符,而不是點的運算符號。
例如:
anchors.top: parent.top anchors.left: parent.left anchors.topMargin: 10 anchors.leftMargin: 10
應該改寫成:
anchors { top: parent.to left: parent.left topMargin: 10 leftMargin: 10 }
在qml文件中,有許多的邏輯處理既能夠在qml中完成,也能夠在c++中處理,原則是qml代碼只處理界面顯示邏輯,其它邏輯能在c++中處理的,一概在c++中處理,這樣有利於提升qml的加載速度 。
MouseArea除了處理Item的鼠標響應,還有一個典型的用途是,MouseArea { anchors.fill: parent }可以屏蔽鼠標事件的滲透。
例如:
// main.qml Rectangle{ width: 800 height: 480 Button{ width: 150 height: 50 text: "Button" onClicked: { if(ld.source === 「 」){ ld.source= "TestChild.qml" } } } Loader{ id:ld } } // TestChild.qml Rectangle{ width: 800 height: 480 }
當TestChild界面加載後,在TestChild界面上與main界面的Button所處位置的重疊處點擊,會觸發main界面Button的點擊事件,
這就是一個界面的鼠標事件參透到另外一界面。爲了不鼠標滲透事件的發生,TestChild.qml應該修改成:
Rectangle{ width: 800 height: 480 MouseArea { anchors.fill: parent } }
注意:MouseArea { anchors.fill: parent }要做爲TestChild界面的第一個子控件,才能屏蔽整個TestChild的鼠標滲透,又不影響TestChild界面中其它可操做控件事件。
1.Button -> btn
1.CheckBox -> chk
1.ComboBox -> cmb
1.RadioButton -> rdo
1.Text -> txt
1.Label -> lbl
1.Image -> img
1.DateTimePicker -> dtp
1.ListView -> lvw
1.ToolTip -> tip
1.GroupBox -> grp
1.Panel -> pnl
1.Calendar -> cal
1.TreeView ->trvw
1.ListView->lvw
1.GridView -> gvw
1.Repeater->rpt
1.Menu ->mnu
1.ProgressBar ->prg
所謂屬性綁定,就是創建目標對象對其它對象的依賴關係,當其它對象發生變化時,目標對象也會變化,自動更新相關的屬性值,達到動態屬性的效果,例如:
Item { id: root width: 800 height: 480 focus: true Keys.enabled: true // 從新綁定 Keys.onEnterPressed: redRect.width = Qt.binding(function() {return parent.width/2}) Rectangle { id: redRect anchors.fill: parent width: parent.width/3 // 綁定 height: 100 color: "red" } Button{ width: 60 height: 30 text: 「Button」 onClicked: redRect.width = 150 // 綁定解除 } }
上述例子中,redRect的width屬性最初綁定到了root的width屬性上,redRect的width會隨着root的width變化而變化;Button按下後,redRect的width屬性綁定解除;Enter鍵按下後,redRect的width屬性從新綁定;Button再次按下後,redRect的width屬性綁定解除。
也就是說若是屬性綁定已經完成,那麼在別的地方從新給這個屬性賦值時,不論是賦個靜態值,仍是想換個別的屬性綁定表達式,都會破壞原來的綁定關係,而新的值也不會有動態屬性的效果。
Keys是專門用來處理鍵盤事件KeyEvent的,它定義了許多針對特定按鍵的信號,例如digit0Pressed(KeyEvent event)、spacePressed(KeyEvent event)等,不過使用pressed(KeyEvent event)和released(KeyEvent event)這兩個普通的信號就能夠處理大部分按鍵事件了,信號的參數類型是KeyEvent,參數名是event,包含了按鍵的詳細信息。
這裏說所的載體是指Keys所屬的父組件,Keys的載體必須是Item,或從Item繼承而來,不能是Window或從Window繼承而來的組件,不然Keys無效。
例如:
Window { visible: true width: 360 height: 360 Keys.enabled: true; Keys.onEscapePressed: Qt.quit() // 沒有功能: 不退出
Window { visible: true width: 360 height: 360 Item{ anchors.fill: parent focus: true Keys.enabled: true; Keys.onEscapePressed: Qt.quit() // 有功能: 退出 }
QML的Loader元素常常備用來動態加載QML組件。可使用source屬性或者sourceComponent屬性加載。這個元素最有用的地方是它能在qml組件須要的時候再建立,即延遲建立QML的時間。
加載與被加載組件中都有相同的事件,那麼須要設置Loader的屬性focus爲true,且設置被加載組件 focus: true才能讓事件不被傳播到被加載組件。
main.qml Item { property bool isFirst : false; width: 200 height: 200 Loader { id: pageLoader source: "Page2.qml" focus: true } Keys.onPressed: { console.log("Captured: ", event.text); event.accepted = true; } } // Page2.qml Rectangle { width: 100 height: 62 Text { anchors.centerIn: parent text: "Page2 Test" } focus: true Keys.onPressed: { console.log("Loaded item captured: ", event.text) event.accepted = true } }
若將main.qml中Item的focus設置爲true,則上述代碼的只有main響應按鍵消息。
若是source或者sourceComponent更改了,任何先前實例化的項目都將被自動銷燬。
將source設置爲空字符串或者sourceComponent設置爲undefined,則銷燬當前加載的項目,釋放資源,並將Loader設置爲空。
Loader出來的qml界面,在界面關閉時,必定要清空Loader資源。
在獨立項目或者Roshan插件中,qml的目錄結構都是相似的。
資源
qml.qrc
components
img
views
main.qml
QML中,model是爲ListView,GridView和Repeater等元素來提供要顯示數據的數據模型.這些元素須要一個爲模型中的每一項數據生成一個實例的代理組件(delegate component).給代理提供的數據經過叫作角色的數據綁定到代理.
例以下述代碼中,ListModel有兩個角色:text 和 age,與代理模型Text的屬性text的名稱相沖突。
在這種狀況下,若是直接在代理中綁定角色名稱,則代理Text的text不會顯示模型中text角色值,而是顯示它自身的屬性值。爲了不這種衝突的產生,在角色前面加上model.便可正常顯示。
Item{ width: 200 height: 200 ListView{ id: testList anchors.fill: parent model: testModel delegate: Text{ width: lst.width height: 30 // text: text + "-" + age text: model.text + "-" + model.age } } ListModel{ id: testModel ListElement{ text: "ccw" age: 12 } ListElement{ text: "ym" age: 13 } ListElement{ text: "zfb" age: 35 } ListElement{ text: "ded" age: 30 } } }
js代碼中,可以用條件運算符(? :)代替 if...else...地方,要用條件運算符,這樣是代碼更簡潔明瞭。
js中「==」和「===」的區別:
==用於通常比較,在比較的時候能夠轉換數據類型。
===用於嚴格比較,只要類型不匹配就返回flase。
當一個qml文件中,代碼行達到200至300行時,要從其qml文件中分解出子qml文件。
這樣能夠避免出現控件關聯複雜,篇幅較長的代碼。
只有一條語句的代碼塊,寫成一行,例如:
MouseArea { anchors.fill: parent }
在信號處理代碼中,若是也只有一條執行語句,則寫成一行,且不須要{},例如:
onPressed: { rootRect.color= "gray" }
應該寫成:
onPressed: rootRect.color= "gray"