Название: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Eugene Efremov от Август 28, 2008, 10:15 Имеем примерно такой код:
Код: bool insertRows(int pos, int rows, const QModelIndex &ind) И в нем две проблемы: Во-первых, в хэше оно вставиться туда, куда само захочет. А отнюдь не в заданную позицию. И меня это вполне устраивает. Но саму QAbstractItemModel это не устраивает. В результате, оно ругается в консоль и ресетит модель. Как ему объяснить, что я буду вставлять строку по произвольному индексу? А если hasName из данного примера вернула false и ничего вставлять не надо? Как это откатить? Во-вторых сами данные, которые вставляются в модель. Функции приходится как-то добывать их самой, хотя ей, вообще говоря, неизвестно, что именно надо вставлять. Зато все это известно в том месте, где была вызвана insertRow. В этом примере это обходится таким образом: сперва внешний объект (ну, или функция-оболочка самой модели) получает данные, которые будет вставлять, сохраняет их где-то во временных переменных, потом вызывает insertRow, а затем getName и getValue их оттуда получают. Понятно, что этот способ мне сильно не нравится. И при использовании с потоками с ним будут большие проблемы. Есть ли способ лучше? Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: ритт от Август 28, 2008, 15:03 я, конечно, извиняюсь, но все перечисленные проблемы от черезжопности написания модели. оно всегда так бывает, когда вставляешь неизвестно что неизвестно куда
Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Eugene Efremov от Август 29, 2008, 02:53 я, конечно, извиняюсь, но все перечисленные проблемы от черезжопности написания модели. Хорошо. Оставим данную конкретную модель в покое. Рассмотрим общий случай. Вот у нас модель. Хорошая, не «черезжопная». Вот поступил ей сигнал: вставь в себя строку с такими-то данными. Куда конкретно — на твое усмотрение. Как она должна это делать, если это хорошая модель? Дальше. Допустим, вставляет она эти данные, используя свой внутренний интерфейс к чему-то там, во что она их вставляет. И второй интерфейс, через который она их получает. А один из этих интерфейсов ей и говорит: опаньки, сервер недоступен/кончилось место на винте/permission denied/недостающее вписать, лишнее вычеркнуть. Что она должна делать в этом случае? Если beginInsertRows уже вызван? Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Hort от Август 29, 2008, 11:35 Хорошо. Оставим данную конкретную модель в покое. Рассмотрим общий случай. Вот у нас модель. Хорошая, не «черезжопная». Вот поступил ей сигнал: вставь в себя строку с такими-то данными. Куда конкретно — на твое усмотрение. Как она должна это делать, если это хорошая модель? ну вообще-то пользователь указывает куда вставлять, например поле выделенного элемента в пердставлении, вот и узнаешь индекс этого элемента через курентИндекс() и после него вставляешь.Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Tonal от Август 29, 2008, 12:52 У меня делается так:
Ищется куда надо вставлять и надо ли вообще. Если надо, вызываем beginInsertRows, вставляем, endInsertRows. То же самое для удаления. Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: ритт от Август 29, 2008, 14:58 Вот поступил ей сигнал: вставь в себя строку с такими-то данными. Куда конкретно — на твое усмотрение. Как она должна это делать, если это хорошая модель? так не бывает. модель не решает сама куда что вставить - модель работает с индексами (если это index-based модель) или с айтемами (если это item-based модель). модель не получает сигналов на вставку данных неизвестно куда - ты можешь сигнализировать модели, что данные в источнике изменились/доавились/удалились и опционально "место", где произошли изменения (свой ид привязки или указатель на объект, или позицию в массиве).для примера приведу таблицу в бд и связанную с ней модель на основе QSqlTableModel: допустим, мы подписались на уведомления о событиях бд (либо наплодили хп/триггеров в бд и по периодически зачитываем таблицу изменений) - если третья сторона добавит в таблицу свои записи, мы об этом узнаем в лучшем случае узнаем значение примарикей для изменившейся записи и тогда два варианта: найдём в модели индекс, содержащий данное значение ключа, и изменим данные всей строки в соответсвии с изменениями в бд - изменим значения или удалим строку; не найдём индекса с таким ключем и добавим в модель строчку с этим ключем и соответствующими ему данными - куда вставить строку в модели зависит исключительно от реализации: либо вставим её по какому-то признаку сортировки (например, ид по возрастанию), либо уверенные в том, что над моделью сидит прокся, просто добавим строку после всех существующих строк; в худшем случае мы узнаем лишь в какой таблице произошли изменения и придётся перевыбирать таблицу целиком; +не стоит забывать, что даже в случае, когда в таблицу пишем мы и точно знаем что и куда пишем, какие-то триггеры могут модифицировать данные в этой же таблице - поэтому в стандартной троллёвской реализации после отправки изменений в бд модель перевыбирается полностью; конечный пользователь вносит изменения в модель ( и посредством модели - в источник) _только_ через представление - будь то тэйблвью или группа самописных виджетов; и чем меньше в публичном апи модели отступлений от апи абстрактной модели, тем лучше (причин множество)... Дальше. Допустим, вставляет она эти данные, используя свой внутренний интерфейс к чему-то там, во что она их вставляет. И второй интерфейс, через который она их получает. А один из этих интерфейсов ей и говорит: опаньки, сервер недоступен/кончилось место на винте/permission denied/недостающее вписать, лишнее вычеркнуть. Что она должна делать в этом случае? Если beginInsertRows уже вызван? а что мешает спросить "второй интерфейс, через который она их (данные) получает" доступен ли сервер, есть ли место на винте и др., и пр. _до_ того, как позвать beginInsertRows ? я вижу только одну причину - это когда "второй интерфейс" сам не знает о всех этих засадах, пока не попробует записать данные. тогда настаёт вопрос о "черезжопности" написания этого-самого "второго интерфейса", который в частном случае может быть сторонним и с закрытым кодом...но и тут можно _сначала_ записать данные во "второй интерфейс", а лишь при успешной записи звать beginInsertRowsНазвание: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Eugene Efremov от Апрель 25, 2009, 16:19 Мда. Перечитав тему полугодовой давности, прихожу к выводу, что я слоупок. Похоже, в моем понимании всего этого механизма отсутствует какая-то важная деталь. По сему — еще раз, медленно и по буквам:
конечный пользователь вносит изменения в модель ( и посредством модели - в источник) _только_ через представление - будь то тэйблвью или группа самописных виджетов; и чем меньше в публичном апи модели отступлений от апи абстрактной модели, тем лучше (причин множество)... Вернемся к примеру, с которого все начиналось. Имеем простейшую модель, в качестве источника данных у нее QHash<QString, QString>. Т.е. имеем таблицу с двумя столбцами — key и value. Мы хотим предоставить пользователю возможность вставлять в нее новые строки, проверяя, при этом, введенные пользователем ключи на уникальность. Как мы должны все это реализовывать? Если более конкретно, то вопросы такие: 1. Где хранить сам хеш — в классе модели, где-то еще? 2. Как реализовать саму вставку, соблюдая требование «_только_ через представление»? Мне ничего более путного, чем кнопка Insert, вызывающая простейший диалог, в голову не приходит — а это с существующими View никак не связано... 3. Главное — сама вставка. Как и в каком порядке производить следующие операции:
модель работает с индексами (если это index-based модель) или с айтемами (если это item-based модель). А можно подробнее — чем одно отличается от другого? И/или где можно про это прочитать? Или под «item-based» имеется в виду просто использование стандартных Q(List|Table|Tree)Widget и связанных с ними ...WidgetItem? P.S. Подумал, что если тему раскрыть — может получиться неплохое howto, которое, при удаче, можно будет добавить в соответствующий раздел (http://www.prog.org.ru/board_61_0.html)... Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: ритт от Апрель 26, 2009, 03:00 пришлось перечитывать тему, т.к. уже ни черта не помню (кому на что отвечал и кому так и забыл ответить)
1. для такого простого случая я бы хранил хэш (источник) приватом в модели 2. под «_только_ через представление» я не имел в виду Q*View (возможно, я путанно изъяснился, но перечитывать снова лениво). важный пункт - все манипуляции с данными моделями _очень и очень_ желательно организовывать через абстрактный интерфейс модели. редко когда требуется нечто странное с извращениями...и чаще это от дефицита мозга. для нашего конкретного примера вариантов больше одного... 3. ...самым простым мне представляется вариант с диалогом добавления/редактирования записи: если требуется отредактировать запись, то данные из текущей строки (model::data(...)) переносим на виджеты новоиспечённого диалога (для простейшего случая можно просто воспользоваться сеттерами соответствующих виджетов-редакторов), а по завершении редактирования валидируем данные и возвращаем в модель (model::setData(...)); при добавлении данных лишь валидируем данные по окончании редактирования и возвращаем их в модель, предварительно создав строку посредством insertRows (т.к. в данном случае валидация уже прошла и мы уверены в корректности, уникальности и т.п. данных). вставлять строку можно до/после текущей строки или в начало/конец списке - в зависимости от требований insertRows нужен! - это стандартная интерфейсная часть абстрактной модели. документация призывает не забывать об insertRows в кастомных моделях - документация не врёт (обычно) попросту говоря, item-based модель, это модель, в которой работа с индексами частично или полностью завуалирована работой с айтемами. айтемом может служить указатель на объект, структура, цифробуквенный шифр - в зависимости от задачи и фантазии. > Подумал, что если тему раскрыть... пожалуйста, я не против ) копирайтов на свои посты не накладываю :) Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Eugene Efremov от Апрель 26, 2009, 20:30 2. важный пункт - все манипуляции с данными моделями _очень и очень_ желательно организовывать через абстрактный интерфейс модели. редко когда требуется нечто странное с извращениями...и чаще это от дефицита мозга. для нашего конкретного примера вариантов больше одного... Пока что у меня картинка не складывается: получается, что для валидации нужно или расширять интерфейс модели, или работать с источником напрямую (см. ниже). 3. ...самым простым мне представляется вариант с диалогом добавления/редактирования записи: если требуется отредактировать запись, то данные из текущей строки (model::data(...)) переносим на виджеты новоиспечённого диалога (для простейшего случая можно просто воспользоваться сеттерами соответствующих виджетов-редакторов), а по завершении редактирования валидируем данные и возвращаем в модель (model::setData(...)); при добавлении данных лишь валидируем данные по окончании редактирования и возвращаем их в модель, предварительно создав строку посредством insertRows (т.к. в данном случае валидация уже прошла и мы уверены в корректности, уникальности и т.п. данных). вставлять строку можно до/после текущей строки или в начало/конец списке - в зависимости от требований Так... Попробую разобрать по порядку. Получается следующая последовательность действий: 1. Вызов диалога и ввод пользователем данных. 2. Валидация, дальнейшие действия — только при ее успехе. 3. Вызов insertRows и добавление строки. 4. Собственно добавление данных в хэш через вызов setData. Так? Если так, то по всем пунктам — кроме первого — сразу же возникают вопросы: Валидация: Чтобы проверить ключ на уникальность — нужно получить доступ к самому хэшу и вызвать contains. А это стандартным интерфейсом абстрактной модели не предусмотрено. Т.е. нужно или расширять интерфейс модели, или — лезть непосредственно к источнику данных мимо этой модели... И то, и другое — противоречит п.2. Или я в этом стандартном интерфейсе что-то пропустил и заготовка для валидации там имеется? insertRows: Если данные в хэш мы вставляем в другом месте — то что мы делаем между вызовом beginInsertRows и endInsertRows? Или insertRows вообще не следует переопределять? Вставка данных: Вызываем setData — и имеем ту же проблему, с которой все начиналось, но уже внутри нее. Данные в хэш вставляются не в заданной позиции, а туда, куда этот хэш сам захочет. И как это корректно обработать? И еще — допустимо ли вставлять оба поля сразу, или придется ее два раза вызывать — для ключа и значения? попросту говоря, item-based модель, это модель, в которой работа с индексами частично или полностью завуалирована работой с айтемами. айтемом может служить указатель на объект, структура, цифробуквенный шифр - в зависимости от задачи и фантазии. Т.е. здесь у нас — item-based, поскольку работаем мы, в основном, с парой ключ/значение? > Подумал, что если тему раскрыть... пожалуйста, я не против ) копирайтов на свои посты не накладываю :) Спасибо. Только пока у меня задача — самому понять что к чему. А потом уже можно и накатать что-нибудь на тему... P.S. Кстати, заметил, что непонятки у меня в том месте, где в стандартной MVC находится контроллер. Здесь контроллера нет, а делегаты всех случаев не охватывают. Дырка приходится как раз на вставку новых элементов — где меня и заклинило... Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: ритт от Апрель 26, 2009, 21:14 по моему скромному мнению кутэ чудно обходится и без контроллеров.
валидация: упущен важный момент - QAbstractItemModel::match(...) insertRows: между beginInsertRows и endInsertRows можно ничего не делать. это всего лишь необходимые звенья в цепи уведомлений об изменении данных. в сложных моделях, критичных к производительности между beginInsertRows и endInsertRows выполняется работа с источником, т.к. между данными звеньями перевыборки данных и т.п. гарантированно не будет. чтобы было более понятно, поясню на простом примере: Код: void MyModel::insertMyRow(const QString& key, const MyData& data) Цитировать Т.е. здесь у нас — item-based <snip> согласен, в данном случае было бы уместно реализовать навигацию по ключу вместо навигации по индексусоветую почитать исходники QStringListModel - хоть и не совсем то, что нужно, но тепло... Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Eugene Efremov от Апрель 27, 2009, 04:21 валидация: упущен важный момент - QAbstractItemModel::match(...) Так... (посмотрев исходники) Ну и крокодил. Впрочем, переопределить ее нужно лишь для случая полного соответствия с ключем, во всех остальных случаях можно юзать дефолтовый вариант... insertRows: между beginInsertRows и endInsertRows можно ничего не делать. это всего лишь необходимые звенья в цепи уведомлений об изменении данных. в сложных моделях, критичных к производительности между beginInsertRows и endInsertRows выполняется работа с источником, т.к. между данными звеньями перевыборки данных и т.п. гарантированно не будет. А способ просто запретить эту перевыборку/etc. существует? Если, к примеру, с источником работать надо, а строки вставлять — нет? Код: void MyModel::insertMyRow(const QString& key, const MyData& data) Это уже не стандартный интерфейс. А как же «все манипуляции с данными моделями _очень и очень_ желательно организовывать через абстрактный интерфейс модели»? Значит, расширять этот интерфейс все-таки стоит? Код: beginInsertRows(...); Но оно здесь вставляется не туда, куда указывает beginInsertRows, а туда, куда хэш захочет (subj, однако). Это нормально? Может быть, после endInsertRows стоит вызвать dataChanged, указав ему «от нуля и до конца»? Цитировать Т.е. здесь у нас — item-based <snip> согласен, в данном случае было бы уместно реализовать навигацию по ключу вместо навигации по индексуКак это сделать? В чем будет выражаться отличие от навигации по индексу? советую почитать исходники QStringListModel - хоть и не совсем то, что нужно, но тепло... Посмотрел. Только там, как раз, все ясно, никаких вставок по произвольному адресу. Собственно, хэш я выбрал именно для того, чтобы с этими вставками разобраться... Хотя, кое-что полезное я оттуда извлек — похоже, что insertRows используется только для вставки пустых строк (нигде в документации этого явно не сказано). Это так? Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: ритт от Апрель 27, 2009, 06:43 меня этот тред начинает утомлять...
запретить перевыборку нельзя - вьюха не спрашивает у модели готова ли модель отдать данные - если модель заявляет, что имеет 2 столбца и 3 строки, должны быть доступны данные для каждой из 6 ячеек (плюс 5 ячеек вертикального и горизонтального заголовков). чтобы подсказать вьюхе не делать запросы данных, нужно использовать стандартный механизм - layoutAboutToBeChanged()/layoutChanged(), rowsAboutToBeInserted()/rowsInserted() и т.д. > void MyModel::insertMyRow(const QString& key, const MyData& data) да, это нестандартный интерфейс. я специально назвал это примером. если читать этот пример, не выдирая его из контекста, это пример оптимизированной вставки данных. такой конструкцией я пользуюсь, когда необходимо заполнить модель из кэша - не вставлять же данные поиндексно, если после endInsertRows всё-равно произойдёт выборка всех данных для новых строк! и строк там обычно куда более одной. > Как это сделать? В чем будет выражаться отличие от навигации по индексу? смотреть документацию и код любой item-based модели... Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Eugene Efremov от Апрель 27, 2009, 14:33 меня этот тред начинает утомлять... Жаль. Хотя, в принципе, понимаю, что таким дотошным выяснением деталей можно достать кого угодно. :) Тогда предлагаю сделать так. Я иду в тему «Уроки и статьи (http://www.prog.org.ru/board_61_0.html)», пишу там — в меру своего текущего понимания — заготовку для обещанного howto, после чего общими усилиями доводим его до ума. На готовом примере с описанием все ошибки и неточности сразу будут видны. А в конечном счете получится (надеюсь) что-то, полезное для всего форума. > Как это сделать? В чем будет выражаться отличие от навигации по индексу? смотреть документацию и код любой item-based модели... Можно список таких моделей — код которых можно посмотреть? В документации, кстати, термин «item-based» употребляется только в контексте «Item View Convenience Classes». Про «item-based model» там ни слова... P.S. Тема переехала... QAbstractItemModel — это, оказывается, GUI. Не знал... Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: ритт от Апрель 28, 2009, 22:05 QStandardItemModel, например
Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Eugene Efremov от Май 01, 2009, 19:42 Тогда предлагаю сделать так. Я иду в тему «Уроки и статьи (http://www.prog.org.ru/board_61_0.html)», пишу там — в меру своего текущего понимания — заготовку для обещанного howto, после чего общими усилиями доводим его до ума. На готовом примере с описанием все ошибки и неточности сразу будут видны. А в конечном счете получится (надеюсь) что-то, полезное для всего форума. Статья готова (http://www.prog.org.ru/topic_9376_0.html). Приглашаю принять участие в обсуждении и вычистке... QStandardItemModel, например Хм... Там каждый элемент соответствует одной ячейке. Вроде бы, обычно элемент занимает строку, так что пример, похоже, не слишком характерный... Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: drsm от Май 05, 2009, 03:20 статью не читал еще ;D, хочу отметить, по поводу "неизвестно куда".
при наличии сложной семантики добавления/удаления элементов, проще перещитать самому persistentIndexList у модели и заэмитить нужные сигналы, где-то тут я уже об этом подробнее писал. Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Eugene Efremov от Май 11, 2009, 19:10 статью не читал еще Как выяснилось, способ, который я применил в статье, не работает. Точнее — в моем примере работает, но в более сложных случаях будет глючить... Так что — вопрос остается открытым... при наличии сложной семантики добавления/удаления элементов, проще перещитать самому persistentIndexList у модели и заэмитить нужные сигналы, где-то тут я уже об этом подробнее писал. Ммм... Имеется в виду, как я понимаю, вот это: если невозможно использовать эти функции то алгоритм такой: 1. emit layoutAboutToBeChanged(); 2. сохранить данные по индексам из persistentIndexList() 3. добавить/удалить элементы 4. пересчитать индексы в persistentIndexList() используя данные из 2. см changePersistentIndex() 5. emit layoutChanged(); Проблема в том, что там еще где-то вызывается сигнал rowsInserted, про который сказано, что юзер его вызывать не должен, а токмо через begin/endInsertRows... Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: ритт от Май 11, 2009, 22:33 юзер его вызывать _не_может_ (соответственно, и не должен)
посмотри код begin/endInsertRows - там всё как бы просто, но не сделай этого - модель работать не будет (или будет, но через ж)...вот и упростили Название: Re: QAbstractItemModel::beginInsertRows и вставка неизвестно чего неизвестно куда. Отправлено: Eugene Efremov от Май 13, 2009, 18:11 юзер его вызывать _не_может_ (соответственно, и не должен) (посмотрев код) Во как... private signals — не знал, что такое вообще возможно... посмотри код begin/endInsertRows - там всё как бы просто, но не сделай этого - модель работать не будет (или будет, но через ж)...вот и упростили И того, получается, что вся эта чехарда с beginInsertRows нужна для двух вещей:
Так? Или там еще что-то делается? |