Название: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 13:16 Всем привет! Вообще нечасто создаю темы на форумах стараюсь до всего дойти,
сам но тут проблема решения (правильного решения) которой не вижу уже дня 2. И так профессионалы, представляю проблему подробно (она тут в других ветках освещалась но толи я дурак, то ли не совсем с того ракурса в котором она возникла у меня). Начнем. Есть 2 сущности(класса или структуры как угодно) задача (Task) и проект (Project) Есть два списка QVector<Task> tasks - это список с задачами и QVector>Project> projects - это список с проектами причем у задачи есть поле Task::ProjectID и у проекта есть поле Project::ProjectID эти поля нужны так как проект может включать в себя задачи из списка задач. Есть функция QVector <Task> tasksAtProject(int id) //вернуть все задачи с id проекта Есть класс: Код: class Storage Моделей 2 штуки №1 Иерархическая модель проектов (class ProjectModel : public QAbstractItemModel) + QTreeView: проект1 задача1 задача2 проект2 задача3 №2 Модель списка задач (class TaskModel : public QAbstractItemModel) + QTableview: задача1 задача2 задача3 (для справки во всех моделях columncount ==1, все данные из структур Task и Project передаются с помощью ролей и отображаются самописными делегатами) Вот.... Еще к каждой модели привязана QSortFilterProxyModel для фильтрации данных. Теперь скажу что меня не устраивает: При добавлении или редактировании данных в модели с проектами или с задачами (например вставка строки(новой задачи)) не обновляется модель и представление с задачами, Код: beginInsertRows(QModelIndex(), ind, ind); я понимаю из за чего это я вставляю новую задачу в вектор с задачами и оповещаю об этом только модель и представление №1 Начал пробовать пойти по другому пути оставил модель №1 так как она содержит в себе все данные (и задачи и проекты) и создал свою производную от QAbstractProxyModel (источник данных для которой модель #1) взамен модели #2. Но тут столкнулся с двумя проблемами 1. Терзают сомнения правильный ли это путь 2. Немогу ф функции mapToSource преобразовать иерархическую модель #1 к модели списка (то есть пускать в прокси модель только задачи а проекты не пускать) Какие бы советы хотелось бы услышать? 1. Правильным путем я сделал все или нет? 2. Какие есть еще варианты? Вобщем если кто нибудь дочитал до конца большое спасибо, а если еще и посоветуете что нибудь полезное то вообще огрмнющщее. Сам я в тупике.... Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 13:29 Чтобы совсем все было понятно приведу даже картинки
http://www.imagepost.ru/?v=projectsandtasks.png слева QTreeView которую обеспечивает данными иерархическая модель №1 справа QTableView которую обеспечивает данными модель списка №2 (правильно говорить модель таблицы а не списка но так как у меня там 1 столбец то для меня она модель списка:) Повторюсь, о проблемах 1. добавляем или редактируем задачу в модели №1 данные в модели №2 не обновляются 2. А правильно ли это что у меня 2 модели? или может быть лучше оставить модель #1 и сделать проксю к ней для списка задач, но тут проблема как преобразовать иерархическую модель в модель списка Название: Re: Большая проблема, может быть даже философская Отправлено: m_ax от Август 02, 2011, 13:52 Мне кажется в данном случае должна быть одна модель. Та что с проектами, поскольку, как я понял, она содержит в себе данные и для задач.
Зачем тогда дублировать данные? Хотя, подозреваю, что я чёта не так понял.. Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 14:04 да все правильно вторая модель дублирует данные с задачами, просто я не знаю как преобразовать данные из иерархической модели с проектами в модель списка, меня хватило только вот на это:
Код: QModelIndex TaskProxyModel::mapToSource(const QModelIndex &proxyIndex) const Вобщем я не знаю как функцией mapToSource вернуть индексы всех задач, в результате работы этого кода у меня получается вот что: если модель проектов проект 1 задача 1 проект 2 задача 2 задача 3 задача 4 то на выходе из TaskProxyModel в представление QTableview получается задача 2 задача 3 задача 4 ПУСТАЯСТРОКА так как количество строк с задачами в исходной модели я вычисляю верно их 4 а самих индексов задач передаю только 3 штуки. Может я просто не силен в алгоритмах? Где копать? что то в функции mapToSource хитрое нужно дописать? или может как то по особому нужно сделать иерархическую модель №1. Она у меня сделана кстати по примерам из доков стандартно ,Или даже проще в ней жестко задано 2 уровня индексов, верхний проекты нижний задачи вкратце Код: QModelIndex ProjectModel::index(int row, int column, const QModelIndex &parent) to m_ax: да нет вроде понял как надо тут вроде бы все просто две сущности проект и задача. проекты верхний уровень иерархии задачи нижний. Просто отображать и редактировать их нужно двумя разными способами либо деревом проекты плюс задачи либо списком одних только задач. Название: Re: Большая проблема, может быть даже философская Отправлено: m_ax от Август 02, 2011, 14:29 Тогда, как вариант, пишите свой класс (адаптер, так сказать), который содержит список проектов.
А указатель на этот класс передавайте в ваши модели. Можно в этом адапторе повесить сигнал dataChanged(параметры по-вкусу) который будет имитеть при изменении данных. В конструкторе моделей соединяете сие сигнал со слотом где вызываете сигнал соответствующей модели dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight). Примерно так.. Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 14:53 m_ax: хороший совет спасибо.
Помоему вы меня правильно поняли. То есть что получается, оповещать представления по идеологии qt нужно в трех случаях (insertrows)вставка (removerows)удаление (datachanged)изменение сейчас у меня это делается в каждой из двух моделей особняком т.е. вставили данные в модель №1 вызвался №1::insertrows, вставили данные в модель №2 вызвался №2::insertrows. Соответственно отсюда и проблема представление №2 необновляется если мы изменили модель №1. Я попробую сделать так как вы посоветовали. Но все таки остаются вопросы 1 я наверное вынесу в отдельную тему 1. Как преобразовать через прокси иерархическую модель из двух уровней в модель списка которая содержит все элементы из нижнего уровня иерархической модели? 2. Какой путь все таки лучше мой текущий 2 модели и 2 представления или 1 модель 1 прокси модель и 2 представления Название: Re: Большая проблема, может быть даже философская Отправлено: m_ax от Август 02, 2011, 15:09 Адаптер предоставляет интерфейс для радактирования ваших данных, а так же для получения списка всех проектов, списка всех задач в конкретном проекте (это уж как вы организуете архитектуру адаптера).
В нём также можете объявить сигнал, срабатывающий при изменении данных, через интерфейс адаптера. Создаёте две модели: одну от QAbstractListModel, вторую от QAbstractItemModel, обе будут внутри содержать указатель на адаптер. В методах setData вызываете соответствующие функции адаптера. Никаких сигналов типа dataChanged здесь явно не вызываете. Это будет делать адаптер. Просто в конструкторе соедините его сигнал (адаптора) с соответствующем слотом (в котором один раз будет происходить обновление модели) и всё. У вас будут обновляться сразу две модели при изменении какой-либо одной. Вобщем, бог в помощь) Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 15:12 еще раз спасибо очень понятно все обьяснили, буду делать, о том получилось или нет скоро напишу
Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 15:33 И все таки еще кое-что спрошу.
1. Значит я выбрал правильную архитектуру для своей задачи, 2 модели + 2 представления + один адаптер?? Просто получается дублирование задач в этих двух моделях. Или это же опять вопрос философский есть несколько путей в том числе и этот и они все правильные?? 2. Я еще не сказал кое что, есть еще одна задача группировать задачи по какому либо полю. Например у задачи есть поле важность от 0 до 5. И мне нужно сгруппировать задачи по важности. Я думаю сделать это так: создать еще одну иерархическую модель + еще один QTreeView и подцепить их к нашему адаптору из адаптора буду брать задачи для нижнего уровня модели и список групп для верхнего уровня модели например QVector<int> groups {1,2,3,4,5} . Вы бы сделали так же? Таким образом получается еще одно дублирование списка задач и 3 модели + 3 представления + адаптер. (честно говоря меня до сего момента смущала во всем этом подходе гемморой синхронизации между моделями, но если воспользоваться вашим советом то можно делать хоть 10 моделей и представлений, а адаптер то останеться один и останется в них только вызывать функции адаптера ) Ну как сказать дублирование сам я эти списки загружаю в память один раз в класс адаптер. Что делают с ними эти 3 кутешные модели я не знаю, можно это считать дублированием или нет? Ну соответственно придерживаясь такого понимания модели представления, получается что сама НАСТОЯЩАЯ модель данных это наш класс адаптер а кутешные модели лишь средство доставки данных из нашей модели в кутешные стандарные представления?? m_ax: Ответьте еще разок пжалста успокойте мой разум, если на все вопросы ответите Да, то я буду считать что неплохо разобрался с mvc в qt)) Название: Re: Большая проблема, может быть даже философская Отправлено: m_ax от Август 02, 2011, 15:58 Мне не совсем понятно:
Представление для задач - отображает все задачи или только задачи для одного проекта? Если все задачи, то при добавлении задачи получается не однозначность? в смысле не определено к какому именно проекту будет добавлена задача. Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 16:04 m_ax: и еще фатальную нестыковку обнаружил в методе с адаптером, а уж было обрадовался.
Например я редактирую данные в модели №1 вызываю метод адаптера Adapter::editTask(int task_id, QModelindex index) - где task_id идентификатор по которому я найду задачу в своей векторе а index - индекс строки модели которую мы редактируем. В методе Adapter::editTask мы по логике должны испустить свой сигнал myDataChanged(QModelindex index) который в свою очередь присоединен к соответствующим сигналам моделей но если в ту модель которую мы редактируем придет правильный QModelindex, то в другую модель придет QModelindex от чужой модели и соответственно будет ошибка. Название: Re: Большая проблема, может быть даже философская Отправлено: UNION labs от Август 02, 2011, 16:08 Здравствуйте.
А каким образом у вас создаются модели? что-то вроде вот этого: Код: class Storage Если да, то может просто следует уведомлять класс Storage когда обновляется одна из моделей, а он в свою очередь будет обновлять вторую? P.S. Это еще про случай, когда не было адаптера... Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 16:15 m_ax: к вашему предыдущему посту, ну вообще идея такова пользователю доступны два представления (два окошка в интерфейсе) "проекты - treeview" и "задачи - listview". В первое он может добавить проект а так же к проекту добавить задачу. во второе может добавить только задачи но в нем так же отображаются и те задачи которые были добавлены в окошке "проекты"
Изначальное состояние системы QVector<Task> tasks - ничего не содержит, QVector<Project> projects содержить в себе один проект (пользователь не может его отредактировать и он его не видит но в него попадают все задачи которые он создает в представлении со списком, как бы такой системный проект "Без проекта") Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 16:24 :UNION labs только наоборот класс Storage это и есть адаптор просто к моменту открытия темы я не знал такого умного слова да он у меня им и не был наверное. Да что говорить вот реализация
Код: Storage::Storage() { экземпляр сего создается в самом начале вычитыват из бд задачи и проекты в конструкторе, а дальше создаются модели в них передается указатель на данное чудо, и они с ним работают только вот в чем вопрос то был правильна ли сама архитектура такая? И вот нестыковка вышла если в методе replaceTask вызвать сигнал для изменения моделей то непонятно как получит индекс изменяемых данных во второй модели? :UNION как я вас понимаю все происходящее вы предлагаете в принципе тоже что и m_ax Название: Re: Большая проблема, может быть даже философская Отправлено: m_ax от Август 02, 2011, 16:32 m_ax: и еще фатальную нестыковку обнаружил в методе с адаптером, а уж было обрадовался. Да нет, адаптер это класс унаследованный от QObject (чтоб можно было сигналы прикручивать), что то вроде:Например я редактирую данные в модели №1 вызываю метод адаптера Adapter::editTask(int task_id, QModelindex index) - где task_id идентификатор по которому я найду задачу в своей векторе а index - индекс строки модели которую мы редактируем. В методе Adapter::editTask мы по логике должны испустить свой сигнал myDataChanged(QModelindex index) который в свою очередь присоединен к соответствующим сигналам моделей но если в ту модель которую мы редактируем придет правильный QModelindex, то в другую модель придет QModelindex от чужой модели и соответственно будет ошибка. Код Адаптер не должен знать ни о каких QModelIndex и всё что связано с моделями Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 17:04 max да у меня так как вы написали сейчас все только вот смотрите
что я делаю с вашим сигналом dataChanged только у меня он TaskEdited Код: ProjectModel::ProjectModel(Storage *storage, QObject *parent) это для редактирования..... для вставки аналогичная схема только с begininsertrows endinsertrows..... То есть в модели сигналом emit dataChanged(this->index(0,0,QModelIndex()),this->index( rowCount(QModelIndex),0,QModelIndex())); приходится указывать не конкретный индекс для обновления а весь диапазон индексом модели я правильно вас понял? А это не то же самое что reset()? извиняюсь за может быть дилетантские вопросы конечно... Название: Re: Большая проблема, может быть даже философская Отправлено: UNION labs от Август 02, 2011, 17:49 Вот какие мысли по теме родились:
1. Изначально назрела правильная архитектура -> одна модель (класс storage) и два представления (дерево проектов+задач и список задач). 2. Реализовать такую архитектуру средствами model/view фреймворка от Qt сложно (если вообще возможно (избегая дублирования данных и роста сложности кода проекта)... затраты наверняка не окупятся). - Как вариант можно реализовать наследника ListView который будет показывать элементы второго уровня дерева 3. По-моему для таких ситуаций целесообразней использовать ItemWidget'ы чем ItemView'ы, т.е. используя классическую схему mvc/mvp а не от Qt. Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 18:09 UNION labs: наследника от ListView реализовать..... на такое не способен, и я думаю вряди ктонибудь такими делами на этом форуме вообще занимается (ну я имею ввиду свои вьюхи делает). Наверное создать свое представление из QAbstractItemView но это тоже сложно. А можно ссылку где тролли пишут про itemWidget. Я первым делом тоже о них подумал но помоему гибкости больше с mvc.
Цитировать 2. Реализовать такую архитектуру средствами model/view фреймворка от Qt сложно (если вообще возможно (избегая дублирования данных и роста сложности кода проекта)... затраты наверняка не окупятся). а что делать приходится и в целом даже местами нравится что получается, хотя не знаю я с mvc больше нигде кроме qt не работал, может гдето это круче реализовано.Вообще пессимистичный у вас пост получился >:( я все таки верю в Qt :) Название: Re: Большая проблема, может быть даже философская Отправлено: UNION labs от Август 02, 2011, 18:15 http://qt.nokia.com/learning/education/course-materials
20 слайд "Sometimes separating the model from the view is too complex" Название: Re: Большая проблема, может быть даже философская Отправлено: Igors от Август 02, 2011, 18:16 Никогда не пользовался MVC (не нужно) и помочь здесь не смогу. Но задачи такого плана встречаются в моей практике часто. По поводу "правильно ли 2 модели" - безусловно правильно.
Представим себе что нет MVC, базы - надо просто это сделать на контейнерах (напр STL). Стопудово задачи храним в отдельном контейнере (вот и модель). Хотя бы потому что возможен вариант когда задача не входит ни в 1 из проектов. О каком "дублировании" данных идет речь? Если я правильно понял, проект хранит ID задач - какое же это дублирование? Да, 2 структуры данных должны быть синхронизированы, но это возникает в любой задаче "выше травы". Так что смелее и не позволяйте себя запугать высокопарным фуфлом типа "правильнвя архитектура" :) Название: Re: Большая проблема, может быть даже философская Отправлено: UNION labs от Август 02, 2011, 18:18 Вообще пессимистичный у вас пост получился >:( я все таки верю в Qt :) :) я же не говорю, что это не реально в принципе реализовать, я говорю о целесообразности затрат (затраченное время/результат). Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 02, 2011, 18:45 Igors: спасибо, ага почти правильно 2 вектора в одном задачи в другом проекты у каждой задачи есть id проекта к которому она принадлежит, да это и есть моя собственная модель данных, ну плюс обертка из класса storage и методов для доступа к этим данным.Это все отлично. То что сам пишешь всегда понимаешь. А вот дальше когда настраиваешь взаимодействие своей модели Storage и Qt-шных я же свою модель Storage не могу напрямую к QTreeView например прикрутить, потому что у меня нет интерфейса для этого. А есть qabstractitemmodel котороый как раз и позволяет любую модель которую я нафантазирую типа моего Storage, прикрутить к уже реализованной в Qt вьюхе. И все хорошо и все мне нравится но вот есть некоторые моменты которые не понятны.
На данный момент сделал как посоветовыал max но осталась проблема Цитировать То есть в модели сигналом emit dataChanged(this->index(0,0,QModelIndex()),this->index( rowCount(QModelIndex),0,QModelIndex())); приходится указывать не конкретный индекс для обновления Как то это неправильно.. или я неправильно сам понял max-aа весь диапазон индексом модели я правильно вас понял? А это не то же самое что reset()? извиняюсь за может быть дилетантские вопросы конечно... UNION labs: А что делать фреймворк не выбирают :) в моем случае, а самому писать модели представления и контроллеры , вообще нереально. Название: Re: Большая проблема, может быть даже философская Отправлено: m_ax от Август 03, 2011, 11:24 Цитировать Как то это неправильно.. или я неправильно сам понял max-a Если данные меняются в адапторе минуя представление, то да предётся вызывать reset для модели. И похоже, что это придётся делать всегда, поскольку обе модели ничего друг о друге не знают. А как Storage у вас реализован? Название: Re: Большая проблема, может быть даже философская Отправлено: developer от Август 03, 2011, 13:24 Попробуй так.
Есть дерево. pid type name id 1 0 project SuperPr id 2 1 task Task1 id 3 1 task Task2 id 4 1 task Task3 id 5 0 project SuperPr1 id 6 5 task Task4 Ето была структура, дерево виглядит так id 1, pid 0 SuperPr id 2, pid 1 |__Task1 id 3, pid 1 |__Task2 id 4, pid 1 |__Task3 id 5, pid 0 SuperPr1 id 6, pid 5 |__Task4 Как видиш, мы работаем только с одной моделью. Каждый елемент имеет уникальный идентификатор - ID, поле PID нужно для дереваб есло оно 0 значит ето корень, если не ноль значит ето подкорень. Теперь тебе нужно только правильно переписать что-то вроде refresh(bool) метода. Если нам нужно дерево - тогда строим дерево по приведеной више структуре з учетом всех PID, если нужни только задачи, значить фильтруем по TYPE=task и выводим просто список Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 03, 2011, 13:47 developer: пока я пытаюсь осмыслить..... то есть я так понимаю реализовываь вы все это предлагаете средствами QAbstractItemModel. то есть у нас будет один экземпляр самописного подкласса QAbstractItemModel подключенный одновременно к двум представлениям treeview и tableview? Данные в нем будут организваны по предложенной вами схеме, и будет метод refresh котроый будет менять эту модель превращая ее из списка в дерево и обратно?
Но тогда придется подключать к этой модели вьюхи попеременно, потому что эта наша модель одновременно не может же находится в двух состояниях и обеспечивать две разные (QTreeView и QTAbleView) вьюхи данными. Интересна практическая часть приментиельно к моделям Qt Название: Re: Большая проблема, может быть даже философская Отправлено: developer от Август 03, 2011, 14:37 Не совсем.
Тебе будет нужно что-то вроде: 1. QVector<MyItem*> items; где MyItem - ето структура типа class MyItem{ char type;//1- Task, 2 - Project int id; int pid; QString name; } 2. Лучше использовать QTreeWidget. Поскольку он создаеться из QTreeWidgetItem'ов. А они подходят под задачу лучше всего. Поскольку есть возможность задавать парента QTreeWidgetItem ( QTreeWidget * parent, int type = Type ); 3. Теперь когда используеш refresh(bool) - если true тогда делаем дерево, иначе не делаем дерево, а просто добавляем итемы (как будто все они корневые). Название: Re: Большая проблема, может быть даже философская Отправлено: explorer.85 от Август 03, 2011, 19:01 ВНИМАНИЕ!!! Формулирую впрос еще раз БОЛЕЕ ПОНЯТНЫМ языком
ЗАДАДЧА. Исходные данные: Есть две сущности 1. Задача Код: struct Task Код: struct Project Эти сущности можно добавлять в списки QVector <Task> tasks; QVector <Project> projects; Причем при добавлении Задачи в tasks мы указываем ей ProjectID что указывает на ее принадлежность к проекту с таким же ProjectID при добавлении Проекта в projects мы указываем ему ProjectID. Таким образом получается иерархическая связь на верхнем уровне проекты на нижнем уровне задачи. У одного проекта может быть несколько задач. Задача одновременно может находится только в одном проекте. НУ вобщем я думаю понятно двухуровневое дерево получается проекты родители, задачи дети. ----------------------------------------------------------- Необходимо реализовать GUI для работы со списками этих сущностей. ----------------------------------------------------------- Реализовать интерфейс ввода необходимо следующим образом, в двух видах: 1. Двухуровневое дерево проектов и задач. Проект1 Задача 1 Задача 2 Проект2 Задача 3 Без проекта Задача 4 Здесь можно добавить проект, удалить проект, отредактировать проект Здесь можно добавить задачу к проекту, удалить задачу из проекта, отредактировать задачу. Проект "Без проекта" отредактировать и удалить нельзя (он создается приложением автоматически чтобы было куда добавлять задачи во втором виде) 2. Список задач. Задача 1 Задача 2 Задача 3 Задача 4 Здесь можно добавить задачу, отредактировать задачу, удалить задачу При создании задачи в этом виде она попадает в проект "Без проекта" Вопрос как это сделать с помощью mvc фреймворка qt |