Russian Qt Forum

Qt => Model-View (MV) => Тема начата: Cyrax от Январь 02, 2008, 21:46



Название: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 02, 2008, 21:46
Вопрос следующий: каким образом модель данных (наследник QAbstractTableModel) может определить, какое из представлений вызывает её методы (в частности, data() и setData()) для случая, когда эта модель связана с двумя представлениями ?

Ситуация следующая. Имеется два представления: табличное (с двууровневыми заголовками) и древовидное (QTreeView). Табличное представление содержит значения некоторых параметров, имена которых указаны в горизонтальном заголовке. При наведении мышки на имя какого-либо параметра в горизонтальном заголовке всплывает подсказка с дополнительной информацией об этом параметре. Древовидное представление содержит подробную информацию об этих заголовках (дерево параметров): имя параметра/подпараметра (содержащееся также в табличном представлении в горизонтальном заголовке) + ещё несколько характеристик параметра (часть из которых указывается во всплывающей подсказке горизонтального хидера табличного представления).
Таким образом, почти вся информация, отображаемая древовидным представлением, отображается в табличном представлении. В то же время табличное представление отображает информацию, отсутствующую в древовидном представлении (значения параметров), и наоборот, древовидное представление содержит информацию, отсутствующую в табличном (некоторые характеристики параметров).
Оптимальный способ организации данных - создание общей модели данных (наследника QAbstractTableModel) для обоих представлений, каждое из которых будет работать с соответствующими методами этой модели. Задача заключается в определении моделью данных, какое из представлений вызвало её метод, поскольку для одного и того же индекса QModelIndex табличное и древовидное представления будут запрашивать/изменять разные данные.
Другой способ - создание для каждого представления своей модели данных (табличная и древовидная). Но при этом будет происходить дублирование данных, в связи с чем придётся синхронизировать эти модели. Но это уже отказ от архитектуры "модель-представление" (проще взять QTableWidget и QStandartItemModel + их синхронизация)...
Как лучше это всё организовать ?


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Racheengel от Январь 02, 2008, 23:54
Я бы предложил один из 2 вариантов:
1. Модель насделуется от QAbstractTreeItemModel и содержит общие данные.
2. Общие данные содержатся в отдельном классе. Далее, создается один класс-модель для таблицы (с данными, уникальными для таблицы), и второй класс-дерево (с данными, уникальными для дерева). Оба этих класса содержат также указатели на класс с общими данными.


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 03, 2008, 09:35
Цитировать
1. Модель насделуется от QAbstractTreeItemModel и содержит общие данные.
Наверное, имеешь ввиду QAbstractItemModel ?  (иерархическая структура)
Как в этом случае определить, какое из представлений запрашивает данные ?  Получили, например, некоторый индекс. Для этого индекса древовидному представлению нужно вернуть одни данные, табличному - другие...
И почему именно QAbstractItemModel, а не QAbstractTableModel ?

В любом случае модель данных должна будет хранить 2 структуры данных: табличную (например, двумерный массив) и древовидную - для хранения информации об иерархии и характеристиках параметров. При этом для табличного представления будет нужна информация как из табличной структуры данных, так и из древовидной, для древовидного представления - только из древовидной структуры данных.

Цитировать
2. Общие данные содержатся в отдельном классе. Далее, создается один класс-модель для таблицы (с данными, уникальными для таблицы), и второй класс-дерево (с данными, уникальными для дерева). Оба этих класса содержат также указатели на класс с общими данными.
Тогда можно всю информацию вынести в отдельный класс и создать 2 модели (без данных) - служащие только для обеспечения соответствующего интерфейса и использующие этот класс.

А вообще, хотелось бы иметь одну общую модель данных (например, наследник QAbstractTableModel).
Только вот модель всё-таки оказывается привязанной к структуре представления (для некоторого индекса табличному представлению нужно вернуть одни данные, древовидному - другие)...


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: vaprele07 от Январь 03, 2008, 10:30
Делается одна модель данный, работающая непосредственно с бд (множества А+Б) и 2 прокси модели (QAbstractProxyModel), реализующие нужные тебе представления. В таком случае тебе не придётся придумывать всяких костылей синхронизации данных.


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 03, 2008, 11:25
Цитировать
Делается одна модель данный, работающая непосредственно с бд (множества А+Б) и 2 прокси модели (QAbstractProxyModel), реализующие нужные тебе представления. В таком случае тебе не придётся придумывать всяких костылей синхронизации данных.
Что значит "реализующие нужные тебе представления" ?  Одна прокси-модель работает с табличным представлением, другая - с древовидным ?  Т.е. задача прокси заключается в конвертировании индексов таким образом, чтобы их допустимые множества для обоих представлений не пересекались ?  Тогда компоненты многих индексов потеряют физический смысл. Скажем, для табличного представления смысл сохранится (строка, столбец), а для древовидного - исказится.
Для древовидного представления тоже можно сохранить физический смысл компонентов индекса, если, например, для древовидного представления в прокси будем менять знак строки и столбца на отрицательный...




Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Racheengel от Январь 03, 2008, 11:31
Именно так, две модели - одно для дерева, второе для таблицы (мой вариант 2 - та же идея). Обе модели берут реальные данные из третьей и конвертируют индексы так, как нужно представлениям.


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 03, 2008, 11:46
vaprele07
А ещё лучше рассмотреть конкретный пример.
Горизонтальный заголовок таблицы содержит имена параметров. Вертикальный - номера значений параметров. Т.е., если в таблице 50 строк, то для каждого параметра будет 50 значений.
Древовидное представление содержит иерархию параметров (имена которых указаны в горизонтальном заголовке табличного представления) с их характеристиками/описанием.
Пусть M - главная модель с данными, работающая с БД.
V_tree - древовидное представление
V_table - табличное представление
P_tree - прокси-модель, работающая с древовидным представлением
P_table - прокси-модель, работающая с табличным представлением
Скажем, табличное представление V_table запросило у своей модели (у прокси-модели P_tree) данные по индексу (0, 0, null) для роли DisplayRole. Т.е. данные для своей ячейки в первом столбце первого ряда (это первое значение первого параметра X). Затем древовидное представление V_tree запросило данные у своей прокси-модели P_tree по индексу (0, 0, null) для роли DisplayRole. Т.е. данные для своего первого столбца первого ряда (это имя первого параметра X).
Какие значения индексов должны передать прокси-модели P_table и P_tree главной модели M ?
(учитываем, что модель M должна получать разные индексы для разных данных)


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 03, 2008, 11:50
Цитировать
Именно так, две модели - одно для дерева, второе для таблицы (мой вариант 2 - та же идея). Обе модели берут реальные данные из третьей и конвертируют индексы так, как нужно представлениям.
А что лучше взять - 2 модели, конвертирующие индексы, или 2 прокси, конвертирующих индексы ?

Racheengel , предлагаю рассмотреть конкретный пример из моего предыдущего поста касательно конвертирования индексов ?


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Racheengel от Январь 03, 2008, 14:55
А что лучше взять - 2 модели, конвертирующие индексы, или 2 прокси, конвертирующих индексы ?
В данном случае это одно и тоже. Прокси - это модель, которая не содержит реальных данных.

Что касается индексов, надо сначала ответить на вопрос: как представлены данные в модели М?
Совсем необязательно, что они будут вообще основаны на индексах. Индекс всего лишь указывает на то, какие данные реально нужны. Для P_table по индексу (0, 0, null)  нужно будет вернуть значение 0 параметра 0 из модели М, а для P_tree - имя параметра 0 из модели М.


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 03, 2008, 15:20
Цитировать
Что касается индексов, надо сначала ответить на вопрос: как представлены данные в модели М?
2 структуры данных: таблица (двумерный массив) и дерево параметров (списки + указатели, либо двумерный массив). Вся информация, необходимая для формирования этих двух структур данных, извлекается из БД в конструкторе модели M (вопросы синхронизации данных модели M с данными в БД пока не рассматривает - это отдельный вопрос).
А вот здесь желателен пример, какие индексы могут формировать прокси-модели P_tree и P_table из получаемого индекса (0, 0, null) с учётом указанных структур данных модели M.

Цитировать
Совсем необязательно, что они будут вообще основаны на индексах. Индекс всего лишь указывает на то, какие данные реально нужны. Для P_table по индексу (0, 0, null)  нужно будет вернуть значение 0 параметра 0 из модели М, а для P_tree - имя параметра 0 из модели М.
Да, данные в модели M не обязательно должны быть основаны на индексах, но иначе как по индексам модель M не сможт определить, какие данные запрашивают представления V_tree и V_table. Т.е. в любом случае данные модели M придётся привязывать к индексам.




Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Racheengel от Январь 03, 2008, 15:35
Нет, я имею в виду, что индексы нужны для доступа представлений к прокси-моделям. А уже прокси-модели будут извлекать реальные данные из М в зависимости от того, какой был запрошен индекс, и передавать представлению в виде QVariant.


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 03, 2008, 16:15
Цитировать
А уже прокси-модели будут извлекать реальные данные из М в зависимости от того, какой
был запрошен индекс
Т.е. предлагаешь изменить (расширить) стандартный Qt-интерфейс для "общения" модели M с M_tree и M_table ?
Потому что если оставить тот же интерфейс (data, setData и др.), то придётся модель M привязывать к индексам...


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: ритт от Январь 03, 2008, 16:41
извините, конечно, что вмешиваюсь...
"расширять интерфейс" не нужно. нужно почитать/перечитать документацию по модель-вью и по прокси-моделям в частности. доступ к данным во ВСЕХ моделях кутэ осуществляется через некий индекс и категоризацию через роли. в худшем случае (в зависимости от конкретной задачи) придётся написать наследника прокси-модели и перегрузить некоторые методы, чтобы заточить под конкретные нужды.


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 03, 2008, 17:06
Цитировать
доступ к данным во ВСЕХ моделях кутэ осуществляется через некий индекс и категоризацию
через роли. в худшем случае (в зависимости от конкретной задачи) придётся написать наследника прокси-модели и перегрузить некоторые методы, чтобы заточить под конкретные нужды.
Поконкретнее, пожалуйста. Ближе к моей "задаче".
Предлагаешь обходиться дополнительными пользовательскими ролями ?
(схема "одна общая модель + 2 представления + дополнительные роли") ?


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: ритт от Январь 03, 2008, 20:48
да


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: ритт от Январь 03, 2008, 20:50
схема "одна общая модель + 1-2 прокси-модели + 2 представления + дополнительные роли


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 03, 2008, 21:48
Цитировать
1-2 прокси-модели
1 прокси-модель, я так понимаю, - когда общая модель данных M заточена под одно из представлений (например, наследник QAbstractTableModel, работающий с представлением напрямую, не нуждается в прокси P_table) ?
(в данном случае понадобится одна прокси-модель P_tree для работы с представлением V_tree)

Таким образом, пусть модель M будет заточена под табличное представление V_table. Т.е. V_table пусть будет основным представлением, V_tree - дополнительным, нуждающимся в прокси-модели P_tree:
- одна общая модель M,
- одна прокси-модель P_tree для работы общей модели M с представлением V_tree,
- 2 представления V_table и V_tree.
Общая модель M работает с основным представлением V_table напрямую, с дополнительным V_tree - через прокси P_tree. Тогда:
1. При запросе/установке данных представлением V_table общая модель M работает с обычными индексами (индекс таблицы), соответствующими ячейкам таблицы и со стандартными ролями Qt.
2. При запросе/установке данных представлением V_tree прокси-модель P_tree индекс (индекс дерева) оставляет неизменным, а роль изменяет на одну из пользовательских (дополнительных) ролей.

Так ?


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: ритт от Январь 03, 2008, 22:06
неверно, начиная с понимания, что модели заточены под вьюхи


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Tonal от Январь 03, 2008, 22:09
Я бы сделал так:
Реализовал слой логики, которая ничего о Qt может и не знать.
В этом слое есть твой хитрый контейнер, у которого есть методы для доступа как к таблице (имя атрибута, номер строки) и как к дереву (родитель, итератор по детям)...
Далее в логике представления рисуем модели, которые не имеют своих данных, а лишь обращаются к соответствующим методам доступа логики.


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 03, 2008, 23:06
Цитировать
неверно, начиная с понимания, что модели заточены под вьюхи
Именно. Тогда как архитектура/концепция "модель-представление" подразумевает абстракцию данных от представления...
xep, касательно моего предыдущего поста - такую реализацию ты имелл вииду или нет ?
Или не думал над конкретикой/деталями ?

Цитировать
Я бы сделал так:
Реализовал слой логики, которая ничего о Qt может и не знать.
В этом слое есть твой хитрый контейнер, у которого есть методы для доступа как к таблице (имя атрибута, номер строки) и как к дереву (родитель, итератор по детям)...
Далее в логике представления рисуем модели, которые не имеют своих данных, а лишь обращаются к соответствующим методам доступа логики.
Все предлагаемые варианты крутятся вокруг одной схемы: "объект с общими данными + 2 представления + 2 промежуточных объекта".
Т.е. имеются следующие варианты:
1. общая модель M + 1 или 2 прокси-модели P_tree и P_table + 2 представления
2. общая модель M + 2 модели M_tree и M_table + 2 представления
3. класс с общими данными + 2 модели M_tree и M_table + 2 представления
Пока предлагаются "голые" варианты. А хотелось бы услышать преимущества и особенности предлагаемых вариантов...
3-й вариант мне пока больше нравится...


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Racheengel от Январь 03, 2008, 23:36
Под вьюхи заточены прокси-модели, а не модель М. Модель М вообще не должна знать, как должны отображаться данные - она просто их содержит. А вот уже прокси-модели выгребают из М то, что надо показать во вьюхе.

Варианты 1 и 3 в принципе идентичны, если считать модель М этим самым классом с общими данными (он может быть вообще не Qt-моделью). Лично я бы так и делал - класс с данными и две прокси.


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: ритт от Январь 04, 2008, 00:49
нет, не такую...

модель, две прокси-модели, две вьюхи
примера ради: одна прокся оперирует данными роли ДисплэйРоле, другая прокся - данными роли ЭдитРоле. соответственно, каждый индекс будет возвращать на ДисплэйРоле, например, название значения, а на ЭдитРоле - само значение...или наоборот...или же вообще провести свои роли. в таком случае та же таблмодель тебе даст массив не двумерный, а размерностью [столбцов * строк * кол-во уникальных ролей]
плюсы: модели срать с прибором на то, кто её пользует, в частности, её же можно использовать в связке с любыми кутэшными вьюхами; не надо "расширять интерфейс"; перестраивать индексы придётся только при скрытии строк/столбцов (прокси-модели это умеют и без тебя)
в идеале можно обойтись только наследованием модели эМ, а проксям тупо указать роль, с которой забирать дату по сырцо-индексам
из минусов: если в дереве действительно древовидное отображение (больше 1-2 дочерних нод), таблмодель в качестве модели эМ плохо подходит, т.к. будет ассертить на валидные парентИндексы

из перечисленных у тебя выше трёх вариантов второй - вообще непотреб. выбирать надо из первого и третьего - всё зависит от структуры/сложности исходных данных


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Tonal от Январь 04, 2008, 09:43
2 Cyrax Насколько я понимаю, мой вариант это №3 по твоей классификации.
Мы успешно его применяем.
Главное достоинство подхода - разделение обязанностей.
Если по пунктам, то примерно так:
1) Класс логики содержит только логику, без ненужных ему привязок к GUI да и вообще к Qt.
Соответственно тестировать, да и вообще использовать его можно совершенно отдельно от остального приложения (у нас, например, такие классы используются в разных конверторах)
2) Классы Qt моделей довольно просты и содержат только логику конвертации и стандартное представление дерева или таблицы соответственно, ошибок в них меньше, а писать проще.


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 04, 2008, 09:47
Блин, ну чё за язык: "срать с прибором", "забирать дату по сырцо-индексам".
Что такое "срать" ?  Что такое "прибор" ?  Что такое "сырцо-индекс" ?
Ничего не понятно...

Цитировать
примера ради: одна прокся оперирует данными роли ДисплэйРоле, другая прокся - данными
роли ЭдитРоле. соответственно, каждый индекс будет возвращать на ДисплэйРоле, например, название значения, а на ЭдитРоле - само значение...или наоборот
Что значит "оперирует данными роли..." ?  Каждый из прокси передаёт/получает данные только для одной какой-то роли, а передачу/приём данных для других ролей игнорирует ?

Цитировать
или же вообще провести свои роли. в таком случае та же таблмодель тебе даст массив не двумерный, а размерностью [столбцов * строк * кол-во уникальных ролей]
xep, у меня имена параметров указаны в горизонтальном заголовке таблицы. В первой строке таблицы указаны первые значения для всех параметров, во второй - вторые и т.д. В каждой ячейке расположено n-е значение (n-строка) некоторого параметра. Посему дополнительные роли для ячеек мне не нужны и двумерного массива будет достаточно.
Да и вообще задача заключается в том, что для одного и того же индекса и для одной и той же роли должны возвращаться/передаваться разные данные для табличного представления и древовидного представления.
Я не понимаю твою мысль. Если к стандартным ролям DisplayRole, EditRole, ToolTipRole и т.д., соответствующих табличному представлению, ввести дополнительные роли TreeDisplayRole, TreeEditRole, TreeToolTipRole и т.д. для древовидного представления, то никаких промежуточных моделей или прокси-моделей не понадобится - общая модель M при получении запроса будет исходить из ролей: если роли DisplayRole, EditRole, ToolTipRole и т.д., то возвращаем данные для табличного представления, если TreeDisplayRole, TreeEditRole, TreeToolTipRole и т.д., - для древовидного. Да и вообще, такая схема ролей - чушь какая-то.

Цитировать
плюсы: модели срать с прибором на то, кто её пользует, в частности, её же можно использовать в связке с любыми кутэшными вьюхами; не надо "расширять интерфейс"; перестраивать индексы придётся только при скрытии строк/столбцов (прокси-модели это умеют и без тебя)
Т.е. ставку в отношении плюсов делаешь на функциональность прокси-моделей. Но у меня по-любому перед представлениями будут стоять прокси-модели для фильтрации и сортировки данных. В случае чего, можно будет "перестраивать индексы придётся ... при скрытии строк/столбцов" этими прокси-моделями. А запихивать их функциональность по сортировке/фильтрации в прокси-модели P_table и P_tree, работающих с общей моделью M - не совсем правильно (т.е. разделение по функциональности в силу сильного различия их функций).

Цитировать
в идеале можно обойтись только наследованием модели эМ, а проксям тупо указать роль, с которой забирать дату по сырцо-индексам
Что значит "тупо указать роль, с которой забирать дату по сырцо-индексам" ?

Цитировать
из минусов: если в дереве действительно древовидное отображение (больше 1-2 дочерних нод), таблмодель в качестве модели эМ плохо подходит, т.к. будет ассертить на валидные парентИндексы
Не понял мысль. Мне по-любому придётся создавать две структуры данных: для таблицы (например, массив) и для дерева. При этом если общая модель будет основана на таблице или дереве, то по-любому придётся анализировать индексы и роли и по ним определять, от кого пришёл запрос - от таблицы или от дерева.

Таким образом, пока не указано ни одно серьёзное преимущество в пользу прокси-моделей P_tree и P_table по отношению к обычным моделям M_tree и M_table. Кроме того, что в прокси-моделях по-любому придётся реализовывать чистые виртуальные методы (не зависимо от необходимости этих методов), "заточку" этих прокси-моделей под дерево и под таблицу придётся реализовывать самому, поскольку QAbstractProxyModel наследуется от QAbstractItemModel, тогда как QAbstractTableModel уже имеет такую "заточку" под таблицу (ну а для дерева - также QAbstractItemModel)...
Пока только минусы...


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Cyrax от Январь 04, 2008, 09:57
Tonal, твой вариант пока лидирует.
Поскольку везкие аргументы в пользу прокси пока отсутствуют, то представляется такая схема:
- общий класс с данными, работающий с БД и содержащий в себе 2 структуры данных: табличную (для хранения значений параметров, отображаемых табличным представлением) и древовидную (для хранения иерархии, имён и характеристик параметров, отображаемых в древовидном представлении)
- две модели M_table (наследник QAbstractTableModel) и M_tree (наследник QAbstractItemModel)
- две прокси-модели P_tree и P_table, сортирующие и фильтрующие данные
- два представления V_tree и V_table
Собственно, отличная реализация принципа "разделения обязанностей"...


Название: Re: QAbstractTableModel и два представления: нужен совет...
Отправлено: Tonal от Январь 04, 2008, 10:09
Общий класс у нас обычно строиться таким образом:
Хеш объектов по id-у (у каждого объекта есть своё уникальный id) + несколько дополнительных структур (индексов) для быстрой индексации по выбранным атрибутам, или навигации по дереву подчинённости (если есть).
Каждый такой индекс содержит указатели на объекты из основного хеша.