欢迎光临
我们一起努力

Qt 的 TreeModel

一般用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);
}

 

赞(0) 打赏
未经允许不得转载:Libero's Blog » Qt 的 TreeModel

评论 抢沙发

评论前必须登录!