QTreeView是用来显示树型结构的数据,比如目录组织,公司组织架构等,数据量小可以用Qt自带的Model实现,如果数据量大,则需要用自定义的Model实现,下面介绍自定义实现的方法,直接上代码:
#ifndef TYPEDEF_H
#define TYPEDEF_H
#include <QVector>
// department信息
typedef struct Department_t{
QString name; // 部门名称
int id; // 部门id号
int number; // 部门人数
QString preOrganizationName; // 上级组织名
Department_t()
{
id = 0;
number = 0;
}
QVector<Department_t*> subList;
} Department;
// 树列号
enum COLUMN
{
COLUMN_NAME = 0,
COLUMN_ID,
COLUMN_NUM,
COLUMN_OrganizationName
};
#endif // TYPEDEF_H
#ifndef TREEITEM_H
#define TREEITEM_H
#include <QVariant>
class TreeItem
{
public:
enum Type
{
UNKNOWN = -1,
PROVINCE,
PERSON
};
explicit TreeItem(TreeItem *parent = nullptr);
~TreeItem();
void addChild(TreeItem *item);
void removeChildren();
TreeItem *child(int row) { return _children.value(row); }
TreeItem *parent() { return _parent; }
int childCount() const { return _children.count(); }
QVariant data(int column) const;
//设置、获取节点存的数据指针
void setPtr(void* p) { _ptr = p; }
void* ptr() const { return _ptr; }
// 保存该节点是其父节点的第几个子节点,查询优化所用
void setRow(int row) { _row = row; }
// 返回本节点位于父节点下第几个子节点
int row() const { return _row; }
Type getType() const { return _type; }
void setType(const Type &value) { _type = value; }
private:
QList<TreeItem*> _children; // 子节点
TreeItem *_parent; // 父节点
Type _type; // 此节点保存的数据类型
void* _ptr; // 存储数据的指针
int _row; // 此item位于父节点中第几个
};
#endif // TREEITEM_H
#include "TreeItem.h"
#include "typedef.h"
TreeItem::TreeItem(TreeItem *parent)
: _parent(parent),
_type(UNKNOWN),
_ptr(nullptr),
_row(-1)
{
}
TreeItem::~TreeItem()
{
removeChildren();
}
// 在本节点下添加子节点
void TreeItem::addChild(TreeItem *item)
{
item->setRow(_children.size());
_children.append(item);
}
// 清空所有子节点
void TreeItem::removeChildren()
{
qDeleteAll(_children);
_children.clear();
}
// 获取本节点第column列的数据
QVariant TreeItem::data(int column) const
{
Department *department = (Department*)_ptr;
switch (column) {
case COLUMN_NAME: return department->name;
case COLUMN_ID: return department->id;
case COLUMN_NUM: return department->number;
case COLUMN_OrganizationName: return department->preOrganizationName;
default:return QVariant();
}
return QVariant();
}
#ifndef TREEMODEL_H
#define TREEMODEL_H
#include <QAbstractItemModel>
class TreeItem;
class TreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit TreeModel(const QStringList& headers, QObject *parent = nullptr);
~TreeModel() override;
TreeItem *root();
QVariant headerData(int section, Qt::Orientation orientation, int role) const override;
QVariant data(const QModelIndex &index, int role) const override;
QModelIndex index(int row, int column, const QModelIndex &parent) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent) const override;
int columnCount(const QModelIndex &parent) const override;
private:
TreeItem *itemFromIndex(const QModelIndex &index) const;
private:
QStringList _headers;
TreeItem* _rootItem;
};
#endif // TREEMODEL_H
#include "TreeModel.h"
#include "TreeItem.h"
TreeModel::TreeModel(const QStringList& headers, QObject *parent)
: QAbstractItemModel(parent)
{
_headers = headers;
_rootItem = new TreeItem();
}
TreeModel::~TreeModel()
{
delete _rootItem;
}
TreeItem *TreeModel::itemFromIndex(const QModelIndex &index) const
{
if (index.isValid())
{
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
return item;
}
return _rootItem;
}
TreeItem *TreeModel::root()
{
return _rootItem;
}
// 获取表头数据
QVariant TreeModel::headerData(int section, Qt::Orientation orientation,int role) const
{
if (orientation == Qt::Horizontal)
{
if(role == Qt::DisplayRole)
{
return _headers.at(section);
}
}
return QVariant();
}
// 获取index.row行,index.column列数据
QVariant TreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
TreeItem *item = itemFromIndex(index);
if (role == Qt::DisplayRole)
{
return item->data(index.column());
}
return QVariant();
}
// 在parent节点下,第row行,第column列位置上创建索引
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
TreeItem *parentItem = itemFromIndex(parent);
TreeItem *item = parentItem->child(row);
if (item)
return createIndex(row, column, item);
else
return QModelIndex();
}
// 创建index的父索引
QModelIndex TreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *item = itemFromIndex(index);
TreeItem *parentItem = item->parent();
if (parentItem == _rootItem)
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
// 获取索引parent下有多少行
int TreeModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() > 0)
return 0;
TreeItem* item = itemFromIndex(parent);
return item->childCount();
}
// 返回索引parent下有多少列
int TreeModel::columnCount(const QModelIndex &parent) const
{
return _headers.size();
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QTreeView>
#include <QHeaderView>
#include <QFile>
#include <QJsonObject>
#include <QJsonDocument>
#include <QJsonArray>
#include "TreeModel.h"
#include "TreeItem.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
treeView = new QTreeView(this);
treeView->setSelectionBehavior(QTreeView::SelectRows); //一次选中整行
treeView->setSelectionMode(QTreeView::SingleSelection); //单选,配合上面的整行就是一次选单行
treeView->setFocusPolicy(Qt::NoFocus); //去掉鼠标移到单元格上时的虚线框
treeView->header()->setStretchLastSection(true); //最后一列自适应宽度
//treeView->setHeaderHidden(true); //设置表头隐藏
setCentralWidget(treeView);
QVector<Department*> proList = initData();
setModel(proList);
}
MainWindow::~MainWindow()
{
delete ui;
}
QVector<Department*> MainWindow::initData()
{
QFile file(":/default2.txt");
file.open(QIODevice::ReadOnly);
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
QJsonObject obj = doc.object();
QJsonArray array = obj.value("nextLevel").toArray();
QVector<Department*> depList;
qDebug() << "TreeModel::setupModelDataJson==============array.size()====" << array.size() << __LINE__;
for(int i = 0; i < array.size(); ++i)
{
QJsonObject subObject = array.at(i).toObject();
QJsonArray subArray = subObject.value("nextLevel").toArray();
Department *topDepartment = new Department();
topDepartment->id = subObject.value("id").toInt();
topDepartment->name = subObject.value("name").toString();
topDepartment->number = subObject.value("nnt").toInt();
topDepartment->preOrganizationName = subObject.value("preOrganizationName").toString();
qDebug() << "TreeModel::setupModelDataJson==============subArray.size()====" << subArray.size() << __LINE__;
//第二级
for(int i = 0; i < subArray.size(); ++i)
{
QJsonObject tempObj = subArray.at(i).toObject();
Department *subDepartment = new Department();
subDepartment->id = tempObj.value("id").toInt();
subDepartment->name = tempObj.value("name").toString();
subDepartment->number = tempObj.value("nnt").toInt();
subDepartment->preOrganizationName = tempObj.value("preOrganizationName").toString();
topDepartment->subList.append(subDepartment);
}
depList.append(topDepartment);
}
return depList;
}
void MainWindow::setModel(const QVector<Department *> &depList)
{
QStringList headers;
headers << QStringLiteral("部门名称")
<< QStringLiteral("部门Id")
<< QStringLiteral("部门人数")
<< QStringLiteral("上级部门");
TreeModel* model = new TreeModel(headers, treeView);
TreeItem* root = model->root();
foreach (auto depNode, depList)
{
TreeItem* depItem = new TreeItem(root);
depItem->setPtr(depNode); // 保存数据指针
root->addChild(depItem);
foreach (auto subNode, depNode->subList)
{
TreeItem* subItem = new TreeItem(depItem);
subItem->setPtr(subNode); // 保存数据指针
depItem->addChild(subItem);
}
}
treeView->setModel(model);
}
运行结果:
参考:
《QTreeView+QAbstractItemModel自定义模型》:系列教程之三_百里杨的博客-CSDN博客_qtreeview自定义
QTreeView使用总结13,自定义model示例,大大优化性能和内存_逆枫゛的博客-CSDN博客_qtreeview用法
Qt 自定义treemodel_sydnash的博客-CSDN博客_qt自定义treemodel
Qt 可编辑的树模型(Tree Model)的一个实例_qq_21291397的博客-CSDN博客_pyqt treemodel
Qt QTreeView 详解_Mr.codeee的博客-CSDN博客_qt treeview
QJsonObject和QString互转_风起时~微凉的博客-CSDN博客_qjsonobject转qstring
完整源码和测试数据
QTreeView+自定义Model实现示例-C++文档类资源-CSDN下载
版权声明:本文为chenyijun原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。