简介:前言最近在实现一个功能时,需要用到QTreeView,因为涉及到树形的插入、删除、添加,于是乎想到用自定义model的方式,平时基本都是用的普通方式生成的QTreeView,很少自定义model,于是...

前言

最近在实现一个功能时,需要用到QTreeView,因为涉及到树形的插入、删除、添加,于是乎想到用自定义model的方式,平时基本都是用的普通方式生成的QTreeView,很少自定义model,于是研究了一下。

实现思路



1. 定义数据结构 QList<TreeNode *>
2. 初始化数据dataList = QList<TreeNode *>
3. 遍历dataList,生成TreeItem(自己的数据结构)
MyTreeItem* root = m_pModel->root();
for (int row = 0; row < dataList.length(); ++row)
{
	TreeNode* rowNode = dataList.at(row);

	MyTreeItem* item = new MyTreeItem(root);
	item->setData(rowNode);
	root->addChild(item);

	// 子节点
	for (int cIndex = 0; cIndex < rowNode->children.length(); ++cIndex)
	{
		TreeNode* cNode = rowNode->children.at(cIndex);

		MyTreeItem* cItem = new MyTreeItem(item);
		cItem->setData(cNode);

		item->addChild(cItem);
	}
}
4. TreeModel: public QAbstractItemModel需要实现默认的接口
另外还需要实现 两个方法 root itemFormIndex

// 此方法可以读取model中真实数据
MyTreeItem* item = static_cast<MyTreeItem *>(index.internalPointer()); 

// 此方式可以将数据写入到model中
MyTreeItem* parentItem = itemFormIndex(parent);
MyTreeItem* item = parentItem->child(row);
return item ? createIndex(row, column, item) : QModelIndex();

MyTreeItem *MyTreeModel::root() const
{
    return m_pRootItem;
}

MyTreeItem *MyTreeModel::itemFormIndex(const QModelIndex &index) const
{
    if(!index.isValid())
    {
        return m_pRootItem;
    }

    MyTreeItem* item = static_cast<MyTreeItem *>(index.internalPointer());
    return item;
}

5. 数据接口定义类:MyTreeItem
int m_row; 定义当前行号 这个如果错误会导致多行同时被选中

实现代码

自定义model

#ifndef MYTREEMODEL_H
#define MYTREEMODEL_H

#include <QAbstractItemModel>

#include "./mytreeitem.h"

class MyTreeModel : public QAbstractItemModel
{
    Q_OBJECT

public:
    explicit MyTreeModel(const QStringList &headers, QObject *parent = nullptr);
    ~MyTreeModel();

    MyTreeItem* root() const;

    MyTreeItem* itemFormIndex(const QModelIndex &index) const;

    // Header:
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;

    // Basic functionality:
    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;

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;

private:
    QStringList m_headers;
    MyTreeItem* m_pRootItem;
};

#endif // MYTREEMODEL_H
---------------------------------------------------

#include "mytreemodel.h"

MyTreeModel::MyTreeModel(const QStringList &headers, QObject *parent)
    : QAbstractItemModel(parent)
    , m_pRootItem(new MyTreeItem())
{
    m_headers = headers;
}

MyTreeModel::~MyTreeModel()
{
    delete m_pRootItem;
    m_pRootItem = nullptr;
}

MyTreeItem *MyTreeModel::root() const
{
    return m_pRootItem;
}

MyTreeItem *MyTreeModel::itemFormIndex(const QModelIndex &index) const
{
    if(!index.isValid())
    {
        return m_pRootItem;
    }

    MyTreeItem* item = static_cast<MyTreeItem *>(index.internalPointer());
    return item;
}

QVariant MyTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole)
        {
            m_headers.at(section);
        }
    }

    return QVariant();
}

QModelIndex MyTreeModel::index(int row, int column, const QModelIndex &parent) const
{
    if(!hasIndex(row, column, parent))
    {
        return QModelIndex();
    }

    MyTreeItem* parentItem = itemFormIndex(parent);
    MyTreeItem* item = parentItem->child(row);
    return item ? createIndex(row, column, item) : QModelIndex();
}

QModelIndex MyTreeModel::parent(const QModelIndex &index) const
{
    if(!index.isValid())
        return QModelIndex();

    MyTreeItem* item = itemFormIndex(index);
    MyTreeItem* parentItem = item->parent();
    if(parentItem == m_pRootItem)
    {
        return QModelIndex();
    }
    else{
        return createIndex(parentItem->row(), 0, parentItem);
    }
}

int MyTreeModel::rowCount(const QModelIndex &parent) const
{
    if (parent.column() > 0)
        return 0;

    MyTreeItem* item = itemFormIndex(parent);
    return item->childCount();

    return 0;
}

int MyTreeModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)
    return m_headers.count();
}

QVariant MyTreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if(role == Qt::DisplayRole)
    {
        MyTreeItem* item = itemFormIndex(index);
        return item->data(index.column());
    }

    return QVariant();
}

自定义数据

#ifndef TYPEDEF_H
#define TYPEDEF_H

#include <QString>
#include <QList>

struct TreeNode {
    TreeNode() {
        num = 0;
    }

    int num;
    QString name;

    QList<TreeNode *> children;

} ;

#endif // TYPEDEF_H

-----------------------------------------
#ifndef MYTREEITEM_H
#define MYTREEITEM_H

#include "./typedef.h"

#include <QVariant>

class MyTreeItem
{
public:
   explicit MyTreeItem(MyTreeItem *parent = nullptr);
    ~MyTreeItem();

    MyTreeItem* parent() const;

    // 获取当前是父节点的第几个
    void setRow(int row);
    int row() const;

    MyTreeItem *child(int row) const;


    // 数据
    QVariant data(int col) const;

    // 设置数据
    void setData(TreeNode *data);

    // 子节点数量
    int childCount();

    // 添加子节点
    void addChild(MyTreeItem *item);

private:
    MyTreeItem* m_parent;
    QList<MyTreeItem *> m_children;
    TreeNode* m_data;
    int m_row;
};

#endif // MYTREEITEM_H
---------------------------------------------

#include "mytreeitem.h"

MyTreeItem::MyTreeItem(MyTreeItem *parent)
{
    m_parent = parent;
    m_data = nullptr;
    m_row = -1;
}

MyTreeItem::~MyTreeItem()
{
    qDeleteAll(m_children.begin(), m_children.end());
    m_children.clear();
}

MyTreeItem *MyTreeItem::parent() const
{
    return m_parent;
}

void MyTreeItem::setRow(int row)
{
    m_row = row;
}

int MyTreeItem::row() const
{
    return m_row;
}

MyTreeItem * MyTreeItem::child(int row) const
{
    return m_children.value(row);
}

QVariant MyTreeItem::data(int col) const
{
    switch (col) {
    case 0:
        return m_data->num;
    case 1:
        return m_data->name;
    }

    return QVariant();
}

void MyTreeItem::setData(TreeNode *data)
{
    m_data = data;
}

int MyTreeItem::childCount()
{
    return m_children.count();
}

void MyTreeItem::addChild(MyTreeItem *item)
{
    item->setRow(m_children.count());
    m_children.append(item);
}



渲染界面

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

#include "./mytreeitem.h"
#include "./mytreemodel.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    void initTree();

public slots:
    /**
     * @brief slotTreeViewMenus 树形菜单
     * @param pos
     */
    void slotTreeViewMenus(const QPoint &pos);

    /**
     * @brief slotAddLine 添加值
     */
    void slotAddValue();

    /**
     * @brief slotAddLine 添加表
     */
    void slotAddTable();

    /**
     * @brief slotDelLine 删除行
     */
    void slotDelLine();

    void slotTreeItemClick(const QModelIndex &index);

private:
    Ui::MainWindow *ui;

    MyTreeModel *m_pModel;
    QMenu       *m_pMenus;
    QModelIndex          m_pCurIndex;
};
#endif // MAINWINDOW_H

------------------------------------------------------

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QMenu>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
    , m_pModel(nullptr)
{
    ui->setupUi(this);

    m_pMenus = new QMenu(ui->treeView);
    // m_pMenus->setWindowFlags(m_pMenus->windowFlags()  | Qt::FramelessWindowHint | Qt::NoDropShadowWindowHint);

    QAction *addValue = m_pMenus->addAction(QStringLiteral("添加"));
    connect(addValue, &QAction::triggered, this, &MainWindow::slotAddValue);

    QAction *addTable = m_pMenus->addAction(QStringLiteral("添加表"));
    connect(addTable, &QAction::triggered, this, &MainWindow::slotAddTable);

    QAction *del = m_pMenus->addAction(QStringLiteral("删除"));
    connect(del, &QAction::triggered, this, &MainWindow::slotDelLine);

    this->initTree();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::initTree()
{
    QList<TreeNode *> dataList;

    for (int row = 0; row < 10; ++row)
    {
        TreeNode* rowNode = new TreeNode();
        rowNode->num = row;
        rowNode->name = QString::fromLocal8Bit("root-%1").arg(QString::number(row));

        for (int col = 0; col < 3; ++col)
        {
            TreeNode* colNode = new TreeNode();
            colNode->num = col;
            colNode->name = QString::fromLocal8Bit("root-%1-%2")
                    .arg(QString::number(row))
                    .arg(QString::number(col));
            rowNode->children.append(colNode);
        }

        dataList.append(rowNode);
    }



    QStringList headers = QStringList() << "Header";
    m_pModel = new MyTreeModel(headers, this);

    MyTreeItem* root = m_pModel->root();
    for (int row = 0; row < dataList.length(); ++row)
    {
        TreeNode* rowNode = dataList.at(row);

        MyTreeItem* item = new MyTreeItem(root);
        item->setData(rowNode);
        root->addChild(item);

        // 子节点
        for (int cIndex = 0; cIndex < rowNode->children.length(); ++cIndex)
        {
            TreeNode* cNode = rowNode->children.at(cIndex);

            MyTreeItem* cItem = new MyTreeItem(item);
            cItem->setData(cNode);

            item->addChild(cItem);
        }
    }

    ui->treeView->setModel(m_pModel);

    ui->treeView->setContextMenuPolicy(Qt::CustomContextMenu);

    connect(ui->treeView, &QTreeView::customContextMenuRequested, this, &MainWindow::slotTreeViewMenus);
    connect(ui->treeView, &QTreeView::clicked, this, &MainWindow::slotTreeItemClick);
}

void MainWindow::slotTreeViewMenus(const QPoint &pos)
{
    QModelIndex index = ui->treeView->indexAt(pos);
    m_pCurIndex = index;
    m_pMenus->exec(QCursor::pos());
}

void MainWindow::slotAddValue()
{

}

void MainWindow::slotAddTable()
{

}

void MainWindow::slotDelLine()
{

}

void MainWindow::slotTreeItemClick(const QModelIndex &index)
{

}

实现效果

phpR2Nfr0