如何使用QAbstractItemModel定製自己想要的數據模型(使用TreeView模型講解)

本例和大家交流如何使用QAbstractItemModel定製自己想要的數據模型.

比如,突然想做一個類似QQ一樣的好友界面:

該怎麼弄?

先別慌,一步步來,今天就和大家交流如何簡單快捷的弄出一個類似的玩意兒。

(直接上代碼,相關說明已經註釋在代碼中。)

CFriendViewModel.h

#ifndef CFRIENDVIEWMODEL_H
#define CFRIENDVIEWMODEL_H

#include <QAbstractItemModel>
#include <QListView>

//* (示例所用)樹狀節點結構體
struct FriendViewItem
{
    bool isGroup; //* 是否是分組(其實可以使用children.isEmpty來替代)
    QString name; //* 名字
    QList<FriendViewItem*> children; //* 子節點
};
typedef QList<FriendViewItem*> FriendViewItemList;

//*(示例所用)自定義樹狀model
class CFriendViewModel: public QAbstractItemModel
{
    Q_OBJECT
public:
    CFriendViewModel(QObject* parent = NULL);
    ~CFriendViewModel();

    //* 用來提供View header部分的數據
	virtual QVariant	headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;

    //* 返回列數
    virtual int	columnCount(const QModelIndex & parent = QModelIndex()) const;

    //* 獲取節點的數據
    virtual QVariant	data(const QModelIndex & index, int role = Qt::DisplayRole) const;

    //* 獲取某一個節點
    virtual QModelIndex	index(int row, int column, const QModelIndex & parent = QModelIndex()) const;

    //* 獲取某一個節點的parent
    virtual QModelIndex	parent(const QModelIndex & index) const;

    //* 返回行數
    virtual int	rowCount(const QModelIndex & parent = QModelIndex()) const;

    //* 設置原始數據
    void setFriendViewItemList(const FriendViewItemList& li);

    //* 返回原始數據
    FriendViewItemList friendViewItemList();


private:
    FriendViewItemList mItemList; //* 樹狀結構的原始數據(父節點list)
};

#endif // CFRIENDVIEWMODEL_H

CFriendViewModel.cpp

#include "CFriendViewModel.h"

CFriendViewModel::CFriendViewModel(QObject *parent)
    :QAbstractItemModel(parent)
{

}

CFriendViewModel::~CFriendViewModel()
{

}

int CFriendViewModel::columnCount(const QModelIndex &parent) const
{
    //* 本示例中只有一列數據
    return 1;
}

QVariant CFriendViewModel::data(const QModelIndex &index, int role) const
{
    //* 如果是無效節點則返回空數據
    if(!index.isValid())
    {
        return QVariant();
    }

    //* 嘗試獲取原始數據
    FriendViewItem* d = static_cast<FriendViewItem*>(index.internalPointer());
    if(d == NULL)//* 沒有原始數據則返回空數據
    {
        return QVariant();
    }

    if(role == Qt::DisplayRole)//* 本例只返回了DisplayRole的數據
    {
        return d->name;
    }

    return QVariant();
}

QModelIndex CFriendViewModel::index(int row, int column, const QModelIndex &parent) const
{
   //* 看父節點是否有效

   if(!parent.isValid()) //* 無效的父節點(那肯定是分組)
   {
        QModelIndex groupIndex;
        if(mItemList.count() > row) //* 使用有效的數據創建一個節點
			groupIndex = createIndex(row, column, mItemList.at(row));

        return groupIndex;
   }
   else //* 有效的父節點
   {
       //* 獲取父節點的數據
       FriendViewItem* parentData = static_cast<FriendViewItem*>(parent.internalPointer());


       if(parentData != NULL)
       {
           if(parentData->children.count() > row) //* 使用父節點下的對應row的子對象結構創建一個節點
           {
               QModelIndex childIndex = createIndex(row, column, parentData->children[row]);
               return childIndex;
           }
       }
   }


   //* 沒有有效的數據項返回一個無效的節點
   return QModelIndex();
}

QModelIndex CFriendViewModel::parent(const QModelIndex &index) const
{
    //* 如果是無效的節點則返回無效節點
    if(!index.isValid())
    {
        return QModelIndex();
    }


    FriendViewItem* indexData = static_cast<FriendViewItem*>(index.internalPointer());
    for(int i = 0; i < mItemList.count(); i++)
    {
        if(mItemList[i] == indexData) //* 如果是父節點(分組)則返回無效父節點(分組沒有父節點)
        {
            return QModelIndex();
        }
        else
        {
            //* 試圖判斷該節點的父節點是不是mItemList[i]
            for(int ii = 0; ii < mItemList[i]->children.count(); ii++)
            {
                if(indexData == mItemList[i]->children[ii]) //* mItemList[i] 成功匹配爲indexData的父節點
                {
                    //* 使用mItemList[i]作爲數據創建一個父節點
                    QModelIndex parentIndex = createIndex(i,0,mItemList[i]);
                    return parentIndex;
                }
            }
        }
    }

    //* 未匹配到有效的父節點數據,返回無效節點
    return QModelIndex();
}


int CFriendViewModel::rowCount(const QModelIndex &parent) const
{
    //* 父節點是否有效

    if(!parent.isValid()) //* 無效的父節點(說明是分組)返回父節點個數
    {
        return mItemList.count();
    }

    //*父節點是有效的,獲取父節點原始數據
    FriendViewItem* d = static_cast<FriendViewItem*>(parent.internalPointer());
    if(d == NULL)
    {
        return 0;
    }

    //* 父節點的原始數據中的子節點個數即爲rowCount
    return d->children.count();
}

void CFriendViewModel::setFriendViewItemList(const FriendViewItemList &li)
{
    mItemList = li;

    layoutChanged(); //* 通知View刷新數據

}

FriendViewItemList CFriendViewModel::friendViewItemList()
{
    return mItemList;
}

QVariant CFriendViewModel::headerData( int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/ ) const
{
    //* 只返回header的DisplayRole數據
	if(role == Qt::DisplayRole)
		return QString("this is friend view model.");

	return __super::headerData(section, orientation, role);
}

主函數main.cpp

#include "mainwindow.h"
#include <QApplication>

#include <QTreeView>
#include "CFriendViewModel.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QTreeView sampleFriendListView;
    sampleFriendListView.show();

    CFriendViewModel friendListModel;

    //sample data
    FriendViewItemList fli;
    for(int i = 0; i <5 ;i++)
    {
        FriendViewItem* gi = new FriendViewItem;
        gi->isGroup = true;
        gi->name = QString("group %1").arg(i);
        for(int ii = 0; ii < 10; ii++)
        {
            FriendViewItem* ci = new FriendViewItem;
            ci->isGroup = false;
            ci->name = QString("children %1").arg(ii);
            gi->children << ci;
        }

        fli << gi;
    }

    friendListModel.setFriendViewItemList(fli);

    sampleFriendListView.setModel(&friendListModel);

    return a.exec();
}

 

運行一下。得到了一個拙劣的好友列表:

如果需要操作裏面的數據就使用

CFriendViewModel中的兩個方法:

//* 設置原始數據
    void setFriendViewItemList(const FriendViewItemList& li);

    //* 返回原始數據
    FriendViewItemList friendViewItemList();

這樣我們就得到了一個屬於自己的好友列表,完成了偉大工程的第一步(最重要的是數據模型)。

接下來就是美化一下我們的TreeView了,不過那是另外一個故事了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章