使用C++實現QML的TreeView Model (二)
上文已經介紹過二維表模型的實現方式,接著分享層次模型的實現,首先實現一個節點類用於保存樹狀數據模型的節點數據和節點關系:
- class TreeNode;
- typedef TreeNode* TreeNodePtr;
- class TreeNode
- {
- public:
- explicit TreeNode();
- TreeNodePtr parent() const;
- void setParent(const TreeNodePtr p); //設置父節點,根節點的父節點為NULL
- void appendNode(TreeNodePtr node);
- void removeNode(int row);
- void setData(int role,QVariant value);
- QListchilds; //用於保存子節點指針,把childs放到public不好,僅僅是為了方便,不要學。
- private:
- TreeNodePtr mParent=NULL; //父節點
- QHashmRecord; //一個節點可以保存一行數據,哈希表的key是整型,用於保存role,QVariant保存數據
- };
TreeNode的實現也是相當簡單:
- TreeNodePtr TreeNode::parent() const
- {
- return mParent;
- }
- void TreeNode::setParent(const TreeNodePtr p)
- {
- this->mParent = p;
- }
- void TreeNode::appendNode(TreeNodePtr node)
- {
- childs.append(node);
- }
- void TreeNode::removeNode(int row)
- {
- childs.removeAt(row);
- }
- QVariant TreeNode::data(int role)
- {
- return mRecord[role];
- }
- void TreeNode::setData(int role, QVariant value)
- {
- mRecord[role] = value;
- }
接下來開始對SqlMenuEntryModel進行手術:
- class SqlMenuEntry:public QAbstractItemModel,public QQmlParserStatus
- {
- Q_OBJECT
- public:
- explicit SqlMenuEntry(QObject *parent=0);
- ~SqlMenuEntry();
- enum MenuEntryRoles{idRole=Qt::UserRole+1,nameRole,defaultEntryRole,customEntryRole,iconRole,iconHoverRole};
- int rowCount(const QModelIndex &parent=QModelIndex()) const;
- int columnCount(const QModelIndex &parent=QModelIndex()) const;
- QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const;
- QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
- QModelIndex parent(const QModelIndex &child) const;
- QHashroleNames() const;
- private:
- QHash mRoleNames;
-
QList<QHash<int,QVariant>> mRecords; //QList不能保存樹狀數據,干掉
- QList mRootEntrys; //用於保存根節點
- void _addEntryNode(TreeNodePtr node,TreeNodePtr parent=0); //用於向樹添加節點
- };
_addEntryNode的實現如下:
- void SqlMenuEntry::_addEntryNode(TreeNodePtr node, TreeNodePtr parent)
- {
- if(parent == NULL)
- {
- mRootEntrys.append(node);
- }else{
- node->setParent(parent);
- parent->appendNode(node);
- }
- }
可以通過如下代碼向模型添加數據:
- beginResetModel();
- auto node = new TreeNode();
- node->setData(nameRole,"parent 1");
- node->setData(iconRole,"parenticon1");
- _addEntryNode(node); //添加第一個根節點
-
- auto subNode = new TreeNode();
- subNode->setData(nameRole,"child1");
- _addEntryNode(subNode,node); //添加子節點
- endResetModel();
上面的代碼向模型添加了一個根節點parent1和parent1的一個子節點child1,接下來可以對SqlMenuEntryModel進行持續改進,讓TreeView可以順利顯示正確的數據和數據關系。首先,更改int rowCount(const QModelIndex &parent)讓模型可以正確的返回根節點數和子節點行數:
- int SqlMenuEntry::rowCount(const QModelIndex &parent) const
- {
- if(parent.isValid()) //parent不為空,則要獲取的行數是某個節點的子節點行數
- {
- TreeNodePtr parentNode = (TreeNodePtr)parent.internalPointer(); //節點信息在index時被保存在QModelIndex的internalPointer中
- return parentNode->childs.size();
- }
- return mRootEntrys.size(); //否則返回的是根節點行數
- }
實現index接口函數,讓模型可以返回子節點的modelIndex同時將對應的節點指針保存在modelIndex的internalPointer中:
- QModelIndex SqlMenuEntry::index(int row, int column, const QModelIndex &parent) const
- {
- if(!parent.isValid()) //parent為空,返回的是根節點的modelIndex,返回的同時,把節點數據指針(TreeNodePtr)保存在QModelIndex的internalPointer中,以便在其它函數中獲取節點數據
- {
- if((row >= 0) && (row < mRootEntrys.size()))
- {
- return createIndex(row,column,mRootEntrys.at(row));
- }
- }else{
- TreeNodePtr parentNode = (TreeNodePtr)parent.internalPointer(); //返回子節點的modelIndex
- return createIndex(row,column,parentNode->childs[row]);
- }
- return QModelIndex();
- }
parent(const QModelIndex &child) 用於返回一個節點的父節點索引:
- QModelIndex SqlMenuEntry::parent(const QModelIndex &child) const
- {
- TreeNodePtr node = (TreeNodePtr)child.internalPointer();
- if(node->parent() == NULL)
- {
- return QModelIndex(); //根節點沒有parent
- }else{
- return createIndex(0,1,node->parent());
- }
實現data(constQModelIndex&index,introle) 返回節點中的數據:
- QVariant SqlMenuEntry::data(const QModelIndex &index, int role) const
- {
- TreeNodePtr node = (TreeNodePtr)index.internalPointer();
- return node->data(role);
- }
經過上面的打造,模型已經可以正常為TreeView提供數據:
- TreeView{
- anchors.fill: parent
- TableViewColumn {
- title: "Name"
- role: "name"
- }
- TableViewColumn {
- title: "Entry"
- role: "icon"
- }
- model:MenuEntryModel{}
- }
繼續干活去了,明天再繼續補充數據添加、刪除和更新的相關經驗。