QML中树形控件TreeView的最基本、最简单粗暴的、一看秒懂的实现方法,之一

Qt 专栏收录该内容
4 篇文章 0 订阅

前言:

Qt Quick5.5alpha出来了,据说提供TreeView。于是这篇文章有大盘6k点入市的悲壮,注定是要做韭菜的。然而我们正在做的项目,还不能使用最新版Qt,本文的意义在于使用比较老版本的QML的项目。

正文:

用Qt很多年,最近才接触Qt Quick,不用不知道,一用果真不会用。C++和QML的语言使用的脑回路完全不一样。不过这种时候就不要追究这些细节了。TreeView是一个非常常用的界面控件,Qt Quick竟然多年以后总算提供,我们还是朋友,还可以问候。。。

因为本文的实现方法太简单粗暴了,不如直接看代码,Talk is cheap, show me the code!

运行环境

Qt5.1.1

IDE:Qt Creator

OS:Fedora

文件名:main.qml

QML源码

import QtQuick 2.0

Item {
   width: 600
   height: 600

   //Model
   ListModel {
      id: objModel
      Component.onCompleted: {
          objModel.append({"name":"A1","level":0,"subNode":[]})
          objModel.append({"name":"A2","level":0,"subNode":[]})
          objModel.append({"name":"A3","level":0,"subNode":[]})
          objModel.get(1).subNode.append({"name":"B1","level":1,"subNode":[]})
          objModel.get(1).subNode.append({"name":"B2","level":1,"subNode":[]})
          objModel.get(1).subNode.get(0).subNode.append({"name":"C3","level":2,"subNode":[]})
      }
   }

   //Delegate
   Component {
      id: objRecursiveDelegate
      Column {
          Row {
               //indent
               Item {
                  height: 1
                  width: model.level * 20
               }

               Text {
                   text:model.name
               }
            }

         Repeater {
            model: subNode
            delegate: objRecursiveDelegate
         }
      }
   }

   //View
  ListView{
       anchors.fill: parent
       model:objModel
       delegate: objRecursiveDelegate
   }
}

运行效果

对于学习TreeView来说就是这么恰到好处!增之一分则太长,减之一分则太短。这里用到了Qt的模型/代理/视图的知识,结构很清晰。

Qt的Model/View

说MVC容易理解的放学别走。

Qt的Model/View和MVC模式差不多,Model是数据,View是视图。重点在于, QtView是一个固定布局的容器,可能是列布局,可能是格子布局,自己脑补一个等分了很多小格的收纳盒。收纳盒里每一个单元格中的数据对应Model中的一份数据,每一份数据怎么显示则是“代理”来决定。比如Model是很多双袜子,堆在一起。View是这个收纳盒,伊每一个小格都装一双袜子。Delegate决定左袜子和右袜子(角色)怎么摆放进一个小格,所以Delegate控制每份数据的显示方式,是一个小模版。

源码解析

回到本文,实现TreeView两个难点:

1.数据模型支持树结构。

上面代码使用的JSON方式,也可以用QML方式,后续实现TreeView的文章会使用。Model有3个角色,name、level、subNode[]。name储存节点的名字,level控制缩进量,subNode[]是用来装子节点的。

2.代理的表达支持递归。

如果不考虑缩进的显示效果,可以把显示缩进的Item和Row元素去掉。objRecursiveDelegate代理中只有Text元素显示节点名字,还有一个Repeater递归显示子节点,这两个元素用Column装起来。最有技巧的就是这个Repeater。我们从头看。

第一步:View从Model中取到一个节点的数据,根据objRecursiveDelegate代理,首先把节点名字显示出来放在Column的第一行,Column的第二行是一个Repeater,数据来自subNode,代理还是这个objRecursiveDelegate代理。

第二步: 在column的第二行,按照objRecursiveDelegate代理描述,View取一个subNode节点的数据,节点名字放第一行。column的第二行重复第一步,只是数据变成subNode。

第三步:直到subNode为空,结束了这个节点数据,View从Model中取下一个节点数据。

可谓,天长地久有时尽,此方法显示无绝期。

TreeView进阶

言归正传,掌握核心力量,就可以做些酷炫的事。比如,

A)有子节点的父节点显示蓝色,无子节点的父节点显示绿色。

B)双击展开/收起子节点。

C)展开显示 – 号,收起显示 + 号。

下面是完整的代码,由上面的代码填充而来,是不是很简单呢! 

QML的TreeView进阶源码

import QtQuick 2.0

Item {
   width: 600
   height: 600   

   //Model
   ListModel {
      id: objModel
      Component.onCompleted: {
          objModel.append({"name":"Zero","level":0,"subNode":[]})
          objModel.append({"name":"One","level":0,"subNode":[]})
          objModel.append({"name":"Two","level":0,"subNode":[]})
          objModel.get(1).subNode.append({"name":"Three","level":1,"subNode":[]})
          objModel.get(1).subNode.append({"name":"Four","level":1,"subNode":[]})
          objModel.get(1).subNode.get(0).subNode.append({"name":"Five","level":2,"subNode":[]})
      }
   }

   //Delegate
   Component {
         id: objRecursiveDelegate
         Column {
            id: objRecursiveColumn
            MouseArea {
               width: objRow.implicitWidth
               height: objRow.implicitHeight
               onDoubleClicked: {
                  for(var i = 1; i < parent.children.length - 1; ++i) {
                     parent.children[i].visible = !parent.children[i].visible
                  }
               }
               Row {
                  id: objRow
                  Item {
                     height: 1
                     width: model.level * 20
                  }
                  Text {
                     text: (objRecursiveColumn.children.length > 2 ?
                              objRecursiveColumn.children[1].visible ?
                              qsTr("-  ") : qsTr("+ ") : qsTr("   ")) + model.name
                     color: objRecursiveColumn.children.length > 2 ? "blue" : "green"
                  }
               }
            }
            Repeater {
               model: subNode
               delegate: objRecursiveDelegate
            }
         }
      }

   //View
      ListView {
         anchors.fill: parent
         model: objModel
         delegate: objRecursiveDelegate
      }
}

【运行效果】


致谢:

         本文提炼自这篇文章。(感谢这位大牛!)初学者的我们重要的是掌握核心!把大牛的文章作为进阶吧!



  • 8
    点赞
  • 2
    评论
  • 12
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值