找回密码
 立即注册
Qt开源社区 门户 查看内容

第4篇Qt Quick入门教程之基础(四)QML语法2

2019-2-27 14:26| 发布者: admin| 查看: 1884| 评论: 0

摘要: 来源于微信公众号:yafeilinux和他的朋友们 导语上一篇内容中我们提到了QML中一些最基本的概念和语法,也许有的同学短时间内还无法完全理解透彻,那么,也没有必要再去死抠那些术语了,因为更多的概念和内容已经到 ...
来源于微信公众号:yafeilinux和他的朋友们



导语



上一篇内容中我们提到了QML中一些最基本的概念和语法,也许有的同学短时间内还无法完全理解透彻,那么,也没有必要再去死抠那些术语了,因为更多的概念和内容已经到来!

下面的内容比较多,我们无心让大家为了学习语法而学习,所以,咱们的主线是设计一个小程序。真正想理解一个概念必须把它放到实例中去理解,虽然,大家学习的很多时候也有示例,但是,那只是简短的几行代码,并不能完全展示应有的语境。

这次我们换一种全新的方式,在编写实例程序的同时来学习QML语法,下面咱们开始!

让界面美观起来



上一篇教程中写的程序界面过于简单,完全影响了我们学习的心情和进一步探索的好奇心,所以,咱们先来对界面进行一些美化。

1.美化字体和圆形


首先在 text1 对象定义中添加如下代码:
font.bold: true
font { pointSize: 14; capitalization: Font.AllUppercase }

Behavior on rotation {
    NumberAnimation { duration: 500 }
}

这里使用 font 设置了 text1 的文本字体。然后下面为 text1 的 rotation 旋转属性设置了动画效果,这个咱们后面学习动画的时候再详细介绍。

然后将 colorRect 对象的定义修改如下:
Rectangle {
    id: colorRect
    width: 20 * 2
    height: width
    radius: 20
    border.color: "green"

    anchors.left: text1.right
    anchors.leftMargin: 10
    anchors.verticalCenter: text1.verticalCenter

    MouseArea {
        anchors.fill: parent
        onClicked: {
            console.debug("colorRect: ", parent.border.color)
            text1.rotation += 360
            text1.color = parent.border.color
        }
    }

    Rectangle {
        width: 12 * 2
        height: width
        radius: 12
        color: parent.border.color
        anchors.centerIn: parent
    }
}

这里主要是将以前绿色圆形修改为了绿色圆环,然后在其中间添加了一个小的绿色圆形对象;在MouseArea 点击处理中让 text1 旋转360度,并且设置 text1 的文本颜色为圆形的颜色。现在运行的效果如下图所示。




  • 属性组

    QML 属性可以按照逻辑关系进行分组,这样的属性会包含一些子属性,子属性可以通过点标记法或者组标记法来赋值。例如代码中的 font 属性,既可以使用 font.bold 这样的点标记方式为单一子属性赋值;也可以通过 font { } 这样的形式同时为多个子属性赋值。前面看到的anchors、border也都是属性组。我们在帮助文档处可以明显看到组提示:




2.添加装饰图片


为了让界面更加美观,我们添加几张图片来做装饰。首先将需要的图片放到源码目录,一般是新建一个目录单独存放,比如这里放到了 images 目录中。然后在Qt Creator左上角项目文件列表的 qml.qrc 上右击,选择 Open in Editor 菜单项,如下图所示。



然后在资源文件编辑器界面下方选择添加文件,选中images目录中所有图片,完成后在资源文件编辑器界面按下Ctrl + S 快捷键保存修改。这样就可以在左侧项目文件列表中看到 images 目录以及其中添加的图片文件了。如下图所示。



下面在 Window 根对象最后添加一个 Image类型的图片对象定义:

Image {
    id: backImg
    source: "images/bg1.png"
    width: parent.width
    anchors.bottom: parent.bottom
    fillMode: Image.PreserveAspectFit
}

Image类型在后面也会详细讲解,一般只需要指定它的大小、路径source属性、填充模式fillMode属性即可。使用资源系统中的文件,需要指明路径,比如这里的 "images/bg1.png" ;填充模式 Image.PreserveAspectFit 表明在缩放图片时会保持宽高比不变,这样图片不会被拉伸变形。下面是运行效果。



接下来我们在colorRect 对象的MouseArea 子对象的 onClicked: { } 中添加一行代码实现点击colorRect 切换装饰图片的效果:

backImg.source = "images/bg1.png"

3.实现圆形的交互特效


现在点击圆形可以让文本变换颜色并旋转,并且底部的图片也会更换。为了点击这个圆形时能给用户一个好的反馈,我们为其添加鼠标指针进入和移出时不同效果的设置,其实也挺简单的,只是在 MouseArea 的最后面添加如下代码:
hoverEnabled: true
onEntered: {
    parent.width = 32
    parent.color = "black"
}
onExited: {
    parent.width = 40
    parent.color = "white"
}

因为MouseArea默认是鼠标点击后才会有效果,为了让鼠标指针进入MouseArea区域就可以进行相应设置,需要使用 hoverEnabled: true 开启悬停。然后分别设置了鼠标进入、移出时圆形的不同效果。

4.复制圆形按钮


现在我们的绿色圆形已经具有了简单的按钮功能,下面我们再复制两个这样的按钮到text1文本的不同位置,其实就是复制colorRect的定义代码,然后简单更改 id、border.color、anchors、backImg.source 等处代码,最终运行效果如下图所示。因为复制后代码较多,就不再列出。



到这里程序要实现的功能已经完成了,不过,代码写成这样会妨碍我们走得更远,所以,教程到这里只是开始,下面我们继续!



自定义组件



前面的代码写完已经150多行了,在main文件中写这么多代码并不是什么好习惯。下面我们将 Text 和所有 Rectangle 对象的代码都剪切到单独的文件中。

1.自定义对象类型和组件


在项目中添加新的qml文件,因为现在qml文件默认都在资源文件中,所以我们需要向qml.qrc文件中添加新文件。在项目文件列表 qml.qrc 文件下面的“/”目录上右击,在弹出菜单中选择 “Add New” ,如下图所示。



在弹出的新建文件对话框中,选择Qt分类下的 QML File (Qt Quick 2)模板,如下图所示。



文件名称设置为 ColorText ,注意首字母大写。完成后ColorText.qml文件内容如下:

// ColorText.qml
import QtQuick 2.9

Item {
    id: root
}

自动生成的 import 语句导入的可能是 QtQuick 2.0,这里修改为了 2.9 。Item 类型是Qt Quick中所有可视类型的基类型,比如前面的 Text、Rectangle 等类型都继承自 Item 。因为这里Item作为根对象,所以我们为其设置了 id为 root 。

接下来,我们把 main.qml 中的 Text 和所有 Rectangle 类型对象的代码都剪切到 Item 中。

完成后需要将代码中 backImg.source = "images/bg1.png" 这样的三行代码删除掉。

然后到main.qml文件中,在 Window 对象的最后添加如下代码:
ColorText {
    anchors.centerIn: parent
}

可以看到我们能够直接使用 ColorText.qml 文件的名称作为类型来创建对象。

  • 使用QML文件定义对象类型

    像这种将一个QML文档放到一个以 TypeName.qml 命名的文件中,就定义了一个对象类型,类型名称就是 TypeName,名称必须以大写字母开头,不能包含除字母、数字和下划线以外的字符。这个文档会被QML引擎自动识别为一个QML类型的定义,在同一个目录中的其他QML文件会被自动设置为可用的。所以,这里我们可以像使用一个普通QML对象类型一样来使用 ColorText 。

  • 组件(Component)

    组件是可重用的、封装好的、并提供了已定义接口的QML类型,最常见的组件就是一个单独的QML文件,比如这里的 ColorText.qml 文件就定义了一个组件。除此之外,还可以使用Component类型在文档中定义组件,这里就不再开展讲述了。

为什么要将代码放到一个单独的文件中,除了前面说的可以简化 main.qml 文件以外,还有可封装、可重用等好处。到这里我们的组件已经可用了,但是在外面使用封装好的组件,要实现一定的功能,还必须提供接口。

2.自定义信号


我们在 ColorText.qml 的 Item 根对象前面添加一个信号定义:
Item {
    id: root
    signal clicked(string buttonColor)
    ... ...
}

然后在下面三个Rectangle对象的MouseArea子对象的onClicked中分别调用该信号:
root.clicked(parent.border.color)

现在回到 main.qml 文件中,将ColorText 对象定义更改如下:
ColorText {
    anchors.centerIn: parent
    onClicked: {
        console.log("colorButton: ", buttonColor)
        var value = buttonColor
        switch(value) {
        case "#ff0000": backImg.source = "images/bg2.png"; break;
        case "#0000ff": backImg.source = "images/bg3.png"; break;
        default: backImg.source = "images/bg1.png";
        }
    }
}

当信号发射后,可以在 onClicked 信号处理器中执行需要的操作,这里根据信号发射参数的不同,显示不同的图片。现在运行程序,已经恢复了我们在第一节完成的代码功能。

  • 信号特性(Signal Attributes)和信号处理器特性(Signal Handler Attributes)

    如果大家以前有Qt的基础,那么对信号和槽应该不陌生。在QML中,信号是发生事件的对象发出的通知,比如前面我们用到的 MouseArea 对象被点击了,会发射 clicked 信号,这时可以在对应的信号处理器( Qt C++ 中叫做槽)中获得通知,信号处理器的格式是 on,其中是信号的名称(首字母必须大写),所以前面我们一直在 onClicked 中进行点击按钮后的操作。

    在QML文档中定义一个信号可以使用如下语法:
    signal [([ [, ...]])]

    例如这里定义的 clicked 信号:
    signal clicked(string buttonColor)

    如果信号有参数,那么参数类型必须声明,如果没有参数,那么可以省略后面的小括号。

    发射一个信号,就是调用该信号。当一个信号被发射后,其对应的信号处理器就会被调用,在其中可以直接访问信号的参数。

    另外,还可以使用connect()函数将信号与一个方法或者另外的信号进行关联,语法是:对象id.信号.connect (方法或者信号)

  • 属性改变信号

    QML类型提供了内建的属性改变信号,每当属性值改变时都会发射该信号,可以在对应的 onChanged 信号处理器中进行相应的操作。


3.自定义属性


一个组件中只有根对象的属性可以在外部被调用,也就是说,ColorText.qml文件中创建的 Text 对象和其他 Rectangle对象中的属性是无法在main.qml中定义的 ColorText 对象中调用的。要想在外部访问组件内子对象的属性,需要通过在根对象中定义属性来实现。

我们在ColorText.qml文件的 Item 根对象中添加如下属性定义:
Item {
    id: root
    property string colorText // 定义一个string类型的colorText属性
    signal clicked(string buttonColor)
    ... ...

然后修改 Text 对象中 text 属性:
Text {
    id: text1
    text: colorText
    ... ...

这样,就可以到main.qml文件,在 ColorText 对象中修改文本的内容了:
ColorText {
        colorText: qsTr("hello world!")
        ... ...

当然,这里只是将自定义的属性当做了一个桥梁,如果只是这个目的,那么也可以不使用自定义属性,而是使用属性别名。属性别名更适合将组件内子对象属性暴露给外部对象。我们在Item根对象中继续添加这样一行代码:
property alias textFont: text1.font

然后就可以在 main.qml 的 ColorText 对象处这样来使用:
textFont.underline: true

可以看到,对于导出组件中的属性,使用属性别名更方便。

  • 定义属性特性(Property Attributes)

    可以使用如下语法来自定义一个属性:
    [default] property   : 

    第一个default修饰符是可选的,如果有则表明定义了一个默认属性; propertyType是属性类型,QML的基本类型、对象类型都可以作为属性的类型;属性名称 propertyName 的命名规则与 id 类似,必须以小写字母开头,可以包含字母、数字和下划线。在定义属性的同时可以为其初始化,如果不需要初始化,那么后面的 : 可以省略。

  • 默认属性

    一个对象至多有一个默认属性,当为该对象添加子对象时,如果没有明确指定子对象要分配到的属性名,那么子对象就会被赋值给默认属性。前面我们看到子对象都是直接写的,一般不会赋值给一个属性,这是因为,所有基于Item 的类型都有一个默认属性 data 。

  • 属性别名(Property Aliases)

    与普通的定义属性不同,属性别名不需要分配一个新的唯一的存储空间,而只是将新声明的属性(称为别名属性)作为已存在属性(称为被别名的属性)的直接引用。相比较语法而言,定义属性别名需要使用alias关键字代替属性类型,并且右侧的赋值(被别名的属性)不可省略:
    [default] property alias 

4.自定义方法


对于复杂的操作我们可以在函数中进行,QML中的方法特性可以让我们自定义方法。我们在 Item 根对象中添加如下代码:
function changeDuration(duration) {
    animation.duration = duration
}

这里定义了一个函数来更改动画的持续时间。为了可以访问text1对象中的动画对象,需要为其设置id:
NumberAnimation { id: animation; duration: 500 }

现在就可以在main.qml的 ColorText 对象中调用该函数来修改动画持续时间了:
onClicked: {
    changeDuration(2000)
    ... ...

现在可以运行程序,测试下效果。大家可能会发现,第一次点击按钮后,动画的持续时间并没有改变,等第二次点击按钮时才会生效。这是因为,当ColorText对象被实例化的时候,duration: 500 已经生效了,当按钮点击信号发**的时候,动画已经开始执行了,所以在这里设置的值没有对动画立即生效。

怎么能在 ColorText 对象创建完成的时候就执行changeDuration()函数呢,可以通过 Component.onCompleted 来完成。在main.qml 的 ColorText 对象定义中添加如下代码:
Component.onCompleted: {
    changeDuration(2000)
}

这样当对象被创建以后就会立即执行,所以我们编译运行程序后,动画持续时间已经改变了。

为了不留遗憾,虽然我们要讲的知识已经讲完了,但是我们还是再修改下 ColorText 对象中的相关代码:
onClicked: {
    console.log("colorButton: ", buttonColor)
    var value = buttonColor
    switch(value) {
    case "#ff0000":
        backImg.source = "images/bg2.png";
        changeDuration(500);
        break;
    case "#0000ff":
        backImg.source = "images/bg3.png";
        changeDuration(1000);
        break;
    default:
        backImg.source = "images/bg1.png";
        changeDuration(2000);
    }
}

这样,当点击不同的按钮,会更改不同的图片并为文本设置新的旋转速度。最后,附上该程序在手机端运行效果的截图。




  • 方法特性(Method Attributes)

    一个对象类型的方法就是一个函数,可以用来执行一些处理或者触发其他事件。QML的方法可以用来定义相对独立的可重用的 JavaScript 代码块,这些方法可以在内部调用,也可以被外部对象调用。定义方法的语法如下:
    function ([[, ...]]) {  }

    functionName是函数名称,一般首字母小写;参数是可选的,如果有,不需要指明参数类型,默认是var类型,可以在函数体中通过参数名称来访问这些参数。

  • 在启动时执行操作(Component.onCompleted)

    每一个QML对象类型都包含一个附加的 Component 属性,在对象被实例化完成后该属性会发射completed信号,其对应的onCompleted 处理器会在QML环境完全建立以后执行。所以,我们想要在程序启动后执行一些操作,可以放到 Component.onCompleted 中执行。

结语



这篇教程内容比较多,本来可以分成两篇文章来写,但是整篇的内容是一个有机整体,为了不打断学习的思路,我们放到了一篇教程中呈现给大家。也希望真正想学习的读者可以从头自己敲一遍代码,不要嫌费事,这篇文章的每一个字包括代码都是我亲自手敲的,本来有现成的东西可以复制粘贴,但是为了理顺思路,获得灵感,还是要亲力亲为的。作为初学者,如果连代码都懒得敲,那么也就没有必要学下去了。

喜欢的朋友可以点个赞,咱们下周见!

相关文章



第3篇 Qt Quick入门教程之基础(三)QML语法1




----------------------------------------------------------------------------------------------------------------------
我们尊重原创,也注重分享,文章来源于微信公众号:yafeilinux和他的朋友们,建议关注公众号查看原文。如若侵权请联系qter@qter.org。
----------------------------------------------------------------------------------------------------------------------

鲜花

握手

雷人

路过

鸡蛋

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了