Qt6.3 终于新增了一个 TreeView,之前 Controls1 中的 TreeView 废弃后,一直没出新的 TreeView 组件。先不说这个新的 TreeView 是继承自 TableView 的,光是看只有几百行源码,就感觉又是个半成品。此外,还附带提供了一个 TreeViewDelegate 默认的 delegate 实现。

TreeView 文档:https://doc.qt.io/qt-6/qml-qtquick-treeview.html

TreeView 继承自 TableView,这意味着即使模型数据为树形结构,TreeView 在内部使用 proxy model 将该结构转换为可由 TableView 呈现的表格模型。树中的每个节点最终占据表中的一行,其中第一列呈现树本身。通过根据模型中的父子深度缩进该列中的每个 delegate Item,它最终看起来像一棵树,即使它在技术上仍然只是一个表格。
其 delegate 也是和 TableView 单元格 delegate 差不多的用法。在 delegate 中,可以定义几个 required 属性,来让 TreeView 填充(之前的 View 都是用附加属性来访问相关的信息,不知道为什么 TreeView 成了自定义属性),分别为以下五个属性:

//指向包含delegate Item的TreeView
required property TreeView treeView
//项目是否代表树中的一个节点
//视图中只有一列将用于绘制树,因此,只有该列中的delegate Item项才会将此属性设置为true。
//树中的节点通常应根据其缩进,如果是则depth显示指示符。
//其他列中的delegate Item将将此属性设置为,并将显示模型中其余列的数据(通常不缩进)。
required property bool isTreeNode
//绘制的model Item是否在视图中展开
required property bool expanded
//绘制的model Item是否在模型中有子项
required property int hasChildren
//包含delegate绘制的model Item的深度。model Item的深度与其在模型中的祖先数量相同
required property int depth

TreeView 的 model 和 QTreeView 定义方式差不多,可以查看 Qt 自带的示例。在我的 demo 中只写了一个最简单的模板。主要有以下几个接口需要重写:

//主要是将节点指针装载到 QModelIndex 中,访问节点属性时再取出
QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const override;
//获取父节点信息,和 index 的操作流程差不多
QModelIndex parent(const QModelIndex &index) const override;
//获取该节点子级行数,也就是子节点的总数。行数列数影响 delegate 的实例化个数
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
//获取该节点子级列数
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
//获取节点信息
//习惯 ListView 就用 role 来区分,但是得重写 roleNames() 接口
//习惯 TableView 就用行列号来区分
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

效果展示:

代码链接:https://github.com/gongjianbo/MyTestCode/tree/master/Qml/TestQml_20220422_Qt6TreeView

主要代码(model+view):

#pragma once
#include <QAbstractItemModel>
#include <QSharedPointer>
#include <qqml.h>//自定义树节点
struct MyTreeItem
{//节点属性QString name;QString detail;//节点位置int row;//父节点QSharedPointer<MyTreeItem> parentItem;//子节点列表QList<QSharedPointer<MyTreeItem>> subItems;
};//treeview数据model
class MyTreeModel : public QAbstractItemModel
{Q_OBJECTQML_ELEMENT
private:enum RoleType{NameRole = Qt::UserRole,DetailRole};
public:explicit MyTreeModel(QObject *parent = nullptr);//需要重写的基类接口QModelIndex index(int row, int column,const QModelIndex &parent = QModelIndex()) const override;QModelIndex parent(const QModelIndex &index) const override;int rowCount(const QModelIndex &parent = QModelIndex()) const override;int columnCount(const QModelIndex &parent = QModelIndex()) const override;QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;QHash<int, QByteArray> roleNames() const override;//初始化数据Q_INVOKABLE void resetItems();private:MyTreeItem *getItem(const QModelIndex &idx) const;private://根节点QSharedPointer<MyTreeItem> rootItem;
};#include "MyTreeModel.h"MyTreeModel::MyTreeModel(QObject *parent): QAbstractItemModel(parent), rootItem(new MyTreeItem)
{
}QModelIndex MyTreeModel::index(int row, int column, const QModelIndex &parent) const
{if(!hasIndex(row, column, parent))return QModelIndex();MyTreeItem *parentItem = getItem(parent);auto childPtr = parentItem->subItems.at(row);if (childPtr){return createIndex(row, column, childPtr.get());}else{return QModelIndex();}
}QModelIndex MyTreeModel::parent(const QModelIndex &index) const
{if(!index.isValid()){return QModelIndex();}MyTreeItem *childItem = getItem(index);auto parentPtr = childItem->parentItem;if(!parentPtr || parentPtr == rootItem){return QModelIndex();}return createIndex(parentPtr.get()->row, 0, parentPtr.get());
}int MyTreeModel::rowCount(const QModelIndex &parent) const
{MyTreeItem *parentItem = getItem(parent);return parentItem->subItems.size();
}int MyTreeModel::columnCount(const QModelIndex &parent) const
{Q_UNUSED(parent)return 2;
}QVariant MyTreeModel::data(const QModelIndex &index, int role) const
{if(!index.isValid()){return QVariant();}MyTreeItem *item = getItem(index);if(role == Qt::DisplayRole){//TreeView继承自TableView,所以可以通过不同的column来取单元格数据role = Qt::UserRole + index.column();}switch (role) {case NameRole: return item->name;case DetailRole: return item->detail;default: break;}return QVariant();
}QHash<int, QByteArray> MyTreeModel::roleNames() const
{QHash<int, QByteArray> names = QAbstractItemModel::roleNames();names.insert(QHash<int, QByteArray>{{NameRole, "name"},{DetailRole, "detail"}});return names;
}void MyTreeModel::resetItems()
{beginResetModel();for(int i=0; i<10; i++){QSharedPointer<MyTreeItem> lv1{new MyTreeItem};lv1->parentItem = rootItem;rootItem->subItems.append(lv1);lv1->row = i;lv1->name = QString("lv1-%1").arg(i);lv1->detail = QString("detail-%1").arg(i);for(int j=0; j<10; j++){QSharedPointer<MyTreeItem> lv2{new MyTreeItem};lv2->parentItem = lv1;lv1->subItems.append(lv2);lv2->row = j;lv2->name = QString("lv2-%1-%2").arg(i).arg(j);lv2->detail = QString("detail-%1-%2").arg(i).arg(j);}}endResetModel();qDebug()<<__FUNCTION__<<rowCount()<<columnCount();
}MyTreeItem *MyTreeModel::getItem(const QModelIndex &idx) const
{if(idx.isValid()){MyTreeItem *item = static_cast<MyTreeItem*>(idx.internalPointer());if(item){return item;}}return rootItem.get();
}
import QtQuick
import QtQuick.Controls
import MyModelTreeView {id: controlmodel: MyTreeModel {id: tree_modelComponent.onCompleted: {tree_model.resetItems();}}//可以用预定义的TreeViewDelegate样式,也可以自定义//delegate: TreeViewDelegate { }delegate: Item {id: rootimplicitWidth: padding + label.x + label.implicitWidth + paddingimplicitHeight: label.implicitHeight * 1.5readonly property real indent: 20readonly property real padding: 5//标记为required的属性将由TreeView填充,与附加属性类似。//delegate间接通知TreeView它应该负责为它们分配值。可以将以下必需属性添加到delegate:////指向包含delegate Item的TreeViewrequired property TreeView treeView//项目是否代表树中的一个节点//视图中只有一列将用于绘制树,因此,只有该列中的delegate Item项才会将此属性设置为true。//树中的节点通常应根据其缩进,如果是则depth显示指示符。//其他列中的delegate Item将将此属性设置为,并将显示模型中其余列的数据(通常不缩进)。required property bool isTreeNode//绘制的model Item是否在视图中展开required property bool expanded//绘制的model Item是否在模型中有子项required property int hasChildren//包含delegate绘制的model Item的深度。model Item的深度与其在模型中的祖先数量相同required property int depth//鼠标点击TapHandler {onTapped: treeView.toggleExpanded(row)}Text {id: indicatorvisible: root.isTreeNode && root.hasChildrenx: padding + (root.depth * root.indent)text: root.expanded ? "▼" : "▶"}Text {id: labelx: padding + (root.isTreeNode ? (root.depth + 1) * root.indent : 0)width: root.width - root.padding - xclip: truetext: model.display}}
}

Qt6 QML TreeView 基本使用相关推荐

  1. [Qt6][QML][教程]Image控件图片的更新以及相对路径的访问

    最近用Qt6+QML仿制网易云切歌时候MAC的通知界面,调用Image控件的时候发现了一些问题. 教程被应用在MediaStateT中 MediaStateT Github项目地址: https:// ...

  2. Qt5 QML TreeView currentIndex当前选中项的一些问题

    0.前言 Qt5 QML Controls1.4 中的 TreeView 存在诸多问题,比如节点连接的虚线不好实现,currentIndex 的设置和 changed 信号的触发等.我想主要的原因在于 ...

  3. Qt6 QML Book/QtQuick控件/图像查看器

    An Image Viewer 图像查看器 Let's look at a larger example of how Qt Quick Controls are used. For this, we ...

  4. Qt6 QML Book/多媒体/播放媒体

    Playing Media 播放媒体 The most basic case of multimedia integration in a QML application is for it to p ...

  5. Qt6 QML Book/图形效果/粒子群组

    Particle Groups 粒子群组 At the beginning of this chapter, we stated particles are in groups, which is b ...

  6. Qt6 QML Book/Qt Quick 3D/材料和灯光

    Materials and Light 材料和灯光 Up until now, we've only worked with basic materials. To create a convinci ...

  7. html5手机号输入框,input输入框限制(座机,手机号码)

    记录一下 座机input输入框: 手机input输入框: 说明: 座机限制只能输入数字和"-"且最大长度20位,手机限制只能输入数字且最大长度11位. 存在一个问题,当是搜狗输入法 ...

  8. ui设计师与开发人员的沟通_开发人员和设计师的27种免费资源

    ui设计师与开发人员的沟通 Design is the face of your product, service or content, without good designs, even if ...

  9. 关于TreeView的简单使用(Qt6.4.1)

    前言 TreeView是在Qt6.3中加入的,弥补了Qt中无官方树图.笔者上手尝试了下,虽然有点麻烦,但官方也做了不少简化. 本次教程,笔者创建一个简单的示例,以帮助读者使用TreeView. 一.创 ...

最新文章

  1. 画原型时需要注意什么?
  2. 3_深度学习中显卡的使用和显存的分配(20181213)
  3. 为什么APF框架初始化时有两个一模一样的analyticConfiguration请求
  4. [luoguP3068] [USACO13JAN]派对邀请函Party Invitations(stl大乱交)
  5. BTREE与其它索引的优缺点对比
  6. 【免费毕设】ASP.NET猜数游戏的设计与开发(源代码+lunwen)
  7. 开课吧:怎样才能做软件架构师?
  8. 大数据治理会遇到哪些难题
  9. 后羿射日般的精准 - 阿里云ECS调度是如何炼成的
  10. Programiz 中文系列教程·翻译完成
  11. 面试题心得--spring注解的原理
  12. WPS 二维表格匹配方式(利用VLOOKUP+IF/SWITCH多条件查询)
  13. 泛微OA流程自动汇总触发(流程触发集成)
  14. flutter自定义渐变背景按钮
  15. java生成tga图片_游戏制作行业为什么使用TGA格式的贴图而不使用PNG格式?
  16. php ci 处理图片 裁剪,jquery.form + Jcrop + CI框架实现图片裁剪上传
  17. WPF教程(二) WPF vs WinForms
  18. C# Format详解
  19. 用pyQt实现信号采集数据图形化显示
  20. Java服务,CPU100%问题如何快速定位?

热门文章

  1. 鸿蒙空间命运法则,鸿蒙法则_仙域官网_跟我玩(Game5)游戏平台
  2. 计算机主板pci插槽,PCI插槽:濒临淘汰依然有用
  3. 同惠TH2817BLCR测试仪+LCR数字电桥
  4. ML.NET Cookbook:(18)如何在文本数据上训练模型?
  5. 考试证件照制作怎么做?电子证件照在线制作的方法
  6. JTable单元格放自定义控件(一)-如何在JTable的单元格放JPanel
  7. 【数学建模】PR-SIR 消息传播模型(分析一条消息在自媒体平台上传播的数学模型)
  8. 科技青年 | 中科院高君宇:本科双学位,直博中科院,好饭不怕晚,学术成果丰...
  9. vscode+ESP-IDF:编译网友移植好的LVGL工程(ESP32+ili9341+xpt2046笔记)
  10. 玩转Ubuntu(Ubuntu查看本机的ip地址,适用于Linux)