QTreeView+自定义Model实现示例

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下载

  • 10
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在PyQt6中,可以使用自定义的模型来在QTreeView中显示数据。下面是一个使用自定义模型的示例代码。 ```python from PyQt6.QtCore import QAbstractItemModel, QModelIndex, Qt from PyQt6.QtWidgets import QApplication, QMainWindow, QTreeView class CustomModel(QAbstractItemModel): def __init__(self, data, parent=None): super().__init__(parent) self.rootItem = CustomItem(None, None) self.setupModelData(data, self.rootItem) def setupModelData(self, data, parent): for item in data: parent.appendChild(item) def index(self, row, column, parent=QModelIndex()): if not self.hasIndex(row, column, parent): return QModelIndex() if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() childItem = parentItem.child(row) if childItem: return self.createIndex(row, column, childItem) else: return QModelIndex() def parent(self, index): if not index.isValid(): return QModelIndex() childItem = index.internalPointer() parentItem = childItem.parent() if parentItem == self.rootItem: return QModelIndex() return self.createIndex(parentItem.row(), 0, parentItem) def rowCount(self, parent=QModelIndex()): if parent.column() > 0: return 0 if not parent.isValid(): parentItem = self.rootItem else: parentItem = parent.internalPointer() return parentItem.childCount() def columnCount(self, parent=QModelIndex()): if parent.isValid(): return parent.internalPointer().columnCount() else: return self.rootItem.columnCount() def data(self, index, role=Qt.ItemDataRole.DisplayRole): if not index.isValid(): return None item = index.internalPointer() if role == Qt.ItemDataRole.DisplayRole: return item.data(index.column()) elif role == Qt.ItemDataRole.TextAlignmentRole: return Qt.AlignmentFlag.AlignVCenter + Qt.AlignmentFlag.AlignLeft def headerData(self, section, orientation, role=Qt.ItemDataRole.DisplayRole): if orientation == Qt.Orientation.Horizontal and role == Qt.ItemDataRole.DisplayRole: return self.rootItem.data(section) def flags(self, index): if not index.isValid(): return Qt.ItemFlag.NoItemFlags return Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable class CustomItem: def __init__(self, data, parent): self.parentItem = parent self.itemData = data self.childItems = [] def appendChild(self, item): self.childItems.append(item) def child(self, row): return self.childItems[row] def childCount(self): return len(self.childItems) def columnCount(self): return len(self.itemData) def data(self, column): return self.itemData[column] def parent(self): return self.parentItem if __name__ == "__main__": app = QApplication([]) window = QMainWindow() treeView = QTreeView(window) data = [ CustomItem(["Item 1"], None), CustomItem(["Item 2"], None) ] model = CustomModel(data) treeView.setModel(model) window.setCentralWidget(treeView) window.resize(500, 500) window.show() app.exec() ``` 在这个示例中,定义了一个`CustomModel`类继承自`QAbstractItemModel`类,并重写了一些必要的方法。同时定义了一个`CustomItem`类作为模型的节点数据。 在`CustomModel`中,`index`方法用于获取给定行列的索引,`parent`方法用于获取给定索引的父索引,`rowCount`和`columnCount`方法用于获取给定索引的行数和列数,`data`方法返回指定索引的数据,`headerData`方法返回给定节的标题数据,`flags`方法定义每个索引的标志。 在主函数中,创建一个自定义模型`CustomModel`的实例,并将数据传递给模型。然后将该模型设置为`QTreeView`的模型,最后显示窗口并进入应用程序的事件循环。 这样,使用自定义模型的QTreeView就可以显示自定义数据了。您可以根据自己的需求扩展并修改此示例代码。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值