当前位置:网站首页>QT的Tree View Model示例
QT的Tree View Model示例
2022-07-25 22:40:00 【物随心转】
一、介绍
使用MVC架构,Tree View与Tree Widget 相比而言,需要为tree view 设置一个model,使Tree View 能有效降低内存的使用率。下面参考Qt官方提供的demo——Simple Tree Model(说是简单的树试图模型Demo,其实一点都不简单)效果图如下:

二 、 自定义树模型结构TreeItem
使用TreeItem对象把数据存储在模型内部,这些对象以基于指针的树结构链接在一起。通常,每个TreeItem都有一个父项,也可以有许多子项。然而,树结构中的根项没有父项,而且它永远不会在模型之外被引用。每个TreeItem都包含它在树结构中的位置信息;它可以返回父项及其行号。有了这些信息就可以更容易地实现模型。
由于树视图中的每一项通常包含几列数据,因此很自然地将这些信息存储在每一项中。为了简单起见,我们将使用QVariant对象列表来存储项中每一列的数据。

在 tree model中顶级项(上图中的 A、C以及同级的Item)对应的模型索引的父索引(QModelIndex::parent()函数获得)是无效的。各项的数据保存在QModelIndex中。
数据结构如下:
treeitem.h
#include <QList>
#include <QVariant>
class TreeItem
{
public:
TreeItem(const QList<QVariant> &data, TreeItem *parent = nullptr);
~TreeItem();
// 构建子Item列表;
void appendChild(TreeItem *child);
// 获取该Item指定行号的子Item;
TreeItem *child(int row);
// 获取该Item的子Item个数;
int childCount() const;
// 获取该Item的列数(数据段数);
int columnCount() const;
// 获取该Item指定段的数据;
QVariant data(int column) const;
// 获取该item所在parent的row;
int row() const;
// 该Item的父Item;
TreeItem *parentItem();
private:
TreeItem* m_parentItem; // 该Item的父Item;
QList<TreeItem*> m_childItems; // 该Item的子Item列表;
QList<QVariant> m_itemData; // 该Item的各列数据;
};treeitem.cpp
#include "tree_item.h"
TreeItem::TreeItem(const QList<QVariant> &data, TreeItem *parent)
:m_itemData(data),
m_parentItem(parent)
{
}
TreeItem::~TreeItem()
{
qDeleteAll(m_childItems);
}
void TreeItem::appendChild(TreeItem *child)
{
m_childItems.append(child);
}
TreeItem *TreeItem::child(int row)
{
// 无效的row;
if (row < 0 || row >= m_childItems.size())
return nullptr;
return m_childItems.at(row);
}
int TreeItem::childCount() const
{
return m_childItems.count();
}
int TreeItem::columnCount() const
{
return m_itemData.count();
}
QVariant TreeItem::data(int column) const
{
// 无效的索引返回空的QVariant;
if (column < 0 || column >= m_itemData.size())
return QVariant();
return m_itemData.at(column);
}
int TreeItem::row() const
{
if (m_parentItem)
m_parentItem->m_childItems.indexOf(const_cast<TreeItem*>(this));
// 尽管根项(没有父项)被自动分配了行号0,但模型从不使用此信息。
return 0;
}
TreeItem *TreeItem::parentItem()
{
return m_parentItem;
}三、自定义Model
一般用TreeModel都是用自己的类,于是,按着文档上说明的,关于继承QAbstractItemModel的时候,必须实现如下几个函数:index(), parent(), rowCount(), columnCount(), data(), 要让Model变成可以编辑的话,必须还要实现 setData(), flags() 这两个函数,让flags()返回值有ItemIsEditable。 同时,还可以实现headerData()和 setHeaderData() 来控制View中的标题。需要重写父类的函数(override标记)每个函数有较详细的解释。
treemodel.h
#ifndef TREEMODEL_H
#define TREEMODEL_H
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QVariant>
class TreeItem;
//! [0]
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit TreeModel(const QString &data, QObject *parent = 0);
~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;
};
//! [0]
#endif // TREEMODEL_H
treemodel.cpp
#include "treeitem.h"
#include "treemodel.h"
#include <QStringList>
//! [0]
TreeModel::TreeModel(const QString &data, QObject *parent)
: QAbstractItemModel(parent)
{
QList<QVariant> rootData;
rootData << "Title" << "Summary"; // set header
rootItem = new TreeItem(rootData);
setupModelData(data.split(QString("\n")), rootItem);
}
//! [0]
//! [1]
TreeModel::~TreeModel()
{
delete rootItem;
}
//! [1]
//! [2]
int TreeModel::columnCount(const QModelIndex &parent) const
{
if (parent.isValid())
return static_cast<TreeItem*>(parent.internalPointer())->columnCount();
else
return rootItem->columnCount();
}
//! [2]
//! [3]
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());
}
//! [3]
//! [4]
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
{
if (!index.isValid())
return 0;
return QAbstractItemModel::flags(index);
}
//! [4]
//! [5]
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,
int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return rootItem->data(section);
return QVariant();
}
//! [5]
//! [6]
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);
else
return QModelIndex();
}
//! [6]
//! [7]
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);
}
//! [7]
//! [8]
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();
}
//! [8]
// 参数1: txt文件所有行的数据,参数2:TreeItem的父节点
void TreeModel::setupModelData(const QStringList &lines, TreeItem *parent)
{
QList<TreeItem*> parents;
QList<int> indentations;
parents << parent;
indentations << 0;
int number = 0;
while (number < lines.count()) {
int position = 0;
// 获取此行中第一个不是' '的位置
while (position < lines[number].length()) {
if (lines[number].at(position) != ' ') {
break;
}
position++;
}
// 获取此位置之后的所有字符串,然后去除字符串前面和后面的的空格
QString lineData = lines[number].mid(position).trimmed();
if (!lineData.isEmpty()) {
//使用“\t”划分字符串为字符串列表.
QStringList columnStrings = lineData.split("\t", QString::SkipEmptyParts);
QList<QVariant> columnData; // 列数据
for (int column = 0; column < columnStrings.count(); ++column)
columnData << columnStrings[column];
// 根据缩进来判断是成为当前分支的兄弟,还是当前分支的儿子
if (position > indentations.last()) {
// The last child of the current parent is now the new parent
// unless the current parent has no children.
//当成为前分支的儿子
if (parents.last()->childCount() > 0) {
parents << parents.last()->child(parents.last()->childCount() - 1);
indentations << position;
}
}
else {
while (position < indentations.last() && parents.count() > 0) {
parents.pop_back();
indentations.pop_back();
}
}
// Append a new item to the current parent's list of children.
parents.last()->appendChild(new TreeItem(columnData, parents.last()));
}
++number;
}
}
四、程序的入口
资源文件

main函数
#include <QApplication>
#include <QFile>
#include <QTreeView>
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE(simpletreemodel);
QApplication app(argc, argv);
QFile file(":/default.txt");
file.open(QIODevice::ReadOnly);
TreeModel model(file.readAll());
file.close();
// 创建以View显示model
QTreeView view;
view.setModel(&model);
view.setWindowTitle(QObject::tr("Simple Tree Model"));
view.show();
return app.exec();
}
参考:
Qt官方demo日拱一卒- simple tree model_feima9999的博客-CSDN博客
边栏推荐
- BIO、NIO、AIO的区别?
- [training day15] good name [hash]
- Formal parameters, arguments and return values in functions
- Mitsubishi FX PLC free port RS command realizes Modbus Communication
- If it is modified according to the name of the framework module
- 谷歌分析UA怎么转最新版GA4最方便
- 自媒体人必备的4个素材网站,再也不用担心找不到素材
- Simple application of partial labels and selectors
- Two methods of printing strings in reverse order in C language
- Learning orientation today
猜你喜欢
随机推荐
【集训DAY12】树!树!树!【贪心】【最小生成树】
Xiaobai programmer day 8
JSON object
Von Neumann architecture
Common source code for ArcGIS development
谷歌分析UA怎么转最新版GA4最方便
对需求的内容进行jieba分词并按词频排序输出excel文档
【集训DAY12】X equation 【高精度】【数学】
Mitsubishi FX PLC free port RS command realizes Modbus Communication
Xiaobai programmer's sixth day
XSS collect common code
[training day15] good name [hash]
IPv4 addresses have been completely exhausted, and the Internet can work normally. NAT is the greatest contributor!
3 词法分析
Share two music playing addresses
Dom and events
JVM内存区域
ML-Numpy
软件测试 pytest pytest的命名规则 用例的前后置 conftest.py 定制allure报告 @pytest.mark.parametrize()装饰器作数据驱动
Today, let's talk about the if branch statement of the selection structure









![[training Day11] Calc [mathematics]](/img/a7/cbb33f0c241e1bad90a282bba990d1.png)