Я пишу приложение, используя классы Qt, где у меня есть иерархическая структура, и мне нужно отобразить ее в виде дерева (я использую виджет QTreeView). Сами данные выглядят так:
class StatisticsEntry
{
// additional data management methods
...
bool setData(int column, const QVariant &value)
{
if (column < 0 || column >= _data.size())
return false;
_data[column] = value;
return true;
}
private:
// each item has a parent item except the top-most one
StatisticsEntry *_parent;
// each item may have child items which are accessible through the following member
QList<StatisticsEntry*> _children;
// each item is defined by a set of vallues stored in the following vector
QVector<QVariant> _data;
}
У меня есть класс, названный им StatisticsModel, который реализует QAbstractItemModel - используйте его для манипулирования и представления данных, хранящихся в дереве StatisticsEntry.
В классе есть метод addStatisticsData, который я использую для добавления записей StatisticsEntry в модель. Метод выглядит примерно так:
QModelIndex StatisticsModel::addStatisticsData(const QString &title, const QString &description, const QModelIndex &parent)
{
int row = rowCount(parent);
if (!insertRow(row, parent))
return QModelIndex();
// Get new item index
QModelIndex child = index(row, 0, parent);
// set item data
setTitle(child, title);
setDescription(child, description);
return child;
}
Методы SetTitle и setDescription идентичны - вот один из методов setTitle:
void StatisticsModel::setTitle(const QModelIndex &index, const QString& title)
{
QModelIndex columnIndex = this->index(index.row(), StatColumn::Title, index.parent());
this->setData(columnIndex, title, Qt::EditRole);
}
Метод setData выглядит следующим образом:
bool StatisticsModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole)
return false;
int column = index.column();
StatisticsEntry *item = getItem(index);
if (!item->setData(column, value))
return false;
emit dataChanged(index, index);
return true;
}
Остался метод getItem:
StatisticsEntry *StatisticsModel::getItem(const QModelIndex &index) const
{
if (index.isValid())
{
StatisticsEntry *item = static_cast<StatisticsEntry*>(index.internalPointer());
if (item)
return item;
}
return _rootItem;
}
Это все, что есть, когда речь идет о добавлении новых и изменении существующих записей. В моем приложении я также реализовал QSortFilterProxyModel - ничего особенного, только метод lessThan. Я использую модель прокси для предоставления функции сортировки в QTreeView, которая отображает данные. Существует следующий код, который соединяет модели с виджетом в виде дерева:
в заголовке главного окна:
...
StatisticsModel* _statisticsModel;
StatisticsProxyModel* _statisticsProxyModel;
...
в конструкторе главной вдовы
...
_statisticsModel = new StatisticsModel(ths);
_statisticsProxyModel = new StatisticsProxyModel(htis);
...
_statisticsProxyModel->setSourceModel(_statisticsModel);
ui->statisticsTreeView->setModel(_statisticsProxyModel);
...
Приложение также имеет кнопку, которая позволяет мне удалить выбранный элемент из модели - в настоящее время я тестирую только с QTreeView :: selectionModel () -> currentIndex, пока нет множественного выбора.
У меня есть следующий код для метода StatisticsModel :: removeRows
bool StatisticsModel::removeRows(int position, int rows, const QModelIndex &parent)
{
StatisticsEntry *parentItem = getItem(parent);
bool success1 = true;
bool success2 = true;
beginRemoveRows(parent, position, position + rows - 1);
for (int i = position + rows - 1; i >= position; i--)
{
QModelIndex child = index(i, 0, parent);
QString title = this->title(child); // the title method is the getter method that matches the setTitle one
success1 = success1 && removeRows(0, rowCount(child), child); //rowCount is implemented to return the number of items stored in StatisticsEntry::_children list for the specified parent index
success2 = success2 && parentItem->removeChild(i); // deletes an entry from the StatisticsEntry::_children list
}
endRemoveRows();
return success1 && success2;
}
Проблема в том, что иногда, когда я удаляю элемент с помощью метода QAbstractItemModel :: removeRow, я получаю исключение, и трассировка стека выглядит следующим образом:
StatisticsModel::parent StatisticsModel.cpp 307 0x13e7bf8
QModelIndex::parent qabstractitemmodel.h 393 0x72e57265
QSortFilterProxyModelPrivate::source_to_proxy qsortfilterproxymodel.cpp 386 0x711963e2
QSortFilterProxyModel::mapFromSource qsortfilterproxymodel.cpp 2514 0x7119d28b
QSortFilterProxyModel::parent qsortfilterproxymodel.cpp 1660 0x7119a32c
QModelIndex::parent qabstractitemmodel.h 393 0x72e57265
QPersistentModelIndex::parent qabstractitemmodel.cpp 347 0x72e58b86
QItemSelectionRange::isValid qitemselectionmodel.h 132 0x70e3f62b
QItemSelection::merge qitemselectionmodel.cpp 466 0x711503d1
QItemSelectionModelPrivate::finalize qitemselectionmodel_p.h 92 0x7115809a
QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved qitemselectionmodel.cpp 623 0x71151132
QItemSelectionModel::qt_static_metacall moc_qitemselectionmodel.cpp 113 0x711561c2
QMetaObject::activate qobject.cpp 3547 0x72e8d9a4
QAbstractItemModel::rowsAboutToBeRemoved moc_qabstractitemmodel.cpp 204 0x72f08a76
QAbstractItemModel::beginRemoveRows qabstractitemmodel.cpp 2471 0x72e5c53f
QSortFilterProxyModelPrivate::remove_proxy_interval qsortfilterproxymodel.cpp 558 0x71196ce7
QSortFilterProxyModelPrivate::remove_source_items qsortfilterproxymodel.cpp 540 0x71196c7f
QSortFilterProxyModelPrivate::source_items_about_to_be_removed qsortfilterproxymodel.cpp 841 0x71197c77
QSortFilterProxyModelPrivate::_q_sourceRowsAboutToBeRemoved qsortfilterproxymodel.cpp 1291 0x711995cc
QSortFilterProxyModel::qt_static_metacall moc_qsortfilterproxymodel.cpp 115 0x7119d506
QMetaCallEvent::placeMetaCall qobject.cpp 525 0x72e883fd
QObject::event qobject.cpp 1195 0x72e894ba
QApplicationPrivate::notify_helper qapplication.cpp 4550 0x709d710e
QApplication::notify qapplication.cpp 3932 0x709d4d87
QCoreApplication::notifyInternal qcoreapplication.cpp 876 0x72e6b091
Как ни странно, это происходит после того, как все непосредственные вызовы методов, касающиеся удаления элемента, уже завершены. Кажется, что модель прокси ищет индексы модели, которые больше не должны присутствовать (или я так думаю). Метод StatisticsModel :: parent выглядит следующим образом:
QModelIndex StatisticsModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
StatisticsEntry *childItem = getItem(index);
StatisticsEntry *parentItem = childItem->parent();
if (NULL != parentItem)
return createIndex(parentItem->childNumber(), 0, parentItem);
return QModelIndex();
}
Когда происходит исключение, значения, связанные с переменными childItem и parentItem из вышеприведенного метода, кажутся недействительными - либо сами указатели указывают на недоступную память, либо списки членов QLists либо не имеют записей, либо их записи приводят к нарушению доступа к памяти.
Возможно, что родительский метод не верен, но тогда как извлечь родительский индекс - документация qabstractItemModel препятствует использованию QModelIndex :: parent в этом методе, поскольку он создает бесконечную рекурсию.
Любая помощь будет оценена,