良好的编程规范能够大幅提升一个程序的可读性、可维护性。编码规范是你们达成一致的约定,这样你们的代码就能够互相看懂,维护起来更容易,思想更畅快的交流,经验更快的获得传播。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"