Russian Qt Forum

Qt => Model-View (MV) => Тема начата: mugabe от Июнь 03, 2009, 09:49



Название: [РЕШЕНО] QTreeView с загрузкой данных при разворачи
Отправлено: mugabe от Июнь 03, 2009, 09:49
Задача:
Есть огромная древовидная структура данных, загружаемая с удаленного сервера. Сразу грузить всю структуру - долго, да и не нужно.
Нужно что бы сначала загружался первый уровень, а при разворачивании загружались только вложенные элементы.

Пытался реализовать такое на базе примера Simple Tree Model (http://doc.trolltech.com/4.5/itemviews-simpletreemodel.html).
Пока что ничего не вышло.

Сначала пробовал загружать данные при первом обращении к TreeItem::data(...), child(...), childCount(...) и т.д.
И в принципе даже работало, но возникала проблема во время отрисовке, когда вызывался один из вышеперечисленных методов, пока предыдущий вызов еще не успел завершить свою работу. Тут жабу подкинул класс-обертка для QHttp, позволяющая делать синхронные запросы - он вызывает QEventLoop::exec() до тех пор пока не получит ответ, что позволяет выполнить paintEvent() до того, как данные получены :(

Это подтолкнуло поступить более правильно (на мой взгляд):
1. При разворачивании элемента он разворачивается, но показывает только один элемент с текстом "Загрузка...". При этом действия пользователя не блокируются и он может делать что-то еще, пока грузятся данные.
2. Когда данные будут загружены, этот элемент удаляется и добавляются настоящие элементы.

Но с реализацией этого возникли трудоности. Возможно, из за недостаточного понимания Qt-шной модели MVC, в частности индексов.

Сейчас у меня происходит следующее:
1. При первом вызове TreeItem::data(...), child(...), childCount(...) возвращается один элемент с надписью "Загрузка...".
2. В это время через обычный QHttp делается ассинхронный запрос к серверу. Сигнал QHttp::finished(...) привязывается к слоту моей модели requestFinished(...).

3. При завершении запроса срабатывает слот requestFinished(...), анализируются данные пришедшие от сервера и корректируются данные в необходимом TreeItem.

И вот тут у меня загвоздка, как после этого дать понять TreeView, что модель обновилась? На сколько я понимаю нужно вызвать QAbstractItemModel::insertRow(int row, const QModelIndex &parent).
Но как получить индекс QModelIndex &parent, если у меня есть только ссылка на TreeItem этого parent-а?


Название: Re: QTreeView с загрузкой данных при разворачивании элемента
Отправлено: EhTemka от Июнь 03, 2009, 14:49
TreeItem - это QTreeWidgetItem? Если да , то есть функция

Код
C++ (Qt)
QModelIndex QTreeWidget::indexFromItem ( QTreeWidgetItem * item, int column = 0 ) const   [protected]


Название: Re: QTreeView с загрузкой данных при разворачивании элемента
Отправлено: mugabe от Июнь 04, 2009, 04:16
TreeItem - это QTreeWidgetItem?
Нет, это класс из Simple Tree Model (http://doc.trolltech.com/4.5/itemviews-simpletreemodel.html).
Я работаю с QTreeView, а не QTreeWidget.


Название: Re: QTreeView с загрузкой данных при разворачивании элемента
Отправлено: drsm от Июнь 04, 2009, 05:33
я тупо храню QHash<RequestId, QModelIndex>,
а вызывать при вставке нужно не insertRow, а begin/endInsertRows().


Название: Re: QTreeView с загрузкой данных при разворачивании n
Отправлено: mugabe от Июнь 04, 2009, 06:19
я тупо храню QHash<RequestId, QModelIndex>,
а вызывать при вставке нужно не insertRow, а begin/endInsertRows().

Ну до вызова insertRow еще не дошло, т.к. индекс я не получил, так что пока не суть :)
Я так понимаю, что QModelIndex можно хранить и в самом элементе, но если честно, плохо понимаю что происходит в QAbstractItemModel::index(...)

Вот кусок из Simple Tree Model:
Код
C++ (Qt)
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
            const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();
 
    TreeItem *parentItem;
 
    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());
 
    TreeItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

Что здесь нужно сделать, что бы хранить этот индекс в элементе?

Код
C++ (Qt)
QModelIndex TreeModel::index(int row, int column, const QModelIndex &parent)
            const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();
 
    TreeItem *parentItem;
 
    if (!parent.isValid())
        parentItem = rootItem;
    else
        parentItem = static_cast<TreeItem*>(parent.internalPointer());
 
    TreeItem *childItem = parentItem->child(row);
    if (childItem) {
        // comment here
        childItem->setIndex(   createIndex(row, column, childItem)   );
        return childItem->index();
    }
    else
        return QModelIndex();
}
 
QModelIndex  TreeItem::index() const {
 return m_index;
}
void TreeItem::setIndex(const QModelIndex &index) {
 m_index = index;
}
 

комменты на русском как-то странно обрабатываются, на месте "comment here" хотел написать:
         // тут что-то вроде такого... сохраняем индекс в итеме и возвращаем его...
         // и может надо работать с указателями на индекс воизбежание копирования?


Так правильно?


Название: Re: QTreeView с загрузкой данных при разворачивании элемента
Отправлено: Tonal от Июнь 04, 2009, 06:38
Посмотри в сторону canFetchMore и fetchMore.


Название: Re: QTreeView с загрузкой данных при разворачивании элемента
Отправлено: mugabe от Июнь 04, 2009, 06:48
Смотрел - он не совсем подходит для такой задачи.
Да и проблемы у него те же.



В общем попробовал сделать так, как предположил выше - работает! :)