LINUX.ORG.RU

Qt Model/View проблема с битыми индексами


0

2

У меня реализована своя модель, унаследованная от QAbstractItemModel. В QModelIndex я храню не только row и column, но и указатель на объект, к которому относится этот индекс. Если объект удалить, то индекс будет содержать в себе ссылку на удаленный объект и я с этим ничего поделать не могу, так как индексы принадлежат не мне.

Проблема возникает вот в такой ситуации. Есть дерево вида:

A
B
+B1
+B2
+B3
Здесь B1,B2,B3 субэлементы B. В QTreeView выделен элемент B3.

Надо удалить B. Вызываем сначала beginRemoveRows(), потом delete для объекта B, потом endRemoveRows(). Деструктор объекта B уничтожает B1, B2, B3. Потом приложение падает в месте, где QTreeView пытается узнать у модели parent(const QModelIndex &child) для индекса B3. Естественно объекты B3 и B уже мертвы а их индексы должны быть битыми.

Как с этим бороться?

Как с этим бороться?

Прочитать еще немного про модели и представления.

MikeDM ★★★★★ ()

В QModelIndex я храню не только row и column, но и указатель на объект, к которому относится этот индекс.

Поделись, как и зачем ты это делаешь?

Вызываем сначала beginRemoveRows(), потом delete для объекта B, потом endRemoveRows().

Код покажи. Нужно с B1-B3 вызывать, imho.

UVV ★★★★★ ()
Ответ на: комментарий от UVV

Поделись, как и зачем ты это делаешь?

У индекса есть возможность хранить указатель на свои данные (QModelIndex::internalPointer()), в него я заношу указатель на объект.

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

Нужно с B1-B3 вызывать, imho.

Интересный вопрос, нужно, или не нужно? Мне казалось, что не нужно. В документации что-то сказано по этому поводу?

pathfinder ★★★ ()
Ответ на: комментарий от pathfinder

У индекса есть возможность хранить указатель на свои данные (QModelIndex::internalPointer()), в него я заношу указатель на объект.

Returns a void * pointer used by the model to associate the index with the internal data structure.

Это не для тебя.

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

Для этого есть QAbstractItemModel::data().

UVV ★★★★★ ()
Ответ на: комментарий от UVV

Это не для тебя.

В документации Qt есть Simple Tree Model Example, там делают точно также как и я.

http://harmattan-dev.nokia.com/docs/library/html/qt4/itemviews-simpletreemode...

Для этого есть QAbstractItemModel::data().

data(const QModelIndex &index, int role)

Ну правильно, дают тебе индекс и роль, и что с ними делать? В этом методе надо вернуть какие-то данные.

pathfinder ★★★ ()

Вызываем сначала beginRemoveRows(), потом delete для объекта B, потом endRemoveRows().

Вот тут ошибка. Надо еще beginRemoveRows() и endRemoveRows() для B1, B2, B3 вызывать

AF ★★★ ()
Ответ на: комментарий от AF

Вот тут ошибка. Надо еще beginRemoveRows() и endRemoveRows() для B1, B2, B3 вызывать

Вот нашел другой пример Editable tree model:

http://qt-project.org/doc/qt-4.8/itemviews-editabletreemodel.html

Там вроде один раз вызывается beginRemoveRows() и endRemoveRows(), без предварительного рекурсивного прохода по потомкам.

Не понимаю.

pathfinder ★★★ ()

Вероятно ты просто не вкурил как модель работает. Модель на то она и модель чтобы в нее данные добавлять, а у тебя один указатель всего лишь. Вот глянь http://lemirep.wordpress.com/2013/04/06/a-practical-case-exposing-qt-c-models... Забей на ту часть что про qml, глянь на саму модель и на айтем

Vernat ★★ ()

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

UVV ★★★★★ ()
Ответ на: комментарий от Vernat

глянь http://lemirep.wordpress.com/2013/04/06/a-practical-case-exposing-qt-c-models...

Мы приносим свои извинения, но доступ к запрашиваемому ресурсу ограничен.

Возможные причины ограничения доступа:
Доступ ограничен  по решению суда или по иным основаниям, установленным законодательством Российской Федерации.

Сетевой адрес, позволяющий идентифицировать сайт в сети «Интернет», включен в Единый Реестр доменных имен, указателей страниц сайтов сети «Интернет» и сетевых адресов, позволяющих идентифицировать сайты в сети «Интернет», содержащие информацию, распространение которой в Российской Федерации запрещено.

:( Большой брат добрался и сюда. Меня уже начинает дико злить.

pathfinder ★★★ ()
Ответ на: комментарий от anonymous

Прочитать еще немного про модели и представления.
Тебе уже дали кучу советов, но ты не прислушиваешься. Сам создал проблему - сам решай.
УМВР. >А ты в РФ? >Нет

:( Что случилось с Development, куча постов, где меня тупо троллят и ни одного, из которого я мог бы получить что-то ценное.

pathfinder ★★★ ()

где QTreeView пытается узнать у модели parent(const QModelIndex &child) для индекса B3. Естественно объекты B3 и B уже мертвы а их индексы должны быть битыми.

В документации написано, что любое удаление узлов должно быть завёрнуто в beginRemoveRows, endRemoveRows.

«This function emits the rowsAboutToBeRemoved() signal which connected views (or proxies) must handle before the data is removed. Otherwise, the views may end up in an invalid state.»

Значит или заворачивай удаление в деструкторе в них или изначально надо перечислять все удаляемые строки.

monk ★★★★★ ()

здесь описывается похожая проблема. может пригодится

Torvus ()
Ответ на: комментарий от monk

В документации написано, что любое удаление узлов должно быть завёрнуто в beginRemoveRows, endRemoveRows.

У меня удаление завернуто в beginRemoveRows, endRemoveRows. Но только один раз, без рекурсивного обхода детей. Мне бы очень хотелось точно знать: обязательно или нет предварительно вызывать beginRemoveRows, endRemoveRows для всех детей удаляемой ветки.

В документации на QAbstractItemModel в месте, где говорится про функции beginXXX, endXXX есть такая строчка.

If you insert or remove an item with children, you do not need to call these functions for the child items.

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

pathfinder ★★★ ()
Ответ на: комментарий от Torvus

здесь описывается похожая проблема. может пригодится

Интересная ссылка, я так понял у ТС проблема заключалась в том, что он рекурсивно вызывал removeRows и у него получалось что-то вроде:

beginRemoveRows
beginRemoveRows
beginRemoveRows
удаляем узел
endRemoveRows
удаляем узел
endRemoveRows
удаляем узел
endRemoveRows

Не думаю, что можно вызывать второй beginXXX не вызвав endXXX

pathfinder ★★★ ()
Ответ на: комментарий от pathfinder

Ты забавный. Но ничего, со временем ты всё поймёшь и даже научишься видить ту информацию, которую до тебя хотят донести.

UVV ★★★★★ ()

Если текущий выделенный элемент является под-узлом удаляемого элемента, то сперва выдели удаляемый элемент и потом удали его.

tnodir ()
Ответ на: комментарий от tnodir

Если текущий выделенный элемент является под-узлом удаляемого элемента, то сперва выдели удаляемый элемент и потом удали его.

Все верно. Падает у меня именно тогда, когда выделен подэлемент удаляемого элемента. Если выбрать что-то другое, то при удалении программа не упадет.

Кхм, интересно, ты определенно что-то знаешь. А можно более более подробно? С чем это связано? По логике, оно само должно корректно обрабатывать такие случаи.

pathfinder ★★★ ()
Последнее исправление: pathfinder (всего исправлений: 1)
Ответ на: комментарий от tnodir

Всё, нашел ошибку. Все оказалось очень просто. Я неправильно для одного из объектов предоставлял информацию, необходимую для функции parent(const QModelIndex &child), и функция parent() делала для одного объекта неверный индекс предка.

Всем спасибо. Особенно tnodir. :)

pathfinder ★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.