QtConcurrent mapped и отчет о проделанной работе

Я использую 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?

2

Решение

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

Возможно, что эта тема может помочь

0

Другие решения

Кажется, что 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.

0

По вопросам рекламы [email protected]