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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: QTreeView + собственная модель - динамическая подгрузка данных  (Прочитано 9902 раз)
Urvin
Гость
« : Февраль 14, 2010, 14:26 »

Здравствуйте!
Я создал модель дерева, и первоначально подгружаю в него объем данных, необходимый для отображения первого уровня иерархии. Для каждого из элементов я знаю, будут ли у него дочерние или нет.
Дочерние должны заполняться только при разворачивании этого элемента.
Если дочерние предполагаются, то для возможности раскрытия элемента и правильного визуального оформления к нему сразу добавляется единственный дочерний элемент-заглушка "Loading".

Модель таблицы собрана на основе примера из книги Бланшета и Саммерфильда. Данные подгружаю через списки, упрощенно это выглядит так:
Код
C++ (Qt)
void modelClients::setLists( treeNodeClient *node,
               const QList<int> &p_IDs,
               const QList<QString> &p_Names)
{
   // Сперва - очистка указанного
   qDeleteAll(node->children);
   node->children.clear();
 
   // Проход по всем добавляемым элементам
   for (int i = 0; i < p_IDs.count(); i++)
   {    
       // Создание нового элемента  дерева и добавление его к раскрываемому элементу
       node->children.append( new treeNodeClient(p_IDs[i], p_Names[i], node) );
 
       // Если выполняется некоторое условие, то добавляем к этому объекту дочерний элемент-заглушку
       if (*некое условие*)
           node->children.last()->children.append(new treeNodeClient(-1, tr("Loading..."), node->children.last() ));
   }
 
   // Обновление модели
   reset();
}

В модели всегда присутствует невидимый элемент - родитель всех, того же типа rootNode = treeNodeClient(-1, "root", 0).
У каждого есть список указателей на детей и указатель на родителя, он и передается в третьем параметре конструктора.

Для заполнения первого уровня иерархии в основной программе использую:
Код
C++ (Qt)
mdlClients = new modelClients(this);
ui->trvClients->setModel(mdlClients);
 
mdlClients->setLists(mdlClients->rootNode, mm_IDs, mm_Names);

Рассчитывал, что при разворачивании дерева можно будет воспользоваться подобным:
Код
C++ (Qt)
void wgtManager::on_trvClients_expanded(QModelIndex index)
{
   ...
 
   treeNodeClient *node = static_cast<treeNodeClient*>(index.internalPointer());
   mdlClients->setLists(node, mm_IDs, mm_Names);
}

Но как-то не срослось... Тут читаю про fetchMore, но никак не могу взять в толк, что с ним делать...

Подскажите, пожалуйста, как делают динамическую подгрузку данных в деревья?
Записан
asvil
Гость
« Ответ #1 : Февраль 15, 2010, 00:51 »

В модели переопределить метод hasChild(QModelIndex parent). Данный метод, грубо говоря, отвечает за отрисовку кнопки раскрытия элемента TreeView.
Затем переопределить rowCount(QModelIndex parent). Здесь вернуть количество загруженных дочерних элементов. Допустим при первом раскрытии элемента TreeView вернет 0.
Переопределить canFetchMore(QModelIndex parent). Здесь вернуть true, если rowCount(parent) < чем кол-во имеющихся где-то данных. Данный метод вызывается TreeView когда, она справшивает есть ли еще дочерние данные для данного индекса, например при скролле к последнему дочернему элементу.
Переопределить fetchMore(QModelIndex parent). Здесь с помощью beginInsertRow endInsertRow дозагрузить необходимые дочерние элементы
Записан
Urvin
Гость
« Ответ #2 : Февраль 15, 2010, 11:47 »

Т.е. источник данных теперь надо встаивать непосредственно в модель?
Спасибо, буду разбираться!
Записан
Urvin
Гость
« Ответ #3 : Февраль 17, 2010, 11:14 »

Друзья, помогите, пожалуйста!

Исправляю свою модель дерева, учитывая что источник данных должен располагаться внутри самой модели.
Исходник с оболочкой для теста положил во вложения к посту.

Имею такой заголовок для каждого узла:
Код
C++ (Qt)
class treeNodeClient
{
public:
   treeNodeClient(treeNodeClient *parent,
                  const int     &p_id,
                  const QString &p_name,
                  const QString &p_phone,
                  const QString &p_email,
                  const QString &p_referal,
                  const bool    &p_haschildren);
 
   ~treeNodeClient();
 
   // Набор данных для каждой из записей
   int     clientId;
   QString clientName;
   QString clientPhone;
   QString clientEmail;
   QString clientReferal;
   bool    clientHasChildren;    // Переменная, показывающая, может ли этот узел иметь детей
 
   int row() const;
 
   treeNodeClient *parentNode;
   QList<treeNodeClient*> childrenList;
};


Модель наследуется от QAbstractItemModel, в конструкторе создается корневой узел, который никогда не будет показан пользователям:
Код
C++ (Qt)
modelClients::modelClients(QObject *parent)
   :QAbstractItemModel(parent)
{
   // Создание первичной ветви модели, никогда не отображается
   rootNode = new treeNodeClient(0,-1,"root","","","",true);
}

Вот некоторые необходимые функции:
Код
C++ (Qt)
// Подсчет количества строк
int modelClients::rowCount(const QModelIndex &parent) const
{
   treeNodeClient *node;
   if (parent.isValid()) node = static_cast<treeNodeClient*>(parent.internalPointer());
   else node = rootNode;
 
   return node->childrenList.count();
}
 
bool modelClients::canFetchMore ( const QModelIndex & parent ) const
{
   if (!parent.isValid()) return false;
   treeNodeClient *node = static_cast<treeNodeClient*>(parent.internalPointer());
   return node->clientHasChildren && !node->childrenList.count();
}
 
void modelClients::fetchMore ( const QModelIndex & parent )
{
   if (!canFetchMore(parent)) return;
 
   treeNodeClient *node = static_cast<treeNodeClient*>(parent.internalPointer());
 
   beginInsertRows(parent,0,3);
   node->childrenList.append(new treeNodeClient(node, 11,"Subnode 1","","","",false));
   node->childrenList.append(new treeNodeClient(node, 12,"Subnode 2","","","",false));
   node->childrenList.append(new treeNodeClient(node, 13,"Subnode 3","","","",false));
   endInsertRows();
}
 
bool modelClients::hasChildren (const QModelIndex & parent) const
{
   if (!parent.isValid()) return true;
   treeNodeClient *node = static_cast<treeNodeClient*>(parent.internalPointer());
 
   return node->clientHasChildren;
}

В функции fetchMore пока заменяю реальный источник данных на его абстракцию, просто добавляя три нераскрываемых дочерних узла.
Первичное заполнение модели выполняю в функции make MainTree, где на данный момент вставляется три узла, два из которых раскрываемы:
Код
C++ (Qt)
void modelClients::makeMainTree()
{
   // Очистка корневого узла
   qDeleteAll(rootNode->childrenList);
   rootNode->childrenList.clear();
 
   rootNode->childrenList.append( new treeNodeClient(rootNode, 1,"First", "111-11-11","email 1","Referal 1",true) );
   rootNode->childrenList.append( new treeNodeClient(rootNode, 2,"Second","222-22-22","email 2","Referal 2",false) );
   rootNode->childrenList.append( new treeNodeClient(rootNode, 3,"Third", "333-33-33","email 3","Referal 3",true) );
 
   reset();
}


Есть проблемы с hasChildren (или где-то еще):
Если я опущу эту функцию, деревья становятся впринципе нераскрываемы.
Если ее первую строчку заменяю на if (!parent.isValid()) return false, то дерево никогда не будет показано.
Если же на if (!parent.isValid()) return true, то подузлы вставляются два раза при первом раскрытии В замешательстве



Цитировать
Переопределить canFetchMore(QModelIndex parent). Здесь вернуть true, если rowCount(parent) < чем кол-во имеющихся где-то данных.
А что если я не знаю заранее, сколько узлов я смогу добавить в fetchMore? Я знаю только то, что я имею возможность их добавить, и их число больше нуля.
Записан
asvil
Гость
« Ответ #4 : Февраль 17, 2010, 16:35 »

Цитировать
........beginInsertRows(parent,0,3);..........
последние два параметра это вставляемые строки "С" "ПО". Значит если с 0 по 3 надо вставить четыре строки. нулевую, первую, вторую, третью.
Цитировать
.....Я знаю только то, что я имею возможность их добавить, и их число больше нуля.
Если что-то определенно знаете, значит в canFetchRow нужно возвращать что-то определенное. Например true.
Цитировать
return node->clientHasChildren && !node->childrenList.count();
Не правильно. Правильный вариант в моем первом посте. return node->childrenList.count() < кол-ва имеющихся где-то там.
Вообще если набор данных небольшой нужно пользоваться QStandardItemModel;
Записан
Urvin
Гость
« Ответ #5 : Октябрь 18, 2010, 11:39 »

Продолжаем забавные задачи...
Подкиньте идею, пожалуйста, как бы организовать подгрузку содержимого ветвей, если данные берутся с QTCPSocket, т.е. в асинхронном режиме?
Записан
CroCIV
Гость
« Ответ #6 : Октябрь 19, 2010, 08:29 »

Эмм.. класс с Q_OBJECT вращающийся в отдельном потоке, генерирующий сигналы "пора подрисовать куст к ветке", слот в modelClients отлавливающий этот сигнал и рисующий пришедший куст к данной ветке... мб так??
условие задачи не совсем понятно
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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