一般用TreeModel都是用自己自带的类,关于继承QAbstractItemModel的时候,必须实现如下几个函数:index(), parent(), rowCount(), columnCount(), data(), 要让Model变成可以编辑的话,必须还要实现 setData(), flags() 这两个函数,让flags()返回值有ItemIsEditable。 同时,还可以实现headerData()和 setHeaderData() 来控制View中的标题。
接下来的问题就简单一些,实现它的数据结构,结构也很简单,就是简单的树形,一行,就是一个数据(当然,这一行也可以是很多数据),这里一行,就是一个item,每个里面的数据就是对应的column。在实现上,这个item可以有list,含有它的child, 也要有一个parent的指针。最Root,只有一个node(记得,我们说的是Tree!), 简单吧? 如果要把这样的结构与其它scene graph一起用,基本上不用实现那个list和parent的指针,因为SG的node会给你这样的信息。
下面的问题,就是实现那几个必须的函数。
QModelIndex index ( int row, int column, const QModelIndex & parent ) const
这个函数对View来说,就是要你给它所需要的行与列上的index.不管这个item有没有parent,view都会给你一个parent,当然,如果没有parent,那这个parent的index就是无效的。来看看怎么实现.
CTreeItem *parentItem; if(parent.isValid()==false) parentItem=m_pRootItem; else parentItem=static_cast(parent.internalPointer()); CTreeItem *childItem = parentItem->child(row); if(childItem) return createIndex(row,column,childItem); else return QModelIndex();
上面 if(parent.isValid()==false)如果view给出的parent无效,那当前所指的item一定是第一层,也就是Root下面的。如果不是,那就用parentItem=static_cast(parent.internalPointer()) 得到这个node的指针,然后,看这个node里view需要的那一行上是否有node,如果没有,就返回无效的index( return QModelIndex())
QModelIndex parent ( const QModelIndex & index ) const
这个函数,是view向你要一个指定index的parent的index.来看看怎么实现
if(index.isValid()){ CTreeItem * childItem=static_cast(index.internalPointer()); CTreeItem * parentItem=childItem->parent(); if(parentItem==m_pRootItem) return QModelIndex(); else return createIndex(parentItem->row(),20,parentItem); } else return QModelIndex();
如果view给我一个无效的index,那我当然告诉它,这个无效的index的parent也是无效的,无效的parent,在这里,就是明确说明这个index是顶层的node的index,无效的含义只有这一个。代码很简单,得到这个index所指node的指针,看它有没有parent,如果它的parent就是root,那就也返回一个无效的index,否则就create一个,返回给view.
5个必须实现的函数中,只有这两个是要返回index的,在createIndex中,parent函数的index中列的数量好象不重要,我把它随便设一个数,结果都是正确的,也可能是因为,我的结构就是这样,列上的东西,就是数据,而不是node,node都是行。在createIndex中,可以包含一个指针,或一个int,这里,我要它在每个index中,都直接有一个node的指针,这对于其它的函数,是非常方便的 int rowCount ( const QModelIndex & parent ) const
这个函数简单,每个index中都有一个指针,我拿到这个指针,看它指向的node有几个child,然后返回这个数,就OK了。如果index无效,那这个index就是root。看代码
CTreeItem *parentItem; if(parent.isValid()) parentItem=static_cast(parent.internalPointer()); else parentItem=m_pRootItem; return parentItem->childCount();
int columnCount(const QModelIndex &parent ) const
一个node有几个数据,就反回就是了,没什么好说的。
QVariant data(const QModelIndex &index, int role ) const
看代码吧,没什么难度的
if(index.isValid()==false) return QVariant(); if(role != Qt::DisplayRole) return QVariant(); CTreeItem *item=static_cast(index.internalPointer()); return item->data(index.column());
这个data,可不要什么role都返回一个数据,那样,就是全空,这个问题搞了我一天,最后才突然发现是这里有问题。
最后看一下CTreeItem的代码:
class CTreeItem { public: CTreeItem(QStringList data,CTreeItem *parent=0); ~CTreeItem(void); void appendChild(CTreeItem *child); CTreeItem *child(int row); int childCount() const; int columnCount() const; QVariant data(int column) const; int row() const; CTreeItem * parent(); private: QStringList m_StrData; QList m_Children; CTreeItem * m_pParent; }; CTreeItem::CTreeItem(QStringList data,CTreeItem *parent) :m_pParent(parent),m_StrData(data) { m_Children.clear(); } CTreeItem::~CTreeItem(void) { qDeleteAll(m_Children); } void CTreeItem::appendChild(CTreeItem *child) { m_Children.append(child); } int CTreeItem::childCount() const { return m_Children.size(); } int CTreeItem::columnCount() const { return m_StrData.size(); } QVariant CTreeItem::data(int column) const { return m_StrData.value(column,”"); } int CTreeItem::row() const { if(m_pParent) return m_pParent->m_Children.indexOf(const_cast(this)); else return 0; } CTreeItem * CTreeItem::parent() { return m_pParent; } CTreeItem * CTreeItem::child(int row) { return m_Children.value(row); }