Ideas to optimise QAbstractItemView::dataChanged
-
@kshegunov said in Ideas to optimise QAbstractItemView::dataChanged:
Okay, this is my bad then.
I see what you mean. if a view only reimplemented
paintEvent
it might break due to this change. fair point probably a note for future releses only thenTo summarise where we are, the current "idea" of
QAbstractItemView::dataChanged
is:void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) { Q_UNUSED(roles) // Single item changed Q_D(QAbstractItemView); if (topLeft == bottomRight && topLeft.isValid()) { const QEditorInfo &editorInfo = d->editorForIndex(topLeft); //we don't update the edit data if it is static if (!editorInfo.isStatic && editorInfo.widget) { QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft); if (delegate) { delegate->setEditorData(editorInfo.widget.data(), topLeft); } } if (isVisible() && !d->delayedPendingLayout) { // otherwise the items will be update later anyway update(topLeft); } } else { d->updateEditorData(topLeft, bottomRight); if (isVisible() && !d->delayedPendingLayout) { const QRect viewportRect = d->viewport->rect(); const QModelIndex parent = topLeft.parent(); const QRect cornerRect = visualRect(topLeft) | visualRect(bottomRight); // If dirty is 1/4th or more of the viewport rect, just trigger a full update const QRect cornerRectViewPort = cornerRect & viewportRect; if(cornerRectViewPort.width() * cornerRectViewPort.height() * 4 > viewportRect.width() * viewportRect.height()) { d->viewport->update(); } else { // Just fall back to iterating over the model indices QRect dirty(cornerRect); const int maxRow = bottomRight.row(); for (int i = topLeft.row(); i < maxRow; ++i) { const int maxColumn = bottomRight.column() - ((i==maxRow-1) ? 1:0); for (int j = topLeft.column()+ ((i==topLeft.row()) ? 1:0); j < maxColumn; ++j) { dirty |= visualRect(d->model->index(i, j, parent)); } } dirty &= viewportRect; if (!dirty.isEmpty()) d->viewport->update(dirty); } } } #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::DataChanged); accessibleEvent.setFirstRow(topLeft.row()); accessibleEvent.setFirstColumn(topLeft.column()); accessibleEvent.setLastRow(bottomRight.row()); accessibleEvent.setLastColumn(bottomRight.column()); QAccessible::updateAccessibility(&accessibleEvent); } #endif d->updateGeometry(); }
EDIT:
Avoid calculating
visualRect
for the corners twice in the iteration case -
If someone has a good idea how to improve the default implementation in QAbstractItemModel - I'm open for suggestions
@Christian-Ehrlicher
Even though it's not strictly the model API we were discussing, have you any thoughts on the proposed snippet? would it be worth changing like this? -
@kshegunov said in Ideas to optimise QAbstractItemView::dataChanged:
Okay, this is my bad then.
I see what you mean. if a view only reimplemented
paintEvent
it might break due to this change. fair point probably a note for future releses only thenTo summarise where we are, the current "idea" of
QAbstractItemView::dataChanged
is:void QAbstractItemView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) { Q_UNUSED(roles) // Single item changed Q_D(QAbstractItemView); if (topLeft == bottomRight && topLeft.isValid()) { const QEditorInfo &editorInfo = d->editorForIndex(topLeft); //we don't update the edit data if it is static if (!editorInfo.isStatic && editorInfo.widget) { QAbstractItemDelegate *delegate = d->delegateForIndex(topLeft); if (delegate) { delegate->setEditorData(editorInfo.widget.data(), topLeft); } } if (isVisible() && !d->delayedPendingLayout) { // otherwise the items will be update later anyway update(topLeft); } } else { d->updateEditorData(topLeft, bottomRight); if (isVisible() && !d->delayedPendingLayout) { const QRect viewportRect = d->viewport->rect(); const QModelIndex parent = topLeft.parent(); const QRect cornerRect = visualRect(topLeft) | visualRect(bottomRight); // If dirty is 1/4th or more of the viewport rect, just trigger a full update const QRect cornerRectViewPort = cornerRect & viewportRect; if(cornerRectViewPort.width() * cornerRectViewPort.height() * 4 > viewportRect.width() * viewportRect.height()) { d->viewport->update(); } else { // Just fall back to iterating over the model indices QRect dirty(cornerRect); const int maxRow = bottomRight.row(); for (int i = topLeft.row(); i < maxRow; ++i) { const int maxColumn = bottomRight.column() - ((i==maxRow-1) ? 1:0); for (int j = topLeft.column()+ ((i==topLeft.row()) ? 1:0); j < maxColumn; ++j) { dirty |= visualRect(d->model->index(i, j, parent)); } } dirty &= viewportRect; if (!dirty.isEmpty()) d->viewport->update(dirty); } } } #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { QAccessibleTableModelChangeEvent accessibleEvent(this, QAccessibleTableModelChangeEvent::DataChanged); accessibleEvent.setFirstRow(topLeft.row()); accessibleEvent.setFirstColumn(topLeft.column()); accessibleEvent.setLastRow(bottomRight.row()); accessibleEvent.setLastColumn(bottomRight.column()); QAccessible::updateAccessibility(&accessibleEvent); } #endif d->updateGeometry(); }
EDIT:
Avoid calculating
visualRect
for the corners twice in the iteration caseAfter some code-mining the above loop should be equivalent to:
for (int i = topLeft.row(); i < maxRow; ++i) { const int maxColumn = bottomRight.column() - ((i==maxRow-1) ? 1:0); for (int j = topLeft.column()+ ((i==topLeft.row()) ? 1:0); j < maxColumn; ++j) { d->viewport->update(visualRect(d->model->index(i, j, parent))); } }
The updates are compressed trough QWidgetBackingStore::markDirty. I know the docs mention this but I couldn't find it in QCoreApplication::compressEvent so I went spelunking, albeit rather short lived.