Название: QModelIndex указывает куда-то не туда... Отправлено: xintrea от Январь 05, 2009, 15:22 Здравствуйте!
Имею модель kntrmodel с древовидными данными (класс knowtreemodel, унаследован от QAbstractItemModel). Делаю метод, который бы перемещал ветку вверх-вниз на одну позицию (ветки перемещаю только в пределах уровня вложенности). То есть, по факту, при перемещении ветки на одну позицию вверх, перемещаемая ветка меняется местами с вышележащей. А при перемещении вниз, перемещаемая ветка меняется местами с нижележащей. Перемещение ветки происходит в два этапа. 1. В TreeItem (аналог примера /qtdemo/examples/itemviews/editabletreemodel) сделал метод move_up() Код: bool TreeItem::move_up(void) 2. После вызова метода move_up(), модель отправляет сигнал что данные изменены dataChanged(). Делается это в методе объекта knowtreemodel, который и является моделью (унаследован от QAbstractItemModel) Код: // Перемещение ветки вверх На экране, после этих действий, ветка, как и положено, перемещается вверх на одну строку. Т.е. было Овощи Фрукты Ягоды <----- Перемещаем вверх Корнеплоды Стало Овощи Ягоды Фрукты Корнеплоды Однако, если теперь ткнуть на Ягоды, и сделать вывод в консоль значения data() для данного QModelIndex Код: // Действия при клике на ветку дерева то окажется, что данный пункт дерева всеравно указывает на Фрукты! Совершенно не понимаю, почему так происходит. Метод dataChanged() все правильно обновил на экране, т.е. метод data() вызывался коственно при dataChanged(), и выдал нужные данные в соответсвии с новым расположением элементов. Однако, при обращении к изменённым элементам, метод data() выдает старые значения... Такое впечатление, что QModelIndex потянулись вслед за ветками, и были обменены между этими двумя ветками, хотя нигде таких действий не проводилось. В любом случае, не могу понять, что я сделал неправильно, и как исправить. Кто что может подсказать? Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 05, 2009, 15:34 А getItem() как находит нужный TreeItem? По index.row()? Или хранит некий хеш с QPersistentModelIndex?
Название: Re: QModelIndex указывает куда-то не туда... Отправлено: xintrea от Январь 05, 2009, 16:48 А getItem() как находит нужный TreeItem? По index.row()? Или хранит некий хеш с QPersistentModelIndex? Этот метод тот же, что и в примере /qtdemo/examples/itemviews/editabletreemodel. Вот его реализация Код: // Получение указателя на Item-злемент связанный с заданным QModelIndex Пояснение, откуда взялось TreeModel. Класс knowtreemodel наследуется по цепочке knowtreemodel->TreeModel->QAbstractItemModel. Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 05, 2009, 19:46 View для оптимизации рисования кеширует индексы, делается это через единственный доступный механизм - QPersistentModelIndex. Это такая штука, которая сама подстраивает row(), column() и parent() индекса, если модель вздумала измениться. Естественно когда вы сами двигаете внутреннее представление - модель никаким образом знать об этом не может.
Вы спрашиваете почему визуально элемент изменился, а в слот вам пришло предыдущее значение? Это потому что TreeItem ассоциирован с указателем на сам элемент. Ваша ошибка в том, что вы не обменяли значения даных между TreeItem'ами, а рокирнули сами указатели. То-есть закешированные View индексы, они же указатели на TreeItem'ы, - не изменились. Вариантов два: 1. Менять не указатели, а данные по указателям. Как вы могли верно подметить, это гемморойно - сохранять структуру указателей такой же, а менять только данные по ним. Кроме того указатель является уникальным идентификатором, через который к TreeItem'у ссылаются не только из модели, а откуда-то ещё. 2. Делать так как делаете вы, но сказать модели, что внутренняя структура модели изменилась через QAbstractItemModel::changePersistentIndex ( const QModelIndex & from, const QModelIndex & to ). И ещё - советую обьединить логику move_up() в одном месте. Название: Re: QModelIndex указывает куда-то не туда... Отправлено: xintrea от Январь 06, 2009, 12:19 2. Делать так как делаете вы, но сказать модели, что внутренняя структура модели изменилась через QAbstractItemModel::changePersistentIndex ( const QModelIndex & from, const QModelIndex & to ). И ещё - советую обьединить логику move_up() в одном месте. Спасиба, вы отлично все расписали. Остался у меня неразрешенный вопрос - в какой момент надо вызывать changePersistentIndex() ? Я пробую делать так Код: if(current->move_up()) // Если перемещение в TreeItem представлении прошло нормально Но поведение осталось прежнее. В какой момент надо вызывать changePersistentIndex() ? Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 06, 2009, 12:35 По идее - в любое время, так как метод только меняет содержимое QPersistentModelIndex. Забыли:
Код: if(current->move_up()) // Если перемещение в TreeItem представлении прошло нормально Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 06, 2009, 12:49 Судя по документации должно быть что-то типа:
Код: if ( willChangeTheModel ) Название: Re: QModelIndex указывает куда-то не туда... Отправлено: xintrea от Январь 06, 2009, 21:32 Спасибо, все получилось.
Но осталась еще одна проблема. После перемещения ветки, нужно установить засветку на новой позиции, куда переместилась ветка. QModelIndex для новой позиции мне известен. И я для вида knowtree даю следующую команду Код: knowtree->selectionModel()->setCurrentIndex(index_after_move,QItemSelectionModel::ClearAndSelect); В результате, синенькая засветка движется вслед за перемещаемой веткой (т.е. после перемещения, перемещаемая ветка оказывается выбраной синей засветкой). Но в Qt помимо синей засветки есть еще и пунктирное обрамление. Пунктирное обрамление появляется например, если ткнуть на любую ветку мышкой - ветка станет выделена синей засветкой, вокруг которой есть пунктирное обрамление. Так вот, этого пунктирного обрамления, после перемещения ветки и установки курсора в нужную позицию, невидно. А мне нужно выделить позицию не только синенькой засветкой, но и пунктиром, то есть сделать так, как будто по данной позиции кликнули мышкой. Как сделать это? ЗЫЖ Попробовал добавить Код: knowtree->selectionModel()->select(index_after_move,QItemSelectionModel::ClearAndSelect); тоже не помогает. Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 06, 2009, 22:07 Весь код в студию.
Название: Re: QModelIndex указывает куда-то не туда... Отправлено: BRE от Январь 06, 2009, 22:11 Попробуй поиграть с флагами QItemSelectionModel::SelectionFlags. Добавить QItemSelectionModel::Current.
Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 06, 2009, 23:01 Добавил в их пример пару кнопок, сделал то же, что а топикстартер. Текущий элемент (пунктиром который) двигается вместе с перемещением. Другое дело что его не видно, когда вьюв теряет фокус.
Название: Re: QModelIndex указывает куда-то не туда... Отправлено: xintrea от Январь 07, 2009, 00:11 Весь код в студию. В обсервере, который знает о виде и модели, имеем Код: void treescreen::move_up_branch(void) В модели имеем Код: // Перемещение ветки вверх В Item-классе имеем Код: bool TreeItem::move_up(void) Добавил в их пример пару кнопок, сделал то же, что а топикстартер. Текущий элемент (пунктиром который) двигается вместе с перемещением. Другое дело что его не видно, когда вьюв теряет фокус. Вот у меня чехорда получается. После первого клика синяя засветка (вместе с веткой) поднимается на строку вверх. Пунктирной засветки нет, но вьюв фокус не потерял (дерево синей рамкой выделено). Если , теперь кликнуть мышкой на синий (перемещаемый) пункт, пункт обведется пунктиром. И если снова нажать на перемещение вверх, пункт нормально поднимется еще на одну строку. А вот если просто нажимать кнопку перемещения вверх несколько раз (не клацая каждый раз по выделенному пункту), то пунктир появляется каждый второй клик (с чего бы?). И в момент, когда пунктира нет, перемещается вверх не выделенный синеньким пункт, а пункт под ним. То есть, при втором клике, пункты опять меняются местами, и список становится прежним. Но после этого синяя засветка с пунктиром (!) выделяет пункт над этими двумя, которые поменялись дважды местами, и все повторяется с новыми, вышележащими пунктами. Чехорда одним словом. Поэтому мне нужно разобраться, как эмулировать клик мышой по нужному пункту, чтобы и синяя засветка на нем стояла, и пунктиром он был выделен. Название: Re: QModelIndex указывает куда-то не туда... Отправлено: BRE от Январь 07, 2009, 00:23 Выделенных (Select) элементов может быть много, текущий (Current) только один, он подсвечивается пунктиром. По идее тебе нужно старое выделение очищать (Clear), а новое подсвечивать (Select) и делать текущим (Current).
Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 07, 2009, 00:49 Сильно вникнуть не получилось, но в коде по крайней мере одна ошибка - QModelIndex knowtreemodel::move_up_branch(const QModelIndex &index) возвращает мусор. Потому что QModelIndex статический обьект и перестаёт быть валидным при нарушении внутренней структуры данных.
Код: // Перемещение ветки вверх Ещё я вам привёл пример, что смена внутренней структуры должна быть между layoutAboutToBeChanged()/layoutChanged(), а у вас она вылезла за пределы. Думайте сами как вам лучше её вынести эту логику внутрь. После получения сигнала layoutAboutToBeChanged() - желающие имеют право получить корректные данные по старым закешированным у них индексам, а у вас уже в этот момент внутренняя структура изменена. Название: Re: QModelIndex указывает куда-то не туда... Отправлено: xintrea от Январь 07, 2009, 21:49 Уф, вчера сайт недоступен был, сразу не ответил.
Я для упрощенья разговора модифицировал пример /qtdemo/examples/itemviews/editabletreemodel. Добавил: 1. Кнопочку перемещения вверх (можно жать Ctrl+U) 2. Метод move_up_branch() в mainwindow.cpp 3. Метод move_up_branch() в treemodel.cpp 4. Метод move_up в treeitem.cpp При запуске программы, и попытке перемещения ветки вверх, можно наблюдать описанную мной чехорду. Ссылка на модифицированный (рабочий) пример http://webfile.ru/2535740 (http://webfile.ru/2535740). Затем я попробовал изменить метод move_up_branch() в treemodel.cpp так как вы советуете. Сделал его вот таким Код: QModelIndex TreeModel::move_up_branch(const QModelIndex &index) Однако при компиляции получаю ошибку Цитировать treemodel.cpp: In member function 'QModelIndex TreeModel::move_up_branch(const QModelIndex&)': treemodel.cpp:306: ошибка: не найден метод для преобразования в '(const QModelIndex) (int&, int&, QModelIndex&)' make: *** [treemodel.o] Ошибка 1 Эта ошибка выше моего понимания, курение ассистента ясности не внесло. ЗЫЖ Да, я готов изменить логику этих методов, но только мне нужно сначала добиться хоть какого-то рабочего результата, который будет работать правильно. ЗЗЫЖ А как вы узнали, что в QModelIndex swap_index мусор находится? Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 07, 2009, 22:11 1. Вы так и не обрамили изменения структуры данных в пару вызовов layoutAboutToBeChanged();layoutChanged(); - оно у вас осталось в методе current->move_up().
2. QModelIndex - статический обьект с временными данными, валидный только за пределами модели только в пределах текущего стека, в котором этот QModelIndex получен. Когда он подставляется обратно в модель - по хорошему должен быть логически разбит обратно на row, column, parent. Если же он выходит за пределы модели - он должен быть пересобран. 3. Ошибка в том, что в области видимости уже есть переменная с именем index. Делайте: this->index( ... ); Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 07, 2009, 22:16 Совет. Откройте Ассистент и прочитайте всю документацию по методам Model/View/Index/PersistentIndex. Если останутся неясности, сомнения что к чему и как работает - открываете исходники и читаете код. Можно перед зеркалом с интонацией. Мне помогает.
Название: Re: QModelIndex указывает куда-то не туда... Отправлено: xintrea от Январь 07, 2009, 22:19 Фух, наконец заработало какнада.
Цитировать Ошибка в том, что в области видимости уже есть переменная с именем index. Делайте: this->index( ... ); никогда бы до этого недодумался! "QModelIndex - статический обьект с временными данными, валидный только за пределами модели только в пределах текущего стека, в котором этот QModelIndex получен", чота я завис на этой фразе... Надо поспать. Название: Re: QModelIndex указывает куда-то не туда... Отправлено: xintrea от Январь 07, 2009, 22:24 Совет. Откройте Ассистент и прочитайте всю документацию по методам Model/View/Index/PersistentIndex. Не, вы один пункт упустили. Правильно будет так - "Выучите английский язык. Откройте Ассистент и прочитайте всю документацию по методам Model/View/Index/PersistentIndex." Название: Re: QModelIndex указывает куда-то не туда... Отправлено: xintrea от Январь 08, 2009, 14:42 А у меня вопрос к гуру.
Если закрыть глаза на весь код, который был в этой ветке, и сделать все "с нуля", то какую бы реализацию перемещения ветки вверх-вниз (без выхода за границы уровня вложенности) вы бы использовали, если в основу взять пример /qtdemo/examples/itemviews/editabletreemodel ? Название: Re: QModelIndex указывает куда-то не туда... Отправлено: Dendy от Январь 08, 2009, 15:52 Всё у вас там нормально. Кроме того что проверка на то может ли Item переместиться вверх/вниз находится в самом Item'е. Замените возвращаемый bool на void, в проверки - на ассерты. Саму проверку вынесите наружу.
Название: Re: QModelIndex указывает куда-то не туда... Отправлено: ритт от Январь 08, 2009, 23:38 а я бы на сортфильтерпроксимодели сделал бы минут за десять :)
Название: Re: QModelIndex указывает куда-то не туда... Отправлено: vaprele07 от Январь 09, 2009, 06:03 на примере: /qtdemo/examples/itemviews/editabletreemodel.
Код: void TreeModel::swap(const QModelIndex &parent, int i, int j) |