网站开发计划书模板,sina app engine wordpress,网页设计图片排版怎么设置,wordpress 模板制作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 QVectorQVariant 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:QVectorTreeItem* m_childItems;QVectorQVariant m_itemData;TreeItem *m_parentItem;};这个类是一个基本的c类。它不继承QObject也不提供信号和插槽。它用于保存一个由 QVariant 组成的列表其中包含列数据以及有关其在树结构中位置的信息。这些函数具有以下特性:
appendChildItem()方法在第一次构建模型时用于添加数据在正常情况下不会用到。child()和childCount()函数允许模型获取任何子项的信息。列数由columnCount()提供每列的数据由data()函数获得。函数row()和parent()用于获取项的行号和父项。
父项和列的数据存储在parentItem和itemData私有成员变量中。childItems变量包含一个指向该元素自己的子项的指针列表。
3. TreeItem类的实现
构造函数只用于记录项的父元素以及与每一列相关联的数据。
TreeItem::TreeItem(const QVectorQVariant 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_castTreeItem*(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_OBJECTpublic: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;elseparentItem static_castTreeItem*(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_castTreeItem*(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;elseparentItem static_castTreeItem*(parent.internalPointer());return parentItem-childCount();}由于每个item管理自己的列数据columnCount()函数必须调用item自己的columnCount()函数来确定给定的模型索引中有多少列。与rowCount()函数一样如果指定了无效的模型索引则返回的列数从根项开始确定: int TreeModel::columnCount(const QModelIndex parent) const{if (parent.isValid())return static_castTreeItem*(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_castTreeItem*(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的实例。
实现的效果就是如下