PDA

View Full Version : QTableView performance



Anchor
1st June 2009, 08:53
Hi, we are trying to implement some kind of real-time data monitor. The bundle of data arrives (in separate thread) about 10 times per second. It contains table of 10x5 cells. When we display it in TableView overall performance too slow. Application's CPU usage is about 80% for 10 of such tables. Analysis shows that there are about 1000 calls per sec from View to Model::data() for each Model. We need a fast way to show table of homogeneous data without such overhead. Does Qt provide solution for such requirements?

PS. Sorry for my English

wysota
1st June 2009, 09:16
Don't add rows to the model one by one but do it in batches (i.e. once a second) using a dedicated method. Performance should improve drastically.

Anchor
1st June 2009, 09:24
Our models have fixed size - it's the size of incoming data. So we don't add rows to the model at all - we replace whole table as often as data arrive. We return item's representaion from model's data() method and see there significant overhead.

If Qt provides more specific solution for our condition It can help a lot.

faldzip
1st June 2009, 13:58
can you show us the code? especially the part which is doing model update

Anchor
1st June 2009, 15:39
We don't use QAbstractTableModel's rows and columns. Instead of this we use own containers and bind View with the dataChanged signal.

void GlassView::setModel(QAbstractItemModel* model)
{
if (model) {
QTableView::setModel(model);
//connect(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), SIGNAL(sizeChanged()));
connect(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), SLOT(updatePropertiesForSaving()));
connect(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)), SLOT(updateTitle()));
connect(model, SIGNAL(resetSelectDataSet()), SLOT(onSelectDataSetReset()));
connect(model, SIGNAL(resetGlassDataSet()), SLOT(onGlassDataSetReset()));

showEarnColumns(isEarnColumnsShown_);
updatePropertiesForSaving();
resizeColumnsToContents();
}
}

As soon as data has been received we notify View by

emit dataChanged();

Model's data method provides access to data


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

int currentDepth = ((userDepth_ > 0) && (userDepth_ < maximumDepth_) ? userDepth_ : maximumDepth_);
int offset = currentDepth - ((sortOrder_ == SortOrder::Ascending) ? agregatedAskQuantity_.count() : agregatedBidQuantity_.count());
offset = ((offset < 0 || !showEmptyLines_) ? 0 : offset);

int currentRow = index.row();
int currentColumn = index.column();

switch(role) {
case Qt::UserRole: { // data needed for emiting; not rounded prices only
if (currentColumn == 2 && (currentRow >= offset && currentRow < (keys_.count() + offset)))
return keys_[currentRow - offset];
if (index.row() < dataTableForDisplay_.count() && index.column() < dataTableForDisplay_[index.row()].count())
return dataTableForDisplay_[index.row()][index.column()];
return emptyVariant_;
}

case Qt::DisplayRole: {
// Draw last line (row)
if (index.row()+1 == rowCount_) {
switch(index.column()) {
case 1: {
QVariant total = (columnOrder_ == ColumnOrder::BuyPriceSell ? buyTotal_ : sellTotal_);
return QString("%L1").arg(total.toDouble(), 0, 'f', 0);
}
case 2:
return QString(tr("Р'С_РчР_Р_"));
case 3: {
QVariant total = (columnOrder_ == ColumnOrder::BuyPriceSell ? sellTotal_ : buyTotal_);
return QString("%L1").arg(total.toDouble(), 0, 'f', 0);
}
default:
return emptyVariant_;
}
}
// Draw data
return dataTableForDisplay_[index.row()][index.column()];
}; break;
case Qt::ToolTipRole:
return emptyVariant_;
case Qt::TextAlignmentRole:
if (index.row()+1 == rowCount_ && index.column() == 2)
return QVariant(Qt::AlignHCenter|Qt::AlignVCenter); // for
return QVariant(Qt::AlignRight|Qt::AlignVCenter);

case Qt::FontRole: {
if((index.row() + 1) == rowCount_ && (index.column() == 1 || index.column() == 3)){
return variantBoldFont_;
}
return emptyVariant_;
}

case Qt::BackgroundRole: //Qt::BackgroundColorRole
if (index.row() % 2)
return QBrush(modelSettings_.evenRowColor);
return QBrush(modelSettings_.oddRowColor);

case Qt::ForegroundRole: {//Qt::TextColorRole
// if (currentRow >= offset && currentRow < (keys_.count() + offset)) {
// Price price = keys_[currentRow - offset];
// if (orderPricesBuy_.contains(price) || orderPricesSell_.contains(price))
// return QBrush(QColor(250, 20, 20)); // indicate our bid
// }
return QBrush(modelSettings_.normalTextColor);
}
case Qt::WhatsThisRole:
return emptyVariant_;

default:
return emptyVariant_;
}
}


dataTableForDisplay_ already contains prepared for displaying data.

So we are looking for any significant performance related improvents of this code. Let me know if such approach isn't applicable for fast data displaying and point me to the right direction if it's possible. Thank you.

PS
Sorry for my English

wysota
1st June 2009, 18:20
Our models have fixed size - it's the size of incoming data. So we don't add rows to the model at all - we replace whole table as often as data arrive. We return item's representaion from model's data() method and see there significant overhead.


My hint still applies. The point is not to emit a signal for every cell but for a group of cells. Provide a custom method which will do the changes in batches and emit the dataChanged() signal once, not many times.

Anchor
1st June 2009, 22:03
My hint still applies. The point is not to emit a signal for every cell but for a group of cells. Provide a custom method which will do the changes in batches and emit the dataChanged() signal once, not many times.

So... we do exactly the same thing. We prepare all data (5x10 cells) and emit dataChanged() signal once (it occures about 10 times per second). But we see that View (after receiving this signal) enumerates cell's to get data. And View do it (enumerating) many times (up to 4 times only for DisplayRole) to get metrics and so on.

wysota
2nd June 2009, 10:15
That you can't avoid, this is the nature of ItemViews. Is your data() implementation so expensive? Could you maybe cache data?