четверг, 18 мая 2017 г.

Qt Bug QAbstractItemModel beginMoveRows Ru

При попытке создания древовидной иерархической модели на базе QAbstractItemModel для QTreeView, и создания кнопки "переместить элемент вниз", столкнулся с неприятным багом в moveRows (точнее - beginMoveRows).

Я получил ошибку:

 ASSERT: "!this->isEmpty()" in file ..\..\include/QtCore/../../src/corelib/tools/qstack.h

Но ведь никакого QStack явно не используется!

При попытке выяснить причину,  оказалось, что программа падает на вызове endMoveRows, если sourceRow < destinationChild. (Реализация "переместить вверх" была тривиальна, но "вниз"... )
Нашлись следующие чем-то похожие баги:
https://bugreports.qt.io/browse/QTBUG-6940
https://bugreports.qt.io/browse/QTBUG-24337

Решение:
Если sourceRow < destinationChild уже нельзя использовать beginMoveRows, и поэтому решением было использовать сигналы layoutAboutToBeChanged и layoutChanged. (Я генерировал их, передавая индексы, однако без параметров тоже всё работает отлично.)
Так же добавил проверку beginMoveRows на истинность для других ситуаций.

Code fragment: 

bool TreeModel::moveRows (const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) {
        ...
         if(sourceRow > destinationChild) {
        // See https://bugreports.qt.io/browse/QTBUG-6940
        if(!beginMoveRows(sourceParent, sourceRow, sourceRow+count-1, destinationParent, destinationChild))
            return false;
        // Действия с данными здесь
        endMoveRows();
        return true;
        }
        // Because Qt bug :(. with endMoveRows, indexes and qstack.
        if(sourceRow < destinationChild) {
            QList<QPersistentModelIndex> parents;
            parents << QPersistentModelIndex(sourceParent) << QPersistentModelIndex(destinationParent);
            emit layoutAboutToBeChanged(parents);
            // Действия с данными здесь
            emit layoutChanged(parents);
            return true;
       }
...
}

1 комментарий:

  1. Это не бага, и утверждение "Если sourceRow < destinationChild уже нельзя использовать beginMoveRows" - ложь. Допустим, есть строки 0,1,2,3. Нельзя перенести 0 на 1, т.к. в итоге все равно получим те же 0,1,2,3 (beginMoveRows вернет в этом случае false). Нужно 0 перенести за 1 (т.е. на 2), тогда получим 1,0,2,3. Разберитесь, как работают moveRows в Qt

    ОтветитьУдалить