Russian Qt Forum
Сентябрь 30, 2024, 18:23 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Большая проблема, может быть даже философская  (Прочитано 11645 раз)
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
{
public:
    Storage() {
        tasks = DataBaseApi::SelectAllTasks();
        projects = DataBaseApi::SelectAllProjects();
    };
    //вернуть все задачи с id проекта
    QVector <Task> tasksAtProject(int id)
.........
private:
    QVector <Task> tasks;
    QVector <Project> projects;
};
Он читает из базы данных задачи и проекты, хранит их в себе и предоставляет эти данные моделям.
Моделей 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);
    Task tsk;
    storage_ptr->tasks.prepend(tsk);
    endInsertRows();
или
emit dataChanged();

я понимаю из за чего это я вставляю новую задачу в вектор с задачами и оповещаю об этом только модель и представление №1

Начал пробовать пойти по другому пути оставил модель №1 так как она содержит в себе все данные (и задачи и проекты) и создал свою производную от QAbstractProxyModel
(источник данных для которой модель #1)   взамен модели #2. Но тут столкнулся с двумя проблемами
1. Терзают сомнения правильный ли это путь
2. Немогу ф функции mapToSource преобразовать иерархическую модель #1 к модели списка (то есть пускать в прокси модель только задачи а проекты не пускать)

Какие бы советы хотелось бы услышать?
1. Правильным путем я сделал все или нет?
2. Какие есть еще варианты?

Вобщем если кто нибудь дочитал до конца большое спасибо, а если еще и посоветуете что нибудь полезное то вообще огрмнющщее.
Сам я в тупике....
« Последнее редактирование: Август 02, 2011, 13:19 от explorer.85 » Записан
explorer.85
Гость
« Ответ #1 : Август 02, 2011, 13:29 »

Чтобы совсем все было понятно приведу даже картинки
http://www.imagepost.ru/?v=projectsandtasks.png
слева QTreeView которую обеспечивает данными иерархическая модель №1
справа QTableView которую обеспечивает данными модель списка №2 (правильно говорить модель таблицы а не списка но так как у меня там 1 столбец то для меня она модель списка:)

Повторюсь, о проблемах
1. добавляем или редактируем задачу в модели №1 данные в модели №2 не обновляются
2. А правильно ли это что у меня 2 модели? или может быть лучше оставить модель #1 и сделать проксю к ней для списка задач, но тут проблема как преобразовать иерархическую модель в модель списка
« Последнее редактирование: Август 02, 2011, 13:36 от explorer.85 » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #2 : Август 02, 2011, 13:52 »

Мне кажется в данном случае должна быть одна модель. Та что с проектами, поскольку, как я понял, она содержит в себе данные и для задач.
Зачем тогда дублировать данные?  

Хотя, подозреваю, что я чёта не так понял..
« Последнее редактирование: Август 02, 2011, 13:57 от m_ax » Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
explorer.85
Гость
« Ответ #3 : Август 02, 2011, 14:04 »

да все правильно вторая модель дублирует данные с задачами, просто я не знаю как преобразовать данные из иерархической модели с проектами в модель списка, меня хватило только вот на это:


Код:
QModelIndex TaskProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
//возвращаем индексы всех задач из второго проекта  ( sourceModel()->index(1,0) )
return sourceModel()->index(proxyIndex.row(), 0,sourceModel()->index(1,0));
}
QModelIndex TaskProxyModel::index(int row, int column, const QModelIndex &parent) const
{
    return createIndex(row, column);
}

QModelIndex TaskProxyModel::parent(const QModelIndex &child) const
{
    return QModelIndex();
}
//вычисляем количество строк нижнего уровня (с задачами) в иерархической модели
int TaskProxyModel::rowCount(const QModelIndex &parent) const
{
    int rows = 0;
    for ( int i = 0; i < sourceModel()->rowCount(QModelIndex()); i++)
    {
        for ( int j = 0; j < sourceModel()->rowCount(sourceModel()->index(i,0)); j++)
        {
                rows++;
        }

    }
    return rows;
}

int TaskProxyModel::columnCount(const QModelIndex &parent) const
{
    return 1;
}
QVariant TaskProxyModel::data(const QModelIndex &index, int role) const
{
   return sourceModel()->data(mapToSource(index), role);
}

Вобщем я не знаю как функцией 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)
            const
{
    if (!hasIndex(row, column, parent))
            return QModelIndex();
    if (!parent.isValid())
    { // Первый уровень
            return createIndex(row, column, -1); // Родитель с индексом -1
    }else if (parent.internalId() == -1)
    {// Второй уровень
            return createIndex(row, column, parent.row()); // номер строки родителя.
    }
    return QModelIndex();
}


to m_ax: да нет вроде понял как надо тут вроде бы все просто две сущности проект и  задача. проекты верхний уровень иерархии задачи нижний. Просто отображать и редактировать их нужно двумя разными способами либо деревом проекты плюс задачи либо списком одних только задач.
« Последнее редактирование: Август 02, 2011, 14:25 от explorer.85 » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #4 : Август 02, 2011, 14:29 »

Тогда, как вариант, пишите свой класс (адаптер, так сказать), который содержит список проектов.
А указатель на этот класс передавайте в ваши модели. Можно в этом адапторе повесить сигнал dataChanged(параметры по-вкусу) который будет имитеть при изменении данных.
В конструкторе моделей соединяете сие сигнал со слотом где вызываете сигнал соответствующей модели dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight).
Примерно так..

 
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
explorer.85
Гость
« Ответ #5 : Август 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 представления


Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #6 : Август 02, 2011, 15:09 »

Адаптер предоставляет интерфейс для радактирования ваших данных, а так же для получения списка всех проектов, списка всех задач в конкретном проекте (это уж как вы организуете архитектуру адаптера).
В нём также можете объявить сигнал, срабатывающий при изменении данных, через интерфейс адаптера.
Создаёте две модели: одну от QAbstractListModel, вторую от QAbstractItemModel, обе будут внутри содержать указатель на адаптер.
В методах setData вызываете соответствующие функции адаптера. Никаких сигналов типа dataChanged здесь явно не вызываете. Это будет делать адаптер.
Просто в конструкторе соедините его сигнал (адаптора) с соответствующем слотом (в котором один раз будет происходить обновление модели) и всё.
У вас будут обновляться сразу две модели при изменении какой-либо одной.     

Вобщем, бог в помощь)
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
explorer.85
Гость
« Ответ #7 : Август 02, 2011, 15:12 »

еще раз спасибо очень понятно все обьяснили, буду делать, о том получилось или нет скоро напишу
Записан
explorer.85
Гость
« Ответ #8 : Август 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))
« Последнее редактирование: Август 02, 2011, 15:37 от explorer.85 » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #9 : Август 02, 2011, 15:58 »

Мне не совсем понятно:
Представление для задач - отображает все задачи или только задачи для одного проекта?
Если все задачи, то при добавлении задачи получается не однозначность? в смысле не определено к какому именно проекту будет добавлена задача.
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
explorer.85
Гость
« Ответ #10 : Август 02, 2011, 16:04 »

m_ax: и еще фатальную нестыковку обнаружил в методе с адаптером, а уж было обрадовался.
Например я редактирую данные в модели №1 вызываю метод адаптера Adapter::editTask(int task_id, QModelindex index)  - где task_id идентификатор по которому я найду задачу в своей векторе а
index - индекс строки модели которую мы редактируем.
В методе Adapter::editTask мы по логике должны испустить свой сигнал myDataChanged(QModelindex index) который в свою очередь присоединен к соответствующим сигналам моделей но если в ту модель которую мы редактируем придет правильный QModelindex, то в другую модель придет  QModelindex от чужой модели и соответственно будет ошибка.

Записан
UNION labs
Гость
« Ответ #11 : Август 02, 2011, 16:08 »

Здравствуйте.
А каким образом у вас создаются модели?
что-то вроде вот этого:

Код:
class Storage
{
public:
.........
    ProjectModel getProjectModel() const;
    TaskModel getTaskModel() const;
.........
};

Если да, то может просто следует уведомлять класс Storage когда обновляется одна из моделей, а он в свою очередь будет обновлять вторую?

P.S. Это еще про случай, когда не было адаптера...
« Последнее редактирование: Август 02, 2011, 16:14 от UNION labs » Записан
explorer.85
Гость
« Ответ #12 : Август 02, 2011, 16:15 »

m_ax: к вашему предыдущему посту, ну вообще идея такова пользователю доступны два представления (два окошка в интерфейсе) "проекты - treeview" и "задачи - listview". В первое он может добавить проект а так же к проекту добавить задачу. во второе может добавить только задачи но в нем так же отображаются и те задачи которые были добавлены в окошке "проекты"
Изначальное состояние системы QVector<Task> tasks - ничего не содержит, QVector<Project> projects содержить в себе один проект (пользователь не может его отредактировать и он его не видит но в него попадают все задачи которые он создает в представлении со списком, как бы такой системный проект "Без проекта")
Записан
explorer.85
Гость
« Ответ #13 : Август 02, 2011, 16:24 »

:UNION labs только наоборот класс Storage это и есть адаптор просто к моменту открытия темы я не знал такого умного слова да он у меня им и не был наверное. Да что говорить вот реализация
Код:
Storage::Storage() {
    tasks = DataBaseApi::SelectAllTasks();
    projects = DataBaseApi::SelectAllProjects();
    contacts = DataBaseApi::SelectAllContacts();

};

//вернуть все задачи с id проекта
QVector <Task> Storage::tasksAtProject(int id)
{
    QVector <Task> _tasks;
    for (int i =0; i<tasks.size(); i++)
    {
        if (tasks.at(i).ProjectID == id)
            _tasks.push_back(tasks.at(i));
    }

    return _tasks;

}
//отредактировать задачу в списке задач
void Storage::replaceTask(Task tsk)
{
    for (int i =0; i<tasks.size(); i++)
    {
        if (tasks.at(i).TaskID == tsk.TaskID)
            tasks.replace(i,tsk);
    }
}
//заменить проект в списке проектов
void Storage::replaceProject(Project prj)
{
    for (int i =0; i<projects.size(); i++)
    {
        if (projects.at(i).ProjectID == prj.ProjectID)
            projects.replace(i,prj);
    }
}

экземпляр сего создается в самом начале вычитыват из бд задачи и проекты в конструкторе, а дальше создаются модели в них передается указатель на данное чудо, и они с ним работают
только вот в чем вопрос то был правильна ли сама архитектура такая? И вот нестыковка вышла если в методе replaceTask вызвать сигнал для изменения моделей то непонятно как получит индекс изменяемых данных во второй модели?

:UNION как я вас понимаю все происходящее вы предлагаете в принципе тоже что и m_ax
« Последнее редактирование: Август 02, 2011, 16:26 от explorer.85 » Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #14 : Август 02, 2011, 16:32 »

m_ax: и еще фатальную нестыковку обнаружил в методе с адаптером, а уж было обрадовался.
Например я редактирую данные в модели №1 вызываю метод адаптера Adapter::editTask(int task_id, QModelindex index)  - где task_id идентификатор по которому я найду задачу в своей векторе а
index - индекс строки модели которую мы редактируем.
В методе Adapter::editTask мы по логике должны испустить свой сигнал myDataChanged(QModelindex index) который в свою очередь присоединен к соответствующим сигналам моделей но если в ту модель которую мы редактируем придет правильный QModelindex, то в другую модель придет  QModelindex от чужой модели и соответственно будет ошибка.


Да нет, адаптер это класс унаследованный от QObject (чтоб можно было сигналы прикручивать), что то вроде:
Код
C++ (Qt)
class Storage : public QObject
{
Q_OBJECT
public:
   Storage() {...}
   addProject(const Project &pr) {
       container.push_back(pt);
       emit dataChanged();
   }
   addTask(int idProject, const Task &task) {
      ...
       emit dataChanged();
   }
   const QVector<Project> &projects() const;
   const QVector<Task> &allTask() const;
   const QVector<Task> &task(int idProject) const;
   ну и так далее
private:
   QVector<Project> container;
};
 
Адаптер не должен знать ни о каких QModelIndex и всё что связано с моделями
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.117 секунд. Запросов: 22.