Я использую QtConcurrent для обработки некоторых фоновых изображений и хочу отображать изображение, пока его части постепенно обновляются.
Каждая строка изображения вычисляется отдельно и передается функтором.
Для вычисления полного изображения у меня есть последовательность элементов, которую я передаю в QtConcurrent, и каждая строка выдает сигнал, когда завершает вычисление.
Вот экземпляр класса Worker:
//living in the main(gui) thread !
Worker::Worker(VideoEngine* engine):_engine(engine){
_watcher = new QFutureWatcher<bool>;
_watcher->setPendingResultsLimit(200);
connect(_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(onProgressUpdate(int)));
connect(_watcher, SIGNAL(finished()), engine, SLOT(engineLoop()));
}
Вот слот для отчета о прогрессе:
void Worker::onProgressUpdate(int i){
if(i < (int)_rows.size() && i%10==0){
cout << " index = " << i << " y = "<< _rows[i] << endl;
_engine->checkAndDisplayProgress(_rows[i],i);
}
}
Теперь использование:
void Worker::_computeTreeForFrame(.../*unrelevant args*/){
....
....
_watcher->setFuture(
QtConcurrent::mapped(_sequence,
boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
}
}
Все сигналы испускаются, но слот onProgressUpdate вызывается только тогда, когда Qtconcurrent :: mapped выполняется со всеми элементами в последовательности.
При выполнении он имеет большую задержку во время обработки последовательности, а затем все слоты выполняются последовательно.
Я перепробовал все типы сигналов / слотов, и ни один из них не изменил это поведение.
Любая подсказка?
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
РЕДАКТИРОВАТЬ после предложения Shf
++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++
Вызов был сделан до сих пор в основной (GUI) поток.
Я изменил вызов на:
_computeFrameWatcher->setFuture(QtConcurrent::run(_worker,&Worker::computeTreeForFrame));
поскольку _computeTreeForFrame
теперь выполняется в другом потоке, я изменил вызов QtConcurrent :: mapped на:
_watcher->setFuture(QtConcurrent::mapped(_sequence,
boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
_watcher->waitForFinished();
Это приводит к тому же поведению, что и раньше.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
РЕДАКТИРОВАТЬ после предложения Марек R
++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++
Итак, я сделал так тесты и вот что я заметил:
QtConcurrent :: map:
resultReadyAt(int)
QtConcurrent :: сопоставляются
resultReadyAt(int)
только когда закончилНе имеет значения, если вызов функции map выполняется в отдельном потоке, встречается такое же поведение.
Я также дал попытку сигнала progressValueChanged(int)
как показывает пример Qt progressDialog.
Сигнал progressValueChanged(int)
испускается только на 2 строки на изображении (первый и последний).
Это действительно странно, так как в примере с диалогом прогресса Qt он генерируется плавно.
Я немного изменил пример Qt для запуска функции map в другом потоке, отличном от основного, и в этом случае он все еще работает хорошо.
Проблема должна возникнуть откуда-то еще.
Может быть, цикл событий GUI делает то, чего я не ожидаю? Я понятия не имею, что.
Сейчас я попробую QtConcurrent :: mappedReduced и сообщу о результатах 🙂
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
РЕДАКТИРОВАТЬ после попытки QtConcurrent :: mappedReduced
++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++
Он не работает и вызывает функцию «уменьшить» ТОЛЬКО после завершения функции «карта». Другими словами, он делает то же, что и предыдущий механизм сигнал / слоты.
Сейчас у меня мало возможностей
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
РЕДАКТИРОВАТЬ Я вернулся к решению так близко, как пример диалога прогресса Qt
++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++
Что-то должно быть не так, если я не могу получить то же поведение, что и в примере с Qt.
Вот код сейчас:
//created in the main thread! (gui)
Worker::Worker(VideoEngine* engine):_engine(engine),_watcher(0){
_watcher = new QFutureWatcher<void>;
_watcher->setPendingResultsLimit(200);
connect(_watcher,SIGNAL(progressValueChanged(int)), _engine,
SLOT(onProgressUpdate(int)));
connect(_watcher, SIGNAL(finished()), engine, SLOT(engineLoop()));
}
//executed on the main thread
void Worker::computeTreeForFrame(...){
...
_watcher->setFuture(QtConcurrent::map(_sequence,boost::bind(metaEnginePerRow,_1,output)));
...
}
Вызов computeTreeForFrame …
...
_worker->computeTreeForFrame();
...
Этот звонок сделан в слоте .
Он излучает сигналы для строки 0 и для последней строки, как сказано выше, но больше ничего не излучает.
Разве это не должно делать ТОЧНО того, что делает пример Qt?
Из описания задачи похоже, что вы должны использовать mappedReduced. Проблема в том, что я не вижу хорошего способа получить частичные результаты. Одним из способов преодоления этой проблемы является использование функции уменьшения формы сигнала.
Возможно, что эта тема может помочь
Кажется, что QtConcurrent :: сопоставляются не помещает VideoEngine :: metaEnginePerRow в другой поток, судя по документации. Если изображение обрабатывается в том же потоке, что и GUI, то ваши слоты действительно будут выполняться после обработки, независимо от того, какой тип соединения вы выберете, как вы описали.
Решение состоит в том, чтобы запустить Worker::_computeTreeForFrame
(как я понял, ваша основная функция обработки) в другом потоке через QtConcurrent::run
или поставить свой Worker
объект в другом потоке возможно через QObject::moveToThread()
, Тогда тип соединения, который вы должны использовать, Qt::QueuedConnection
(или если вы положите Worker
в другом потоке перед подключением вы можете подключиться даже с помощью Qt :: AutoConnectionor Qt::UniqueConnection
, вызывающий и получатель будут в разных потоках, поэтому qt автоматически выберет QueuedConnection`)
РЕДАКТИРОВАТЬ:
Я не уверен, но ваш _watcher = new QFutureWatcher<bool>;
все еще создается в главном потоке, и если вы звоните
_watcher->setFuture(QtConcurrent::mapped(_sequence,
boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
_watcher->waitForFinished();
было бы _watcher
установить поток GUI для ожидания, в каком он был создан или поток, где выполняется эта команда. Если _watcher->setFuture(QtConcurrent::mapped(_sequence,
если конец функции, это
boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));_watcher->waitForFinished();
нужен вообще? Qt уничтожит поток сразу после его выполнения, и вы настроите выполнение своей функции обработки, зачем ждать?
А также _computeFrameWatcher
должно быть QFuture<void*>
тип.
EDIT2:
Хорошо, прежде чем я сдаюсь, я предлагаю вам проверить QObject::moveToThread
:
прежде чем позвонить _worker->computeTreeForFrame();
Поместите это в другую ветку:
QThread *workerThread=new QThread();
_worker->moveToThread();
_worker->computeTreeForFrame();
/* connect _worker's finished signal with workerThread::quit and deleteLater slots */
и все соединения внутри _worker должны быть DirectConnection, а все соединения между _worker и основным потоком (GUI) должны быть связаны с QueuedConnection. Также, вероятно, хорошо создать новый поток в конструкторе _worker и немедленно переместить его в другой поток, таким образом вы можете уничтожить поток в деструкторе _worker и не беспокоиться о проблемах потока в потоке GUI.