Отображение QtConcurrent и отчет о ходе выполнения

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

Это приводит к тому же поведению, что и раньше.

++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++ РЕДАКТИРОВАТЬ после предложения Марека Р. ++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++

Итак, я провел тесты, и вот что я заметил:

QtConcurrent :: map:

  • Не подает сигнал resultReadyAt(int)

QtConcurrent :: mapped

  • Выдает resultReadyAt(int) только после завершения

Не имеет значения, выполняется ли вызов функции карты в отдельном потоке, наблюдается такое же поведение.

Я также попробовал сигнал progressValueChanged(int), как предлагает пример Qt progressDialog. Сигнал progressValueChanged(int) излучается только для 2 строк изображения (первой и последней). Это действительно странно, поскольку в примере диалога выполнения Qt он запускается плавно.

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

Проблема должна возникать откуда-то еще.

Может быть, цикл событий GUI делает то, чего я не ожидаю? Понятия не имею, что именно.

Сейчас я попробую QtConcurrent :: mappedReduced и сообщу о результатах :-)

++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++ EDIT после попытки 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?


person Lex    schedule 30.08.2013    source источник
comment
Описание вашей проблемы неполное. Трудно помочь, но я постараюсь.   -  person Marek R    schedule 30.08.2013
comment
как так? Вы можете быть более конкретным ? Какую информацию вы хотели бы получить?   -  person Lex    schedule 30.08.2013


Ответы (2)


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

Возможно, поможет эта ветка.

person Marek R    schedule 30.08.2013

Кажется, что QtConcurrent :: mapped не помещает VideoEngine: : metaEnginePerRow в другой ветке, судя по документации. Если изображение обрабатывается в том же потоке, что и графический интерфейс, ваши слоты действительно будут выполняться после обработки, независимо от того, какой тип соединения вы выберете, как вы описали.

Решение состоит в том, чтобы либо запустить 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 установит ожидание потока графического интерфейса пользователя, в котором он был создан, или потоке, в котором выполняется эта команда. Если _watcher->setFuture(QtConcurrent::mapped(_sequence, boost::bind(&VideoEngine::metaEnginePerRow,_1,output))); - это конец функции, _watcher->waitForFinished(); нужен ли вообще? Qt уничтожит поток сразу после его выполнения, а вы настроите свою функцию обработки на запуск, зачем ждать?

И _computeFrameWatcher должен быть QFuture<void*> типа.

РЕДАКТИРОВАТЬ2:

Хорошо, прежде чем я сдамся, я предлагаю вам проверить 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 и не беспокоиться о проблемах потока в потоке графического интерфейса

person Shf    schedule 30.08.2013
comment
Я попытался создать _watcher в computeTreeForFrame, чтобы он принадлежал потоку QtConcurrent :: run, но сигналы никогда не испускались, ни один из них - person Lex; 30.08.2013
comment
1) Наличие _watcher в отдельном потоке не дает ему вообще никакого сигнала. 2) Если он находится в том же потоке, что и поток GUI, что и поток GUI, но выполняет функцию карты в другом потоке, он испускает 1-2 сигнала progressValueChanged. 3) Наличие его в том же потоке, что и поток графического интерфейса пользователя, и выполнение функции карты в потоке графического интерфейса пользователя дает точно такое же поведение, как 2). - person Lex; 31.08.2013
comment
@Lex, возможно, QConcurrentRun недостаточно, если QThread не поможет, я не знаю, в чем проблема. Прямо сейчас это действительно похоже на обычную тяжелую обработку в основном потоке - gui зависает, пока обработка не будет завершена. Это типичные симптомы, что все вычисления должны производиться в рабочем потоке, а все рисование - в потоке графического интерфейса. - person Shf; 31.08.2013
comment
Пробовал ваше решение moveToThread, и оно дает в точности те же результаты, что и QtConcurrent :: run ..., которых можно было ожидать. Я действительно не понимаю, что происходит, почему не все сигналы излучаются ... Я собираюсь попробовать стиль примера Мандельброта с одним живым потоком все время, который просыпается по мере необходимости, это последний шанс ! - person Lex; 01.09.2013