qml基础学习 基础概念

一、概括

学习qt已有2年多的时间,从qt4.7开始使用直到现在正在使用的qt5.6,基本都在windows机器上做开发。最近有意向看了下qt的qml部分,觉着还是挺不错的,毕竟可以做嵌入式移动端产品的部分,还是值的一学。后来在网上看了一些资料,算是初步了解了下qml,所以想就自己学习的过程做以记录,也方便自己理解,如果你有机会看到这篇文章,那么我认为你也是来学习qml的,如果你已经是一个有很强qml开发经验的老手,那么这篇文章和接下来的qml学习系列的文章你都不用看下去了,呵呵。。。

关于qml的由来,个人觉着Qt的Script、Quick、QML的关系与总结讲的不错,有兴趣的同学可以去看下。

qml的学习过程我主要是以Qt 学习之路 2博客QmlBook-In-Chinese这本书为主,同时在做小示例的时候查阅帮助文档。每个人的学习方式都不太一样,如果你有更好的办法可以留言。

二、效果预览

如下有4张效果图,分别是4个小示例,关于demo后续章节会有解说,但是都是以代码中的注解为主,有兴趣的同学也可以直接下载示例程序,使用qt提供的qmlscene.exe来直接执行qml文件,或者qmlviewer.exe也可以预览qml文件。

图1 转动的组件

图2 红绿灯1

图3 红绿灯2

图4 GridView使用

三、学习qml必备

  1. 基本元素
  2. 组件,基本元素的复合
  3. 定位器(布局)
  4. 元素布局,锚
  5. 输入元素,一行和多行
  6. quick现有组件
  7. 模型和视图
  8. Canvas元素

1、基本元素

QML 基本元素可以分为可视元素和不可视元素两类。可视元素:Item、Rectangle、Text、Image;不可见元素:MouseArea。关于MouseArea是不可见元素这一点我需要强调一下,因为上边我提到的两篇学习文章都没有说清楚,图5是qt5.7的帮助文档截图,从图中我们一眼就能看出结果,MouseArea确实是不可见元素。

图5 MouseArea帮助文档

关于基本元素我觉着qmlbook这本书相关章节的最后一段说的很有意思,特此说明,如图6所示

图6 qml显示和交互分开

理解这些基本元素,你可以认为他们是一个个被封装好的类,而且他们有非常之多的属性,这里我就不介绍了,因为帮助文档说的太清楚了。

2、组件

组件其实就是基本元素的复合,放到一个单独的文件,方便我们以后重用,关于怎么创建组件,本节的后续我会给出自己做的示例程序,代码很简单只是为了说明问题

3、定位器

定位器主要有 Row、Column、Grid和Flow等。

4、元素布局

除了定位器,我们还可以使用锚(anchor)来布局元素

5、输入元素

键盘输入的两个元素:TextInput和TextEdit。TextInput为一行输入,TextEdit为多行输入

6、quick组件

如下是Qt Quick Controls 1.1 提供的组件

  • ApplicationWindow 对应QMainWindow,提供顶层应用程序窗口
  • MenuBar 对应QMenuBar,提供窗口顶部横向的菜单栏
  • StatusBar 对应QStatusBar,提供状态栏
  • ToolBar 对应QToolBar,提供工具栏,可以添加ToolButton和其它组件
  • Action 对应QAction,提供能够绑定到导航和视图的抽象的用户界面动作
  • 导航和视图
  • 方便用户在一个布局中管理和显示其它组件
  • ScrollView 对应QScrollView,提供滚动视图
  • SplitView 对应QSplitter,提供可拖动的分割视图布局
  • StackView 对应QStackedWidget,提供基于栈的层叠布局
  • TabView 对应QTabWidget,提供带有标签的基于栈的层叠布局
  • TableView 对应QTableWidget,提供带有滚动条、样式和表头的表格
  • 控件
  • 控件用于表现或接受用户输入
  • BusyIndicator 提供忙等示意组件
  • Button 对应QPushButton,提供按钮组件
  • CheckBox 对应QCheckBox,提供复选框
  • ComboBox 对应QComboBox,提供下拉框
  • GroupBox 对应QGroupBox,提供带有标题、边框的容器
  • Label 对应QLabel,提供标签组件
  • ProgressBar 对应QProgressBar,提供进度条组件
  • RadioButton 对应QRadioButton,提供单选按钮
  • Slider 对应QSlider,提供滑动组件
  • SpinBox 对应QSpinBox,提供微调组件
  • Switch 提供类似单选按钮的开关组件
  • TextArea 对应QTextEdit,提供能够显示多行文本的富文本编辑框
  • TextField 对应QTextLine,提供显示单行文本的纯文本编辑框
  • ToolButton 对应QToolButton,提供在工具栏上显示的工具按钮
  • ExclusiveGroup 提供互斥
  • 菜单
  • 用于构建菜单的组件
  • Menu 对应QMenu,提供菜单、子菜单、弹出菜单等
  • MenuSeparator 提供菜单分隔符
  • MenuItem 提供添加到菜单栏或菜单的菜单项
  • StatusBar 对应QStatusBar,提供状态栏
  • ToolBar 对应QToolBar,提供工具栏,可以添加ToolButton和其它组件
  • 表1 Qt Quick Controls 1.1组件

7、模型和视图

模型和视图其实属于qml的高级使用部分了,但是为了能早些理解qml的东西,我提前拿出一些简单的东西,预先学习下。

8、canvas画布

在早些qt4时代,qml只提供了几种基础元素,第一小节也说明了,有很多人期望的圆角矩形,椭圆和圆,但是最终官方没有给出具体的元素,如果是要做这些组件,那么就需要设计师给切图。到了qt5,官方提供了canvas画布,这个画布可以实现复杂的绘图操作,并且画布元素是基于HTML5的画布元素来完成的。支持画笔,填充,渐变,文本和绘制路径创建命令。

四、小示例

接下里就是第二节所展示的效果图对于代码讲解了,那我也就按照上图展示的顺序一个个讲解代码

1、基础组件讲解

在开始示例讲解之前,我先说下我自己封装的一个小组件,代码量很少,只为说明问题,具体请看diamante

import QtQuick 2.5

// 圆角矩形框矩形框,支持点击
Rectangle {
    property alias text: name.text;//导出文本变量
    property alias textColor: name.color;//导出文本颜色

    id: root;
    width: 120;
    height: 120;
    radius:60;
    antialiasing: true;
    signal clicked();//自定义信号  外部可以通过onClicked接收

    MouseArea
    {
        width: root.width;
        height: root.height;

        onClicked:
        {
            //鼠标点击时发送消息 并输入日志
           root.clicked();
            console.log("rectangle clicked");
        }
    }

    Text
    {
        id: name;
        text: "";
        color: "black";
        anchors.centerIn: parent;
    }
}

2、旋转的风车

代码里有多种方式实现矩形旋转,具体使用那一种就由个人喜好了

import QtQuick 2.0
import QtQuick.Window 2.0
import QtGraphicalEffects 1.0

import "../contrl" //导入自定义组件模块

Window {
    id:root;
    visible: true;
    width: 600;
    height: 400;

    //背景色窗口
    Rectangle {
        id: bg;
        color:"lightsteelblue";
        width: root.width;
        height:root.height;
    }
    //鼠标点击背景色时停止旋转图形
    MouseArea {
        width: bg.width;
        height: bg.height;

        onClicked: {
            ro.pause();
        }
    }

    //自定义控件  通过import导入
    Rect
    {
        id: roundItem;
        anchors.centerIn: parent;
        //渐变填充矩形
        ConicalGradient
        {
            anchors.fill: parent
            gradient: Gradient {
                GradientStop { position: 0.0; color: "lightsteelblue" }
                GradientStop { position: 1.0; color: "blue" }
            }
        }
        //旋转动画1  程序刚启动会执行 原因未知
        //        NumberAnimation on rotation {
        //            loops:Animation.Infinite;
        //            from:0;
        //            to:360;
        //            duration: 1000;
        //        }


        //旋转动画2  配合wheel.rotation = 360;使用  动画 不能循环执行
        //        Behavior on rotation {
        //            NumberAnimation {
        //                loops:Animation.Infinite;//无效
        //                duration: 1000;
        //            }
        //        }

        //旋转动画3  相比于动画1  在属性中主动指明了target和property
        //        NumberAnimation {
        //            id:ro;
        //            loops:Animation.Infinite;
        //            property: "rotation";
        //            target:roundItem;
        //            from:0;
        //            to:360;
        //            duration: 1000;
        //        }

        //旋转动画4  和动画1是一样的  因为RotationAnimation和NumberAnimation都是继承自PropertyAcimation
        //因此RotationAnimation动画可以实现和动画2一样的效果,使用RotationAnimation
        //        RotationAnimation on rotation {
        //            loops: Animation.Infinite;
        //            from: 0;
        //            to: 360;
        //            duration: 1000;
        //        }

        //旋转动画5
        RotationAnimation {
            id:ro;
            target:roundItem;
            loops: Animation.Infinite;
            from: 0;
            to: 360;
            duration: 1000;
        }

        onClicked: {
            if (ro.paused)
            {
                ro.resume();
            }
            else
            {
                ro.start();
            }
        }
    }
}

3、红绿灯,下述代码红色的的切换时通过鼠标单击进行

import QtQuick 2.0
import QtQuick.Window 2.0
import QtGraphicalEffects 1.0

import "../contrl"

Window
{
    function dosomething()//测试script脚本运行效果
    {
        console.log("do something");
    }

    id:root;
    visible: true;
    width: 370;
    height: 130;

    Rectangle
    {
        id:rootRect;
        width: root.width;
        height:root.height;
        anchors.centerIn:parent;

        Row
        {
            id:ligheGroup;
            spacing: 2;
            states:
            [
                State {
                    name: "red"
               //     StateChangeScript {name: "myScript"; script: dosomething(); }  //可以正常调用
                    PropertyChanges {
                        target: redLight; color:"red";
                    }
                    PropertyChanges {
                        target: greenLight; color:"black";
                    }
                    PropertyChanges {
                        target: yellowLight; color:"black";
                    }
                },
                State {
                    name: "green"
                    PropertyChanges {
                        target: redLight; color:"black";
                    }
                    PropertyChanges {
                        target: greenLight; color:"green";
                    }
                    PropertyChanges {
                        target: yellowLight; color:"black";
                    }
                },
                State {
                    name: "yellow"
                    PropertyChanges {
                        target: redLight; color:"black";
                    }
                    PropertyChanges {
                        target: greenLight; color:"black";
                    }
                    PropertyChanges {
                        target: yellowLight; color:"yellow";
                    }
                }
            ]

            anchors.centerIn:parent;
            Rect//红灯
            {
                id:redLight;
                color:"black";
                radius: width/2;
            }
            Rect//绿灯
            {
                id:greenLight;
                color:"black";
                radius: width/2;
            }
            Rect//黄灯
            {
                id:yellowLight;
                color:"black";
                radius: width/2;
            }

            transitions:
            [
                Transition //提供从red状态到yellow状态的渐变过程
                {
                    from: "red"
                    to: "yellow"
              //      ScriptAction { script: dosomething(); }  //可以正常调用
                    ColorAnimation{ target: redLight; properties: "color";duration: 1000;}
                    ColorAnimation{ target: yellowLight; properties: "color";duration: 1000;}
                }
            ]
        }
        property bool m_bIsRed : false;
        MouseArea
        {
            anchors.fill: parent;
            onClicked://鼠标点击时,状态切换
            {
                if (ligheGroup.state == "red"
                        || ligheGroup.state == "green")
                {
                    ligheGroup.state = "yellow";
                }
                else
                {
                    if (parent.m_bIsRed == false)
                    {
                        ligheGroup.state = "red";
                        parent.m_bIsRed = true;
                    }
                    else
                    {
                        ligheGroup.state = "green";
                        parent.m_bIsRed = false;
                    }
                }
            }
        }
    }
}

4、红绿灯

不同于上述红绿灯,次红绿灯只需要鼠标单击触发运行,状态是由定时器来控制,红灯运行60秒,绿灯20秒,黄灯3秒,为了程序的迅速反应,在红灯和绿灯的时候定时器触发频率所有提高,具体请看代码,此处我只贴出定时器部分,如果需要整个运行程序,可自行下载demo。

property bool m_bIsRed : false;//是否是红灯亮
    property int m_iTicker : 0;

    Timer
    {
        id:redState;
        interval: 50;//每隔50毫秒触发一次,真实情况下本应该是1000毫秒一次
        repeat: true;
        triggeredOnStart: true;
        property int count : 60;//红灯秒数

        onTriggered: {
            if (lightGroup.state != "red")
            {
                lightGroup.state = "red";
                root.m_bIsRed = true;
            }

            ++m_iTicker;
            redLight.text = count - m_iTicker;
            if (count <= m_iTicker)//到达指定时间 重置计数器,并切换到黄灯定时器,关闭自身定时器
            {
                m_iTicker = 0;
                yellowState.start();
                redState.stop();
            }
        }
    }
    Timer
    {
        id:yellowState;
        interval: 1000;
        repeat: true;
        triggeredOnStart: true;
        property int count : 3;//黄灯秒数

        onTriggered: {
            if (lightGroup.state != "yellow")
            {
                lightGroup.state = "yellow";
            }
            ++m_iTicker;
            yellowLight.text = count - m_iTicker;
            if (count <= m_iTicker)//到达指定时间 重置计数器,并切换到绿灯/红灯定时器,关闭自身定时器
            {
                m_iTicker = 0;
                if (m_bIsRed)
                {
                    greenState.start();
                }
                else
                {
                    redState.start();
                }
                stop();
            }
        }
    }
    Timer
    {
        id: greenState;
        interval: 150;//每隔150毫秒触发一次,真实情况下本应该是1000毫秒一次
        repeat: true;
        triggeredOnStart: true;
        property int count : 20;//绿灯秒数

        onTriggered: {
            if (lightGroup.state != "green")
            {
                lightGroup.state = "green";
                root.m_bIsRed = false;
            }

            ++m_iTicker;
            greenLight.text = count - m_iTicker;
            if (count <= m_iTicker)//到达指定时间 重置计数器,并切换到黄灯定时器,关闭自身定时器
            {
                m_iTicker = 0;
                yellowState.start();
                greenState.stop();
            }
        }
    }

5、日历窗口

代码量不大,有兴趣的可以看看,主要就是界面展示,如果想要做到动态的日历,需要对模型动态的增删,这个功能后续我们在完善。

import QtQuick 2.6
import QtQuick.Window 2.0
import QtGraphicalEffects 1.0

import "../contrl"

Window
{
    visible: true;
    width: 300;
    height: 300;

    Rectangle
    {
        id:root;
        anchors.fill: parent;
        width: root.width;
        height: root.height;
        color: "yellow";

        //日期头
        Row
        {
            id: weekname;
            spacing: 2;
            padding: 5;

            Repeater
            {
                model: ["周天", "周一", "周二", "周三", "周四", "周五", "周六"]
                Rectangle
                {
                    width: (root.width - 6 * weekname.spacing - 10) / 7;
                    height: 30
                    radius: 3
                    color: "lightBlue"
                    Text
                    {
                        anchors.centerIn: parent
                        text: modelData
                    }
                }
            }
        }

        //天
        GridView
        {
            id: weekday;
            boundsBehavior: Flickable.StopAtBounds;
            anchors//布局
            {
                top: weekname.bottom;
                left:root.left;
                leftMargin:5;
                right: root.right;
                rightMargin:5;
                bottom: root.bottom;
            }
            model: 42;//天数

            cellWidth: (root.width - 10) / 7;
            cellHeight: (root.width - 10) / 7;
//            Repeater
//            {
//                Rectangle
//                {
//                    radius: 8;
//                    color: "lightBlue";
//                    Text
//                    {
//                        anchors.centerIn: parent;
//                        text: modelData;
//                    }
//                }
//            }
            delegate: numberDelegate;
            focus: true;//可以获取焦点
        }

        Component//绘制代理
        {
            id: numberDelegate;
            Rectangle
            {
                width: weekday.cellWidth;
                height: weekday.cellHeight;
                color: GridView.isCurrentItem ? "green" : "lightGreen"//根据是否是当前项设置颜色
                border.color: Qt.lighter("green");
                Text
                {
                    anchors.centerIn: parent;
                    font.pixelSize: 10;
                    text: index + 1;//文本取索引值
                }
            }
        }
    }
}

补充:示例代码中:GridView中的repeater元素是不需要的,repeater是配合定位器使用的模型,因为每一个repeater都包含一个默认的绘制代理。

五、下载链接

qml简单示例

注:这是qml学习系列的第一篇文章,后边我还会以这种示例的形式继续更新更多学习的进度,希望大家多多支持,有问题的小伙伴可以私信我。谢谢。。。


如果您觉得文章不错,不妨给个 打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!




很重要–转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords

  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值