QTreeView+自定义Model实现示例

  • Post author:
  • Post category:其他


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 版权协议,转载请附上原文出处链接和本声明。