Use the layout change hint to speed up QItemSelectionModel.
The testcase in the bug report takes 370035 ms to sort the rows with Qt 4, and 5646 ms in Qt 5 (when using the extra hints to layout*Changed). That's an improvement of more than 98%. Task-number: QTBUG-17732 Change-Id: If78f972d80c501e0cb39078228086c4f4ac8a65b Reviewed-by: Marc Mutz <marc.mutz@kdab.com>
This commit is contained in:
parent
0693082c95
commit
d18eb260d7
@ -284,12 +284,21 @@ QItemSelectionRange QItemSelectionRange::intersected(const QItemSelectionRange &
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
\internal
|
||||
|
||||
utility function for getting the indexes from a range
|
||||
it avoid concatenating list and works on one
|
||||
*/
|
||||
static void rowLengthsFromRange(const QItemSelectionRange &range, QVector<QPair<QPersistentModelIndex, uint> > &result)
|
||||
{
|
||||
if (range.isValid() && range.model()) {
|
||||
const QModelIndex topLeft = range.topLeft();
|
||||
const int bottom = range.bottom();
|
||||
const uint width = range.width();
|
||||
const int column = topLeft.column();
|
||||
for (int row = topLeft.row(); row <= bottom; ++row) {
|
||||
// We don't need to keep track of ItemIsSelectable and ItemIsEnabled here. That is
|
||||
// required in indexesFromRange() because that method is called from public API
|
||||
// which requires the limitation.
|
||||
result.push_back(qMakePair(QPersistentModelIndex(topLeft.sibling(row, column)), width));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ModelIndexContainer>
|
||||
static void indexesFromRange(const QItemSelectionRange &range, ModelIndexContainer &result)
|
||||
@ -468,6 +477,14 @@ static QVector<QPersistentModelIndex> qSelectionPersistentindexes(const QItemSel
|
||||
return result;
|
||||
}
|
||||
|
||||
static QVector<QPair<QPersistentModelIndex, uint> > qSelectionPersistentRowLengths(const QItemSelection &sel)
|
||||
{
|
||||
QVector<QPair<QPersistentModelIndex, uint> > result;
|
||||
Q_FOREACH (const QItemSelectionRange &range, sel)
|
||||
rowLengthsFromRange(range, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
Merges the \a other selection with this QItemSelection using the
|
||||
\a command given. This method guarantees that no ranges are overlapping.
|
||||
@ -599,10 +616,10 @@ void QItemSelectionModelPrivate::initModel(QAbstractItemModel *model)
|
||||
q, SLOT(_q_layoutChanged()));
|
||||
QObject::connect(model, SIGNAL(columnsMoved(QModelIndex,int,int,QModelIndex,int)),
|
||||
q, SLOT(_q_layoutChanged()));
|
||||
QObject::connect(model, SIGNAL(layoutAboutToBeChanged()),
|
||||
q, SLOT(_q_layoutAboutToBeChanged()));
|
||||
QObject::connect(model, SIGNAL(layoutChanged()),
|
||||
q, SLOT(_q_layoutChanged()));
|
||||
QObject::connect(model, SIGNAL(layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
|
||||
q, SLOT(_q_layoutAboutToBeChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
|
||||
QObject::connect(model, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)),
|
||||
q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -812,10 +829,12 @@ void QItemSelectionModelPrivate::_q_rowsAboutToBeInserted(const QModelIndex &par
|
||||
preparation for the layoutChanged() signal, where the indexes can be
|
||||
merged again.
|
||||
*/
|
||||
void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged()
|
||||
void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint hint)
|
||||
{
|
||||
savedPersistentIndexes.clear();
|
||||
savedPersistentCurrentIndexes.clear();
|
||||
savedPersistentRowLengths.clear();
|
||||
savedPersistentCurrentRowLengths.clear();
|
||||
|
||||
// optimization for when all indexes are selected
|
||||
// (only if there is lots of items (1000) because this is not entirely correct)
|
||||
@ -836,9 +855,54 @@ void QItemSelectionModelPrivate::_q_layoutAboutToBeChanged()
|
||||
}
|
||||
tableSelected = false;
|
||||
|
||||
if (hint == QAbstractItemModel::VerticalSortHint) {
|
||||
// Special case when we know we're sorting vertically. We can assume that all indexes for columns
|
||||
// are displaced the same way, and therefore we only need to track an index from one column per
|
||||
// row with a QPersistentModelIndex together with the length of items to the right of it
|
||||
// which are displaced the same way.
|
||||
// An algorithm which contains the same assumption is used to process layoutChanged.
|
||||
savedPersistentRowLengths = qSelectionPersistentRowLengths(ranges);
|
||||
savedPersistentCurrentRowLengths = qSelectionPersistentRowLengths(currentSelection);
|
||||
} else {
|
||||
savedPersistentIndexes = qSelectionPersistentindexes(ranges);
|
||||
savedPersistentCurrentIndexes = qSelectionPersistentindexes(currentSelection);
|
||||
}
|
||||
}
|
||||
/*!
|
||||
\internal
|
||||
*/
|
||||
static QItemSelection mergeRowLengths(const QVector<QPair<QPersistentModelIndex, uint> > &rowLengths)
|
||||
{
|
||||
if (rowLengths.isEmpty())
|
||||
return QItemSelection();
|
||||
|
||||
QItemSelection result;
|
||||
int i = 0;
|
||||
while (i < rowLengths.count()) {
|
||||
const QPersistentModelIndex &tl = rowLengths.at(i).first;
|
||||
if (!tl.isValid()) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
QPersistentModelIndex br = tl;
|
||||
const uint length = rowLengths.at(i).second;
|
||||
while (++i < rowLengths.count()) {
|
||||
const QPersistentModelIndex &next = rowLengths.at(i).first;
|
||||
if (!next.isValid())
|
||||
continue;
|
||||
const uint nextLength = rowLengths.at(i).second;
|
||||
if ((nextLength == length)
|
||||
&& (next.row() == br.row() + 1)
|
||||
&& (next.parent() == br.parent())) {
|
||||
br = next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result.append(QItemSelectionRange(tl, br.sibling(br.row(), length - 1)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
@ -913,7 +977,7 @@ static QItemSelection mergeIndexes(const QVector<QPersistentModelIndex> &indexes
|
||||
|
||||
Merge the selected indexes into selection ranges again.
|
||||
*/
|
||||
void QItemSelectionModelPrivate::_q_layoutChanged()
|
||||
void QItemSelectionModelPrivate::_q_layoutChanged(const QList<QPersistentModelIndex> &, QAbstractItemModel::LayoutChangeHint hint)
|
||||
{
|
||||
// special case for when all indexes are selected
|
||||
if (tableSelected && tableColCount == model->columnCount(tableParent)
|
||||
@ -930,15 +994,18 @@ void QItemSelectionModelPrivate::_q_layoutChanged()
|
||||
return;
|
||||
}
|
||||
|
||||
if (savedPersistentCurrentIndexes.isEmpty() && savedPersistentIndexes.isEmpty()) {
|
||||
if ((hint != QAbstractItemModel::VerticalSortHint && savedPersistentCurrentIndexes.isEmpty() && savedPersistentIndexes.isEmpty())
|
||||
|| (hint == QAbstractItemModel::VerticalSortHint && savedPersistentRowLengths.isEmpty() && savedPersistentCurrentRowLengths.isEmpty())) {
|
||||
// either the selection was actually empty, or we
|
||||
// didn't get the layoutAboutToBeChanged() signal
|
||||
return;
|
||||
}
|
||||
|
||||
// clear the "old" selection
|
||||
ranges.clear();
|
||||
currentSelection.clear();
|
||||
|
||||
if (hint != QAbstractItemModel::VerticalSortHint) {
|
||||
// sort the "new" selection, as preparation for merging
|
||||
qStableSort(savedPersistentIndexes.begin(), savedPersistentIndexes.end());
|
||||
qStableSort(savedPersistentCurrentIndexes.begin(), savedPersistentCurrentIndexes.end());
|
||||
@ -950,6 +1017,19 @@ void QItemSelectionModelPrivate::_q_layoutChanged()
|
||||
// release the persistent indexes
|
||||
savedPersistentIndexes.clear();
|
||||
savedPersistentCurrentIndexes.clear();
|
||||
} else {
|
||||
// sort the "new" selection, as preparation for merging
|
||||
qStableSort(savedPersistentRowLengths.begin(), savedPersistentRowLengths.end());
|
||||
qStableSort(savedPersistentCurrentRowLengths.begin(), savedPersistentCurrentRowLengths.end());
|
||||
|
||||
// update the selection by merging the individual indexes
|
||||
ranges = mergeRowLengths(savedPersistentRowLengths);
|
||||
currentSelection = mergeRowLengths(savedPersistentCurrentRowLengths);
|
||||
|
||||
// release the persistent indexes
|
||||
savedPersistentRowLengths.clear();
|
||||
savedPersistentCurrentRowLengths.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -222,8 +222,8 @@ private:
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_rowsAboutToBeRemoved(const QModelIndex&, int, int))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_columnsAboutToBeInserted(const QModelIndex&, int, int))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_rowsAboutToBeInserted(const QModelIndex&, int, int))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_layoutAboutToBeChanged())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_layoutChanged())
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoHint))
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_layoutChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoHint))
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QItemSelectionModel::SelectionFlags)
|
||||
|
@ -76,8 +76,8 @@ public:
|
||||
void _q_columnsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
|
||||
void _q_rowsAboutToBeInserted(const QModelIndex &parent, int start, int end);
|
||||
void _q_columnsAboutToBeInserted(const QModelIndex &parent, int start, int end);
|
||||
void _q_layoutAboutToBeChanged();
|
||||
void _q_layoutChanged();
|
||||
void _q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
|
||||
void _q_layoutChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
|
||||
|
||||
inline void remove(QList<QItemSelectionRange> &r)
|
||||
{
|
||||
@ -100,6 +100,8 @@ public:
|
||||
QItemSelectionModel::SelectionFlags currentCommand;
|
||||
QVector<QPersistentModelIndex> savedPersistentIndexes;
|
||||
QVector<QPersistentModelIndex> savedPersistentCurrentIndexes;
|
||||
QVector<QPair<QPersistentModelIndex, uint> > savedPersistentRowLengths;
|
||||
QVector<QPair<QPersistentModelIndex, uint> > savedPersistentCurrentRowLengths;
|
||||
// optimization when all indexes are selected
|
||||
bool tableSelected;
|
||||
QPersistentModelIndex tableParent;
|
||||
|
Loading…
Reference in New Issue
Block a user