本文主要研究了一下如何把树形结构的数据保存到文件并读取出来。为了更形象说明用了一个界面程序显示,程序用了model/view框架。
数据类
DataItem 就是保存在树形结构的基本数据。其最重要的保存数据的函数是SerialzeData
- class DataItem
- {
- public:
- DataItem(int id = 100,QString name = "root");
- ~DataItem();
- void SetRoot(DataItem *root);
- void SerialzeData(bool isSave,QDataStream &stream);
- void Clear();
- void Init();
- //protected:
- int GetID()
- {
- return ID;
- }
- QString GetName()
- {
- return Name;
- }
- void SetID(int id)
- {
- ID = id;
- }
- void SetName(QString name)
- {
- Name = name;
- }
- int GetSize()
- {
- return dataVec.size();
- }
- void AddItem(DataItem *pItem);
- void DeleteItem(DataItem *pItem);
- void DeleteItem(int index);
- DataItem *GetItem(int index);
- DataItem *GetParent()
- {
- return pRoot;
- }
- int indexOf(DataItem* pItem);
- private:
- int ID;
- QString Name;
- vector<DataItem*> dataVec;
- DataItem *pRoot;
- };
- DataItem::DataItem( int id,QString name ):ID(id),Name(name),pRoot(NULL)
- {
- //pRoot = new DataItem(100,"Root");
- }
- DataItem::~DataItem()
- {
- }
- //SerialzeData 原来是,保存数据时,先保存每个项的数据,在后面保存该项的子节点个数,并递归保存各个子节点数据
- void DataItem::SerialzeData( bool isSave,QDataStream &stream )
- {
- if (isSave)
- {
- stream<<GetID()<<GetName(); //save ID and Name
- stream<<dataVec.size(); //save the number of child
- for(int i = 0; i < dataVec.size(); ++i)
- {
- dataVec[i]->SerialzeData(isSave,stream);
- }
- }
- else
- {
- int id;
- int size;
- QString name;
- stream>>id>>name; //Get ID and Name
- SetID(id);
- SetName(name);
- stream>>size; //Get the number of child
- for(int i = 0; i < size; ++i)
- {
- DataItem *pItem = new DataItem(0,"name");
- pItem->SerialzeData(isSave,stream);
- AddItem(pItem);
- }
- }
- }
- void DataItem::AddItem( DataItem *pItem )
- {
- pItem->SetRoot(this);
- dataVec.push_back(pItem);
- }
- void DataItem::DeleteItem( DataItem *pItem )
- {
- vector<DataItem*>::iterator it = dataVec.begin();
- for (it; it != dataVec.end(); ++it)
- {
- if (*it == pItem)
- {
- dataVec.erase(it);
- break;
- }
- }
- }
- void DataItem::DeleteItem( int index )
- {
- if (index < dataVec.size())
- {
- vector<DataItem*>::iterator it = dataVec.begin();
- it = it + index;
- dataVec.erase(it);
- }
- }
- void DataItem::Init()
- {
- for (int i = 0; i < 5; ++i)
- {
- DataItem *pItem = new DataItem(i,QString("child%1").arg(i));
- pRoot->AddItem(pItem);
- for (int j = 0; j < 2; ++j)
- {
- DataItem *pChild = new DataItem(j,QString("grandchild%0 -%1").arg(i).arg(j));
- pItem->AddItem(pChild);
- }
- }
- }
- void DataItem::SetRoot( DataItem *root )
- {
- pRoot = root;
- }
- void DataItem::Clear()
- {
- dataVec.clear();
- }
- DataItem * DataItem::GetItem( int index )
- {
- if (index < dataVec.size())
- {
- return dataVec[index];
- }
- else
- {
- return NULL;
- }
- }
- int DataItem::indexOf( DataItem* pItem )
- {
- int index = -1;
- for (int i = 0; i < dataVec.size(); ++i)
- {
- if (dataVec[i] == pItem)
- {
- index = i;
- break;
- }
- }
- return index;
- }
数据模型
TreeDataModel的底层数据就是上面定义的DataItem。用这种视图/模型的编程方式可以尽量减少数据与界面的耦合性。由于继承了QAbstractItemModel。所以必须重写其中的五个纯虚函数columnCount (),data(),index (),parent ()和rowCount()。
- class TreeDataModel:public QAbstractItemModel
- {
- Q_OBJECT
- public:
- TreeDataModel(QObject *parent = NULL);
- ~TreeDataModel();
- void SetRoot(DataItem *pRoot)
- {
- m_pTreeData = pRoot;
- }
- QModelIndex parent ( const QModelIndex & index ) const;
- QVariant data ( const QModelIndex & index, int role = Qt::DisplayRole ) const ;
- QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const;
- QModelIndex index ( int row, int column, const QModelIndex & parent = QModelIndex() ) const;
- int columnCount ( const QModelIndex & parent = QModelIndex() ) const;
- int rowCount ( const QModelIndex & parent = QModelIndex() ) const;
- DataItem* dataFromIndex(const QModelIndex &index) const;
- void SaveData(QDataStream &out);
- void LoadData(QDataStream &in);
- protected:
- private:
- DataItem *m_pTreeData;
- };
- TreeDataModel::TreeDataModel( QObject *parent /*= NULL*/ ):QAbstractItemModel(parent)
- {
- m_pTreeData = NULL;
- }
- TreeDataModel::~TreeDataModel()
- {
- }
- QVariant TreeDataModel::data( const QModelIndex & index, int role /*= Qt::DisplayRole */ ) const
- {
- DataItem *pItem = dataFromIndex(index);
- if ((pItem)&&(role == Qt::DisplayRole))
- {
- switch (index.column())
- {
- case 0:
- return pItem->GetID();
- case 1:
- return pItem->GetName();
- }
- }
- return QVariant();
- }
- QVariant TreeDataModel::headerData( int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole */ ) const
- {
- if ((section <2) && (orientation == Qt::Horizontal)&& (role == Qt::DisplayRole))
- {
- switch (section)
- {
- case 0:
- return tr("编号");
- case 1:
- return tr("名称");
- default:
- return QVariant();
- }
- }
- else
- {
- return QVariant();
- }
- }
- QModelIndex TreeDataModel::index( int row, int column, const QModelIndex & parent /*= QModelIndex() */ ) const
- {
- if (!m_pTreeData ||row < 0 || column < 0)
- {
- return QModelIndex();
- }
- else
- {
- DataItem *pItem = dataFromIndex(parent);
- if (pItem)
- {
- DataItem *pChild = pItem->GetItem(row);
- if (pChild)
- {
- return createIndex(row,column,pChild);
- }
- }
- return QModelIndex();
- }
- }
- int TreeDataModel::columnCount( const QModelIndex & parent /*= QModelIndex() */ ) const
- {
- return 2;
- }
- int TreeDataModel::rowCount( const QModelIndex & parent /*= QModelIndex() */ ) const
- {
- DataItem *pItem = dataFromIndex(parent);
- if (pItem)
- {
- return pItem->GetSize();
- }
- return 0;
- }
- DataItem* TreeDataModel::dataFromIndex( const QModelIndex &index ) const
- {
- if (index.isValid())
- {
- return static_cast<DataItem*>(index.internalPointer());
- }
- else
- {
- return m_pTreeData; //这里不要返回NULL
- }
- }
- QModelIndex TreeDataModel::parent( const QModelIndex & index ) const
- {
- if (index.isValid())
- {
- DataItem *pItem = dataFromIndex(index);
- if (pItem)
- {
- DataItem *pParent = pItem->GetParent();
- if (pParent)
- {
- DataItem *pGrandParent = pParent->GetParent();
- if (pGrandParent)
- {
- int row = pGrandParent->indexOf(pParent);
- return createIndex(row,index.column(),pParent);
- }
- }
- }
- }
- return QModelIndex();
- }
- void TreeDataModel::SaveData( QDataStream &out )
- {
- m_pTreeData->SerialzeData(true,out);
- }
- void TreeDataModel::LoadData( QDataStream &in )
- {
- m_pTreeData->SerialzeData(false,in);
- }
主框架类
这个类主要实现左边的树形把数据保存到文件中,然后在右边的树形结构加载显示出来。
- class MainWidget:public QWidget
- {
- Q_OBJECT
- public:
- MainWidget(QWidget *patent = NULL);
- ~MainWidget();
- protected slots:
- void leftSelectBtnSlot();
- void rightSelectBtnSlot();
- void saveBtnSlot();
- void loadBtnSlot();
- private:
- QSplitter *m_pSplitter;
- QTreeView *m_pLeftTreeView;
- QTreeView *m_pRightTreeView;
- QPushButton *m_pLeftSaveBtn;
- QPushButton *m_pRightLoadBtn;
- QPushButton *m_pLeftSelectBtn;
- QPushButton *m_pRightSelectBtn;
- QLineEdit *m_pLeftLEdit;
- QLineEdit *m_pRightLEdit;
- QGridLayout *m_pLeftLayout;
- QGridLayout *m_pRightLayout;
- TreeDataModel *m_pLeftModel;
- TreeDataModel *m_pRightModel;
- };
- MainWidget::MainWidget( QWidget *patent /*= NULL*/ ):QWidget(patent)
- {
- m_pLeftModel = new TreeDataModel();
- m_pRightModel = new TreeDataModel();
- m_pSplitter = new QSplitter(this);
- QFrame *pLeftFrame = new QFrame(this);
- QFrame *pRightFrame = new QFrame(this);
- m_pLeftLayout = new QGridLayout(pLeftFrame);
- m_pRightLayout = new QGridLayout(pRightFrame);
- m_pLeftLEdit = new QLineEdit(this);
- m_pRightLEdit = new QLineEdit(this);
- m_pLeftSaveBtn = new QPushButton(tr("保存"),this);
- m_pRightLoadBtn = new QPushButton(tr("加载"),this);
- m_pLeftTreeView = new QTreeView(this);
- m_pRightTreeView = new QTreeView(this);
- m_pLeftSelectBtn = new QPushButton(tr("选择文件"),this);
- m_pRightSelectBtn = new QPushButton(tr("选择文件"),this);
- m_pRightLEdit->setReadOnly(true);
- m_pLeftLayout->addWidget(m_pLeftSelectBtn,0,0,1,1);
- m_pLeftLayout->addWidget(m_pLeftLEdit,0,1,1,1);
- m_pLeftLayout->addWidget(m_pLeftSaveBtn,0,2,1,1);
- m_pLeftLayout->addWidget(m_pLeftTreeView,1,0,3,3);
- m_pRightLayout->addWidget(m_pRightSelectBtn,0,0,1,1);
- m_pRightLayout->addWidget(m_pRightLEdit,0,1,1,1);
- m_pRightLayout->addWidget(m_pRightLoadBtn,0,2,1,1);
- m_pRightLayout->addWidget(m_pRightTreeView,1,0,3,3);
- m_pLeftTreeView->setModel(m_pLeftModel);
- m_pRightTreeView->setModel(m_pRightModel);
- DataItem *pTreeData = new DataItem();
- pTreeData->SetRoot(pTreeData);
- pTreeData->Init();
- m_pLeftModel->SetRoot(pTreeData);
- //m_pRightModel->SetRoot(pTreeData);
- m_pSplitter->addWidget(pLeftFrame);
- m_pSplitter->addWidget(pRightFrame);
- connect(m_pLeftSelectBtn,SIGNAL(clicked()),this,SLOT(leftSelectBtnSlot()));
- connect(m_pRightSelectBtn,SIGNAL(clicked()),this,SLOT(rightSelectBtnSlot()));
- connect(m_pLeftSaveBtn,SIGNAL(clicked()),this,SLOT(saveBtnSlot()));
- connect(m_pRightLoadBtn,SIGNAL(clicked()),this,SLOT(loadBtnSlot()));
- this->setFixedSize(QSize(650,250));
- }
- MainWidget::~MainWidget()
- {
- }
- void MainWidget::leftSelectBtnSlot() //这里只是选择了一个文件夹路径,在保存之前还需要加文件名
- {
- QFileDialog Dialog(this,tr("选择目录"),"","");
- Dialog.setFileMode(QFileDialog::Directory);
- //Dialog.setNameFilter("*.data");
- if (Dialog.exec())
- {
- QStringList dirs = Dialog.selectedFiles();
- if (dirs.size() > 0)
- {
- m_pLeftLEdit->setText(QDir::toNativeSeparators(dirs.at(0)));
- }
- }
- }
- void MainWidget::rightSelectBtnSlot() //选择之前保存的.data文件进行加载显示
- {
- QFileDialog Dialog(this,tr("选择文件"),"","");
- Dialog.setFileMode(QFileDialog::ExistingFile);
- Dialog.setNameFilter("*.data");
- if (Dialog.exec())
- {
- QStringList files = Dialog.selectedFiles();
- if (files.size() > 0)
- {
- m_pRightLEdit->setText(QDir::toNativeSeparators(files.at(0)));
- }
- }
- }
- void MainWidget::saveBtnSlot()
- {
- QString filePath = m_pLeftLEdit->text();
- if ((filePath.isEmpty()) || filePath.endsWith("\") || filePath.endsWith("/")) //必须得添加文件名,文件名规定后缀为.data
- {
- QMessageBox::information(this,tr("提示"),tr("请输入文件名"),QMessageBox::Ok);
- return;
- }
- else if(filePath.endsWith("data"))
- {
- QFile file(filePath);
- if (file.open(QIODevice::WriteOnly))
- {
- QDataStream outStream(&file);
- m_pLeftModel->SaveData(outStream);
- }
- }
- }
- void MainWidget::loadBtnSlot()
- {
- QString filePath = m_pRightLEdit->text();
- if((!filePath.isEmpty()) &&filePath.endsWith("data"))
- {
- DataItem *pTreeData = new DataItem();
- //pTreeData->SetRoot(pTreeData);
- m_pRightModel->SetRoot(pTreeData);
- QFile file(filePath);
- if (file.open(QIODevice::ReadOnly))
- {
- QDataStream inStream(&file);
- m_pRightModel->LoadData(inStream);
- m_pRightTreeView->setModel(m_pRightModel);
- m_pRightTreeView->reset(); //必须的,不然不会刷新
- }
- }
- }
运行结果如下图
http://blog.csdn.net/hai200501019/article/details/11022581