Qt on Android:QML 语言基础

在《Qt on Android:Qt Quick 简介》中我们提到 QML 语法和 Json 类似,请参考《Qt on Android: http下载与Json解析》查看 Json 语法。当然这里我们是期望从零开始也能学会 QML ,所以呢,你也可以直接往下看。

对象

QML 文件的后缀是 qml ,其实就是个文本文件。下面是 一个简单的 QML 文件:

import QtQuick 2.0
import QtQuick.Controls 1.1
import QtQuick.Dialogs 1.1
import an.qt.ImageProcessor 1.0
import QtQuick.Controls.Styles 1.1

Rectangle {
    width: 320;
    height: 480;
    color: "#121212";

    Image {
        source: "images/IMG_001.jpg";
        anchors.centerIn: parent;
    }
}

这个简单的 QML 文件的开始是 import 语句,如 import QtQuick 2.0 这句,会引入 QtQuick 2.0 模块,哇,真是废话!接着废话吧。 import 和 C++ 中的 #include 类似,与 Java 中的 import 效果一样,与 JavaScript 中的……唐僧了,打住。

Rectangle{ } 语句,定义了一个类型为 Rectangle 的对象。如果你看了《Qt on Android: http下载与Json解析》一文中有关 Json 的语法描述,应该已经知道对象要用一对花括号来描述。没错, QML 里也是这样,不过呢,花括号前要写上对象的类型。就这么简单!

示例 QML 文档中有两个对象,一个是 Rectangle ,一个是 Image 。

在花括号之间,是对象的属性描述(还可以有其它的,后面再说),属性是以 "property: value" 形式指定的,这点和 Json 一样。如你所见, Rectangle 对象有 width 、 color 等属性。

属性可以分行书写,此时语句后可以不要 ";" 号,不过笔者建议 C++ 程序猿都加上 ";" ,这会避免你患上精神分裂症。当然,也可以把多个属性写在一行内,多个属性之间必须以 ";" 分割。如下所示:

Rectangle {
    width: 320; height: 480; color: "#121212";
}

我强烈建议你不要这么干!除非有代码意外的原因,比如排版需要,比如老板觉得你代码行数太多……

表达式

在《Qt on Android:Qt Quick 简介》中笔者已经提到, QML 支持 JavaScript 表达式。比如你可以这样改写 Rectangle 对象的宽、高属性:

Rectangle {
    width: 23*10;
    height: 6*80;
    color: "#121212";
}

我只是示意啊,你在实际项目中可别这么写,这种行为往不好听里说,有点儿脑残……当然我也可以举一个有意义的示例:

Button {
    text: "Quit";
    style: ButtonStyle {
        background: Rectangle {
            implicitWidth: 70;
            implicitHeight: 25;
            border.width: control.activeFocus ? 2 : 1;
        }
    }
}

在这个示例中我指定了按钮风格中的背景矩形,在按钮有焦点时边框宽度为 2 没有焦点时宽度为 1 。语句 "border.width: control.activeFocus ? 2 : 1" 使用了 JavaScript 的 "?:" 三元云算法( C++ 中貌似也有……)。

另外,慧眼如你,可能已经注意到,上面的表达式中我使用了 "control.activeFocus" ,没错,在表达式中可以引用其它对象及其属性。当你这么做的时候,待赋值的属性就和你所引用的对象的那个属性建立了关联,当被引用属性发生变化时,表达式的值会重新计算,而待赋值的属性也会变化。

也许你心中已经有了疑问:如何引用一个对象呢?答案是:通过对象的 id 值来引用一个对象。看这里:

Rectangle {
    width: 320;
    height: 480;

    Button {
        id: openFile;
        text: "打开";
        anchors.left:  parent.left;
        anchors.leftMargin: 6;
        anchors.top: parent.top;
        anchors.topMargin: 6;
    }

    Button {
        id: quit;
        text: "退出";
        anchors.left: openFile.right;
        anchors.leftMargin: 4;
        anchors.bottom: openFile.bottom;
    }
}

上面的示例中,退出按钮使用 id( openFile )引用了打开按钮。

我的乖呀,anchors 是什么东东……先别管它,下一篇会讲到。

注释

在 QML 中,注释与 C++ 中一样,单行以 "//" 开始,多行以 "/*" 开始以 "*/" 结束。

注释是不被执行的,添加注释可对代码进行解释或者提高其可读性。注释同样还可用于防止代码执行,这对跟踪问题是非常有用的。

使用注释的示例 QML :

/*
 * the root element of QML
 */
Rectangle {
    width: 320;
    height: 480;

    Button {
        id: quit;
        text: "退出";
        //use anchors to layout
        anchors.left: openFile.right;
        anchors.leftMargin: 4;
        anchors.bottom: openFile.bottom;
        //set z-order
        z: 1;
    }
}

属性

其实, QML 中的属性,就是我们非常熟悉的 C++ 中的成员变量……

属性命名

属性名的首字母一般以小写开始,如我们看烦了的 width 属性。

如果属性名以多个单词表示,那么第二个及以后的单词,首字母大写。

属性类型

可以在 QML 文档中使用的类型大概有三类:

  • 由 QML 语言本身提供的类型
  • 使用 QML 模块注册 C++ 类型
  • 由 QML 模块提供的类型

我们先看 QML 语言提供的基本类型。

基本类型

QML 支持的基本类型包括整型、实数型、布尔、字符串、颜色、列表等等。这些基本类型有些是和 JavaScript 语言的基本类型对应的。

还是之前的示例,修改了一下,通过注释标注了属性类型:

Rectangle {
    width: 320; //int
    height: 480;

    Button {
        id: quit;
        text: "退出"; //string
        anchors.left: openFile.right;
        anchors.leftMargin: 4;
        anchors.bottom: openFile.bottom;
        z: 1.5; // real
        visible: false; //bool
    }
}

注意, QML 中属性是有类型安全检测的,也就是说你只能指定与属性类型匹配的值,否则会报错。

请使用 Qt 助手的索引模式,以"qml basic types " 为关键字检索,找到 QML Basic Types 页面来查看完整的类型列表和每个类型的详情。

Qt 的 QML 模块还未 QML 引入的很多 Qt 相关的类型,如 Qt 、 QtObject 、Component 、 Connections 、 Binding 等,请使用 Qt 助手检索 "qt qml qml types" 来了解。

id 属性

之前在介绍表达式时提到了 id 属性,这里展开描述一下。

一个对象的 id 属性是唯一的,在同一个 QML 文件中不同对象的 id 属性的值不能重复。当给一个对象指定了 id ,就可以在其它对象或脚本中通过 id 来引用该对象。在“表达式”一节中我们已经演示了如何通过 id 来引用一个对象。

请注意, id 属性的值,首字符必须是小写字母或下划线并且不能包含字母、数字、下划线以外的字符。

列表属性

列表属性类似于下面这样:

Item {
    children:[
        Image{},
        Text{}
    ]
}

列表是包含在方括号内,以逗号分隔的列表元素。看起来是不是挺熟悉?在《Qt on Android: http下载与Json解析》中,我们举过 Json 数组的例子,再看看:

[
  "name":"zhangsan",
  {
    "age":30,
    "phone":"13588888888",
    "other": ["xian", null, 1.0, 28]
  }
]

其实列表和 JavaScript 的数组是类似的,其访问方式也一样:

  • length 属性提供了列表内元素的个数
  • 列表内的元素通过数组下标来访问([index])

值得注意的是,列表内只能包含 QML 对象,不能包含任何基本类型(如整型、布尔型)。这点与 Json 是不一样的。下面是访问列表的示例:

Item {
    children:[
        Text{
            text: "textOne";
        },
        Text{
            text: "textTwo";
        }
    ]
    Component.onCompleted:{
        for (var i = 0; i < children.length; i++)
            console.log("text of label ", i, " : ", children[i].text)
    }
}

如果你一个列表内只有一个元素,也可以省略方括号。如下所示:

Item {
    children:Image{}
}

不过笔者还是建议你始终使用方括号,哪怕其中只有一个元素。

有没有什么问题?有就要说啊,闷在心里会憋坏自己的。好吧,你不说我就说了。在我们访问列表的示例中出现了一个新的内容,Component.onCompleted :{} ,这是什么东东呢?接下来我们就来讲它。

信号处理器

信号处理器,其实等价于 Qt 中的槽。但是我们没有看到类似 C++ 中的明确定义的函数……没错,就是这样,你的的确确只看到了一对花括号!对啦,这是 JavaScript 中的代码块。其实呢,你可以理解为它是一个匿名函数。而 JavaScript 中的函数,其实具名的代码块。函数的好处是你可以在其它地方根据名字调用它,而代码块的好处是,除了定义它的地方,没人能调用它,一句话,它是私有的。代码块就是一系列语句的组合,它的作用就是使语句序列一起执行。

让我们回头再看信号处理器,它的名字还有点儿特别,一般是 on{Signal} 这种形式。比如 Qt Quick 中的 Button 元素有一个信号 clicked() ,那么你要可能会写出这样的代码:

Rectangle {
    width: 320;
    height: 480;

    Button {
        id: quit;
        text: "退出";
        anchors.left: parent.left;
        anchors.leftMargin: 4;
        anchors.bottom: parent.bottom;
        anchors.bottomMargin: 4;
        onClicked: {
            Qt.quit();
        }
    }
}

上面的 QML 代码其实已经是一个简单 QML 应用了,这个应用在窗口的左下角放了个退出按钮,当用户点击它时会触发按钮的 clicked() 信号,而我们定义了信号处理器来响应 clicked() 信号——调用 Qt.quit() 退出应用。

你看到了,当信号是 clicked() 时,信号处理器就命名为 onClicked 。就这么简单,以 on 起始后跟信号名字(第一个字母大写)。

分组属性

在某些情况下使用一个 ‘.‘ 符号或分组符号把相关的属性形成一个逻辑组。分组属性可写以下这样:

Text {
    font.pixelSize: 18;
    font.bold: true;
}

也可以这样写:

Text {
    font { pixelSize: 12; bold: true; }
}

其实呢,可以这么理解,font 属性的类型本身是一个对象,这个对象又有 pixelSize / bold / italic / underline 等等属性。对于类型为对象的属性值,可以使用 "." 操作符展开对象的每一个成员对其赋值,也可以通过分组符号(一对花括号)把要赋值的成员放在一起给它们赋值。对于后者,其形式就和对象的定义一样了,起码看起来木有区别。所以呢,又可以这么理解上面的示例: Text 对象内聚合了 font 对象。 OK ,就是聚合。

附加属性

属性真难搞!到现在还没讲完,不但你烦了,我也快坐不住了。我保证,这是最后一个要点了,不过也是最复杂最难以理解的属性了。对于这种玩意儿,我一向的做法时,不能理解的话就接受,你就当它生来如此,存在即合理,只要学会怎么用它就 OK 了。

在 QML 语言的语法中,有一个附加属性(attached properties)和附加信号处理器(attached signal handlers)的概念,这是附加到一个对象上的额外的属性。从本质上讲,这些属性是由附加类型(attaching type)来实现和提供的,它们可能被附加到另一种类型的对象上。附加属性与普通属性的区别在于,对象的普通属性是由对象本身或其基类(或沿继承层级向上追溯的祖先们)提供的。

举个例子,下面的 Item 对象使用了附加属性和附加信号处理器:

import QtQuick 2.0

Item {
    width: 100;
    height: 100;

    focus: true;
    Keys.enabled: false;
    Keys.onReturnPressed: console.log("Return key was pressed");
}

你看, Item 对象可以访问和设置 Keys.enabled 和 Keys.onReturnPressed 的值。 enabled 是 Keys 对象的一个属性。 onReturnPressed 其实是 Keys 对象的一个信号。对于附加信号处理器,和前面讲到的普通信号处理器又有所不同。普通信号处理器,你先要知道信号名字,然后按照 on{Signal} 的语法来定义信号处理器的名字;而附加信号处理器,你只要通过附加类型名字引用它,把代码块赋值给它即可。

最后说下 Keys 对象,它是 Qt Quick 提供的,专门供 Item 处理按键事件的对象。它定义了很多针对特定按键的信号,比如上面的 onReturnPressed ,还定义了更为普通的 onPressed 和 onReleased 信号,一般地,你可以使用这两个信号来处理按键(请对照 Qt C++ 中的 keyPressEvent 和 keyReleaseEvent 来理解)。它们有一个名字是 event 的 KeyEvent 参数,包含了按键的详细信息。如果一个按键被处理, event.accepted 应该被设置为 true 以免它被继续传递。

下面是使用 onPressed 信号的一个示例,它检测了左方向键:

Item {
    anchors.fill: parent;
    focus: true;
    Keys.onPressed: {
        if (event.key == Qt.Key_Left) {
            console.log("move left");
            event.accepted = true;
        }
    }
}

好啦,关于 QML 语言的基础性介绍就到这里,相信现在你已经可以看懂简单的 QML 文档了。有的同学可能又疑问了,这节还有一些东东只见用不见讲啊,比如 Rectangle / Text / Image / Item / Button / Component / Qt 等等,抱歉,现在只能揣着糊涂装明白了,下一篇我们会讲这些东西。

Qt on Android:QML 语言基础

时间: 2024-11-10 01:37:43

Qt on Android:QML 语言基础的相关文章

QML 语言基础

在<Qt Quick 简单介绍>中我们提到 QML 语法和 Json 相似,请參考<Qt on Android: http下载与Json解析>查看 Json 语法.当然这里我们是期望从零開始也能学会 QML ,所以呢,你也能够直接往下看. 版权全部 foruok,转载请注明出处:http://blog.csdn.net/foruok 对象 QML 文件的后缀是 qml ,事实上就是个文本文件.以下是 一个简单的 QML 文件: import QtQuick 2.0 import Q

Qt Quick 之 QML 与 C++ 混合编程详解

Qt Quick 技术的引入,使得你能够快速构建 UI ,具有动画.各种绚丽效果的 UI 都不在话下.但它不是万能的,也有很多局限性,原来 Qt 的一些技术,比如低阶的网络编程如 QTcpSocket ,多线程,又如 XML 文档处理类库 QXmlStreamReader / QXmlStreamWriter 等等,在 QML 中要么不可用,要么用起来不方便,所以呢,很多时候我们是会基于这样的原则来混合使用 QML 和 C++: QML 构建界面, C++ 实现非界面的业务逻辑和复杂运算. 请给

Qt on Android: Qt Quick事件处理之鼠标、键盘、定时器

在<Qt on Android: Qt Quick 事件处理之信号与槽>中介绍了 QML 中如何使用内建类型的信号以及如何自定义信号,这次我们来看看如何处理鼠标.键盘.定时器等事件.这些时间在处理时,通常是通过信号来完成的. 广而告之:我正在参加 CSDN 博文大赛,请给我的参赛文章<Qt on Android: Qt Quick 事件处理之信号与槽>投票,谢谢. 鼠标事件处理 桌面开发的话,难免要处理鼠标事件-- 变色矩形示例 看一个简单的处理鼠标事件的例子,先看代码(handl

Qt on Android: Qt Quick 事件处理之信号与槽

前面两篇文章< Qt on Android:QML 语言基础>和<Qt on Android: Qt Quick 简单教程>中我们介绍了 QML 语言的基本语法和 Qt Quick 的常见元素,亲们,通过这两篇文章,您应该已经可以完成简单的 Qt Quick 应用了.接下来呢,哈,我们要介绍 Qt Quick 中一个灰常灰常重要的主题:事件处理.这将是比较长长长长的一篇,哦,不还有后续好几篇--废话少说,还是谈正事儿吧兄弟姐妹们. 本文参加 CSDN 博文大赛,请点这里投我一票,谢

Qt on Android: Qt Quick 事件处理之捏拉缩放与旋转

上一篇<Qt on Android: Qt Quick事件处理之鼠标.键盘.定时器>我们介绍了常见的鼠标.键盘.定时器的处理,鼠标.键盘都是电脑上我们最常使用的事件,这节我们来介绍 Android 智能手机上的一个非常重要的手势:捏拉手势.捏拉手势最早在苹果手机上得到应用,苹果还曾经尝试为此操作申请专利,借以钳制三星在美国的手机和平板销售.这些我们且不管它,咱们只说在 Qt Quick 中如何处理捏拉手势. 广而告之:我正在参加 CSDN 博文大赛,请给我的参赛文章<Qt on Andr

Qt on Android: Qt Quick 简单教程

上一篇<Qt on Android: Qt Quick 之 Hello World 图文详解>我们已经分别在电脑和 Android 手机上运行了第一个 Qt Quick 示例-- HelloQtQuickApp ,这篇呢,我们就来介绍 Qt Quick 编程的一些基本概念,为创建复杂的 Qt Quick 应用奠定基础. 版权所有 foruok ,如需转载请注明来自博客 http://blog.csdn.net/foruok . 首先看一下<Qt on Android: Qt Quick

Qt on Android: Qt Quick 之 Hello World 图文详解

在上一篇文章,<Qt on Android:QML 语言基础>中,我们介绍了 QML 语言的语法,在最后我们遗留了一些问题没有展开,这篇呢,我们就正式开始撰写 Qt Quick 程序,而那些问题,随着本系列文章的展开也会一一被干掉. 在开始介绍 Qt Quick 应用的基本元素之前,我们先来创建一个 HelloQtQuickApp 项目,就是经典的 Hello World 了. 笔者的教程最终会面向 Qt Quick 与 C++ 混合编程,所以我们 HelloQtQuickApp 从零开始.

Qt on Android: Qt Quick 组件与对象动态创建详解

在<Qt on Android: Qt Quick 事件处理之信号与槽>一文中介绍自定义信号时,举了一个简单的例子,定义了一个颜色选择组件,当用户在组建内点击鼠标时,该组件会发出一个携带颜色值的信号,当时我使用 Connections 对象连接到组件的 colorPicked 信号,改变文本的颜色. 当时用到的 Component . Loader 两个特性,一直没来得及介绍,可能很多人都还在雾里看花呢.这次呢,我们就来仔仔细细地把他们讲清楚. 请给我的参赛文章<Qt on Androi

Qt Quick 之 QML 与 C++ 混合编程具体解释

Qt Quick 技术的引入.使得你能够高速构建 UI ,具有动画.各种绚丽效果的 UI 都不在话下.但它不是万能的.也有非常多局限性,原来 Qt 的一些技术,比方低阶的网络编程如 QTcpSocket ,多线程,又如 XML 文档处理类库 QXmlStreamReader / QXmlStreamWriter 等等,在 QML 中要么不可用,要么用起来不方便,所以呢,非常多时候我们是会基于这样的原则来混合使用 QML 和 C++: QML 构建界面. C++ 实现非界面的业务逻辑和复杂运算.