简介:前言最近在实现一个功能时,需要用到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)
{
}