QML语法
1.QML基本语法
1.1导入声明
导入声明允许客户端告诉QML引擎可以在QML文档中使用哪些模块,JavaScript资源以及组件目录。文档中可以使用的类型依赖于在文档中导入的模块、资源以及目录。
导入类型
总共有三种类型的导入。对于每一种导入类型在语法上都有细微的差别,并且不同的导入类型有着不同的语义。
*模块(命名空间)的导入
最常用的导入类型就是模块导入。客户端可以导入那些已经注册QML对象类型和JavaScript资源到给定命名空间的QML模块。
导入模块最通用的格式如下:
a) <ModuleIdentifier>使用URI格式指定,用于唯一标示该模块提供的类型命名空间
b) <Version.Number>是以如下格式MajorVersion.MinorVersion标示模块版本号的
c) <Qualifier>是一个可选的,本地命名空间标示符。模块的对象类型和JavaScript资源都安装到这个命名空间中。如果没有指 定,那么该模块的对象类型和JavaScript资源则安装到了全局的命名空间。
一个没有使用限定语的模块导入声明如下:
这个导入示例允许使用者使用QtQuick模块提供的所有类型而不用指定限定词。例如,客户端可以如下创建一个矩形区域:
一个使用了限定词的导入声明的示例如下:
注意:如果一个QML文档使用了模块并未导入的对象类型,那么将会产生错误。例如,下面的文档没有导入QtQuick但是却试图使用Rectangle类型:
在这种情况下,QML引擎将会触发一个错误,并且拒绝加载该QML文件。
【非模块命名空间的导入】
类型可以直接通过C++中的多种注册方法(例如qmlRegisterType())注册到命名空间中。如果类型通过这种方式注册到命名空间中,而且该命名空间又是模块的标示符,那么我们可以通过导入该命名空间来导入该类型。
【 导入到一个本地限定的命名空间】
导入声明可选的使用as关键词将类型导入至指定的文档范围有效的本地命名空间中。如果指定了一个命名空间,那么之后使用 这些类型就必须加上这个限定词作为前缀。
在下面,QtQuick模块被导入到命名空间”CoreItems”。现在,任何引用QtQuick模块中的类型必须加上CoreItems名称前缀:
一个命名空间就像是一个模块在文件内的标示符。命名空间不能成为根对象的属性,根对象可以使用属性、信号和方法。
命名空间导入对于在两个不同模块提供了相同名称的QML对象的情况下十分有效。在这种情况下我们可以将两个模块导入两个不同的本地命名空间中,以此来确保代码可以引用到需要的类型。
注意:多个模块可以导入到同一个命名空间中,同理多个模块可以导入到全局命名空间中。例如:
*目录的导入
我们可以导入包含QML文档的目录到QML文档中。这提供一种重用QML类型的方式:文件系统上的目录
导入目录的一般形式如下:
注意:导入的路径是网络可传输的:应用程序可以像导入本地路径那样导入远端路径。可以查看QML文档网络传输一节中URL解析的规则。如果导入的是远端的
目录,那么那么它必须包含一个目录导入列表文件,因为如果qmldir文件不存在,那么QML引擎就不能识别出远端目录中的内容。
至于<Qualifier>的语义跟前面介绍的一样。关于这种类型的导入,更新信息请见“目录导入”。
【目录导入】
一个包含QML文件的本地目录可以无需任何额外配置直接导入。一个包含QML文件的远端目录也可以被导入,但是需要一个目录列表qmldir文件存在。
一个本地目录也可以有一个可选的目录列表qmldir文件用以定义那些必须导入给客户端的类型名称以及指定那些必须导入给客户端的JavaScript资
源。
【本地目录导入】
任何本地文件系统上的QML文件都可以使用导入声明以及目录的绝对或者相对路径导入本地目录,使得QML文件可以使用定义在该目录中的对象系统。
如果本地目录包含一个目录列表qmldir文件,那么可以使用qmldir文件中指定的对象类型,否则就会使用来自QML文档文件名作为类型名。如果目录
中没有qmldir文件,那么仅仅那些文件名以大写字母开头,以”.qml”结尾的文件名会作为对象类型。
示例:
考虑如下的QML工程目录结构。在顶层目录myapp下,有一个用于存放公共UI组件的子目录mycomponents,主要的程序代码在子目录main下:
Main/application.qml文件可以使用相对路径导入mucomponents目录,使得应用程序可以使用该目录下的QML对象类型:
在导入该目录的时候也可以指定一个限定词:
导入本地路径是十分方便的,但是当目录的位置发生变化,那么我们不得不更新应用程序中的导入声明。但是这也可以通过QML模块来避免,因为一个安装的模块用一个唯一确定的字符串就可以导入,而不需要文件系统路径。
【远端目录导入】
如果远端目录包含一个目录列表qmldir文件,那么QML文件也可以导入远端目录。
例如,如果之前的应用程序myapp目录存放在”http://www.my-example-server.com”,mycomponents目录包含如下定义的qmldir文件:
接着,我们就可以使用URL来导入mycomponents远端目录了:
注意:当导入一个远端目录,我们仅仅可以访问qmldir文件中声明的QML和JavaScript文件。
警告:当导入远端目录时,开发者必须十分小心仅仅加载那些信任的资源而应该避免加载非法代码。
【目录列表qmldir文件】
目录列表文件qmldir区别于模块定义qmldir文件。目录列表qmldir文件允许一组QML文档快速简单的共享,但是它并不定义类型命名空间到文档中,它也不支持QML对象类型版本。
目录列表qmldir文件语法如下:
上面两行的区别在于:没有internal的代表导入该目录的QML文档可使用该对象类型;而包含internal则代表这部分的对象类型为内部的,不能供客户端使用。
一个本地目录可选的包含qmldir文件。这允许引擎仅仅暴露明确罗列在qmldir文件中的对象类型和JavaScript资源给客户端。
[导入目录示例]( qtdeclarative\examples\quick\tutorials\gettingStartedQml)
*JavaScript资源的导入
JavaScript资源可以在QML文档中直接导入。每一个JavaScript资源都必须有一个标示符用以标示对该资源的引用。
通用的JavaScript资源导入声明如下:
注意:这里的<Identifier>必须在文档中是唯一的,而不能像本地命名空间那样可以应用在多个模块的导入。
模块中的JavaScript资源
模块可以提供JavaScript文件,通过添加JavaScript资源标示符到标示模块的qmldir文件中 。
例如,如果projects.MyQMLProject.MyFunctions模块使用如下qmldir文件指定,并且安装到了QML导入路径之中:
那么一个应用程序就可以通过导入该模块来导入JavaScript资源,并且使用JavaScript资源的标示符来引用JavaScript资源:
如果该模块被导入到本地的命名空间中,那么在JavaScript资源标示符之前还要加上本地命名空间的限定词才可以引用JavaScript资源,如下所示:
更多信息
关于JavaScript资源的更多信息,可以参见文档在QML中定义JavaScript资源,对如何导入JavaScript资源以及如何在JavaScript资源中使用导入,则可以参见文档在QML中导入JavaScript资源。
[JavaScript资源导入示例]( qtdeclarative\examples\quick\demos\photoviewer)
QML导入路径
当已安装的模块被导入,QML引擎就会在导入路径中搜寻匹配的模块。
导入路径可以使用QQmlEngine::importPathList()方法获取,定义了引擎默认搜索的路径。默认情况下包含:
² 当前文件所在目录
² 由QLibraryInfo::Qml2ImportsPath指定的路径
² 由环境变量QML2_IMPORT_PATH指定的路径
我们可以通过QQmlEngine::addImportPath()或者是环境变量QML2_IMPORT_PATH添加导入路径。当执行qmlscene工具的时候,你也可以使用-I选项添加导入路径。
调试
在寻找以及加载模块出错的时候QML_IMPORT_TRACE环境变量就十分有用。可以查看模块导入调试获取更多的信息。
【小结】
QML是一种声明语言,允许定义对象和对象的属性,以及它们是如何和其它对象交互的。与原生代码相比较,在原生代码中属性和状态的改变都是经过一系列的处理传递的,但是声明性的QML语言直接在独立的对象中集成了属性和状态的改变。
QML源代码一般都是通过QML文档由QML引擎加载的,QML引擎和QML代码是分离的。这就允许我们定义可重用的QML对象类型。
一个QML文档的开始部分可能有一个或多个导入声明。一个导入声明可能是以下几种情况之一:
*一个带有版本号的命名空间,对象都注册到该命名空间中(例如:插件)
*一个包含了QML文档定义类型的相关目录
*一个JavaScript文档
注意:JavaScript文档导入的时候必须制定限定词,这样我们就可以引用它所提供的属性和方法。
一般的导入类型如下:
例如:
1.2对象声明
一个QML代码块定义了将被创建的QML对象树。对象都是使用对象声明定义的,对象声明描述了对象的类型以及赋予该对象的属性。每一个对象都可以使用嵌套的声明语法定义子对象。
一个简单对象的声明如下:
上例中声明了一个Rectangle类型的对象,之后跟着一对括号包围了该对象的属性。Rectangle对象是QtQuick模块提供的,这里定义的三个属性也都是rectangle的width,height和color属性。
如果上述对象的声明是QML文档的一部分,那么它就可以被QML引擎加载。不过该文档必须导入QtQuick模块:
将上述内容存入.qml文件并使用QML引擎加载,则效果如下:
注意:如果定义的对象只有少数几个属性,那么就可以像这样写在一行,各个属性之间用分号分隔:
显然,在这个例子中定义的Rectangle对象十分简单,只是仅仅定义了它的几个属性值。要创建更加有用的对象,一个对象也许就有许多的属性:这个在QML对象属性文档中描述。另外,一个对象也可能包含子对象,我们下面就来讨论这种情况。
1.2.1子对象
任何对象都可以通过嵌套对象声明子对象。在这种情况下,任何对象的声明都隐式的声明了一个可能包含任意数目子对象的对象树。
例如,一个Rectangle对象的声明可以包含一个Gradient对象的声明,而Gradient对象又包含了两个GradientStop对象的声明:
当这个代码被QML引擎加载时,QML引擎就会创建一个以Rectangle为根对象的对象树;该根对象有一个Gradient子对象,该子对象又包含两个GradientStop子对象。
注意:在QML对象树的上下文中是父-子关系的对象在可视化场景的上下文中不是父-子关系。在可视化场景中的父-子关系是由QtQuick模块提供的
Item类型表达的,Item类型是QML类型的基本类型,因为绝大多数的QML对象都会被渲染。例如:Rectangle和Text都是基于Item类
型的对象,在下面的例子中Text被作为Rectangle对象的可视化子对象声明的:
在上述代码中,如果Text对象引用它父亲的值,那么它引用的就是它的可视化场景中的父亲,而不是对象树中的父亲。在这个例子中,它们是相同的:此时
Rectangle既是Text在对象树中的父对象也是Text在可视化场景中的父对象。然而,父亲属性是可以被修改的用以改变可视化场景中的父对象,而
我们是无法在QML中修改来修改对象树上下文中的父对象。
另外,注意:Text对象并没有作为Rectangle的一个属性声明,不像之前的例子中Gradient对象是指定为rectangle的gradient属性。这是因为Item的孩子属性直接设置为了该类型的默认属性,这样可以使得语法更简单。
可以查看可视化父对象文档获取Item类型的关于可视化父对象更多概念信息。
1.3注释
QML中的注释和JavaScript中的注释十分类似:
*单行的注释是以//开始直到行尾结束;
*多行注释是以/*开始,以*/结束
QML引擎在处理QML代码的时候会忽略注释信息。