美文网首页首页投稿(暂停使用,暂停投稿)程序员
Qt Quick 学习笔记(一) QML界面模型

Qt Quick 学习笔记(一) QML界面模型

作者: 3f9b7dcb0845 | 来源:发表于2017-06-23 15:00 被阅读1924次

    基本语法

    QML是类似于HTML的标记型语言,有与HTML类似的树形节点结构,同时又包含类似JS的函数语法。一个基础的QML文档类似这个样子

    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.0
    
    ApplicationWindow {
        objectName: "root"
        visible: true
        width: 1280
        height: 720
        maximumHeight: this.height
        minimumHeight: this.height
        maximumWidth: this.width
        minimumWidth: this.width
        title: qsTr("Security Check")
        font.family: "Source Han Sans CN Normal"
    
        Image{
            x: 0
            y: 0
            property int _test: 8
            property var list: []
        }
    }
    

    其中 <code>ApplicationWindow</code> 就是整个界面窗口的根节点,可以通过在C++文件中使用

    QObject* rootObject;
    rootObject = engine.rootObjects().first();
    

    得到指向根节点的指针,便于信号槽的链接和函数的调用(后文

    声明一个元素

    在QML中,一个元素类似这样

    Image{
        property a: x
        property b: y
    }
    

    每一种元素有预定义的属性,具体每个类型的元素有什么属性,可以参考All QML Types.


    动态增加一个元素

    在QML中,动态增加元素是基于一个模版(Component)的,所有需要动态添加的对象,都需要先存在一个模版元素中。
    例如动态添加一个Image元素,可以这样构建:

        Component{
            id:create
            Image {
                id: tourist
                source: sprites[Math.floor((Math.random()*3))]
                width: 20
                height: 20
                property int x_axid: 1010
                property int y_axid: 120
                x:x_axid
                y:y_axid
                Behavior on x{
                    SmoothedAnimation{ duration: 500 }
                }
                Behavior on y{
                    SmoothedAnimation{ duration: 500 }
                }
            }
    
    

    虽然整个QML文档中不允许同时有多个id相同的元素(类似HTML),但是给动态创建的内容id属性却是合法的。

    当需要创建这个图形对象时,就可以调用 <code>create.createObject(parentNode)</code> 这个QML函数来在parentNode节点下创建一个Image元素,该函数返回创建的元素的指针。
    需要重复创建多个相同的对象时,可以将创建的object存入一个列表中,便于管理,例如这样:

    var obj = create.createObject(image,{});//从模版生成元素
    list.push(obj)//将元素加入列表
    

    绑定自定义属性

    每个QML元素类型除了预定义的属性(例如x,y,opacity)以外,也可以添加一些我们自定义的属性。
    给元素添加绑定属性类似这样:

    Image{
        id: img
        x: 0
        y: 0
        property int maxNum: 15//绑定一个整形属性
        property var list: []//绑定一个列表
    }
    

    访问一个节点的属性类似这样

    img.maxNum = 15//赋值操作
    

    绑定属性有很多应用,例如在对象的动画中,为了避免对x,y值重复操作,可以使用绑定属性来避免冲突(后文)


    使用数据模型

    在QML中,有一些预制好的数据模型,例如 ListModel.
    使用 ListModel, 可以随意创建基于 ListItem 的列表,每个元素是一个 ListItem,元素内部又可以包含许多属性,类似C 中的结构体数组
    一个典型的 ListModel 类似这样:

    ListModel {
                                id:cpStateList
                                ListElement {
                                    cpIndex:0
                                    cpState: "空闲"
                                    cpCurrentTime:0
                                    cpNeedTime:0
                                }
    
                                ListElement {
                                    cpIndex:1
                                    cpState:  "空闲"
                                    cpCurrentTime:0
                                    cpNeedTime:0
                                }
    
                                ListElement {
                                    cpIndex:2
                                    cpState:  "空闲"
                                    cpCurrentTime:0
                                    cpNeedTime:0
                                }
    
                                ListElement {
                                    cpIndex:3
                                    cpState: "空闲"
                                    cpCurrentTime:0
                                    cpNeedTime:0
                                }
    }
    

    这样,当我们需要进行数据绑定时,可以将一个列表中的数据和 ListModel 中的 ListItem 一一绑定,当需要操作数据时,使用这样的方法:

    var obj = cpStateList.get(Index)//得到在ListModel中编号为Index的元素
    obj.xxx = ???//对xxx属性进行赋值
    

    ListModel还有一个重要的应用,就是在一些预置的 View 中作为数据源,渲染到对应的模版上。
    例如,一个 GridView 可以方便的构造一个网格视图,每一个网格都是一个基于delegate(代表性模版)的结构,结构中的数据会自动调用对应的 model 内部的数据。
    一个典型的 GridView 类似这样:

    GridView {
    
                            id: cpStateBoard
                            x: 56
                            y: 13
                            width: 894
                            height: 84
                            cellHeight: height
                            cellWidth: width/8
    
                            delegate: Item {
                                Column {
                                    spacing: 30
                                        Text{
                                            x:0
                                            y:10
                                            text:cpState
                                            font.family: "Source Han Sans CN Normal"
                                        }
    
                                        Text {
                                            x: 0
                                            y: 30
                                            text: cpCurrentTime + '/' + cpNeedTime
                                            opacity: cpState == "关闭" ? 0 : 1
                                            font.family: "Source Han Sans CN Normal"
                                            //anchors.horizontalCenter: parent.horizontalCenter
                                        }
                                    }
                                }
                            }
                            model: ListModel {
                                id:cpStateList
                                ListElement {
                                    cpIndex:0
                                    cpState: "空闲"
                                    cpCurrentTime:0
                                    cpNeedTime:0
                                }
    
                                ListElement {
                                    cpIndex:1
                                    cpState:  "空闲"
                                    cpCurrentTime:0
                                    cpNeedTime:0
                                }
    }
    

    当使用的model内部有两个数据时,就会渲染出两个网格有两个 Text元素,每个网格内部布局和delegate一致,分别使用了ListElement中的两个数据。通过对model的操作,一个网格就可以动态的增删并且保持统一的格式。


    动画的使用

    给一个元素添加动画主要有几种形式:

    • 在元素中定义一个完整的动画,需要运动时调用
    • 给某个属性绑定插值,当某个属性发生改变时自动进行补间

    比较常用的是第二种。例如,我希望这个图像运动时能够平滑的移动,可以这样:

        Rectangle {
            id: details
            opacity: 0
    
    
            Behavior on opacity{
                NumberAnimation{
                    duration: 200
                }
            }
    
            Behavior on x{
                SmoothedAnimation{
                    duration: 100
                }
            }
    
            Behavior on y{
                SmoothedAnimation{
                    duration: 100
                }
            }
        }
    

    这样,当x、y值发生改变时,就自动进行一个 SmmoothedAnimation ,经历时长100ms
    这个方法也可以绑定到opacity属性上,实现淡入淡出的效果。

    需要注意的是,当一个元素在运动过程中,如果检查他的x,y属性,会发现是当前运动中的值,而不是最终值。所以一定避免在运动过程中进行类似<code> item.x -= 100 </code>这样的赋值,否则最终的位置将不是第一次赋值-100,而是运动到某个时刻的位置-100.
    为了避免这样的情况,也可以使用绑定属性的方法,例如(注意x,y属性):

    Image {
                id: tourist
                source: sprites[Math.floor((Math.random()*3))]
                width: 20
                height: 20
                property int x_axid: 1010
                property int y_axid: 120
                x:x_axid
                y:y_axid
                Behavior on x{
                    SmoothedAnimation{ duration: 500 }
                }
                Behavior on y{
                    SmoothedAnimation{ duration: 500 }
                }
            }
    

    每次需要改变位置时,改变x_axid/y_axid的值,而x,y属性会随之改变,同时进行运动动画,但是运动过程中,进行<code> item.x_axid -= 100 </code>,改变的仍然是最终值-100.


    QML进阶

    最后我想说,QML和HTML类似之处太多,甚至当在QML中不懂怎么写时,试试用JavaScript的写法,说不定能用,例如QML中也有Math.floor()、Number()、String()等等一众函数,QML中的变量也是var(偶尔又是int,傻傻分不清),总之可以多借鉴。

    相关文章

      网友评论

        本文标题:Qt Quick 学习笔记(一) QML界面模型

        本文链接:https://www.haomeiwen.com/subject/hjrccxtx.html