Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: vovan1982 от Июня 18, 2012, 14:22



Название: Окно загрузки данных
Отправлено: vovan1982 от Июня 18, 2012, 14:22
Здравствуйте.

Интересует следующий вопрос:

Есть программа которая вытягивает данные из mysql, пока данных в базе было мало всё было ок, но когда объём данных вырос то в процессе получения данных программа подвисает.

Подскажите пожалуйста как сделать чтоб на момент подвисания программы появлялось окно с надписью, к примеру "Ждите идёт загрузка...", а после отвисания исчезало.

Ни как не могу понять с какой стороны подступится к данной проблеме.

Спасибо.


Название: Re: Окно загрузки данных
Отправлено: Serr500 от Июня 18, 2012, 14:36
Вытягивать данные в отдельном потоке. По окончании процесса генерировать сигнал. В GUI сделать таймер. Если после запуска запроса прошла секунда (100мс, 3 секунды, и т.п. - выбрать по вкусу. Можно и сразу, но при коротких запросах это будет некрасиво.), вывести окошко "Ждите...". Получив сигнал, похерить окошко.


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 18, 2012, 15:38
А без второго потока ни как?
а то я с потоками не работал ни когда.


Название: Re: Окно загрузки данных
Отправлено: Serr500 от Июня 18, 2012, 15:47
Насколько мне известно, никак.


Название: Re: Окно загрузки данных
Отправлено: trot от Июня 18, 2012, 15:50
Цитировать
А без второго потока ни как?
Никак.
Еще надо учитывать, такую особенность, что пользователь может своими действиями не дождавшись одного результата запроса, запустить следующий запрос и такми образом выстроить очередь запросов, ну так далее. Т.е. необходимо продумать политику реакции итерфейса на действия пользователя.


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 18, 2012, 15:58
...
Есть программа которая вытягивает данные из mysql, пока данных в базе было мало всё было ок, но когда объём данных вырос то в процессе получения данных программа подвисает.
...
Может быть стоит подумать над оптимизацией кода взаимодействия программы с БД? Часто совершенно не имеет смысла переливать всю информацию из таблиц в память. Или в Вашем случае нужно именно так?


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 18, 2012, 16:44
...
Есть программа которая вытягивает данные из mysql, пока данных в базе было мало всё было ок, но когда объём данных вырос то в процессе получения данных программа подвисает.
...
Может быть стоит подумать над оптимизацией кода взаимодействия программы с БД? Часто совершенно не имеет смысла переливать всю информацию из таблиц в память. Или в Вашем случае нужно именно так?

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


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 18, 2012, 16:46
Спасибо всем за ответы, буду учить потоки :)


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 18, 2012, 17:21
Мне кажется, что вьюха всё же должна более плотно работать с моделью. Как-то в пустую тратятся ресурсы. Действительно, ну зачем гнать из модели во вьюху 1000 строк, если максимум штук 100 будет выведено. Если пользователь двинет ползунок прокрутки, значит только тогда и подгружать следующую порцию данных. А может так статься, что ползунок сместится на незначительное расстояние, что будет требовать ещё меньшего количества подгружаемых данных. В итоге - минимум тормозов, если таковые вообще возникнут.

А если ещё взглянуть в сторону веба, то там подобные проблемы решаются при помощи пагинации, часто вкупе с аяксом. Другими словами, выглядит более рационально. В чём я не прав?


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 18, 2012, 17:31
Мне кажется, что вьюха всё же должна более плотно работать с моделью. Как-то в пустую тратятся ресурсы. Действительно, ну зачем гнать из модели во вьюху 1000 строк, если максимум штук 100 будет выведено. Если пользователь двинет ползунок прокрутки, значит только тогда и подгружать следующую порцию данных. А может так статься, что ползунок сместится на незначительное расстояние, что будет требовать ещё меньшего количества подгружаемых данных. В итоге - минимум тормозов, если таковые вообще возникнут.

На данном этапе тормоза возникают именно при выборке данных из БД, а на счёт постепенной загрузки данных во вьюху, это как?
У меня есть модель, я выбираю данные из БД, потом эти данные выстраиваю в дерево и как потом мне передавать во вьюху не все данные, я что то не понимаю, можно пример?


Название: Re: Окно загрузки данных
Отправлено: mutineer от Июня 18, 2012, 17:36
У меня есть модель, я выбираю данные из БД, потом эти данные выстраиваю в дерево и как потом мне передавать во вьюху не все данные, я что то не понимаю, можно пример?

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


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 18, 2012, 17:54
На данном этапе тормоза возникают именно при выборке данных из БД, а на счёт постепенной загрузки данных во вьюху, это как?
Если данных очень много, то вполне ожидаемо, что будут тормоза. Под это дело выделяется память. И помимо собственно самих табличных данных, выделяется память и под всякого рода "служебную" инфу. Всё это безусловно нужно, но нужно ли всё сразу, вот в чём вопрос.

У меня есть модель, я выбираю данные из БД, потом эти данные выстраиваю в дерево и как потом мне передавать во вьюху не все данные, я что то не понимаю, можно пример?
Боюсь, что решения у меня для Вас нет. Здесь нужен гуру кьютишного Model/View (я больше в другой области). Просто когда-то давно, ещё на первых версиях четвёрки (Qt4) я делал фронтэнд к менеджеру пакетов для Linux. Тоже был вьювер, тоже была модель, тоже куча позиций, измеряемых десятками тысяч. Единственное, в роли БД выступало API пакетного менеджера, но суть задачи сводилась к тому же, что и у Вас. Стоило мне всех их загружать разом, так сразу возникали жуткие GUI-тормоза. Поэтому я сделал решение, логику которого описал в посте выше, т.е. частичную подгрузку. И всё стало шикарно работать. Во время подгрузки новых позиций, если пользователь шнырял ползунком прокрутки туда-сюда, нужные итемы всегда довольно быстро подгружались.

Как я это сделал - не помню, да и к сожалению код у меня не сохранился. Вроде там была реализована плотная связка между моделью и вьювером, то есть каждый знал о состоянии каждого. Конечно, это вроде как нарушение принципа Qt MV, но такое частное решение позволило обойтись без потоков и работало весьма сносно.


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 18, 2012, 17:55
Вьюха ведь у модели спрашивает не все данные разом, а только попадающие на экран
...
Тем более тогда непонятно, почему возникла эта проблема


Название: Re: Окно загрузки данных
Отправлено: mutineer от Июня 18, 2012, 18:04
Вьюха ведь у модели спрашивает не все данные разом, а только попадающие на экран
...
Тем более тогда непонятно, почему возникла эта проблема

Конечно непонятно - кода ведь нет


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 18, 2012, 20:02
Вьюха ведь у модели спрашивает не все данные разом, а только попадающие на экран
...
Тем более тогда непонятно, почему возникла эта проблема

Конечно непонятно - кода ведь нет

Исходник модели

.h - http://gitorious.org/workerplace/workerplace/blobs/master/headers/devicemodel.h
.cpp - http://gitorious.org/workerplace/workerplace/blobs/master/source/devicemodel.cpp

Исходник класса дерева

.h - http://gitorious.org/workerplace/workerplace/blobs/master/headers/treeitem.h
.cpp - http://gitorious.org/workerplace/workerplace/blobs/master/source/treeitem.cpp

Исходник класса управления модели

.h - http://gitorious.org/workerplace/workerplace/blobs/master/headers/devicemodelcontrol.h
.cpp - http://gitorious.org/workerplace/workerplace/blobs/master/source/devicemodelcontrol.cpp

Ну и виджет который использует модель

.h - http://gitorious.org/workerplace/workerplace/blobs/master/headers/device.h
.cpp - http://gitorious.org/workerplace/workerplace/blobs/master/source/device.cpp


На данном этапе тормоза возникают именно при выборке данных из БД, а на счёт постепенной загрузки данных во вьюху, это как?
Если данных очень много, то вполне ожидаемо, что будут тормоза. Под это дело выделяется память. И помимо собственно самих табличных данных, выделяется память и под всякого рода "служебную" инфу. Всё это безусловно нужно, но нужно ли всё сразу, вот в чём вопрос.

У меня есть модель, я выбираю данные из БД, потом эти данные выстраиваю в дерево и как потом мне передавать во вьюху не все данные, я что то не понимаю, можно пример?
Боюсь, что решения у меня для Вас нет. Здесь нужен гуру кьютишного Model/View (я больше в другой области). Просто когда-то давно, ещё на первых версиях четвёрки (Qt4) я делал фронтэнд к менеджеру пакетов для Linux. Тоже был вьювер, тоже была модель, тоже куча позиций, измеряемых десятками тысяч. Единственное, в роли БД выступало API пакетного менеджера, но суть задачи сводилась к тому же, что и у Вас. Стоило мне всех их загружать разом, так сразу возникали жуткие GUI-тормоза. Поэтому я сделал решение, логику которого описал в посте выше, т.е. частичную подгрузку. И всё стало шикарно работать. Во время подгрузки новых позиций, если пользователь шнырял ползунком прокрутки туда-сюда, нужные итемы всегда довольно быстро подгружались.

Как я это сделал - не помню, да и к сожалению код у меня не сохранился. Вроде там была реализована плотная связка между моделью и вьювером, то есть каждый знал о состоянии каждого. Конечно, это вроде как нарушение принципа Qt MV, но такое частное решение позволило обойтись без потоков и работало весьма сносно.

Жаль что у вас не осталось исходников было бы интересно взлянуть на реализацию, думаю не только мне :).


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 18, 2012, 20:07
...
Жаль что у вас не осталось исходников было бы интересно взлянуть на реализацию, думаю не только мне :).
Да не, раз говорят, что вьюха забирает с модели только нужные строки, значит смысл мой пример теряет. Может быть тогда это не было реализовано, а только спустя время. Я не помню, да и не суть. Значит Вам нужно лишь что-то подправить в модели, чтобы было тип-топ :)


Название: Re: Окно загрузки данных
Отправлено: mutineer от Июня 18, 2012, 20:13
У меня вытекли глаза от этой простыни... Сегодня читать точно не буду, может завтра


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 18, 2012, 20:16
У меня вытекли глаза от этой простыни... Сегодня читать точно не буду, может завтра

:)

Да не вопрос, буду рад совету, заранее прошу прощения за то что коментов в коде нет, это мой первый проект и пишу его сам, собственно процесс обучения и написания идёт параллельно.


Название: Re: Окно загрузки данных
Отправлено: Bepec от Июня 18, 2012, 20:23
Поток нужен. Обязательно. Собственно что можно посоветовать.
Простые варианты:
1) при перемещении ползунка отсчитывать позицию. Потом запрашивать лишь видимое число элементов. Т.е. максимум для 24 дюймового монитора - где то от 500 до 1,5к строк.
2) использовать Q*SqlModel. Насколько я помню, она прекрасно оптимизирована и у неё отсутствует описанный недостаток.
3) ну хз. Самый кривой способ - кешировать сразу все записи. В самом начале. Т.е. делаем окошко ожидания и загружаем сразу всё. Сколько-то там времени займёт, зато потом будет летать, если хватит оперативки :D

PS и маленький совет. Можно вместо окошка ожидания, выводить гифку загрузки аля Ютуб посередине Q*View. Гораздо понятнее пользователю и читать ненадо :D


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 18, 2012, 20:36
Поток нужен. Обязательно. Собственно что можно посоветовать.
Простые варианты:
1) при перемещении ползунка отсчитывать позицию. Потом запрашивать лишь видимое число элементов. Т.е. максимум для 24 дюймового монитора - где то от 500 до 1,5к строк.
2) использовать Q*SqlModel. Насколько я помню, она прекрасно оптимизирована и у неё отсутствует описанный недостаток.
3) ну хз. Самый кривой способ - кешировать сразу все записи. В самом начале. Т.е. делаем окошко ожидания и загружаем сразу всё. Сколько-то там времени займёт, зато потом будет летать, если хватит оперативки :D

PS и маленький совет. Можно вместо окошка ожидания, выводить гифку загрузки аля Ютуб посередине Q*View. Гораздо понятнее пользователю и читать ненадо :D

Спасибо :), буду учить потоки.


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 18, 2012, 20:48
Верес, Вы порвали в клочья последнюю надежду


Название: Re: Окно загрузки данных
Отправлено: Bepec от Июня 18, 2012, 20:50
Работа у меня такая. Там снайпер, тут эльф, здесь программист и разрушитель надежд, что всё так просто :D


Название: Re: Окно загрузки данных
Отправлено: Syveren от Июня 19, 2012, 10:35
А что если хранить только ключи в модели. А сам запрос на получение данных делать в методе QAbstractModel::data().
Я дулал так для списка - работало на ура.
Данный способ увеличил быстродействие загрузки с 2 минут, до 2 секунд
И ещё зависит от Субд подойдёт ли вам этот способ или нет.
Код
C++ (Qt)
// Первоночально при помощи запроса заполняем только idList данными первичного ключа.
//mutable QCache<const QString,CashData> cashList;
// cashData вызываеться при изменение положения вертикального скролл бара у QlistView.
void MyListModel::cashData(int from, int to)
{
   // Сохраняем в кеше информацию
   // from и to это номера строк в поле видимости
   // Загружаем информацию для строк с from по to
   // и сохраняем её в cashList;
   // (загрузка сразу нескольких значений из БД происходит быстрее, чем выгрузка их по отдельности)
   QStringList list;
   // Не имеет смысла запоминать больше максимального ограничения на cashList
   if(to - from > cashList.maxCost())
       to = from  + cashList.maxCost();
   for(int row = from; row <= to ;++row)
   {
       const QString* id  = & idList.at(row);
       if(!cashList.contains(*id))
           list<<"'" + *id + "'";
   }
   query.exec("SELECT id, ... , WHERE id IN (" + list.join(",") + ")") +   QString(" LIMIT %1 ").arg(to - from));
   while(query.next())
       cashList.insert(query.value(0).toString(),new CashData(query.value(1).toString(),query.value(2).toBool()));
}
 
QVariant MyListModel::data(const QModelIndex& index, int role) const
{
   switch(role)
   {
   case Qt::SizeHintRole:return size_hint;
   case Qt::DisplayRole:
   case Qt::ToolTipRole:
   {
       const QString* id  = & idList.at(row);
       if(id->isNull())
           return QVariant(tr("загрузка..."));
       if(cashList.contains(*id))//если в кеше уже есть текст для данной строчки выводим его
           return cashList.object(*id)->display_text;
       query.exec("...запрос... WHERE id ='" + *id + "' LIMIT 1");
       query.first();
       QString display_text = query.value(0).toString() + query.value(1).toString() +...;
       cashList.insert(*id,new CashData(display_text, query.value(1).toBool()));
       return  display_text;
   }
   case Qt::DecorationRole:
       return  QIcon(":icon");
   case  Qt::UserRole: return idList.at(row);
   default: return QVariant();
   }
}
 


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 19, 2012, 10:42
А что если хранить только ключи в модели. А сам запрос на получение данных делать в методе QAbstractModel::data().
Я дулал так для списка - работало на ура.
С одной стороны - хорошо, а с другой - будет множество мелких запросов к БД. Подход как у ORM.


Название: Re: Окно загрузки данных
Отправлено: Syveren от Июня 19, 2012, 10:59
 
Цитировать
С одной стороны - хорошо, а с другой - будет множество мелких запросов к БД. Подход как у ORM
Всё зависит от использоваемого драйвера SQL. Для драйвера QPSQL данный подход не принёс ничего ценного (он и без этих наворотов работал быстро). Но при использовании QODBC было колосальное ускорение.


Название: Re: Окно загрузки данных
Отправлено: Syveren от Июня 19, 2012, 11:03
Я и начал использовать данный подход, так как из-за смены СУБД, загрука стала вдруг такой медленной, что ни в какие ворота не лезло.


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 19, 2012, 11:21
Вот мне это всё непонятно. Сразу скажу, что с БД в Qt практически не занимался. Можно сказать, что опыт на нуле по этой теме. Работал всё больше в вебе, да и только с мускулом (MySQL).

По логике, если сравнить два подхода, то в первом:
А. парсинг мускулом запроса - O(1);
Б. возврат данных большим куском - O(n);

... а во втором появляется зависимость от кол-ва записей:
А. парсинг мускулом запроса - O(1);
Б. возврат данных маленьким куском - O(1).

При этом, второй подход выполняется n раз.

Дополнительно: в первом варианте можно использовать транзакцию, которая гарантирует существенное снижение длительности выполнения пункта Б.

Вопрос: что будет работать быстрее?


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 19, 2012, 13:11
А что если хранить только ключи в модели. А сам запрос на получение данных делать в методе QAbstractModel::data().
Я дулал так для списка - работало на ура.
Данный способ увеличил быстродействие загрузки с 2 минут, до 2 секунд
И ещё зависит от Субд подойдёт ли вам этот способ или нет.

Интересный вариант, надо будет попробовать.

Дополнительно: в первом варианте можно использовать транзакцию, которая гарантирует существенное снижение длительности выполнения пункта Б.

Вопрос: что будет работать быстрее?

Ни попробуешь не узнаешь :).

А на счёт транзакции можно подробней, я просто с ними не работал, каким образом транзакция уменьшает время запроса на выборку, я всегда думал что транзакция нужна для отката изменений и блокировки таблицы на время работы с ней.


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 19, 2012, 14:37
...
А на счёт транзакции можно подробней, я просто с ними не работал, каким образом транзакция уменьшает время запроса на выборку, я всегда думал что транзакция нужна для отката изменений и блокировки таблицы на время работы с ней.
За счёт одноразового обновления индексов у таблицы, одноразовых блокировки и предоставления доступа.


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 19, 2012, 15:17
А как mysql организовать транзакцию для выборки данных?
На сколько я знаю для обновления таблицы используется BEGIN; для начала транзакции и COMMIT; для внесения сделанных изменений или ROLLBACK для их отката.


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 19, 2012, 15:32
Для начала скажите, какой из движков Вы используете у таблицы. Если MyISAM, то он транзакции как таковые не поддерживает. У него каждая операция - есть отдельная транзакция. Если InnoDB, то транзакции имеет смысл использовать однозначно.


Название: Re: Окно загрузки данных
Отправлено: Syveren от Июня 19, 2012, 16:35
QSqlDatabase db;
db.driver ()->hasFeature (QSqlDriver::Transactions); - поддерживаются ли транзакции
Если да, то удобно использовать
db.transaction(); db.rollback();db.commit();


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 19, 2012, 16:46
Для начала скажите, какой из движков Вы используете у таблицы. Если MyISAM, то он транзакции как таковые не поддерживает. У него каждая операция - есть отдельная транзакция. Если InnoDB, то транзакции имеет смысл использовать однозначно.

использую InnoDB

QSqlDatabase db;
db.driver ()->hasFeature (QSqlDriver::Transactions); - поддерживаются ли транзакции
Если да, то удобно использовать
db.transaction(); db.rollback();db.commit();

насколько я знаю для mysql транзакции поддерживаются


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 19, 2012, 17:00
использую InnoDB
Зачастую правильное решение.

насколько я знаю для mysql транзакции поддерживаются
Да, но не всеми движками. В мускуле их много. InnoDB как раз поддерживает. И для него транзакции нужны, иначе производительность отличается в худшую сторону от того же MyISAM.


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 20, 2012, 09:41
Попробовал сделать транзакцию на выборку данных,
сначала выполнил два запроса в самой mysql
1. SELECT * FROM table;
2. BEGIN; SELECT * FROM table; COMMIT;

2-й реально быстрей отрабатывает, но при вставке второго запроса в query.exec(), содержимое query оказывается пустым, хотя в самой mysql всё ок.

В чём может быть проблема, или я что-то делаю не так?


Название: Re: Окно загрузки данных
Отправлено: alexis031182 от Июня 20, 2012, 09:53
Может быть надо запрос этот разбить на три query.exec()


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 20, 2012, 13:20
на 2-а :)

BEGIN и всё остальное


Название: Re: Окно загрузки данных
Отправлено: vovan1982 от Июня 20, 2012, 13:28
спасибо всем за помощь, после оптимизации запросов и использования транзакций удалось в разы сократить время загрузки, теперь вместо 10 сек. загрузка идёт 3 сек.

Без второго потока действительно не обойтись, попытался использовать qApp->processEvents(); но использование gif анимации в таком случае не возможно, анимация начинает работать только после полной загрузки виджета, а если у честь что при полной загрузке виджета окно с анимацией закрывается то смысла в анимации попросту нет.

Так что пошёл разбираться с потоками :).

Ещё раз спасибо за помощь.