【QML 快速入门】QML语法-基础篇
Qt Quick 为 Qt 引入了一门叫 QML(Qt Meta/Modeling Language)的脚本语言,它是 ECMAScript 标准的实现。所以 QML 语法是在 ECMAScript 语法的基础上实现的。
ECMAScript 语言的标准是由 Netscape、Sun、微软、Borland 等公司基于 JavaScript 和 JScript 锤炼、定义出来的。
ECMAScript 仅仅是一个描述,定义了脚本语言的所有属性、方法和对象。其他语言可以实现 ECMAScript 来作为功能的基础,正如 JavaScript 那样。这个实现又可以被扩展,包含特定于宿主环境的新特性,比如 QML 就引入了 Qt 对象系统中的信号与槽,还增加了动态属性绑定等非常有特色的新功能。
作为一种全新的编程语言,QML 有三个核心:
- ECMAScript
- Qt 对象系统
- Qt Quick 标准库
一、语法
下面一个个来看 ECMAScript 的语法。(QML 是 ECMAScript 标准的实现,所以两者语法是基本一样的)
1. 区分大小写
与 C++ —样,变量、函数名、运算符以及其他一切东西都是区分大小写的,也就是说, 变量 area 和 Area 是不同的。
2. 弱类型
与 C++ 不同,ECMAScript 中的变量没有特定的类型,定义变量时只用 var 运算符,可以将它初始化为任意的值,你可以随时改变变量所存储的数据类型(实际上应当尽量避免这样做)。例如:
var i = 0
console.log(i)
i = "hello"
console.log(i)
尽管在语法上这么做没有问题,但好的编码习惯是一个变量始终存放相同类型的值。
3. 语句后的分号可有可无
C、C++、Java 等语言都要求每条语句以分号(;)结束。ECMAScript 则允许开发者自行决定是否以分号结束一行代码。如果没有分号,ECMAScript 就把这行代码的结尾看作该语句的结束(与 Lua、Python、Visual Basic 相似)。至于加不加分号,那就看自己的喜好了。下面两行代码的语法都是正确的:
var background = "white"
var i = 0
二、变量
在 ECMAScript 中使用 var 运算符声明变量,与 C++ 类似,变量名需要遵循一定的规则。
1. 变量声明
变量用 var 运算符加变量名来定义。例如:
var i = 0
在这个例子中,声明了变量 i 并把它初始化为 0。你也可以不初始化,在用到时再初始化。
一个 var 语句可以定义多个变量。例如:
var i = 0 , name = "j"
这个例子定义了变量 i,初始化为数字;还定义了变量 name,初始化为字符串。你看到了,这和 C++ 或 Java 不同,一个 var 语句定义的多个变量可以有不同的类型。
2. 变量命名规则
变量命名需要遵守两条简单的规则:
- 第一个字符必须是字母、下画线(_)或美元符号($)。
- 余下的字符可以是下画线、美元符号或者任何字母或数字字符。
下面这些变量名都是合法的:
var test
var objectName
var —phone
var $1
为了代码的可读性,在命名变量时还应该遵循一定的命名风格。因为 Qt 是基于 C++ 的应用框架,QML 又是 Qt 框架的一部分,这里建议和 Qt C++ 代码采取同样的命名风格—驼峰命名法。
对于变量(包括函数名),以小写字母开始,单词之间采用驼峰命名法。对于类名,以大写字母开始,单词之间采用驼峰命名法。
三、原始类型
ECMAScript 有 5 种原始类型,即 Undefined、Null、Boolean、Number 和 String。每种原始类型定义了它包含的值的范围及其字面量表示形式。
ECMAScript 提供了 typeof 运算符来判断一个值的类型,如果这个值是原始类型,typeof 还会返回它具体的类型名字;而如果这个值是引用值,那么 typeof 统一返回 ”object” 作为类型名字。示例如下:
import QtQuick 2.2
Rectangle {
Component.onCompleted:{
var name = "Zhang San Feng"
console.log(typeof name) // 输出:qml:string
console.log(typeof 60) // 输出:qml:number
}
}
变量 name 的类型是 string,字面量 60 的类型是 number。其中 “qml:” 是使用 console.log 输出信息时携带的前缀。
1. Undefined 类型
Undefined 类型只有一个值,即 undefined。当声明的变量未初始化时,该变量的默认值就是 undefined。例如:
var temp
上面的代码声明了变量 temp 但并未显式地讲行初始化,它的值将被设置为 undefined, 这和 C++ 不同。ECMAScript 的这一特性:未初始化的变量也有固定的初始值,我们可以将一个变量和 undefined 比较来实现一些业务逻辑。比如:
var runOnce;
...
if(runOnce == undefined) {
runOnce = true
}
else {
...
}
当函数没有明确的返回值时,返回的值也是 undefined,如下所示:
function blankFunc(){}
console.log(blankFunc() == undefined) // 输出:true
2. Null 类型
Null 类型也只有一个值,即 null。
你可以显式地将一个变量初始化为 null,然后据此实现一些逻辑。
3. Boolean 类型
Boolean 是 ECMAScript 中最常用的类型之一,它有 true 和 false 两个值。
4. Number 类型
Number 类型是最特殊的,它既可以表示 32 位的整数,也可以表示 64 位的浮点数。你在 QML 代码中直接输入的任何数字都被看作是 Number 类型的字面量。
下面的代码声明了存放整数值的变量:
var integer = 10
数字类型的最大值是 Number.MAX_VALUE,最小值是 Number.MlN_VALUE,它们定义了 Number 值的外边界,所有的 ECMAScript 数都必须在这两个值之间。
5. String 类型
ECMAScript 中的 String 类型是作为原始类型存在的,它存储 Unicode 字符,对应的 Qt C++ 类型为 QString。当你混合 C++ 和 QML 编程时,所有的 QString 类型的变量都会被映射为 ECMAScript 中的 String。
字符串字面量可以用双引号(")或单引号(')来声明。而在 Qt 中,只能用双引号, 单引号表示字符。为了一致性,建议你尽可能不要使用单引号表示字符串。在 ECMAScript 中没有字符类型,这也是为什么你可以使用单引号来表示字符串的原因。下面的两行代码都是有效的:
var name = 'Lv Bu'
var name = "Guan Yu"
四、类型转换
如果一种编程语言不支持类型转换,那真是无法想象。在 ECMAScript 中,类型转换非常简单。
1. 转换成字符串
Boolean、Number、String 三种原始类型,都有 toString() 方法,可以把它们的值转换为字符串。比如下面的代码在 Qt 中可以正常运行:
var name = "Zhang San Feng"
console.log(name.toString())
console.log(true.toString())
var visible = false
console.log(visible.toString())
var integer = 3.14159
console.log(integer.toString())
Number 类型的 toString() 方法还可以按基转换,比如:
var integer = 13
console.log (integer.toString(16)) // 输出: D
如果你不指定数基,那不管原来是用什么形式声明的 Number 类型,toString() 都按十进制输出。
2. 转换成数字
parselnt() 和 parseFloat() 可以把非数字的原始值转换成数字,前者把值转换为整数,后者把值转换成浮点数。这两个方法只能用于 String 类型,如果你对其他类型调用它们, 返回值将是奇葩的 NaN。
parselnt() 和 parseFloat() 会扫描字符串,直到遇到第一个非数字字符时停止,将转换的结果返回。比如parselnt(”2014年")
将会返回 2014。对于 parseFloat(),会将第一个小数点作为有效字符,而 parselnt() 则不会。
下面是一些示例:
var numl = parselnt ("2014 年") // 输出:2014
var num2 = parselnt ("OxC") // 输出:12
var num3 = parselnt ("3.14") // 输出:3
var num4 = parselnt ("green") // 输出:NaN_
var num5 = parseFloat ("3.14159") // 输出:3.14159
var num7 = parseFloat ("Am I Float") // 输出:NaN
parselnt() 还支持基模式,下面是一些示例:
var numl = parselnt ("AK47", 16) // 输出:10
var num2 = parselnt ("AK47", 10) // 输出:NaN
var num3 = parselnt ("010", 8) // 输出:8
var nun4 = parselnt ("010", 10) // 输出:10
需要注意的是,代表浮点数的字符串必须以十进制形式表示,比如parseFloat(“OxFE”)
,返回 NaN。
3. 强制类型转换
如果你是 C/C++ 程序员,对强制类型转换一定又爱又恨。ECMAScript 也支持强制类型 转换,有三种转换:
Boolean(value)
,把 value 转换成 Boolean 类型。Number(value)
,把value转换为数字(整数或浮点数)。String(value)
,把value转换成字符串。
五、函数
ECMAScript 中的函数,就是具名的、可重复使用的代码块。另外,ECMAScript 不支持函数重载。
1. 函数语法
函数语法如下:
function functionName(arg1, arg2, ..., argN){
// 要执行的代码
}
function 是定义函数时必须使用的关键字。functionName可以任意取,符合变量命名规则即可。 arg1 到 argN 是函数的参数,当然也可以没有参数。花括号内是要执行的代码块。
无参函数示例:
function quitApp(){
Qt .quit ();
}
带参函数示例:
function showError(msg){
console.log("error - ", msg);
}
function travel(country, city){
console.log("Welcome to ", city, " , ", country);
}
当我们使用函数参数的时候,参数就像不带 var 运算符的变量声明一样。这与 C++ 中必须给函数参数指明类型大相径庭。
2. 函数的返回值
ECMAScript 中的函数,默认都是有返回值的,即便你没有显式使用 return 语句,它也会返回 undefined。如果你想把函数运算的结果返回给调用它的地方,可以使用 return 语句。下面是个简单的示例:
function add(numberl, number2){
var result = number1 + number2;
console.log(number1, "+" ,number2, result);
return result;
}
你可以这样调用 add() 函数:var ret = add(100, 34);
。
六、运算符
ECMAScript 的运算符和 C++、Java 等语言的差不多,具体内容不再赘述。这里只重点介绍一下关键字运算符。void、typeof、instanceof、new、delete 这些都是关键字运算符。
-
void 运算符比较特殊,它放在一个表达式前,舍弃表达式的值,返回 undefined。
-
typeof 前面讲过了,对于原始值,返回原始类型;对于引用值,返回 object。这导致你无法准确判断一个对象的引用类型,所以 ECMAScript 引入了 instanceof 运算符。
-
instanceof 用来测试一个对象的实际类型,你需要显式指定要测试的类型。例如:
var str = new String ("hello world"); console.log (str instanceof String); // 输出:true
-
new 运算符用来创建一个对象,前面用了很多次了,不再赘述。 delete 运算符比较特别,在 QML 中,一般它只能删除一个对象内由你定义的属性,而框架定义的那些核心属性,多数是你不能删除的。我们在 ECMAScript 中调用 delete,多数时候是解除对对象的引用,以免老有人引用某个对象而导致它逍遥法外。
七、使用 console
console 提供了输出日志信息、断言、计时器、计数器、性能分析等功能,这里只介绍前三个我们经常用到的功能。
1. 输出日志信息
console对象提供了多个打印调试信息的方法:
- console.log();
- console.debug();
- console.info();console.warn();
- console.error();
2. 断言
console.assert() 提供断言功能,它接受一个表达式,当表达式的值为 false 时会输出调试信息,打印 QML 所在行。例如:console.assert (false)
。
如果你传递了额外的参数给 console.assert(),它会在控制台输出这些信息。示例:
var years = 0;
for (; years < 18; years++){
console.log("I\'m minor");
continue;
console.log ("You shoult not see me"};
}
console.assert(years < 18, years);
上面的断言语句,将会输出下列信息:
18
onCompleted (file:///F:/projects/qtquick/qmls/show_type.qml:187)
需要注意的是,在 QML 中,使用 console.assert(),断言失败,程序并不会终止运行。
3. 计时器
console 提供了计时器功能,方便我们测量某些代码的耗时情况。
console.time(tag) 启动定时器,字符串类型的 tag 是必需的。console.timeEnd(tag) 停止计时器,在控制台输出某个标签对应的耗时信息。tag 是必需的。 下面是简单的示例:
console.time("regexp");
var str = "We are dogs;\nYour dogs;\nWe want meat.\nPlease.";
var lines = str.match(/^We.*/mg);
console.log(lines.length);
console.log(lines);
console.timeEnd("regexp");
// 输出:regexp: 7ms
参考:
《Qt Quick 核心编程》第5章 ECMAScript初探
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探