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

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

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

Сообщений: 3260


Просмотр профиля
« : Сентябрь 14, 2012, 14:26 »

Встала задача выполнения долгой операции в треде. Казалось бы - берем QtConcurrent и пользуемся. Но не тут-то было - в документации к QtConcurrent::run написано:
Цитировать
Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.
Погуглив, я ничего не нашел и полез в сорцы креатора. Оказывается, там есть чудный файлик runextensions.h (который можно, при желании, легко написать самому), который добавляет возможность уведомлять о прогрессе выполнения функции.

Вот пример использования (проект и runextensions.h можно скачать в аттаче):
Функция запускает задачу в потоке, а та пишет прогресс в диалог. Также можно отменить задачу кнопкой на диалоге.
Код:
#include <QApplication>

#include <QFuture>
#include <QFutureWatcher>
#include <QProgressDialog>

#include "runextensions.h"

//heavy work :)
static void doWork(QFutureInterface<void> &future)
{
    future.setProgressRange(0, 100);

    static const int iterations = 10*1000*1000;
    for (int i = 0; i < iterations; i++) {
        if (future.isCanceled())
            break;

        future.setProgressValue(100.0*i/iterations);

        QObject *object = new QObject;
        object->setObjectName("Object");
        delete object;
    }
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    app.setQuitOnLastWindowClosed(false);

    QProgressDialog progress;
    progress.setWindowTitle(QObject::tr("Work"));
    progress.setLabelText(QObject::tr("Working..."));
    progress.setRange(0, 100);

    QFutureWatcher<void> watcher;
    QObject::connect(&watcher, SIGNAL(finished()), &app, SLOT(quit()));
    QObject::connect(&watcher, SIGNAL(progressValueChanged(int)), &progress, SLOT(setValue(int)));
    QObject::connect(&progress, SIGNAL(canceled()), &watcher, SLOT(cancel()));

    QFuture<void> future = QtConcurrent::run(&doWork);
    watcher.setFuture(future);
    progress.show();

    return app.exec();
}
« Последнее редактирование: Сентябрь 14, 2012, 14:28 от Авварон » Записан
AlekseyK
Гость
« Ответ #1 : Май 19, 2015, 21:26 »

Спасибо за находку! В документации об этом ни гу-гу.

Цитировать
Погуглив, я ничего не нашел и полез в сорцы креатора. Оказывается, там есть чудный файлик runextensions.h (который можно, при желании, легко написать самому), который добавляет возможность уведомлять о прогрессе выполнения функции.

А почему нельзя, например, передать объект QFuture напрямую в doWork()?
« Последнее редактирование: Май 19, 2015, 22:01 от AlekseyK » Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #2 : Май 22, 2015, 13:35 »

Потому что футура предназначена для чтения, ее нельзя (нет API) менять. Для этого сделан FurureInterface (aka std::promise)
Записан
AlekseyK
Гость
« Ответ #3 : Май 22, 2015, 18:04 »

Понятно, спасибо. Правда я вызываю метод класса и у меня выдаёт ошибку при компиляции:
Цитировать
c:\qt\4.8.6\src\corelib\concurrent\qtconcurrentrun.h:115: error: C2064: term does not evaluate to a function taking 1 arguments

Пытаюсь по Вашей аналогии вызвать:
Код:
    QFuture<void> backgroundJob = QtConcurrent::run(report, &XMLReport::Export);

Определение этого метода в классе:
Код:
void XMLReport::Export(QFutureInterface<void> &future)

Как такой метод можно вызвать? А если он ещё и виртуальный (virtual) - вызовется ли правильный метод в наследуемом классе?
« Последнее редактирование: Май 23, 2015, 02:03 от AlekseyK » Записан
AlekseyK
Гость
« Ответ #4 : Май 24, 2015, 00:18 »

Попробовал обёртки по типу как написано в Qt - всё равно эта ошибка вылетает при компиляции, видимо это фишка только 5-й Qt, а в qt 4.8.6 не работает. Или нет?
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #5 : Май 30, 2015, 09:44 »

Потому что у вас мембер ф-ия, надо this  передать.
Записан
AlekseyK
Гость
« Ответ #6 : Май 30, 2015, 12:44 »

Я передавал this, сделал обёртку над мембер функцией, всё равно ошибку выбивает, потому, что мне QFutureInterface туда передать нужно. Пришлось сигналы привязать напрямую в контейнере классов, а хотелось бы конечно спрятать внутри класса XMLReport и диалога с ProgressBar, передав им только QFutureInterface&.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #7 : Июнь 02, 2015, 19:08 »

Вы runextensions.h вообще скачали?)
Записан
AlekseyK
Гость
« Ответ #8 : Июнь 02, 2015, 23:34 »

Конечно! Улыбающийся Первым делом. И подключил.
Записан
__Heaven__
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2130



Просмотр профиля
« Ответ #9 : Июнь 03, 2015, 11:11 »

Да, забавненько, что про QFutureInterface нет статейки в доках. Я же для отображения прогресса использовал общие переменные. Из прогрессдиалога считывал каждые 100мс.
А в qthread как-то можно следить за прогрессом иначе? Не понятен смысл строчки
Цитировать
Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #10 : Июнь 03, 2015, 16:35 »

Да, забавненько, что про QFutureInterface нет статейки в доках. Я же для отображения прогресса использовал общие переменные. Из прогрессдиалога считывал каждые 100мс.
А в qthread как-то можно следить за прогрессом иначе? Не понятен смысл строчки
Цитировать
Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting.

Как раз понятен - т.к. интерфейс в ф-ии не передается, то она и не поддерживает.

AlekseyK
Это странно, т.к. у вас он пытается юзать оверлоады из qtcore, судя по выхлопу компилятора.
Записан
AlekseyK
Гость
« Ответ #11 : Июнь 03, 2015, 16:41 »

AlekseyK
Это странно, т.к. у вас он пытается юзать оверлоады из qtcore, судя по выхлопу компилятора.

Видимо так, не знаю как его нужно было заставить использовать runextensions, потратил много времени и так и сяк - не собралось, поэтому пока сигналы привязал напрямую. А хотелось бы конечно их спрятать внутри классов и передавать только QFutureInterface.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #12 : Июнь 03, 2015, 16:45 »

Пример у вас работает? Пробовали добавить туда мембер ф-ию?
Записан
AlekseyK
Гость
« Ответ #13 : Июнь 03, 2015, 16:59 »

Пример у вас работает? Пробовали добавить туда мембер ф-ию?

Пример работает, если переделать на мембер функцию - не собирается:

Код
C++ (Qt)
#include <QApplication>
 
#include <QFuture>
#include <QFutureWatcher>
#include <QProgressDialog>
 
#include "runextensions.h"
 
class Work
{
 //heavy work :)
public:
 void doWork(QFutureInterface<void> &future)
 {
   future.setProgressRange(0, 100);
 
   static const int iterations = 10*1000*1000;
   for (int i = 0; i < iterations; i++) {
     if (future.isCanceled())
       break;
 
     future.setProgressValue(100.0*i/iterations);
 
     QObject *object = new QObject;
     object->setObjectName("Object");
     delete object;
   }
 }
};
 
int main(int argc, char *argv[])
{
   QApplication app(argc, argv);
   app.setQuitOnLastWindowClosed(false);
 
   QProgressDialog progress;
   progress.setWindowTitle(QObject::tr("Work"));
   progress.setLabelText(QObject::tr("Working..."));
   progress.setRange(0, 100);
 
   QFutureWatcher<void> watcher;
   QObject::connect(&watcher, SIGNAL(finished()), &app, SLOT(quit()));
   QObject::connect(&watcher, SIGNAL(progressValueChanged(int)), &progress, SLOT(setValue(int)));
   QObject::connect(&progress, SIGNAL(canceled()), &watcher, SLOT(cancel()));
 
   Work work;
 
   QFuture<void> future = QtConcurrent::run(work, &Work::doWork);
   watcher.setFuture(future);
   progress.show();
 
   return app.exec();
}
 
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3260


Просмотр профиля
« Ответ #14 : Июнь 03, 2015, 17:03 »

Ну сигнатуру ф-ии всё-таки смотреть надо:)
Код:
QtConcurrent::run(&Work::doWork, &work);
Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


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