Russian Qt Forum
Август 17, 2018, 09:17 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Помогите добавить отображение прогресс диалога  (Прочитано 874 раз)
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2040



Просмотр профиля
« : Апрель 26, 2018, 09:45 »

Привет, друзья!
Прошу помочь корректно решить задачу по добавлению прогресс диалога. Отображение самого прогреса операции не интересует. Хочу только антифриз окна.
Сейчас имеется такая иерархия:
MainWindow
|=GLWidget
|=MyModel
    |=MyObject - объект большого размера

Модель предоставляет наружу указатель на объект
Изменения объекта и модели испускают сигналы, которые связаны с GL опцией direct.
MyModel::load(fileName) выглядит примерно так:
Код:
parser = selectParser();
try {
prepareOperations();
model.Load(fileName);
}
catch(const FileOpenEx &)
{}
gl->setDefaultView();

Model::Load:
Код:
prepare();
myObj.clear();
myObj = parser->parse(fileName); // emit signals on assign
emit ...
emit ...

Наивная передача Model::Load в поток через QtConcurent::run выливается в необновление GL и MainWindow. Да и результат он вызывает по значению, что для меня расточительно по использованию памяти
Прошу поделиться мыслями, какое архитектурное решение здесь лучше применить.
Пока вижу, что нужно как-то в главном потоке чистить объект, далее отображать прогресс и запускать парсер, который будет отправляться в поток, а по завершению делать перемещающее присваивание полученного объекта пустому.
В таком способе не вижу, как оповестить основной поток о том, что операция чтения окончена
Записан
ViTech
Бывалый
*****
Offline Offline

Сообщений: 478



Просмотр профиля
« Ответ #1 : Апрель 26, 2018, 15:14 »

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

Сигнал о завершении чтения есть или как? Буду предполагать, что есть. Тогда, как вариант, метод Model::Load можно разделить на две части: первая выполняет подготовительные действия и отправляет в поток парсер, после этого завершается. Вторая часть оформлена слотом, который получает результат загрузки.

Ещё может как-то QFutureWatcher поможет.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 10108


Просмотр профиля
« Ответ #2 : Апрель 27, 2018, 10:08 »

Хочу только антифриз окна.
Обычно никакой активности юзера нельзя допускать пока идет загрузка. Поэтому просто модальный QProgressDialog, он сам вызовет processEvents. Ну конечно не забывать апдейтить диалог из загрузчика. "Вынос в поток" здесь не нужен.

Тут правда одна бяка: в процессе загрузки рисование окон может ссылаться на данные что уже разрушены. Пресекается с помощью setUpdatesEnabled(false), хотя и с приключениями (недавно там ковырялся).
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2040



Просмотр профиля
« Ответ #3 : Май 01, 2018, 14:41 »

Сигнал о завершении чтения есть или как? Буду предполагать, что есть. Тогда, как вариант, метод Model::Load можно разделить на две части: первая выполняет подготовительные действия и отправляет в поток парсер, после этого завершается. Вторая часть оформлена слотом, который получает результат загрузки.

Ещё может как-то QFutureWatcher поможет.
Про вотчер я и забыл. Реализовал на нём.
StartLoading запускает процесс, футуру, которого я передаю вотчеру, далее вотчер выполняет FinishLoading.
Есть вопрос. У меня может случиться FinishLoading до завершения StartLoading или вообще не отработать завершение? Например, если потоку не особо много что нужно делать

Столкнулся ещё с небольшой проблемой. Почему-то нельзя наполнять данными QOpenGLBuffer... Видимо, какие-то тонкости связанные с контекстом...

Обычно никакой активности юзера нельзя допускать пока идет загрузка. Поэтому просто модальный QProgressDialog, он сам вызовет processEvents. Ну конечно не забывать апдейтить диалог из загрузчика. "Вынос в поток" здесь не нужен.

Не, не хотелось такой связки. У меня предполагается несколько парсеров, возможно в будущем и внешние появятся. Придётся в каждом помимо парсинга заниматься и обновлением окна
Записан
ViTech
Бывалый
*****
Offline Offline

Сообщений: 478



Просмотр профиля
« Ответ #4 : Май 02, 2018, 12:04 »

StartLoading запускает процесс, футуру, которого я передаю вотчеру, далее вотчер выполняет FinishLoading.
Есть вопрос. У меня может случиться FinishLoading до завершения StartLoading или вообще не отработать завершение? Например, если потоку не особо много что нужно делать

Параллельный поток с загрузкой вполне может завершиться до конца выполнения StartLoading, такую возможность отбрасывать нельзя. Другое дело, как, куда и когда вернётся результат загрузки. Сигнал QFutureWatcher::finished вполне можно соединить со слотом в вызывающем потоке по Qt::QueuedConnection, тогда StartLoading завершится и результат загрузки будет обработан в порядке очереди (сообщений). Вроде как-то так, надо на практике проверять Улыбающийся.
Записан

Пока сам не сделаешь...
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 10108


Просмотр профиля
« Ответ #5 : Май 02, 2018, 15:12 »

У меня предполагается несколько парсеров, возможно в будущем и внешние появятся. Придётся в каждом помимо парсинга заниматься и обновлением окна
Так или иначе придется если хотите отображать ход загрузки(ок). Иногда удается проскочить привязавшись к сколько % считано из файла.

Ну конечно конкретными окнами загрузчик не занимается, он обычно вызывает callback или испускает сигнал, а там уже обновят. Вообще не понял причем тут QtConcurrent если никакого параллелизма не требуется. Хотите непременно "в др потоке" (круто) - ну хорошо, тогда просто заведите рабочую QThread, создайте пресловутого worker'a и пульните его сигналом. Или QThreadPool, там еще меньше писать. В любом случае главная нитка свободна и проблема замерзания не стоит.
« Последнее редактирование: Май 03, 2018, 05:14 от Igors » Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2040



Просмотр профиля
« Ответ #6 : Май 03, 2018, 07:43 »

Вроде как-то так, надо на практике проверять Улыбающийся.
Я рассуждал, что QFutureWatcher занимается опросом QFuture во время проворачивания EventLoop. Таким образом получается, что EventLoop заблокирован на момент выполнения  StartLoading. Беспокойство вызывает момент присваивания результата run в QFuture и передача её в QFutureWatcher - не выполнился ли поток уже, иначе ж не стоит ожидать смены статуса QFuture.

Igors, пробовал на processEvents выполнять обновление интерфейса - остались неприятные впечатления, связанные с уменьшением скорости выполнения вычислений. Не хочется возвращаться к этому варианту. С QThread мало работал, с QThreadPool вообще не работал. QThread, наверняка, потребует больше строк кода чем QtConcurrent::run. Пока не требуется отображать прогресс.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 10108


Просмотр профиля
« Ответ #7 : Май 04, 2018, 07:10 »

Igors, пробовал на processEvents выполнять обновление интерфейса - остались неприятные впечатления, связанные с уменьшением скорости выполнения вычислений. Не хочется возвращаться к этому варианту. 
Возможно Вы столкнулись с типовой проблемой - индикатор дергается слишком часто, в результате "работает на индикатор" (а не на расчеты). При выносе в поток это менее заметно, но решать все равно придется.

Пока не требуется отображать прогресс.
А как же тогда название темы  Непонимающий  Улыбающийся

Неясно что Вы хотите обновлять. Модель грузится целиком, для нее создаются всякие внутренние структуры данных (хотя бы буфера рисования), поэтому что там обновлять в ходе загрузки - хз. Целиком загрузили, потом целиком отобразили - нормальная, стандартная практика. 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3918



Просмотр профиля
« Ответ #8 : Май 04, 2018, 07:42 »

Я рассуждал, что QFutureWatcher занимается опросом QFuture во время проворачивания EventLoop.
Нет. Скорее всего этот механизм сделан на условных переменных.

Таким образом получается, что EventLoop заблокирован на момент выполнения  StartLoading.
Ничего не блокируется и весело крутится. Улыбающийся

Беспокойство вызывает момент присваивания результата run в QFuture и передача её в QFutureWatcher - не выполнился ли поток уже, иначе ж не стоит ожидать смены статуса QFuture.
Нужно проверить, но скорее всего при помещении в вотчер уже завершенной задачи сигнал finished будет послан сразу. По крайней мере, это было бы логичным.
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2040



Просмотр профиля
« Ответ #9 : Май 17, 2018, 18:39 »

Ничего не блокируется и весело крутится. Улыбающийся
StartLoading ведь в том же потоке, где и processEvents (a.exe()). До веселья ещё одна }  Улыбающийся

Нужно проверить, но скорее всего при помещении в вотчер уже завершенной задачи сигнал finished будет послан сразу. По крайней мере, это было бы логичным.
Проверил. Под linux на Qt5.10. При return a.exec()  выводится два сообщения,  при return 0  - одно, но если вызвать a.processEvents(), то 2
Сопутствующий вопрос появился: почему qDebug()<< "msg" в этом примере не работает? Подскажите, пожалуйста.
Код
C++ (Qt)
#include <QCoreApplication>
#include <QtConcurrent/QtConcurrent>
#include <iostream>
 
using namespace std;
 
void f() {
   cout << "ololo" << endl;
}
void d() {
   cout << "done" << endl;
}
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
   auto future = QtConcurrent::run(f);
   QFutureWatcher<void> watcher;
   future.waitForFinished();
   QObject::connect(&watcher, &QFutureWatcher<void>::finished, []{d();});
   watcher.setFuture(future);
 
   return a.exec();
}
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  

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