2015-07-22 5 views
3

Я подклассифицирую QAbstractItemModel для отображения элементов в QTreeView, и в этом подклассе (projectModel) у меня есть функция для удаления выбранного в данный момент индекса в древовидном представлении. Component класс используется для представления всех членов модели:QModelIndex становится недопустимым при удалении строк

void 
projectModel::deleteComponent() 
{ 
    QModelIndex child_index = _treeview->selectionModel()->currentIndex(); 
    Component* child = static_cast<Component*>(child_index.internalPointer()); 

    Component* parent = child->Parent(); 
    QModelIndex parent_index = createIndex(parent->row(), 0, parent); 

    int row = child->row(); 

    beginRemoveRows(parent_index, row, row); 
    parent->delete_child(child); 
    endRemoveRows(); 
} 

Родительские и дочерние indicies и сырые указатели хороши только перед вызовом beginRemoveRows; Отладчик показывает, что они указывают на правильный элемент и его родительский элемент. Однако программа вылетает после вызова beginRemoveRows. В частности, он выходит из строя в projectModel::parent():

QModelIndex 
projectModel::parent(const QModelIndex &index) const 
{  
    if (!index.isValid()) 
     return QModelIndex(); 

    Component* item = getItem(index);  //Fails to cast index internal pointer to a valid Component* 
    Component* parentItem = item->Parent(); 

    if (parentItem == _rootnode) 
     return QModelIndex(); 

    return createIndex(parentItem->row(), 0, parentItem); 
} 

Когда я нарушу на аварии и изучить вывод отладчика, это показывает, что в функции parent() переменная item мусор. Как-то похоже, что мой QModelIndex поврежден между вызовом deleteComponent и вызовом parent. Есть ли что-то явно неправомерное в том, что я делаю, или проблема, возможно, более тонкая?

+0

Сторона примечания: модель никогда не должна содержать ссылок на представление, так как она вполне применима для использования модели с нулевыми представлениями, а также с «представлениями», которые не подкласса «QAbstractItemView». Метод 'deleteComponent' принадлежит где-то за пределами класса модели. Скорее всего, вы должны подклассифицировать 'QTreeView'. –

+0

Я также 'Q_ASSERT (child_index.isValid())', так как он не должен быть действительным. –

+0

Что такое стек вызовов при сбое? Вероятно, вы не должны называть 'deleteComponent', в то время как любой код представления, который у вас отсутствует, находится в стеке вызовов. –

ответ

7

Это вполне ожидаемо.

Непрерывные индексы действительны до тех пор, пока вы не измените структуру модели. Структурным изменением является любое изменение, которое сигнализируется другое, чем путем испускания dataChanged.

Структурные изменения должны рассматриваться как барьеры на пути к жизни индекса: вы должны отбросить любые неустойчивые индексы, удерживаемые до структурного изменения.

В зависимости от того, где вызывается deleteComponent, вероятно, произойдет то, что вызывающий абонент имеет некоторые индексы, deleteComponent делает их недействительными, и вы находитесь в неопределенной области поведения оттуда.

Вам нужно использовать постоянные индексы, если вы хотите, чтобы они оставались действительными в отношении структурных изменений, и даже тогда они будут действительны только в том случае, если данный элемент все еще существует. Если вы используете свою собственную модель, вам необходимо явно поддерживать постоянные индексы, и пользователи вашей модели должны их использовать.

+0

* ", которые поддерживают постоянные индексы" * На самом деле, все модели. Если модель не обновляет свои постоянные индексы, это ошибка в модели. – peppe

+0

@peppe Поскольку в документации не указано, что они являются необязательными, вы думаете, что это не так. Но не все модели поддерживают их.Это «ошибка» в том смысле, что никто не мешал им реализовывать. Взгляды Qt не используют их, поэтому это не имеет большого значения, если вы ограничиваете себя этими. –

+0

Извините, что вы имеете в виду? Какие модели «не поддерживают» их? – peppe