QML-Model-View

一、Model-View

1.1、Qt中的model-view是什么

有点类似MVC模式,Qt中,数据通过model-view模式,将数据和显示分离开来

  • model: 数据模型,负责存储和管理数据
  • view: 视图容器,负责布局和显示管理
  • delegate: 可视化代理,负责单个数据项的可视化呈现

1.2、有哪些view

常见视图组件:

  • ListView - 列表视图
  • GridView - 网格视图
  • TableView - 表格视图
  • PathView - 路径视图
  • Repeater - 重复器(非视图组件,但常用于重复创建项目)

1.3、Repeater

Repeater是Qt中一个非常有用的组件,它可以重复渲染一组数据。它类似于其他框架中的列表或表格控件,但它的灵活性更高,因为它可以处理任意类型的模型(如字符串、图像等)。

  • 属性:
    • count: 创建多少项
    • delegate: 每个项的渲染模板
    • model: 数据源
  • 信号:
    • itemAdded(int index, Item item): 当一个新的项被添加到Repeater时发出。
      • onItemAdded(int index, Item item): 处理信号
    • itemRemoved(int index, Item item): 当一个项被从Repeater中移除时发出。
      • onItemRemoved: 处理信号

注意:

Repeater通常会创建其所有delegate项;如果存在大量delegate项,并且并非所有项都必须同时可见,会降低效率,需要考虑其他view

示例代码:

javascript 复制代码
import QtQuick 

Column{
    spacing:10
    Repeater{
        model: 5
        delegate: Rectangle{
            width: 100; height: 50
            color:"red"
        }
       onItemAdded: {
            console.log("item " + index + "added")
        }
        onItemRemoved: {
            console.log("item" + index + "removed")
        }
    }
}

1.4、ListView

类似于Repaeter,只不过delegate之间有间隔,并且可以滑动,所谓的瀑布流布局

关键属性:

  • spacing: 项间距
  • orientation: 布局方向(默认垂直)
  • clip: 是否裁剪超出区域的内容
  • cacheBuffer: 预渲染缓冲区大小
  • currentIndex: 当前选中项索引
  • highlight: 高亮组件

示例代码:

javascript 复制代码
ListView{
    anchors.fill: parent
    anchors.margins: 20
    spacing: 15
    clip: true                  // 默认是false,设置为true,在视觉上,会看不见元素创建与销毁的过程
    model: 100
    cacheBuffer: 20             // 如果需要展示的元素过多,可以设置缓存区大小,节省频繁创建销毁的内存开销

    delegate: Rectangle{
        required property int index
        width: 20
        height: 25
        color: "red"

        Component.onCompleted: {
            console.log(index + " added")
        }
        Component.onDestruction: {
            console.log(index + " deleted")
        }
    }
}

1.5、GridView

使用GridView可以创建网格布局,类似于ListView,只不过是将delegate布局成网格

关键属性:

  • cellWidthcellHeight: 网格大小
  • flow: 布局流向(FlowTopToBottom/FlowLeftToRight)
  • layoutDirection: 布局方向(LeftToRight/RightToLeft)

示例代码:

javascript 复制代码
GridView{
        anchors.fill: parent
        clip: true

        model:100

        cellWidth:40            // 设置每个小网格的长宽
        cellHeight:40

        flow:GridView.FlowTopToBottom   // 布局方式是从上到下
        //flow:GridView.FlowLeftToRight   // 布局方式是从左到右

        delegate: Rectangle{
            width: 20
            height: 20
            color: "red"
        }
}

1.6、Delegete

delegate翻译过来是代理,委托的意识;在model-view中,delegate是介于数据和视图之间的桥梁

  • 从model获取数据
  • 定义数据可视化方式
  • 响应视图状态变化

示例代码:

javascript 复制代码
    ListView{
        anchors.fill: parent
        anchors.margins: 20
        spacing: 15
        clip: true                  // 默认是false,设置为true,在视觉上,会看不见元素创建与销毁的过程
        model: 100
        cacheBuffer: 20             // 如果需要展示的元素过多,可以设置缓存区大小,节省频繁创建销毁的内存开销

        delegate: numberDelegate
    }
    Component{
        id: numberDelegate
        Rectangle{
            required property int index

            width: 40
            height: 25
            color: ListView.isCurrentItem ? "black" : "red"

            id: wrapper
            Text{
                id:label1
                text: wrapper.index

                anchors.centerIn: parent
            }
        }
    }
  • delegate动画
    • onAdd
    • onRemove

示例代码:

javascript 复制代码
delegate: Rectangle {
    required property int index
        
    // 添加动画
    GridView.onAdd: SequentialAnimation {
        NumberAnimation { 
            property: "scale"; from: 0; to: 1; duration: 300 
        }
    }
    
    // 移除动画  
    GridView.onRemove: SequentialAnimation {
        NumberAnimation { 
            property: "scale"; to: 0; duration: 300 
        }
    }
}

二、测试

场景:两个按钮,一个按钮点击,在网格中添加一个元素;另一个按钮点击,删除最后一个元素

javascript 复制代码
import QtQuick

Rectangle{
    anchors.fill: parent

    gradient: Gradient{
        GradientStop{position:0.0; color:"#dbdbdb"}
        GradientStop{position:1.0; color:"#5fc9f8"}
    }

    // model
    ListModel{
        id:theModel
        ListElement{ num:0}
        ListElement{ num:1}
        ListElement{ num:2}
        ListElement{ num:3}
    }

    //button
    Rectangle{
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.margins: 20

        height: 40
        color: "#53f769"
        border.color: Qt.lighter(color, 1.1)

        Text{
            anchors.centerIn: parent
            text: qsTr("add")
        }

        MouseArea{
            anchors.fill: parent
            onClicked: {
                theModel.append({"num": theModel.count})
                console.log(theModel.count)
            }
        }
    }

    // 删除按钮
    Rectangle{
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.margins: 20
        anchors.bottomMargin: 60  // 在添加按钮上方

        height: 40
        color: "#f75353"
        border.color: Qt.lighter(color, 1.1)

        Text{
            anchors.centerIn: parent
            text: qsTr("remove last")
        }

        MouseArea{
            anchors.fill: parent
            onClicked: {
                if (theModel.count > 0) {
                    theModel.remove(theModel.count - 1)
                    console.log(theModel.count)
                }
            }
        }
    }

    // view
    GridView{
        anchors.fill: parent
        anchors.margins: 20
        anchors.bottomMargin: 80

        clip: true
        cellHeight: 40
        cellWidth: 60

        model:theModel
        delegate: numberDelegate
    }

    // Delegate
    Component{
        id: numberDelegate
        Rectangle{
            id: wrapper
            required property int index
            required property int num

            gradient: Gradient{
                GradientStop{position:0.0; color:"#dbdbdb"}
                GradientStop{position:1.0; color:"#5fc9f8"}
            }

            width: 40
            height: 25
            color: ListView.isCurrentItem ? "black" : "red"

            Text{
                id:label1
                text: qsTr(wrapper.index)

                anchors.centerIn: parent
            }

            // 添加动画
            GridView.onAdd:addAnimation.start()
            GridView.onRemove:removeAnimation.start()

            NumberAnimation{
                id:addAnimation
                target:wrapper
                property: "scale"
                from: 0
                to:1
                duration: 250
                easing.type: Easing.InOutQuad
            }

            NumberAnimation{
                id:removeAnimation
                target:wrapper
                property: "scale"
                to:0
                duration: 250
                easing.type: Easing.InOutQuad
            }
        }
    }
}
相关推荐
峥无7 小时前
《从适配器本质到面试题:一文掌握 C++ 栈、队列与优先级队列核心》
开发语言·c++·queue·stack
七夜zippoe7 小时前
仓颉FFI实战:C/C++互操作与性能优化
c语言·c++·性能优化
蒜香拿铁7 小时前
Angular【核心特性】
前端·javascript·angular.js
西哥写代码8 小时前
基于dcmtk的dicom工具 第十三章 dicom文件导出bmp、jpg、png、tiff、mp4
c++·mfc·dicom·dcmtk·tiffopen·dipngplugin·dijpegplugin
艾小码8 小时前
前端新手必看!困扰90%人的10个JavaScript问题,一次性帮你解决
前端·javascript
永远有缘9 小时前
Java、Python、C# 和 C++ 在函数定义语法上的主要区别
java·c++·python·c#
绛洞花主敏明10 小时前
Go切片的赋值
c++·算法·golang
xixixin_11 小时前
【React】为什么移除事件要写在useEffect的return里面?
前端·javascript·react.js
嘗_11 小时前
react 源码2
前端·javascript·react.js