基于Qt的Model-View显示树形数据

目标

用qt的模型-视图框架实现树型层次节点的显示,从QAbstractItemModel派生自己的模型类MyTreeItemModel,用boost::property_tree::ptree操作树型数据结构,为了演示,此处只实现了个只读的模型

在这里插入图片描述

MyTreeItemModel的定义

#pragma once

#include <QAbstractItemModel>
#include "boost/property_tree/ptree.hpp"

class MyTreeItemModel : public QAbstractItemModel
{
	Q_OBJECT

public:
	MyTreeItemModel(QObject *parent = 0);
	~MyTreeItemModel();

	virtual QModelIndex index(int row, int column,
		const QModelIndex &parent = QModelIndex()) const override;
	virtual QModelIndex parent(const QModelIndex &child) const override;
	virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override;
	virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override;
	virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
	virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
private:
	const boost::property_tree::ptree* GetParent(const boost::property_tree::ptree* pChild,
		int& nParentRow ) const;

private:
	// 创建一个property_tree
	boost::property_tree::ptree m_TreeItem;
};

MyTreeItemModel的实现

  1. 简化代码,直接在构造函数创建出树型数据结构,用的是boost库的property_tree::ptree

    MyTreeItemModel::MyTreeItemModel(QObject *parent)
    	: QAbstractItemModel(parent)
    {
    	// 添加数据
    	m_TreeItem.put("node", "root value");
    	m_TreeItem.put("node.child1", "child1 value1");
    	m_TreeItem.put("node.child2", "child2 value2");
    	m_TreeItem.put("node.child3", "child3 value3");
    
    	// 添加子节点
    	boost::property_tree::ptree child;
    	child.put("grandchild", "grandchild value4");
    	m_TreeItem.add_child("node.child4", child);
    	m_TreeItem.put("node.child4", "child4 value4");
    }
    
  2. 模型类必须实现index虚函数,视图、委托会调用index函数来访问模型数据,具体参看qt帮助文档

    QModelIndex MyTreeItemModel::index(int row, int column,
    	const QModelIndex &parent /*= QModelIndex()*/) const
    {
    	if (!hasIndex(row, column, parent))
    	{
    		return QModelIndex();
    	}
    
    	const boost::property_tree::ptree* pParent = nullptr;
    	if (!parent.isValid())
    	{
    		pParent = &m_TreeItem;
    	}
    	else
    	{
    		pParent = reinterpret_cast<boost::property_tree::ptree*>( parent.internalPointer() );
    	}
    
    	auto it = pParent->begin();
    	it = std::next(it, row);
    	const boost::property_tree::ptree* pThisItem = &(it->second);
    	if (pThisItem)
    	{
    		return createIndex(row, column, (void*)pThisItem);
    	}
    
    	return QModelIndex();
    }
    
  3. 模型类必须实现parent虚函数

    QModelIndex MyTreeItemModel::parent(const QModelIndex &child) const
    {
    	if (!child.isValid())
    		return QModelIndex();
    
    	boost::property_tree::ptree *childItem = static_cast<boost::property_tree::ptree*>(child.internalPointer());
    	
    	int nParentRow = 0;
    	const boost::property_tree::ptree* pParentItem = GetParent(childItem, nParentRow);
    	if (!pParentItem)
    	{
    		Q_ASSERT(false);
    		return QModelIndex();
    	}
    
    	if (pParentItem == &m_TreeItem)
    		return QModelIndex();
    
    	return createIndex(nParentRow, 0, (void *)pParentItem);
    }
    
  4. 模型类必须实现rowCount虚函数

    int MyTreeItemModel::rowCount(const QModelIndex &parent /*= QModelIndex()*/) const
    {
    	if (parent.column() > 0) //第一列才有子节点
    		return 0;
    
    	const boost::property_tree::ptree* pParent = nullptr;
    	if (!parent.isValid())
    		pParent = &m_TreeItem;
    	else
    		pParent = static_cast<const boost::property_tree::ptree*>(parent.internalPointer());
    
    	return pParent->size();
    }
    
  5. 模型类必须实现columnCount虚函数

    int MyTreeItemModel::columnCount(const QModelIndex &parent /*= QModelIndex()*/) const
    {
    	return 1; //只支持一列
    }
    
  6. 模型类必须实现data虚函数,因为是只读,只实现了Qt::DisplayRole

    QVariant MyTreeItemModel::data(const QModelIndex &index, int role /*= Qt::DisplayRole*/) const
    {
    	if (!index.isValid())
    		return QVariant();
    
    	if (role != Qt::DisplayRole)
    		return QVariant();
    
    	auto pTreeItem = static_cast<const boost::property_tree::ptree*>(index.internalPointer());
    	if (!pTreeItem)
    	{
    		Q_ASSERT(false);
    		return QVariant();
    	}
    
    	QString strValue = QString::fromStdString(pTreeItem->data());
    	return QVariant(strValue);
    }
    
  7. 重写flags函数,告诉使用者,本模型只提供只读功能,这里基类QAbstractItemModel的实现正好符合需求,可不重写,我这里写出来只是说明下而已

    Qt::ItemFlags MyTreeItemModel::flags(const QModelIndex &index) const
    {
    	if (!index.isValid())
    		return 0;
    
    	//The base class implementation returns a combination of flags that enables 
    	//the item (ItemIsEnabled) and allows it to be selected (ItemIsSelectable).
    	return QAbstractItemModel::flags(index);
    }
    
  8. boost::property_tree::ptree没有提供访问父节点的功能,故加了个GetParent函数,仅测试用,实际不应该这么用,效率低

    const boost::property_tree::ptree* MyTreeItemModel::GetParent(const boost::property_tree::ptree* pChild,
    	int& nParentRow) const
    {
    	if (!pChild)
    	{
    		return nullptr;
    	}
    
    	std::stack<const boost::property_tree::ptree*> mNodeStack;
    	mNodeStack.push(&m_TreeItem);
    	while (!mNodeStack.empty())
    	{
    		const boost::property_tree::ptree* pParent = mNodeStack.top();
    		mNodeStack.pop();
    		if (!pParent)
    		{
    			continue;
    		}
    
    		//在子节点列表中搜索指定节点
    		int nRow = 0;
    		for (auto it = pParent->begin(); it != pParent->end(); ++it, ++nRow)
    		{
    			const boost::property_tree::ptree* pChildItem = &(it->second);
    			if (pChildItem == pChild)
    			{
    				nParentRow = nRow;
    				return pParent;
    			}
    			
    			mNodeStack.push(pChildItem);
    		}
    	}
    
    	nParentRow = 0;
    	return nullptr;
    }
    

使用自写的模型类

QtGuiApplication1::QtGuiApplication1(QWidget *parent)
	: QMainWindow(parent)
{
	//ui.setupUi(this);

	auto pCentralWidget = new QWidget(this);
	this->setCentralWidget(pCentralWidget);

	auto pVLayout = new QVBoxLayout();
	QAbstractItemModel *pModel = new MyTreeItemModel();
	QTreeView *pTreeView = new QTreeView();
	pTreeView->setModel(pModel);
	pVLayout->addWidget(pTreeView);
	pCentralWidget->setLayout(pVLayout);
}

运行演示

在这里插入图片描述

  • 27
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。 基于QT - SOCKET 的 C++ 实现矩阵压力数据采集及压力云图现实。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值