QML Book 第四章 入门 1

4. 出发

本章概述 Qt 5 中使用的声明式用户界面语言 QML。我们将讨论 QML 语法,这是一个元素树,其次是最重要的基本元素概述。稍后我们将简要介绍如何创建我们自己的元素,称为组件,以及如何使用属性操作来转换元素。最后,我们将看看如何在一个布局中排列元素,最后看看可以提供给用户进行输入的元素。

4.1. QML 语法

QML 是用于描述应用程序的声明式用户界面语言。它将用户界面分解成可以随意组合成组件的更小的元素。QML 描述了这些用户界面元素的外观和行为。该用户界面描述可以通过使用简单的 JavaScript 代码,以提供更丰富但也更复杂的逻辑。在这个角度来说,QML 遵循 HTML-JavaScript 模式,但 QML 是从头开始设计的,用于描述用户界面而不是文本文档的语言。

以最简单的方式理解,QML 是元素的层次结构语言。子元素从父元素继承坐标系。子元素的x,y 坐标始终相对于父对象。

示例

我们从一个简单的 QML 文件示例开始,以解释不同的语法。

// RectangleExample.qml

import QtQuick 2.5

// The root element is the Rectangle
Rectangle {
    // name this element root
    id: root

    // properties: <name>: <value>
    width: 120; height: 240

    // color property
    color: "#4A4A4A"

    // Declare a nested element (child of root)
    Image {
        id: triangle

        // reference the parent
        x: (parent.width - width)/2; y: 40

        source: 'assets/triangle_red.png'
    }

    // Another child of root
    Text {
        // un-named element

        // reference element by id
        y: triangle.y + triangle.height + 20

        // reference root element
        width: root.width

        color: 'white'
        horizontalAlignment: Text.AlignHCenter
        text: 'Triangle'
    }
}
  • import 语句用于导入特定版本的模块。一般来说,我们总是要将 QtQuick 2.0 作为初始模块导入
  • 就像 C/C ++ 和 JavaScript 那样,我们可以使用 // 进行单行注释或者使用 /* */ 进行多行注释。
  • 与 HTML 类似,每个 QML 文件都需要有一个根元素
  • 元素由类型后跟 { } 进行声明
  • 元素可以具有自己的属性,它们的格式为 name: value
  • QML 文档中的任意元素可以通过使用其 id(无引号标识符)来访问
  • 元素可以嵌套,这意味着父元素可以有子元素。可以使用 parent 关键字访问父元素

提示:
你会经常使用 id 访问对象或者使用关键字 parent 来访问父对象。有一个比较好的方法是命名我们的根元素对象 id 为 root(id: root),这样就不用去思考我们的 QML 文档中的根元素应该用什么方式命名更好了。记住:尽早确定你的开发风格,并坚持它,是一个好习惯。

注意:
您可以使用操作系统的命令行中的 Qt Quick 运行环境运行示例:

$ $QTDIR/bin/qmlscene rectangle.qml

上述命令行中我们需要将 $QTDIR 替换为 Qt 的实际安装路径。qmlscene 可执行程序会初始化 Qt Quick 运行环境并解释执行我们提供的 QML 文件。
在 Qt Creator 中就更简单一些了,我们可以打开相应的项目文件并运行文档 RectangleExample.qml。

4.1.1. 属性

元素通过使用其元素类型名称进行声明,但通过使用其属性或创建自定义属性进行定义。属性是一个简单的键值对,例如。width: 100text: 'Greetings'color: '#FF0000'。属性具有明确的类型,可以具有初始值。

    Text {
        // (1) identifier
        id: thisLabel

        // (2) set x- and y-position
        x: 24; y: 16

        // (3) bind height to 2 * width
        height: 2 * width

        // (4) custom property
        property int times: 24

        // (5) property alias
        property alias anotherTimes: thisLabel.times

        // (6) set text appended by value
        text: "Greetings " + times

        // (7) font is a grouped property
        font.family: "Ubuntu"
        font.pixelSize: 24

        // (8) KeyNavigation is an attached property
        KeyNavigation.tab: otherLabel

        // (9) signal handler for property changes
        onHeightChanged: console.log('height:', height)

        // focus is need to receive key events
        focus: true

        // change color based on focus value
        color: focus?"red":"black"
    }

我们来看看属性的不同特征:

  1. id 是一个非常特殊的属性值,它用于引用 QML 文件中的元素(在 QML 中称为“文档”)。id 不是字符串类型,而是标识符和 QML 语法的一部分。文档中的 id 需要是唯一的,不能重新设置为不同的值,它的值也不能被查询。(它的行为更像是 C++ 世界中的一个指针)
  2. 属性可以设置为一个值,具体取决于其类型。如果没有给出属性值,将选择一个初始值。我们需要查阅特定元素的文档以获取有关属性初始值的更多信息。
  3. 属性可以依赖于一个或多个其他属性。这被称为绑定。当其依赖属性更改时,也会更新绑定属性。它的工作原理像是一个合同,在上面的例子中的情况下,高度应该是宽度的两倍。
  4. 使用属性限定符后跟类型,名称和可选的初始值(属性 <type> <name>: <value>)来向元素添加自己的属性。 如果没有给出初始值,则选择系统初始值。
    注意:
    如果属性名与已定义的默认属性名不重复,使用 default 关键字你可以将一个属性定义为默认属性。例如,当我们添加子元素时,如果他们是可视化的元素,子元素会自动的添加默认属性的子类型链表(children property list)。
  5. 声明属性的另一个重要方法是使用别名关键字(alias <name>: <reference>)。alias 关键字允许我们将对象或对象本身的属性从类型转发到另一个作用域。稍后在定义组件以将内部属性或元素 id 导出到根级别时,我们将使用此技术。属性别名不需要类型,它使用引用的属性或对象的类型。
  6. 上例中 text 属性的值取决于 int 类型的属性 times 的值,基于 int 的值将自动转换为字符串类型。这个表达式本身就是绑定的另一个实际使用例子,并且每次 times 属性更改时将更新文本属性 text 的结果。
  7. 一些属性是分组的属性。当属性更加结构化并且相关属性应该分组在一起时,将使用此功能。使用分组属性的另一种方法是
font {family: “Ubuntu”; pixelSize: 24}
  1. 上例中的第八点表示:一些附加到元素本身的属性。这适用于在应用程序中只出现一次的全局相关元素(例如键盘输入)。其使用语法是 <Element>.<property>: <value>
  2. 对于上例中的第九点,可以这样理解:对于每个属性,我们都可以提供一个信号处理程序。当属性更改后调用此处理程序。例如,在这里,我们希望在高度变化时收到通知,并使用内置控制台将该属性改变的消息记录到系统。

警告
一个元素 id 应该只在当前文档中被引用。QML 提供了动态作用域的机制,后加载的文档会覆盖之前加载文档的元素 id 标志,这样就可以引用已加载并且没有被覆盖的元素 id 标志,这有点类似创建全局变量。但不幸的是这样的代码阅读性很差。目前这个还没有办法解决这个问题,所以你使用这个机制的时候最好仔细一些,最好不要使用这种机制。如果我们想向文档外提供元素的调用,我们可以在根元素上使用属性导出的方式来提供。

4.1.2. 脚本

QML 和 JavaScript(也称为 ECMAScript)是最好的开发组合。在 JavaScript 章节中,我们将详细介绍这种共生关系。目前我们只需要简单了解这种关系。

    Text {
        id: label

        x: 24; y: 24

        // custom counter property for space presses
        property int spacePresses: 0

        text: "Space pressed: " + spacePresses + " times"

        // (1) handler for text changes
        onTextChanged: console.log("text changed to:", text)

        // need focus to receive key events
        focus: true

        // (2) handler with some JS
        Keys.onSpacePressed: {
            increment()
        }

        // clear the text on escape
        Keys.onEscapePressed: {
            label.text = ''
        }

        // (3) a JS function
        function increment() {
            spacePresses = spacePresses + 1
        }
    }
  1. 文本改变的处理器 onTextChanged 用于在每次按下空格键时打印当前的文本
  2. 当文本元素接收到空格键(因为用户按下键盘上的空格键)消息时,我们调用一个 JavaScript 函数 increment()
  3. 我们需要以 function <name>(<parameters>) {...} 的形式来定义 JavaScript 函数。这个 increment() 函数会在每次我们按下空格键时,为计数器属性 spacePresses 加一,与之绑定的 text 属性的值也会随之改变

注意
QML :(绑定) 和 JavaScript =(赋值)之间的区别在于绑定是一个协议,并且存在于整个生命周期。然而 JavaScript 赋值(=)只会产生一次效果。当一个新的绑定生效或者使用 JavaScript 赋值给属性时,QML 绑定的生命周期就会结束。 例如,将文本属性设置为空字符串的键处理程序将会破坏我们的每次按下空格键时都会产生的增量显示效果:

Keys.onEscapePressed: {
    label.text = ''
}

按下退出键后,按住空格键将不再更新文本的显示内容,因为之前的文本属性绑定(text: "Space pressed: " + spacePresses + " times")已经被破坏了。

当我们对改变属性的策略有冲突时(文本的改变基于一个增值的绑定并且可以被 JavaScript 赋值清零),类似于这个例子,我们最好不要使用绑定属性。我们需要使用赋值的方式来改变属性,属性绑定会在赋值操作后被销毁(销毁绑定协议!)。

本文参考链接:Quick Starter

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,561评论 18 139
  • QML 性能上的注意事项和建议 赵者也[https://www.jianshu.com/u/7b2ff27d6fd...
    赵者也阅读 15,871评论 1 11
  • 13.动态 QML(Dynamic QML) 本章的作者:e8johan ** 注意: **最新的构建时间:201...
    赵者也阅读 2,667评论 0 5
  • 早上好!#幸福实修#~每天进步1%#幸福实修12班-09-唐洁--富阳# 20171019(24/60) 【幸福三...
    你谢谢阅读 338评论 0 2
  • 问世间情为何物,直教人生死相许、以身相许、一生相许…… 相爱时,对方就是自己的全世界。情人眼里出西施,月亮月亮我爱...
    万伊刀阅读 1,964评论 1 2