PDA

View Full Version : Insert rows...



steg90
18th September 2007, 09:56
Hi,

Can anybody please explain why the following keeps adding rows to my table view :



void DAStaticModel::setCanData( int nAmount )
{
if( nAmount <= 0 )
return;

beginInsertRows(QModelIndex(), 0, 0 );
endInsertRows();

}

QVariant DAStaticModel::data( const QModelIndex& index, int role) const
{
QVariant data;

if (!index.isValid())
return QVariant();

if(index.column() > 3 )
{
return QVariant();
}

if( role == Qt::DisplayRole )
{

QString strId = "";
static QMapIterator<QString, QObject*>i(*theApp->GetCanMap());
static CanMessages* pMess = NULL;
if( i.hasNext() && index.column() == 0 )
{
i.next();
pMess = (CanMessages*)i.value();
strId = i.key();
}
else
if( !i.hasNext() ) // need to reset to begining of the list
{
i.toFront();
}

switch( index.column() )
{
case 0:
data = strId;
break;
case 2:
{
if( pMess )
{
data = pMess->m_strData;
}
}
break;
case 1:
{
if( pMess )
{
data = pMess->m_strTime;
}
}
break;
case 3:
{
if( pMess )
{
QString strCount;
strCount.sprintf( "%d", pMess->m_nCount );
data = strCount;
}
}
break;
}
}

return data;
}

int DAStaticModel::rowCount( const QModelIndex& parent ) const
{
return theApp->GetCanMap()->size();
}


If I don't call the beginInsertRows / endInsertRows nothing is displayed in my table view.

Please note that the rowCount is always one...

Thanks,
Steve

wysota
18th September 2007, 11:03
There is something wrong with your model. Could you attach the complete model code? You can skip the contents of data(), we already have it here. What does GetCanMap() return? A copy or a reference?

steg90
18th September 2007, 11:10
Hi,

This is the model :



DAStaticModel::DAStaticModel(QObject *parent)
: QAbstractTableModel(parent)
{
}


DAStaticModel::~DAStaticModel()
{

}


int DAStaticModel::rowCount( const QModelIndex& parent ) const
{
return theApp->GetCanMap()->size();
}

int DAStaticModel::columnCount(const QModelIndex &) const
{
return 4;
}

void DAStaticModel::SetHeaders( QStringList& strHeaders )
{
strHeaderList = strHeaders;
}

QVariant DAStaticModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if( role != Qt::DisplayRole )
return QVariant();

if (orientation == Qt::Horizontal)
return strHeaderList[section];

return QVariant();
}


void DAStaticModel::setCanData( int nAmount )
{
if( nAmount <= 0 )
return;

beginInsertRows(QModelIndex(), 0, 0);//theApp->GetCanMap()->size() ); // insert nAmount of rows


endInsertRows();

}



The GetCanMap :



QMap<QString, QObject*>* GetCanMap() { return &m_oCanMessages; }


where m_oCanMessages is :



QMap<QString, QObject*> m_oCanMessages;


Many thanks for looking.

Steve

wysota
18th September 2007, 11:14
You surely have to get rid of those begin/end statements in setCanData. Use "emit layoutChanged()" instead. It should suffice.

steg90
18th September 2007, 11:23
Hi,

Getting rid of the begin/end rows and adding emit layoutChanged results in nothing being shown?

I'm at a loss :confused:

Thanks,
Steve

wysota
18th September 2007, 11:41
How and when exactly do you create and populate the model?

steg90
18th September 2007, 11:47
Hi,

Model is populated every second via a thread.

The following function is called from the thread :



void QTCanMonitor::AddStaticCanData( QString strId, QString strTime, QString strData )
{
CanMessages* pMess = NULL;
QMap<QString, QObject*>::const_iterator i = m_oCanMessages.find( strId );
if( i != m_oCanMessages.end() ) // must be one
{
pMess = (CanMessages*)i.value();
pMess->m_nCount++;
pMess->m_strData = strData;
pMess->m_strTime = strTime;
}
else // new one
{
pMess = new CanMessages();
pMess->m_nCount = 1;
pMess->m_strData = strData;
pMess->m_strTime = strTime;
pMess->m_strId = strId;
m_oCanMessages.insert( strId, (QObject*)pMess );
m_oMessages.append( (QObject*)pMess );
}
}


Once the thread has called the above, it emits a signal back to the view which then calls the setCanData method of the model.

The function theApp->GetCanMap() returns the above m_oCanMessages map which is used in the models data method.

Regards and thanks,
Steve

wysota
18th September 2007, 11:59
The model is not populated here. You're manipulating its data structures and I might add without any synchronisation which will sooner or later lead to inconsistency of the data. Every time you add a row to the model, you need to call beginInsertRows() and endInsertRows(). There is no point in having two interfaces to the same data storage - either use the model approach from within the thread as well or forget the model and use item-based approach for your view. Because now you're manipulating two different APIs for the same set of data which is just a waste of time. Start treating the model as a mean to access data, not only as a mean to visualize data using a view.

steg90
18th September 2007, 12:02
So I guess I really should have AddStaticCanData within the model code which would then in turn call begin/end rows?

Thanks,
Steve

wysota
18th September 2007, 12:12
Yes, you should. Or you can use the model's insertRow() method (provided you implement it).

steg90
18th September 2007, 12:53
Thanks.

One more thing though which I still can't get my head round...in order for the data method to be 'fired' you have to update the models data within the model class - is this a correct assumption?

Thanks,
Steve

wysota
18th September 2007, 13:18
I don't know if I understand you correctly, but if I do, then the answer is "no". The view will use data() to ask for the data it needs when it knows that a particular index is valid and it is calculated based on what the rowCount() and columnCount() of the model return. Of course most of such info is cached in the view, so you have to notify the outside world from within the model that something has changed in the model. That's where the model's signals enter the scene.

steg90
18th September 2007, 13:23
Thanks again for all your help, it has been very much appreciated.

I have removed the methods to update the data to all now be contained in the data model which now in turn lets me call emit layoutChanged :-)

Many thanks!

Steve

wysota
18th September 2007, 13:36
Actually calling beginInsertRows() and endInsertRows() should be enough. If not, you should emit rowsInserted() after inserting rows. That'll make it a bit faster as the view only needs to update itself if it currently shows the inserted rows. With layoutChanged() you don't carry information about how the layout was changed, so the view has to update itself regardless of the part of the model changed.