Valheru
13th October 2006, 16:27
This one is a bit complex, for me at least :p
I have a QAbstractItemModel. This is because a large amount of items are added at one go, and using a normal QTreeWidget is horribly slow.
The model uses a self-made ModelItem that is more or less copied from the Qt documentation for each row in it. This works fine. When I want to add children to the existing rows however, something goes horribly wrong.
What I'm doing is displaying newsgroup posts in a threaded view, ie. replies to posts show up under that parent rows in the QTreeView and can be expanded with a "+". Using qDebug() I can see that my code does exactly as expected...except that Qt somehow messes up what it displays in the QTreeView. I suspect that this is because the internal index of the children I am adding is being set wrong, but I can't tell what is going wrong. If anyone can help with this I'd be very grateful, I've been staring at this for the last hour trying everything I can think of, but my understanding of how to implement this just isn't on par with what I'm trying to do :(
The code of interest is in the constructor of the QAbstractItemModel. Basically what happens is that none of the children actually show up. What is shown in their place are multiple instances of more-or-less random various parent rows.
/************************************************** *************************
* Copyright (C) 2006 by Lawrence Lee *
* valheru@facticius.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
************************************************** *************************/
#include "modelItem.h"
#include "articleModel.h"
#include <QtCore/QDebug>
#include <QtCore/QStringList>
ArticleModel::ArticleModel( const QStringList &data, QObject *parent ) : QAbstractItemModel( parent )
{
QList<QString> rootData;
rootData << "From" << "Subject" << "Date" << "Message-ID" << "References" << "Lines";
rootItem = new ModelItem( rootData );
for ( int i = 0; i < data.count(); i += 6 ){
if( data[ i + 4 ] == "" || data[ i + 4 ].isEmpty() ){
///It has no reference, so we simply add it to the root of the model
ModelItem *item = new ModelItem( QStringList() << data[ i ] << data[ i + 1 ]
<< data[ i + 2 ] << data[ i + 3 ]
<< data[ i + 4 ] << data[ i + 5 ], rootItem );
rootItem->appendChild( item );
///Remove it from the list
// for( int x = i; x < i + 6; ++x ){
// const_cast< QStringList& >( data ).removeAt( x );
// }
}
}
int maxReferences = 0;
for ( int i = 0; i < data.count(); i += 6 ){
if( !data[ i + 4 ].isEmpty() ){
QStringList references = data[ 4 ].split( " ", QString::SkipEmptyParts );
if( references.count() > maxReferences ){
maxReferences = references.count();
}
}
}
int iteration = 1;
while( iteration <= maxReferences ){
for( int i = 0; i < data.count(); i += 6 ){
if( !data[ i + 4 ].isEmpty() ){
QStringList references = data[ i + 4 ].split( " ", QString::SkipEmptyParts );
if( references.count() == iteration ){
for( int p = 0; p < rowCount(); ++p ){
if( references[ 0 ] == rootItem->child( p )->data( 3 ).toString() ){
ModelItem *item = new ModelItem( QStringList() << data[ i ] << data[ i + 1 ]
<< data[ i + 2 ] << data[ i + 3 ]
<< data[ i + 4 ] << data[ i + 5 ], rootItem );
rootItem->child( p )->appendChild( item );
for( int t = 0; t < item->columnCount(); ++t ){
qDebug() << item->data( t ).toString();
}
qDebug() << data[ i + 1 ] << "is the child of" << rootItem->child( p )->data( 1 ).toString();
qDebug() << "because reference" << references[ 0 ] << "in child refers to parents Message-ID"
<< rootItem->child( p )->data( 3 ).toString() << "\r\n";
}
}
}
}
}
++iteration;
}
}
ArticleModel::~ArticleModel()
{
delete rootItem;
}
QModelIndex ArticleModel::index( int row, int column, const QModelIndex &parent ) const
{
ModelItem * parentItem;
if ( !parent.isValid() )
parentItem = rootItem;
else
parentItem = static_cast<ModelItem*>( parent.internalPointer() );
ModelItem *childItem = parentItem->child( row );
if ( childItem )
return createIndex( row, column, childItem );
else
return QModelIndex();
}
QModelIndex ArticleModel::parent( const QModelIndex &index ) const
{
if ( !index.isValid() )
return QModelIndex();
ModelItem *childItem = static_cast<ModelItem*>( index.internalPointer() );
ModelItem *parentItem = childItem->parent();
if ( parentItem == rootItem )
return QModelIndex();
return createIndex( parentItem->row(), 0, parentItem );
}
int ArticleModel::rowCount( const QModelIndex &parent ) const
{
ModelItem * parentItem;
if ( !parent.isValid() )
parentItem = rootItem;
else
parentItem = static_cast<ModelItem*>( parent.internalPointer() );
return parentItem->childCount();
}
int ArticleModel::columnCount( const QModelIndex &parent ) const
{
if ( parent.isValid() )
return static_cast<ModelItem*>( parent.internalPointer() ) ->columnCount();
else
return rootItem->columnCount();
}
QVariant ArticleModel::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() )
return QVariant();
if ( role != Qt::DisplayRole )
return QVariant();
ModelItem *item = static_cast<ModelItem*>( index.internalPointer() );
return item->data( index.column() );
}
Qt::ItemFlags ArticleModel::flags( const QModelIndex &index ) const
{
if ( !index.isValid() )
return Qt::ItemIsEnabled;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant ArticleModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
return rootItem->data( section );
return QVariant();
}
/************************************************** *************************
* Copyright (C) 2006 by Lawrence Lee *
* valheru@facticius.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
************************************************** *************************/
#include "modelItem.h"
ModelItem::ModelItem( const QList<QString> &data, ModelItem* parent )
{
parentItem = parent;
itemData = data;
}
ModelItem::~ ModelItem()
{
qDeleteAll( childItems );
}
void ModelItem::appendChild( ModelItem *item )
{
childItems.append( item );
}
ModelItem *ModelItem::child( int row )
{
return childItems.value( row );
}
int ModelItem::childCount() const
{
return childItems.count();
}
int ModelItem::row() const
{
if ( parentItem != 0 ) {
return parentItem->childItems.indexOf( const_cast<ModelItem*>( this ) );
}
return 0;
}
int ModelItem::columnCount() const
{
return itemData.count();
}
QVariant ModelItem::data( int column ) const
{
return itemData.value( column );
}
ModelItem *ModelItem::parent()
{
return parentItem;
}
I have a QAbstractItemModel. This is because a large amount of items are added at one go, and using a normal QTreeWidget is horribly slow.
The model uses a self-made ModelItem that is more or less copied from the Qt documentation for each row in it. This works fine. When I want to add children to the existing rows however, something goes horribly wrong.
What I'm doing is displaying newsgroup posts in a threaded view, ie. replies to posts show up under that parent rows in the QTreeView and can be expanded with a "+". Using qDebug() I can see that my code does exactly as expected...except that Qt somehow messes up what it displays in the QTreeView. I suspect that this is because the internal index of the children I am adding is being set wrong, but I can't tell what is going wrong. If anyone can help with this I'd be very grateful, I've been staring at this for the last hour trying everything I can think of, but my understanding of how to implement this just isn't on par with what I'm trying to do :(
The code of interest is in the constructor of the QAbstractItemModel. Basically what happens is that none of the children actually show up. What is shown in their place are multiple instances of more-or-less random various parent rows.
/************************************************** *************************
* Copyright (C) 2006 by Lawrence Lee *
* valheru@facticius.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
************************************************** *************************/
#include "modelItem.h"
#include "articleModel.h"
#include <QtCore/QDebug>
#include <QtCore/QStringList>
ArticleModel::ArticleModel( const QStringList &data, QObject *parent ) : QAbstractItemModel( parent )
{
QList<QString> rootData;
rootData << "From" << "Subject" << "Date" << "Message-ID" << "References" << "Lines";
rootItem = new ModelItem( rootData );
for ( int i = 0; i < data.count(); i += 6 ){
if( data[ i + 4 ] == "" || data[ i + 4 ].isEmpty() ){
///It has no reference, so we simply add it to the root of the model
ModelItem *item = new ModelItem( QStringList() << data[ i ] << data[ i + 1 ]
<< data[ i + 2 ] << data[ i + 3 ]
<< data[ i + 4 ] << data[ i + 5 ], rootItem );
rootItem->appendChild( item );
///Remove it from the list
// for( int x = i; x < i + 6; ++x ){
// const_cast< QStringList& >( data ).removeAt( x );
// }
}
}
int maxReferences = 0;
for ( int i = 0; i < data.count(); i += 6 ){
if( !data[ i + 4 ].isEmpty() ){
QStringList references = data[ 4 ].split( " ", QString::SkipEmptyParts );
if( references.count() > maxReferences ){
maxReferences = references.count();
}
}
}
int iteration = 1;
while( iteration <= maxReferences ){
for( int i = 0; i < data.count(); i += 6 ){
if( !data[ i + 4 ].isEmpty() ){
QStringList references = data[ i + 4 ].split( " ", QString::SkipEmptyParts );
if( references.count() == iteration ){
for( int p = 0; p < rowCount(); ++p ){
if( references[ 0 ] == rootItem->child( p )->data( 3 ).toString() ){
ModelItem *item = new ModelItem( QStringList() << data[ i ] << data[ i + 1 ]
<< data[ i + 2 ] << data[ i + 3 ]
<< data[ i + 4 ] << data[ i + 5 ], rootItem );
rootItem->child( p )->appendChild( item );
for( int t = 0; t < item->columnCount(); ++t ){
qDebug() << item->data( t ).toString();
}
qDebug() << data[ i + 1 ] << "is the child of" << rootItem->child( p )->data( 1 ).toString();
qDebug() << "because reference" << references[ 0 ] << "in child refers to parents Message-ID"
<< rootItem->child( p )->data( 3 ).toString() << "\r\n";
}
}
}
}
}
++iteration;
}
}
ArticleModel::~ArticleModel()
{
delete rootItem;
}
QModelIndex ArticleModel::index( int row, int column, const QModelIndex &parent ) const
{
ModelItem * parentItem;
if ( !parent.isValid() )
parentItem = rootItem;
else
parentItem = static_cast<ModelItem*>( parent.internalPointer() );
ModelItem *childItem = parentItem->child( row );
if ( childItem )
return createIndex( row, column, childItem );
else
return QModelIndex();
}
QModelIndex ArticleModel::parent( const QModelIndex &index ) const
{
if ( !index.isValid() )
return QModelIndex();
ModelItem *childItem = static_cast<ModelItem*>( index.internalPointer() );
ModelItem *parentItem = childItem->parent();
if ( parentItem == rootItem )
return QModelIndex();
return createIndex( parentItem->row(), 0, parentItem );
}
int ArticleModel::rowCount( const QModelIndex &parent ) const
{
ModelItem * parentItem;
if ( !parent.isValid() )
parentItem = rootItem;
else
parentItem = static_cast<ModelItem*>( parent.internalPointer() );
return parentItem->childCount();
}
int ArticleModel::columnCount( const QModelIndex &parent ) const
{
if ( parent.isValid() )
return static_cast<ModelItem*>( parent.internalPointer() ) ->columnCount();
else
return rootItem->columnCount();
}
QVariant ArticleModel::data( const QModelIndex &index, int role ) const
{
if ( !index.isValid() )
return QVariant();
if ( role != Qt::DisplayRole )
return QVariant();
ModelItem *item = static_cast<ModelItem*>( index.internalPointer() );
return item->data( index.column() );
}
Qt::ItemFlags ArticleModel::flags( const QModelIndex &index ) const
{
if ( !index.isValid() )
return Qt::ItemIsEnabled;
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
QVariant ArticleModel::headerData( int section, Qt::Orientation orientation, int role ) const
{
if ( orientation == Qt::Horizontal && role == Qt::DisplayRole )
return rootItem->data( section );
return QVariant();
}
/************************************************** *************************
* Copyright (C) 2006 by Lawrence Lee *
* valheru@facticius.net *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
************************************************** *************************/
#include "modelItem.h"
ModelItem::ModelItem( const QList<QString> &data, ModelItem* parent )
{
parentItem = parent;
itemData = data;
}
ModelItem::~ ModelItem()
{
qDeleteAll( childItems );
}
void ModelItem::appendChild( ModelItem *item )
{
childItems.append( item );
}
ModelItem *ModelItem::child( int row )
{
return childItems.value( row );
}
int ModelItem::childCount() const
{
return childItems.count();
}
int ModelItem::row() const
{
if ( parentItem != 0 ) {
return parentItem->childItems.indexOf( const_cast<ModelItem*>( this ) );
}
return 0;
}
int ModelItem::columnCount() const
{
return itemData.count();
}
QVariant ModelItem::data( int column ) const
{
return itemData.value( column );
}
ModelItem *ModelItem::parent()
{
return parentItem;
}