Chinaunix首页 | 论坛 | 博客
  • 博客访问: 27721
  • 博文数量: 4
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 0
  • 用 户 组: 普通用户
  • 注册时间: 2016-06-19 02:51
个人简介

关注QT,单片机和LINUX内核开发。向往漫无目的、漫不经心的生活态度。喜欢中岛美雪更喜欢二手玫瑰。

文章分类
文章存档

2016年(4)

我的朋友

分类: C/C++

2016-04-13 23:52:31

上文已经介绍过二维表模型的实现方式,接着分享层次模型的实现,首先实现一个节点类用于保存树状数据模型的节点数据和节点关系:

点击(此处)折叠或打开

  1. class TreeNode;

  2. typedef TreeNode* TreeNodePtr;

  3. class TreeNode
  4. {
  5. public:
  6.     explicit TreeNode();
  7.     TreeNodePtr parent() const;
  8.     void setParent(const TreeNodePtr p);  //设置父节点,根节点的父节点为NULL
  9.     void appendNode(TreeNodePtr node);
  10.     void removeNode(int row);
  11.     void setData(int role,QVariant value);  
  12.     QList<TreeNodePtr>childs;   //用于保存子节点指针,把childs放到public不好,仅仅是为了方便,不要学。

  13. private:
  14.     TreeNodePtr mParent=NULL;   //父节点
  15.     QHash<int,QVariant>mRecord; //一个节点可以保存一行数据,哈希表的key是整型,用于保存role,QVariant保存数据

  16. };
TreeNode的实现也是相当简单:

点击(此处)折叠或打开

  1. TreeNodePtr TreeNode::parent() const
  2. {
  3.     return mParent;
  4. }

  5. void TreeNode::setParent(const TreeNodePtr p)
  6. {
  7.     this->mParent = p;
  8. }

  9. void TreeNode::appendNode(TreeNodePtr node)
  10. {
  11.     childs.append(node);
  12. }

  13. void TreeNode::removeNode(int row)
  14. {
  15.     childs.removeAt(row);
  16. }


  17. QVariant TreeNode::data(int role)
  18. {
  19.     return mRecord[role];
  20. }

  21. void TreeNode::setData(int role, QVariant value)
  22. {
  23.    mRecord[role] = value;
  24. }
接下来开始对SqlMenuEntryModel进行手术:

点击(此处)折叠或打开

  1. class SqlMenuEntry:public QAbstractItemModel,public QQmlParserStatus
  2. {
  3.     Q_OBJECT
  4. public:
  5.     explicit SqlMenuEntry(QObject *parent=0);
  6.     ~SqlMenuEntry();
  7.     enum MenuEntryRoles{idRole=Qt::UserRole+1,nameRole,defaultEntryRole,customEntryRole,iconRole,iconHoverRole};
  8.     int rowCount(const QModelIndex &parent=QModelIndex()) const;
  9.     int columnCount(const QModelIndex &parent=QModelIndex()) const;
  10.     QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const;
  11.     QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const;
  12.     QModelIndex parent(const QModelIndex &child) const;
  13.     QHash<int,QByteArray>roleNames() const;
  14. private:
  15.     QHash<int,QByteArray> mRoleNames;
  16.     QList<QHash<int,QVariant>> mRecords; //QList不能保存树状数据,干掉
  17.     QList mRootEntrys;      //用于保存根节点
  18.     void _addEntryNode(TreeNodePtr node,TreeNodePtr parent=0); //用于向树添加节点
  19. };
_addEntryNode的实现如下:

点击(此处)折叠或打开

  1. void SqlMenuEntry::_addEntryNode(TreeNodePtr node, TreeNodePtr parent)
  2. {
  3.     if(parent == NULL)
  4.     {
  5.         mRootEntrys.append(node);
  6.     }else{
  7.         node->setParent(parent);
  8.         parent->appendNode(node);
  9.     }
  10. }

可以通过如下代码向模型添加数据:


点击(此处)折叠或打开

  1. beginResetModel();

  2.            auto node = new TreeNode();
  3.            node->setData(nameRole,"parent 1");
  4.            node->setData(iconRole,"parent icon1");
  5.            _addEntryNode(node);   //添加第一个根节点
  6.          
  7.            auto subNode = new TreeNode();
  8.            subNode->setData(nameRole,"child 1");
  9.            _addEntryNode(subNode,node); //添加子节点

  10. endResetModel();
上面的代码向模型添加了一个根节点parent1和parent1的一个子节点child1,接下来可以对SqlMenuEntryModel进行持续改进,让TreeView可以顺利显示正确的数据和数据关系。首先,更改int rowCount(const QModelIndex &parent)让模型可以正确的返回根节点数和子节点行数:

点击(此处)折叠或打开

  1. int SqlMenuEntry::rowCount(const QModelIndex &parent) const
  2. {
  3.     if(parent.isValid()) //parent不为空,则要获取的行数是某个节点的子节点行数
  4.     {
  5.         TreeNodePtr parentNode = (TreeNodePtr)parent.internalPointer(); //节点信息在index时被保存在QModelIndex的internalPointer中
  6.         return parentNode->childs.size();
  7.     }
  8.     return mRootEntrys.size();  //否则返回的是根节点行数

  9. }


实现index接口函数,让模型可以返回子节点的modelIndex同时将对应的节点指针保存在modelIndex的internalPointer中:

点击(此处)折叠或打开

  1. QModelIndex SqlMenuEntry::index(int row, int column, const QModelIndex &parent) const
  2. {
  3.     if(!parent.isValid())  //parent为空,返回的是根节点的modelIndex,返回的同时,把节点数据指针(TreeNodePtr)保存在QModelIndex的internalPointer中,以便在其它函数中获取节点数据
  4.     {
  5.         if((row >= 0) && (row < mRootEntrys.size()))
  6.         {
  7.             return createIndex(row,column,mRootEntrys.at(row));  
  8.         }
  9.     }else{
  10.         TreeNodePtr parentNode = (TreeNodePtr)parent.internalPointer(); //返回子节点的modelIndex
  11.         return createIndex(row,column,parentNode->childs[row]);
  12.     }
  13.     return QModelIndex();
  14. }
parent(const QModelIndex &child) 用于返回一个节点的父节点索引:

点击(此处)折叠或打开

  1. QModelIndex SqlMenuEntry::parent(const QModelIndex &child) const
  2. {
  3.     TreeNodePtr node = (TreeNodePtr)child.internalPointer();
  4.     if(node->parent() == NULL)
  5.     {
  6.         return QModelIndex(); //根节点没有parent
  7.     }else{
  8.         return createIndex(0,1,node->parent());
  9.     }
实现 data(const QModelIndex &index, int role)  返回节点中的数据:

点击(此处)折叠或打开

  1. QVariant SqlMenuEntry::data(const QModelIndex &index, int role) const
  2. {
  3.     TreeNodePtr node = (TreeNodePtr)index.internalPointer();
  4.     return node->data(role);
  5. }

经过上面的打造,模型已经可以正常为TreeView提供数据:

点击(此处)折叠或打开

  1. TreeView{
  2.         anchors.fill: parent
  3.         TableViewColumn {
  4.             title: "Name"
  5.             role: "name"
  6.         }

  7.         TableViewColumn {
  8.             title: "Entry"
  9.             role: "icon"
  10.         }
  11.         model:MenuEntryModel{}
  12. }
继续干活去了,明天再继续补充数据添加、删除和更新的相关经验。







阅读(25573) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~