2017-02-20 74 views
1

mainwindow.ui中,我創建了一個名爲的名稱爲progressBar的名爲speckleQPushButton,它開始大量計算。Qt - 如何爲QProgressBar結合使用QtConcurrent和QThreadPool?

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應該手錶即會從QtConcurrent返回的QFuture對象:

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(); 
} 

但應用不喜歡的工作。在編輯並點擊斑點mainwindow不響應。這裏是progressSpecle成員函數,其中x線程被創建:在while(true)

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,因爲整個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