Qt扫盲-QTreeView 理论总结

news2024/12/27 18:11:13

QTreeView 理论使用总结

  • 一、概述
  • 二、快捷键绑定
  • 三、提高性能
  • 四、简单实例
    • 1. 设计与概念
    • 2. TreeItem类定义
    • 3. TreeItem类的实现
    • 4. TreeModel类定义
    • 5. TreeModel类实现
    • 6. 在模型中设置数据

一、概述

QTreeView实现了 model 中item的树形表示。这个类用于提供标准的层次列表,这些列表以前是由QListView类提供的,但是使用Qt的model/view体系结构提供的更灵活的方法。

QTreeView类是model/view类之一,是Qt model/view框架的一部分。
QTreeView实现了由QAbstractItemView类定义的接口,以允许它显示由QAbstractItemModel类派生的模型提供的数据。
构造显示模型数据的树状视图很简单。在下面的例子中,目录的内容由一个QFileSystemModel提供,并显示为一个树:

QFileSystemModel *model = new QFileSystemModel;
model->setRootPath(QDir::currentPath());
QTreeView *tree = new QTreeView(splitter);
tree->setModel(model);

model/view 体系结构确保树视图的内容随着模型的变化而更新。
有子item的item可以处于展开状态(可见子item)或折叠状态(隐藏子item)。当此状态发生变化时,将发出一个带有相关item的模型索引的collapse()或expanded()信号。

用于指示层次结构级别的缩进量由缩进属性控制。
树视图中的头文件是使用QHeaderView类构造的,可以使用header()->hide()隐藏。请注意,每个头部都配置了将其stretchLastSection属性设置为true,以确保视图不会浪费为其头部分配的任何空间。如果此值设置为true,则此属性将覆盖标题中最后一节设置的调整大小模式。
默认情况下,树视图中的所有列都是可移动的,除了第一列。要禁用这些列的移动,请使用QHeaderView的setSectionsMovable()函数。有关重新排列节的详细信息,请参见移动头节。

二、快捷键绑定

QTreeView支持一组键绑定,使用户能够在视图中导航并与item的内容交互:

功能
Up将光标移动到前一行同一列中的item。如果当前item的父item没有更多的行可导航,则光标移动到父item前面的兄弟item的最后一行中的相关item。
Down将光标移动到下一行同一列中的item。如果当前item的父item没有更多的行可导航,则光标移动到其父item后面的兄弟item的第一行中的相关item。
Left通过折叠分支隐藏当前item(如果存在)的子item。
Minus和左一样。
通过展开分支显示当前item(如果存在)的子item。
Plus和Right一样。
星号展开当前item及其所有子item(如果存在)。
PageUp将光标向上移动一页。
PageDown将光标向下移动一页。
Home将光标移动到模型中第一个顶级item的第一行同一列中的item。
End将光标移动到模型中最后一个顶级item的最后一行同一列中的item。
F2在可编辑模型中,这将打开当前item进行编辑。Escape键可用于取消编辑过程并恢复对所显示数据的任何更改。

三、提高性能

在显示大量item时,可以为视图提供有关其正在处理的数据的提示,以提高其性能。对于想要显示相等高度的item的视图,可以采用的一种方法是将uniformRowHeights属性设置为true。

四、简单实例

这个简单的树模型示例展示了如何使用Qt标准视图类的层次模型。
在这里插入图片描述

Qt的model/view体系结构为视图操作数据源中的信息提供了一种标准方式,它使用数据的抽象模型来简化和标准化访问数据的方式。简单模型将数据表示为一个项目表,并允许视图通过基于索引的系统访问这些数据。更一般地说,模型可以用树结构的形式来表示数据,它允许每个元素作为子项表的父元素。

在尝试实现树模型之前,有必要考虑数据是由外部来源提供的,还是由模型本身维护。因为我们操作数据后,model/view 会自动替我们去渲染数据。在这个例子中,我们将实现一个内部结构来保存数据(也就是model里面维护了数据),而不是讨论如何包装来自外部源的数据。

1. 设计与概念

我们用来表示数据结构的数据结构是由TreeItem对象构建的树。每个TreeItem代表树视图中的一个项,包含多个数据列。
在这里插入图片描述

  • 简单的树模型结构
    数据使用TreeItem对象存储在模型内部,这些对象以基于指针的树结构链接在一起。一般来说,每个TreeItem都有一个父项,并且可以有多个子项。然而,树结构中的根项没有父项,而且它永远不会在模型外部被引用。
    每个TreeItem都包含了其在树结构中的位置的信息。它可以返回其父项及其行号。使这些信息随时可用使模型的实现更容易。
    由于树视图中的每个项通常包含若干列数据(本例中为标题和摘要),因此很自然地将这些信息存储在每个项中。为简单起见,我们将使用一个QVariant对象的列表来存储项目中每一列的数据。

使用基于指针的树结构意味着,当将模型索引传递给视图时,我们可以记录索引中相应项的地址(参见QAbstractItemModel::createIndex()),并稍后使用QModelIndex::internalPointer()检索它。这使得编写模型更容易,并确保所有引用同一项的模型索引具有相同的内部数据指针。

有了适当的数据结构,我们可以使用最少的额外代码创建树模型,以向其他组件提供模型索引和数据。

2. TreeItem类定义

TreeItem类定义如下:

  class TreeItem
  {
  public:
      explicit TreeItem(const QVector<QVariant> &data, TreeItem *parentItem = nullptr);
      ~TreeItem();

      void appendChild(TreeItem *child);

      TreeItem *child(int row);
      int childCount() const;
      int columnCount() const;
      QVariant data(int column) const;
      int row() const;
      TreeItem *parentItem();

  private:
      QVector<TreeItem*> m_childItems;
      QVector<QVariant> m_itemData;
      TreeItem *m_parentItem;
  };

这个类是一个基本的c++类。它不继承QObject,也不提供信号和插槽。它用于保存一个由 QVariant 组成的列表,其中包含列数据以及有关其在树结构中位置的信息。这些函数具有以下特性:

  • appendChildItem()方法在第一次构建模型时用于添加数据,在正常情况下不会用到。
  • child()和childCount()函数允许模型获取任何子项的信息。
  • 列数由columnCount()提供,每列的数据由data()函数获得。
  • 函数row()和parent()用于获取项的行号和父项。

父项和列的数据存储在parentItem和itemData私有成员变量中。childItems变量包含一个指向该元素自己的子项的指针列表。

3. TreeItem类的实现

构造函数只用于记录项的父元素以及与每一列相关联的数据。

TreeItem::TreeItem(const QVector<QVariant> &data, TreeItem *parent)
      : m_itemData(data), m_parentItem(parent)
  {}

指向属于该项的每个子项的指针将存储在childItems私有成员变量中。在调用类的析构函数时,必须删除这些元素,以确保它们的内存能被重用:

TreeItem::~TreeItem()
{
      qDeleteAll(m_childItems);
}

因为每个子项都是在模型初始填充数据时构建的,所以添加子项的函数很简单:

void TreeItem::appendChild(TreeItem *item)
{
      m_childItems.append(item);
}

当给定一个合适的行号时,每个元素都可以返回它的任何子项。
在这里插入图片描述

例如,在上图中,标记为“A”的项对应于row = 0的根项的子项,“B”项对应row = 1的“A”项的子项,“C”项对应row = 1的根项的子项。

child()函数返回子项列表中指定行号对应的子项:

  TreeItem *TreeItem::child(int row)
  {
      if (row < 0 || row >= m_childItems.size())
          return nullptr;
      return m_childItems.at(row);
  }

子元素的数量可以通过childCount()得到:

  int TreeItem::childCount() const
  {
      return m_childItems.count();
  }

TreeModel使用这个函数来确定给定父元素项的行数。

row()函数报告了元素在父元素列表中的位置:

  int TreeItem::row() const
  {
      if (m_parentItem)
          return m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));

      return 0;
  }

注意,虽然根项(没有父项)被自动分配了行号0,但是这个信息从来没有被模型使用过。

列数可以通过函数columnCount()简单地返回。

  int TreeItem::columnCount() const
  {
      return m_itemData.count();
  }

列数据由data()函数返回。在使用数据访问容器之前会检查边界:

  QVariant TreeItem::data(int column) const
  {
      if (column < 0 || column >= m_itemData.size())
          return QVariant();
      return m_itemData.at(column);
  }

可以用parent()找到元素的父元素:

  TreeItem *TreeItem::parentItem()
  {
      return m_parentItem;
  }

请注意,由于模型中的根项没有父项,在这种情况下,此函数将返回0。在实现TreeModel::parent()函数时,我们需要确保模型能够正确地处理这种情况。

4. TreeModel类定义

TreeModel类定义如下:

  class TreeModel : public QAbstractItemModel
  {
      Q_OBJECT

  public:
      explicit TreeModel(const QString &data, QObject *parent = nullptr);
      ~TreeModel();

      QVariant data(const QModelIndex &index, int role) const override;
      Qt::ItemFlags flags(const QModelIndex &index) const override;
      QVariant headerData(int section, Qt::Orientation orientation,
                          int role = Qt::DisplayRole) const override;
      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;

  private:
      void setupModelData(const QStringList &lines, TreeItem *parent);

      TreeItem *rootItem;
  };

这个类类似于提供只读模型的QAbstractItemModel的大多数子类。只有构造函数和setupModelData()函数的形式是特定于这个模型的。此外,我们还提供了一个析构函数来在模型被销毁时进行清理。

5. TreeModel类实现

为简单起见,模型不允许编辑其数据。因此,构造函数接受一个参数,其中包含模型将与视图和委托共享的数据:

  TreeModel::TreeModel(const QString &data, QObject *parent)
      : QAbstractItemModel(parent)
  {
      rootItem = new TreeItem({tr("Title"), tr("Summary")});
      setupModelData(data.split('\n'), rootItem);
  }

由构造函数为模型创建根项。为了方便起见,此项只包含垂直标题数据。我们还用它来引用包含模型数据的内部数据结构,并且用它来表示模型中顶层项目的假想父元素。

模型的内部数据结构由setupModelData()函数填充。我们将在本文档的最后单独讨论这个函数。
析构函数确保在模型被销毁时删除根元素及其所有后代元素:

  TreeModel::~TreeModel()
  {
      delete rootItem;
  }

由于我们不能在模型构建和设置之后向其添加数据,这简化了管理内部项目树的方式。

模型必须实现index()函数,以便为视图和委托访问数据提供索引。当其他组件被它们的行号和列号以及它们的父模型索引引用时,就会为它们创建索引。如果将无效的模型索引指定为父索引,则由模型返回对应于模型中的顶级项的索引。
当提供模型索引时,我们首先检查它是否有效。如果不是,我们就认为引用的是顶级元素项;否则,使用internalPointer()函数从模型索引中获取数据指针,并使用它来引用TreeItem对象。请注意,我们构建的所有模型索引都将包含一个指向现有TreeItem的指针,因此我们可以保证任何有效的模型索引都将包含一个有效的数据指针。

  QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
  {
      if (!hasIndex(row, column, parent))
          return QModelIndex();

      TreeItem *parentItem;

      if (!parent.isValid())
          parentItem = rootItem;
      else
          parentItem = static_cast<TreeItem*>(parent.internalPointer());

      TreeItem *childItem = parentItem->child(row);
      if (childItem)
          return createIndex(row, column, childItem);
      return QModelIndex();
  }

因为这个函数的row和column参数引用的是对应父元素项的子元素,所以可以使用TreeItem::child()函数来获取该元素项。

createIndex()函数用于创建要返回的模型索引。我们指定行号和列号,以及一个指向元素本身的指针。稍后可以使用模型索引来获取项目的数据。

定义TreeItem对象的方式使得编写parent()函数很容易:

  QModelIndex TreeModel::parent(const QModelIndex &index) const
  {
      if (!index.isValid())
          return QModelIndex();

      TreeItem *childItem = static_cast<TreeItem*>(index.internalPointer());
      TreeItem *parentItem = childItem->parentItem();

      if (parentItem == rootItem)
          return QModelIndex();

      return createIndex(parentItem->row(), 0, parentItem);
  }

我们只需要确保永远不会返回与根项对应的模型索引。为了与index()函数的实现方式保持一致,我们为模型中任何顶级元素的父元素返回一个无效的模型索引。

在创建要返回的模型索引时,我们必须指定父元素中的行号和列号。使用TreeItem::row()函数可以很容易地找到行号,但我们遵循约定,将父元素的列号指定为0。和index()函数一样,用createIndex()创建模型索引。

rowCount()函数返回给定模型索引对应的TreeItem子元素的个数,如果指定了无效索引,则返回顶层元素的个数:

  int TreeModel::rowCount(const QModelIndex &parent) const
  {
      TreeItem *parentItem;
      if (parent.column() > 0)
          return 0;

      if (!parent.isValid())
          parentItem = rootItem;
      else
          parentItem = static_cast<TreeItem*>(parent.internalPointer());

      return parentItem->childCount();
  }

由于每个item管理自己的列数据,columnCount()函数必须调用item自己的columnCount()函数来确定给定的模型索引中有多少列。与rowCount()函数一样,如果指定了无效的模型索引,则返回的列数从根项开始确定:

  int TreeModel::columnCount(const QModelIndex &parent) const
  {
      if (parent.isValid())
          return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
      return rootItem->columnCount();
  }

通过Data()从模型中获取数据。由于item管理自己的列,我们需要使用列号来通过TreeItem::data()函数取得数据:

  QVariant TreeModel::data(const QModelIndex &index, int role) const
  {
      if (!index.isValid())
          return QVariant();

      if (role != Qt::DisplayRole)
          return QVariant();

      TreeItem *item = static_cast<TreeItem*>(index.internalPointer());

      return item->data(index.column());
  }

请注意,我们在此实现中只支持DisplayRole,并且我们还为无效的模型索引返回无效的QVariant对象。
我们使用flags()函数来确保视图知道模型是只读的:

  Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
  {
      if (!index.isValid())
          return Qt::NoItemFlags;

      return QAbstractItemModel::flags(index);
  }

headerData()函数返回我们方便地存储在根项中的数据:

  QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
                                 int role) const
  {
      if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
          return rootItem->data(section);

      return QVariant();
  }

这些信息可以通过其他方式提供:可以在构造函数中指定,也可以硬编码到headerData()函数中。

6. 在模型中设置数据

我们使用setupModelData()函数在模型中设置初始数据(也就是输入如下的这些文件)。该函数解析文本文件,提取用于模型的文本字符串,并创建记录数据和整体模型结构的item对象。当然,这个函数的工作方式是特定于这个模型的。我们提供了以下对其行为的描述,并请读者参考示例代码本身以获取更多信息。

我们从一个如下格式的文本文件开始:
在这里插入图片描述

我们使用以下两条规则处理这个文本文件:

  • 对于每一行上的每一对字符串,在树结构中创建一个项(或节点),并将每个字符串放在项中的一列数据中。
  • 当某一行的第一个字符串相对于前一行的第一个字符串缩进时,将该项设置为前一项的子项。

为了确保模型正确工作,只需要用正确的数据和父项创建TreeItem的实例。

实现的效果就是如下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1066564.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python3.x安装Pandas教程

python3.x 安装pandas总是会出现一些乱七八糟的问题&#xff0c;那现在就给你们讲述一种超级简单的安装方法&#xff0c;非常简单。 1&#xff0c;检查自己的python版本&#xff0c;我的是python3.4 32位的 2&#xff0c;https://www.lfd.uci.edu/~gohlke/pythonlibs/ 进入此网…

IIS部署Flask

启用 CGI 安装wfastcgi pip install wfastcgi 启用 wfastcgi 首先以管理员身份运行wfastcgi-enable来在IIS上启用wfastcgi&#xff0c;这个命令位于c:\python_dir\scripts&#xff0c;也就是你需要确保此目录在系统的PATH里&#xff0c;或者你需要cd到这个目录后再执行。 #…

np.clip()函数用法

代码&#xff1a; import numpy as npdelta np.clip(1.2, a_min0, a_max1)print(delta) 结果&#xff1a;

防御安全第五次作业

1. 什么是数据认证&#xff0c;有什么作用&#xff0c;有哪些实现的技术手段&#xff1f; 数据认证是指保证数据的真实性、完整性和可信度&#xff0c;以确保数据不被篡改或伪造。其作用包括但不限于&#xff1a; 保护关键数据不被恶意篡改或损坏 提供数据来源的可靠性和安全性…

ToBeWritten之记录狩猎过程

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…

nodeJs 图片下载功能实现

const cheerio require("cheerio"); const axios require("axios"); const fs require("fs"); const request require("request"); const path require(path); // 图片存储位置 const fileImgUrl "./images/"; // 获取…

外部中断的基本操作

题目背景 定义一个 Working() 函数&#xff0c;使L1指示灯不断闪烁。将P32引脚定义成外部中断功能&#xff0c;按下S5按键就会产生外部中断触发信号&#xff0c;在中断响应函数中&#xff0c;点亮L8指示灯&#xff0c;延时较长一段时间后熄灭&#xff0c;该功能用两种方法实现…

ToBeWritten之评估数据质量

也许每个人出生的时候都以为这世界都是为他一个人而存在的&#xff0c;当他发现自己错的时候&#xff0c;他便开始长大 少走了弯路&#xff0c;也就错过了风景&#xff0c;无论如何&#xff0c;感谢经历 转移发布平台通知&#xff1a;将不再在CSDN博客发布新文章&#xff0c;敬…

BLIP-2小结

paper&#xff1a;BLIP-2: Bootstrapping Language-Image Pre-training with Frozen Image Encoders and Large Language Models 引用量&#xff1a;376&#xff08;截止2023-09&#xff09; motivation BLIPv2主要从模态对齐、高效训练两个方向对图文多模态预训练任务&…

Unity AI Sentis 基础教程

Unity AI Sentis基础教程 Unity AI Sentis基础教程Unity AI 内测资格申请Unity 项目Package Manager开始尝试模型下载识别图片完整代码代码搭载运行 射线绘画 URP&#xff08;扩展&#xff09;射线绘画脚本脚本搭载效果 Sentis 是 AI 模型的本地推理引擎&#xff0c;它利用最终…

漏洞扫描与利用

1.通过Kali对服务器场景server2003以半开放式不进行ping的扫描方式并配合a&#xff0c;要求扫描信息输出格式为xml文件格式&#xff0c;从生成扫描结果获取局域网&#xff08;例如172.16.101.0/24&#xff09;中存活靶机&#xff0c;以xml格式向指定文件输出信息&#xff08;使…

轻量级接口自动化测试框架

大致思路: jmeter完成接口脚本,Ant完成脚本执行并收集结果生成报告,最后利用jenkins完成脚本的自动集成运行. 环境安装: 1.jdk1.7 配置环境变量(参考前面的分页) 2.jmeter解压到本地,ant解压到本地 3.Ant解压到本地,并配置环境变量 ANT_HOME:D:\jmeter\apache-ant-1.9.6 P…

学习css 伪类:has

学习抖音&#xff1a; 渡一前端提薪课 首先我们看下:has(selector)是什么 匹配包含&#xff08;相对于 selector 的 :scope&#xff09;指定选择器的元素。可以认为 selector 的前面有一个看不见的 :scope 伪类。它的强大之处是&#xff0c;可以实现父选择器和前面兄弟选择器…

2023年最新问卷调查工具排行榜:揭秘实力榜单

问卷调查是从目标受众那里收集有价值的反馈和见解的有效方式。正确的调查问卷工具可以使问卷的创建、分发和分析变得更加容易和高效。在本文中&#xff0c;我们将问卷调查工具排行榜实力榜&#xff0c;为大家选择问卷平台的时候提供有价值的参考意见。 1、Zoho Survey Zoho S…

三分钟阿里云服务器全方位介绍(看一篇就够了)

阿里云服务器ECS英文全程Elastic Compute Service&#xff0c;云服务器ECS是一种安全可靠、弹性可伸缩的云计算服务&#xff0c;阿里云提供多种云服务器ECS实例规格&#xff0c;如经济型e实例、通用算力型u1、ECS计算型c7、通用型g7、GPU实例等&#xff0c;阿里云百科aliyunbai…

经典文献阅读之--MapTR(环视车道线地图提取)

0. 简介 最近环视图像处理其实已经非常火了&#xff0c;最近地平线&华科则是提出了一种新的环视车道线地图提取工具。高清&#xff08;HD&#xff09;地图提供了驾驶场景丰富而精确的环境信息&#xff0c;是自动驾驶系统规划中基础且不可或缺的组成部分。《MapTR: Structu…

“益路同行”栏目专访 第06期—小星星关爱联盟创始人魏洁荣老师

中国善网在本届&#xff08;第十届&#xff09;慈展会上特别推出了《益路同行》采访栏目&#xff0c;《益路同行》栏目旨在寻觅公益之路上同行者的故事&#xff0c;挖掘公益更深层次的内涵&#xff0c;探索新时代公益发展道路。希望公益企业、人物、故事被更多人看到&#xff0…

软件测试/测试开发丨App自动化测试-弹窗异常处理

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/27692 黑名单处理 运行过程中不定时弹框&#xff08;广告弹窗&#xff0c;升级提示框&#xff0c;新消息提示框等等&#xff09; 弹框不是 BUG&#xff0…

2023/9/28 -- ARM

【内存读写指令】 int *p0X12345678 *p100;//向内存中写入数据 int a *p;//从内存读取 1.单寄存器内存读写指令 1.1 指令码以及功能 向内存中写&#xff1a; str:向内存中写一个字(4字节)的数据 strh:向内存写半个字&#xff08;2字节&#xff09;的数据 strb:向内存写一个字…

TS中Class类的继承

我们有下面一个代码&#xff0c;其中创建了一个Dog类和Cat类&#xff0c;这两个类中都有姓名和年龄属性和bark方法 class Dog {name: string;age: number;constructor(name: string, age: number) {this.name name;this.age age;}bark() {console.log(this.name "汪汪…