【QT-QML】2. QML语法

目录

    • [1. QML 语法](#1. QML 语法)
      • [1.1 概述](#1.1 概述)
      • [1.2 属性](#1.2 属性)
      • [1.3 脚本](#1.3 脚本)
      • [1.4 绑定](#1.4 绑定)

1. QML 语法

1.1 概述

QML 是一种声明式语言,用于描述对象和对象之间的关系。QtQuick 是一个基于 QML 构建的框架,用于构建应用程序的用户界面。它将用户界面分解成更小的元素,这些元素可以组合成组件。QtQuick 描述了这些用户界面元素的外观和行为。这种用户界面描述可以通过 JavaScript 代码进行丰富,从而提供简单或更复杂的逻辑。从这个角度来看,它遵循 HTML-JavaScript 模式,但 QML 和 QtQuick 从一开始就被设计用于描述用户界面,而不是文本文档。

QtQuick 最简单的用法是创建元素层级结构。

子元素继承父元素的坐标系。x,y 坐标始终相对于父元素。
提示:
QtQuick 基于 QML 构建。QML 语言本身只包含元素、属性、信号和绑定。
QtQuick 是一个基于 QML 的框架。通过使用默认属性,可以优雅地构建 QtQuick 元素的层次结构。

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

复制代码
// RectangleExample.qml

// import 语句用于导入模块。可以添加一个可选的版本号,格式为 <major>.<minor>
import QtQuick

// 可以使用 // 进行单行注释,或使用 /* */ 进行多行注释。这与 C/C++ 和 JavaScript 类似。
// 每个 QML 文件都必须只有一个根元素,就像 HTML 文件一样。
// The root element is the Rectangle
// 元素由其类型后跟 { } 声明。
Rectangle {
	// 可以使用 id (未加引号的标识符)访问 QML 文档中的任意元素。
    // name this element root
    id: root

	// 元素可以拥有属性,属性格式为 name: value
    // properties: <name>: <value>
    width: 120; height: 240

    // color property
    color: "#4A4A4A"

	// 元素可以嵌套,也就是说,一个父元素可以有子元素。可以使用 parent 关键字访问父元素。
    // 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'
    }
}

通常情况下,您需要通过 ID 访问特定元素,或者使用 parent 关键字访问 parent 元素。因此,最佳实践是将根元素命名为"root",并使用 id: root 。这样,您就无需考虑如何在 QML 文档中命名根元素了。

1.2 属性

元素通过元素名称声明,但通过属性或创建自定义属性来定义。属性是一个简单的键值对,例如 width: 100 , text: '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) 一个属性可以依赖于一个或多个其他属性。这被称为绑定。当所依赖的属性发生变化时,绑定的属性也会随之更新。它的工作机制类似于一种契约,在这种情况下, height 应该始终是 width 的两倍。
  • (4) 使用 property 限定符后跟类型、名称和可选的初始值 ( property <type> <name> : <value> ),即可向元素添加新属性。如果没有给出初始值,则会选择一个默认初始值。
  • (5) 声明属性的另一种重要方式是使用 alias 关键字( property alias <name>: <reference> )。 alias 关键字允许我们将类型内部的对象属性或对象本身转发到外部作用域。稍后在定义组件时,我们将使用这种技术将内部属性或元素 ID 导出到根级。属性别名不需要指定类型,它直接使用所引用的属性或对象的类型。
  • (6) text 属性依赖于 int 类型的自定义属性 times 。基于 int 的值会自动转换为 string 类型。该表达式本身是绑定的另一个示例,其结果是每当 times 属性发生变化时,文本都会随之更新。
  • (7) 某些属性是分组属性。当一个属性具有更复杂的结构,且相关属性应当组合在一起时,就会使用此特性。编写分组属性的另一种方式是 font { family: "Ubuntu"; pixelSize: 24 }
  • (8) 某些属性属于元素类本身。这适用于在应用程序中仅出现一次的全局设置元素(例如键盘输入)。其写法是 <Element>.<property>: <value>
  • (9) 对于每一个属性,你都可以提供一个信号处理器。该处理器会在属性发生变化后被调用。例如,在这里我们希望在高度变化时获得通知,并使用内置的控制台向系统记录一条消息。

1.3 脚本

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. Need to use function to capture parameters
    onTextChanged: function(text) { 
        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 会在每次因按下空格键导致文本更改时打印当前文本。由于我们使用了信号注入的参数,因此这里需要使用函数语法。虽然也可以使用箭头函数((text) => {}),但我们认为 function(text) {} 的可读性更高。
  • (2) 当文本元素接收到空格键(因为用户按下了键盘上的空格键)时,我们会调用一个 JavaScript 函数 increment()
  • (3) 以function <name>(<parameters>) { ... }形式定义的 JavaScript 函数,用于递增我们的计数器 spacePresses 。每当 spacePresses 递增时,绑定的属性也会随之更新。

1.4 绑定

QML : (绑定)与 JavaScript = (赋值)的区别在于绑定是一种契约,并在绑定的生命周期内始终保持有效;而 JavaScript 赋值( = )则是一次性的数值分配。

当为属性设置新的绑定,甚至当为属性分配一个 JavaScript 数值时,绑定的生命周期就会结束。例如,一个将 text 属性设置为空字符串的按键处理器,将会破坏我们的增量显示

复制代码
Keys.onEscapePressed: {
    label.text = ''
}

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

当遇到如本例中这种冲突的属性更改策略时(文本既通过绑定随属性增量更新,又通过 JavaScript 赋值被清空),你就不能使用绑定!你需要在两条属性更改路径上都使用赋值,因为绑定会被赋值操作破坏(契约失效!)。

相关推荐
用户805533698034 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner4 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz9 天前
QML Hello World 入门示例
qt
xcyxiner12 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner13 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner13 天前
DicomViewer (添加模型类)3
qt
xcyxiner14 天前
DicomViewer (目录调整) 2
qt
xcyxiner14 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
LDR00615 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术15 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript