目标
用qt的模型-视图框架实现树型层次节点的显示,从QAbstractItemModel派生自己的模型类MyTreeItemModel,用boost::property_tree::ptree操作树型数据结构,为了演示,此处只实现了个只读的模型
MyTreeItemModel的定义
#pragma once#include <QAbstractItemModel>
#include "boost/property_tree/ptree.hpp"class MyTreeItemModel : public QAbstractItemModel
{Q_OBJECTpublic: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_treeboost::property_tree::ptree m_TreeItem;
};
MyTreeItemModel的实现
-
简化代码,直接在构造函数创建出树型数据结构,用的是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"); }
-
模型类必须实现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(); }
-
模型类必须实现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); }
-
模型类必须实现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;elsepParent = static_cast<const boost::property_tree::ptree*>(parent.internalPointer());return pParent->size(); }
-
模型类必须实现columnCount虚函数
int MyTreeItemModel::columnCount(const QModelIndex &parent /*= QModelIndex()*/) const {return 1; //只支持一列 }
-
模型类必须实现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); }
-
重写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); }
-
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);
}