2017-02-20 30 views
1

В mainwindow.ui я создал QProgressBar по имени progressBar и QPushButton с именем speckle, который начинает тяжелые вычисления.Qt - Как объединить QtConcurrent и QThreadPool для QProgressBar?

Внутри mainwindow.h У меня есть подходящая private slot для кнопки и частная функция, которая представляет собой тяжелое вычисление. mainwindow.h:

class MainWindow : public QMainWindow 
{ 
    Q_OBJECT 

public: 
    explicit MainWindow(QWidget *parent = 0); 
    ~MainWindow(); 

private slots: 
    void on_speckle_clicked(); 
    ... 

private: 
    Ui::MainWindow *ui; 
    QFutureWatcher<std::vector<cv::Mat>> futureWatcher; 
    std::vector<cv::Mat> progressSpecle();//heavy computation 

}; 

futureWatcher предполагается часыQFuture объект, который получает вернулся из QtConcurrent:

MainWindow::MainWindow(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::MainWindow) 
{ 
    ui->setupUi(this); 
    ... 
    connect(&this->futureWatcher, SIGNAL(progressValueChanged(int)), ui->progressBar, SLOT(setValue(int))); 
    ... 
} 

... 

void MainWindow::on_speckle_clicked() 
{  
    //Start the computation. 
    QFuture<std::vector<cv::Mat>> future; 
    future = QtConcurrent::run(this, &MainWindow::progressSpecle); 
    this->futureWatcher.setFuture(future); 

    QThreadPool::globalInstance()->waitForDone(); 

    vector<cv::Mat> result = future.result(); 

    specklevisualization *s = new specklevisualization; 
    s-> setAttribute(Qt::WA_DeleteOnClose); 
    s-> start(result); 
    s-> show(); 
} 

Но приложение не работает так. После компиляции и нажатия на specklemainwindow не реагирует. Вот функция progressSpecle члена, в котором Тема получает создана х:

void MainWindow::progressSpecle(){ 
    vector<cv::Mat> input; 
    ...//do something with input 

    vector<cv::Mat> result; 
    vector<cv::Mat> *all; 
    all = &result; 

    QThreadPool *threadPool = QThreadPool::globalInstance(); 

    for(unsigned int i = 1; i<input.size(); i++) { 
     cv_speckle_algorithm *work = new cv_speckle_algorithm(input.at(i-1), input.at(i), all, i-1); 
     work->setAutoDelete(false); 
     threadPool->start(work); 
    } 

    while(true){ 
     if(threadPool->activeThreadCount() == 1) return result; 
    } 

} 

Приложения работает без ошибок, но MainWindow не несет ответственности, потому что (я думаю) в while(true). Но я не понимаю, почему это должно блокировать mainWindow, потому что вся функция progressSpecle работает в отдельном потоке, созданном и запущенном с QtConcurrent.

Почему функция progressSpecle Блокирует mainWindow? Итак, как я могу получить progressBar?

+0

Немного не по теме, но ... если 'specklevisualization' наследует в любом случае из 'QWidget', то вы не можете создать экземпляр его в потоке, отличном от GUI. –

+0

@ G.M. Я все изменил функцию progressSpecle, чтобы он возвращал вектор 'result'. – goulashsoup

+0

Создал ли объект приложения перед конструктором MainWindow? QObject :: connect() печатает ошибку в окне консоли? – falkb

ответ

2

Сигнал QFutureWatcher испускается из объединенной нити. Это означает, что слот QProgressBar будет вызываться через «очередь в очереди»: событие будет помещено в очередь на цикл событий основного потока, и слот будет вызываться, когда это событие будет обработано.

Звонок в QThreadPool::waitForDone блокирует основной поток, поэтому цикл событий не работает, и очередь в очереди не будет вызываться. Вам нужно, чтобы цикл событий основного потока выполнялся, пока он ждет завершения параллельной задачи.

Есть два способа, я могу думать, чтобы выполнить это. Во-первых, для подключения обратного вызова к QFutureWatcher::finished сигнала и возвращает управление основной цикл событий:

void MainWindow::on_speckle_clicked() 
{ 
    //Start the computation. 
    QFuture<std::vector<cv::Mat>> future; 
    future = QtConcurrent::run(this, &MainWindow::progressSpecle); 

    connect(&futureWatcher, &QFutureWatcherBase::finished, this, [result] { 
     vector<cv::Mat> result = future.result(); 
     specklevisualization *s = new specklevisualization; 
     s->setAttribute(Qt::WA_DeleteOnClose); 
     s->start(result); 
     s->show(); 
    }); 
    this->futureWatcher.setFuture(future); 

    // return control to event loop 
} 

Вы можете использовать именованный метод вместо лямбда, если вы предпочитаете.

Второй способ запустить вложенный цикл обработки событий внутри функции, и подключить его слот quit к QFutureWatcher::finished сигнала:

void MainWindow::on_speckle_clicked() 
{ 
    QEventLoop localLoop; 

    //Start the computation. 
    QFuture<std::vector<cv::Mat>> future; 
    future = QtConcurrent::run(this, &MainWindow::progressSpecle); 

    connect(futureWatcher, &QFutureWatcherBase::finished, &localLoop, &QEventLoop::quit); 
    this->futureWatcher.setFuture(future); 

    localLoop.exec(); // wait for done 

    vector<cv::Mat> result = future.result(); 
    specklevisualization *s = new specklevisualization; 
    s->setAttribute(Qt::WA_DeleteOnClose); 
    s->start(result); 
    s->show(); 
} 
+0

В функции' connect' необходимо использовать шаблон: & QFutureWatcher > :: завершено. Шаблон > необходим, потому что «вы не можете использовать шаблон класса или общий класс как идентификатор без шаблона или общего списка аргументов». -> Результаты в ошибке компилятора C2955 – goulashsoup

+1

Рад, что это сработало. Спасибо за ответ. Я исправил ошибку несколько иначе, как вы можете видеть, я думаю, что она выглядит красивее. – Oktalist