QML implements a TreeView

0 Preface

There is no TreeView control in QtQuick.Controls 2.x in Qt5, but only in QtQuick.Controls 1.x. Therefore, when we use a higher version of QtQuick.Controls, we cannot use TreeView, so we have to find a way to implement a TreeView ourselves.

1 Design ideas

The easiest way to realize TreeView is to use ListView iteratively. We can use ListView, and TreeView is actually ListView with ListView (matryoshka), so you can make a TreeView by using ListView flexibly.

1.1 TreeView data format (model)

What I take here is to read the data in JSON format and use it as the model (model data) of TreeView. The following is a simple example of model data:

[
  {
    "title": "第一章"
  },
  {
    "title": "第二章",
    "childrens": [
      {
        "title": "第二章-第一节"
      },
      {
        "title": "第二章-第二节"
      }
    ]
  },
  {
    "title": "第三章"
  }
]

The above is the data model that TreeView needs to read. As long as we modify this data model, we can construct any TreeView display type we want.

1.2 TreeView display style (delegate)

The delegate part is the key point. How to design this delegate is the most important thing whether the entire TreeView can be displayed correctly.

The display style of the delegate is shown in the figure above. We need to use a Component to implement the above part. The following is the simple code of the Component (the specific code will be given later):

Component {
  Item {
    RowLayout { // 主题(图标、标题)部分采用行布局
      Image { // 图标
        
      }
      Text { // 标题
        
      }
    }

    ColumnLayout { // 子项目中采用列布局
      Item {
        ListView { // 子项目中也应该有一个ListView,实现迭代
          
        }
      }
    }
  }
}

A detail that needs to be noted here is: Component height = RowLayout (theme) height + ColumnLayout (sub-item) height, so that the display will not overlap.

2 Final effect and code

2.1 Rendering

The final effect is shown in the figure above: the following is a TreeView, you can see that multi-level expansion can be performed, and the icon change part uses animation instead of switching between two pictures, which has a more dynamic effect.

 

2.2 Attached code

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.3

Item {
    id: root
    width: 360
    height: 300
    // 使用别名model,并为listView提供数据模型
    property alias model: listView.model

    // 设置背景颜色
    Rectangle {
        id: bg
        anchors.fill: parent
        color: "#353535"
    }

    ListView {
        id: listView
        anchors.fill: parent
        delegate: listDelegate
    }

    // 定义数据显示方式
    Component {
        id: listDelegate
        Item {
            id: item
            width: parent.width
            height: itemTitle.height + itemChildren.height
            property bool isSpread: false // 当前节点是否展开
            function clickedEvent() { // 鼠标点击时需要完成的事件
                console.log(modelData.title, "on clicked")
                itemChildren.visible = !itemChildren.visible
                if (item.isSpread) {
                    iconAnimation.from = 90
                    iconAnimation.to = 0
                    iconAnimation.start()
                    item.isSpread = !item.isSpread
                } else {
                    iconAnimation.from = 0
                    iconAnimation.to = 90
                    iconAnimation.start()
                    item.isSpread = !item.isSpread
                }
            }

            // 主题(图标、标题)使用行布局
            RowLayout {
                id: itemTitle
                anchors.left: parent.left
                spacing: 5

                // 图标
                Image {
                    id: itemICON
                    source: "qrc:/CaretRight.png"
                    Layout.preferredWidth: 25
                    Layout.preferredHeight: 25
                    RotationAnimation {
                        id: iconAnimation
                        target: itemICON
                        duration: 100
                    }

                    MouseArea {
                        anchors.fill: parent
                        onClicked: {
                            item.clickedEvent()
                        }
                    }
                }

                // 标题
                Text {
                    text: qsTr(modelData.title)
                    font {
                        pixelSize: 18
                    }
                    color: "white"
                    Layout.fillWidth: true
                    MouseArea {
                        anchors.fill: parent
                        onClicked: {
                            item.clickedEvent()
                        }
                    }
                }
            }

            // 子项使用列布局
            ColumnLayout {
                id: itemChildren
                visible: modelData.hasChildren && item.isSpread // 有子项目并且展开显示
                anchors.left: parent.left
                anchors.top: itemTitle.bottom
                anchors.leftMargin: 25
                height: itemChildren.visible ? itemChildrenListView.contentHeight : 0
                spacing: 5
                Item {
                    width: parent.width - 20
                    height: itemChildrenListView.contentHeight
                    ListView {
                        id: itemChildrenListView
                        anchors.fill: parent
                        delegate: listDelegate
                        model: modelData.childrens
                    }
                }
            }
        }

    }
}
import QtQuick 2.15
import QtQuick.Controls 2.15

Item {
    id: item1
    width: 800
    height: 700

    // 用来设置背景颜色
    Rectangle {
        id: rect_bg
        anchors.fill: parent
        color: "#252525"
    }

    // 主题:帮助
    Text {
        id: text_help
        width: 70
        height: 30
        text: qsTr("帮助")
        color: "#ffffff"
        anchors.left: parent.left
        anchors.top: parent.top
        font.pixelSize: 18
        horizontalAlignment: Text.AlignLeft
        verticalAlignment: Text.AlignVCenter
        font.bold: true
        anchors.topMargin: 10
        anchors.leftMargin: 10
    }

    // 按钮行布局
    Row {
        id: row
        width: 500
        height: 40
        anchors.left: parent.left
        anchors.top: text_help.bottom
        spacing: 30
        anchors.topMargin: 10
        anchors.leftMargin: 10

        Button {
            id: btn_userMannual
            width: 150
            height: parent.height
            text: qsTr("在线用户手册")
            background: Rectangle {
                color: "#177ddc"
            }
        }

        Button {
            id: btn_communicateUs
            width: 150
            height: parent.height
            text: qsTr("联系我们")
            background: Rectangle {
                color: "#177ddc"
            }
        }
    }

    // 主题:常见问题
    Text {
        id: text_commonQs
        width: 70
        height: 30
        text: qsTr("常见问题")
        color: "#ffffff"
        anchors.left: parent.left
        anchors.top: row.bottom
        font.pixelSize: 18
        horizontalAlignment: Text.AlignLeft
        verticalAlignment: Text.AlignVCenter
        font.bold: true
        anchors.topMargin: 10
        anchors.leftMargin: 10
    }

    // 实现的TreeView
    MTreeView {
        id: treeView
        width: 780
        height: 500
        anchors.left: parent.left
        anchors.right: parent.right
        anchors.bottom: parent.bottom
        anchors.top: text_commonQs.bottom
        anchors.margins: 10
        Component.onCompleted: {
            setTreeViewModelData()
        }
    }

    // 该函数用于TreeView设置需要测试的模型数据(model)
    function setTreeViewModelData() {
        treeView.model = JSON.parse('[
            {
                "title": "第一章",
                "hasChildren": true
            },
            {
                "title": "第二章",
                "hasChildren": true,
                "childrens": [
                    {
                        "title": "第二章-第一节",
                        "hasChildren": false
                    },
                    {
                        "title": "第二章-第二节",
                        "hasChildren": true,
                        "childrens": [
                            {
                                "title": "第二章-第二节-第一段",
                                "hasChildren": false
                            },
                            {
                                "title": "第二章-第二节-第二段",
                                "hasChildren": false
                            }
                        ]
                    }
                ]
            },
            {
                "title": "第三章",
                "hasChildren": true
            }
        ]')
    }
}
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15

Window {
    width: 640
    height: 480
    visible: true
    MPage {
        anchors.fill: parent
    }
}

3 summary

The above method of implementing TreeView is for reference only. There are still some bugs. For example, the scroll bar next to it is not implemented, and it will exceed its boundary when it is pulled up, as shown in the following figure:

 

Guess you like

Origin blog.csdn.net/hu853712064/article/details/130890815