簡體   English   中英

Qt無法弄清楚如何在我的程序中處理我的返回值

[英]Qt can't figure out how to thread my return value in my program

我已經在網上閱讀了有關如何在Qt中使用多線程應用程序的各種文章, 例如這篇文章 ,我注意到Qt也更新了他們關於這個主題的官方文檔 ,但是我仍然在努力理解如何創建一個線程,做一些圖像處理並返回一個新的QImage來更新我的GUI。

我正在努力澄清的事情是:

  1. 我在哪里放置連接代碼,在大多數示例中,我看到在對象的構造函數中聲明的連接。

  2. 為什么連接語句需要這么多行才能完成一個進程? 即在我的情況下我有一個滑塊來改變QGraphicsView上圖像的飽和度,我想生成一個線程來處理圖像像素的操作,然后將格式化的QPixmap返回到我的GUI並運行一個渲染方法來繪制新的圖像到畫布(我不認為我可以從我的線程更新我的畫布?)

在第2點之后,這是我為我的線程編寫的當前代碼(我不是QThread的子類,我認為我正確地遵循文檔。)

WorkerThread.h

#include "sliders.h"

class WorkerThread : public QObject
{
    Q_OBJECT
public:
    WorkerThread();
    ~WorkerThread();

public slots:
    void modifySaturation(const int, const QPixmap);

signals:
    void SaturationChanged(const QPixmap);

private:
    Sliders *slider;

};

WorkerThread.cpp

WorkerThread::WorkerThread()
{

}

WorkerThread::~WorkerThread()
{

}

// Create a new Sliders object on the thread (declaring in construct would place it on the main thread?)
// Call the modifySaturation() method in the slider class and store its returned QPixmap into the variable to emit it back to the GUI
void WorkerThread::modifySaturation(const int value, const QPixmap image)
{
   slider = new Sliders;
   QPixmap img = slider->modifySaturation(value, image);
   emit resultReady(img);
}

希望上面的評論能夠傳達我想要做的事情,將新創建的Pixmap發送回主線程以繪制到GUI。

我遇到麻煩的步驟是編寫邏輯來橋接我的主線程和工作線程之間的連接,到目前為止,我已經在mainwindow.h創建了一個名為'thread'的QThread對象,然后在我的mainwindow.cpp我做了以下:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    // Instanciate a scene object that we can draw to and then set up the ui
    scene  = new QGraphicsScene(this);
    filter = new Filters;
    worker = new WorkerThread;
    ui->setupUi(this);

    thread = new QThread;
    worker->moveToThread(&thread);

    // This is what I am struggling to understand
    connect(thread, SIGNAL(started()), worker, SLOT(modifySaturation(int,QPixmap)));
    connect(worker, SIGNAL(SaturationChanged(QPixmap)), MainWindow, SLOT(onSaturationChanged()));
}    

// Public slot on my main window to update the GUI
void MainWindow::onSaturationChanged(QPixmap)
{
    // image is a private member describing the current loaded image
    m_image = QPixmap;
    renderImageToCanvas();
}

根據我的閱讀,我應該在開始任務時生成一個新線程,但我怎么能:

  1. 重用這個線程用於多種方法(改變飽和度,改變亮度,改變色調......),我是否需要為每個不同的任務創建一個新線程(這看起來有點復雜)?
  2. 如何連接飽和度滑塊的value changed方法以在新線程上啟動計算然后返回以使用主窗口中的OnSaturationChanged插槽更新GUI?

正如你剛才提到的那篇偉大的文章如何真正使用QThread ,讓我們開始通過打破這個來解釋你不確定的代碼

QThread* thread = new QThread;
Worker* worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();

這段代碼通常放在主線程上的一個對象中,也許在MainWindow中:

創建一個新的線程對象 - QThread實際上更多的是線程控制器而不是線程

QThread* thread = new QThread;

創建一個新的工作對象。 這是一個可以在不同的線程上工作的對象。 由於這可以移動到不同的線程,請注意您可以創建多個對象並將它們移動到同一個線程

Worker* worker = new Worker();

將對象及其子對象移動到新線程

worker->moveToThread(thread);

設置有用的連接以監視和控制工作人員。 讓我們從任何錯誤開始,所以我們知道工人是否有問題

connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));

為了啟動worker對象處理,我們將線程的started()信號連接到worker的process()槽。 在您的示例中,process將是modifySaturation插槽

connect(thread, SIGNAL(started()), worker, SLOT(process()));

當worker完成處理后,如果它是唯一的對象,則需要退出並清理,因此線程應該退出

connect(worker, SIGNAL(finished()), thread, SLOT(quit()));

為了整理,現在不再需要工人和線程,確保他們自己整理

connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

最后,讓我們開始調用thread-> start(),它將觸發初始的started()信號,我們之前連接到worker的process()函數

thread->start();

考慮到所有這些,讓我們解決提出的問題: -

1 如何將這個線程重用於多種方法(改變飽和度,改變亮度,改變色調......),是否需要為每個不同的任務創建一個新線程(這看起來有點過於復雜)?

不,您不需要為每個方法添加新線程。 您可以使用當前對象並對其進行擴展以執行所有處理,通過主線程中的信號和插槽控制它,或者您可以為每個方法創建單獨的對象並將它們全部移動到新線程。

如果使用移動到新線程的多個對象,請確保在其他對象仍在使用該線程時,不要將對象的finished()信號連接到線程上調用quit()。 但是,在完成對象和線程后,仍需要清理對象和線程。

2為什么連接語句需要這么多行才能完成一個進程? 即在我的情況下,我有一個滑塊來改變QGraphicsView上圖像的飽和度,我想生成一個線程來處理圖像像素的操作,然后將格式化的QPixmap返回到我的GUI並運行一個渲染方法來繪制新的圖像到畫布(我不認為我可以從我的線程更新我的畫布?)

一般規則是您只能從主線程更新圖形對象(小部件,圖形項等)。 (有一個例外,但它超出了本次討論的范圍,在此處無關緊要。)

當只使用一個對象時,多個連接信號中有三個用於在完成時刪除對象,一個用於處理錯誤消息,最后一個連接確保工作線程在線程開始時啟動。

沒有什么可以阻止你通過創建線程並首先啟動它來創建工作對象,連接相關信號並在之后將它們移動到線程,但是你需要觸發工作人員開始做某事,例如處理飽和度,一旦他們被轉移到新的線程。

QThread* pThread = new QThread;
pThread->start();

Worker* worker1 = new Worker();
Worker* worker2 = new Worker();
Worker* worker3 = new Worker();

worker1->moveToThread(pThread);
worker2->moveToThread(pThread);
worker3->moveToThread(pThread);

這里的worker對象已被移動到正在運行的新線程。 但是,工作對象是空閑的。 沒有連接,我們可以調用一個要調用的槽。 我們假設進程槽采用整數參數......

QMetaObject::invokeMethod( worker1, "process", Q_ARG( int, param ) );
QMetaObject::invokeMethod( worker2, "process", Q_ARG( int, param ) );
QMetaObject::invokeMethod( worker3, "process", Q_ARG( int, param ) );

因此,正如您在此處所看到的,您並不總是需要連接信號,但這很方便。

我會回答你的一個問題,因為Merlin很好地完成了其余的工作。

how can I connect the value changed method of my saturation slider to launch the computation on a new thread and then return it to update the GUI using the OnSaturationChanged slot in my main window?

好吧,例如,在你的MainWindow::OnSaturationChanged插槽中,你可以發出一個信號,將QImageslider值傳遞給你的線程。 此信號將連接到WorkerThread一個插槽,該插槽執行一些圖像操作。

mainwindow.h

public slots:        
    void addNewImage(QImage image);

signals:
    void requestImageUpdate(QImage image, int sliderValue);

mainwindow.cpp

    //in your MainWindow constructor or wherever you create your worker...
    connect(this, SIGNAL(requestImageUpdate(QImage, int)), worker, SLOT(updateImage(QImage, int)));
    connect(worker, SIGNAL(imageUpdated(QImage)), this, SLOT(addNewImage(QImage)));
    ...

void MainWindow::OnSaturationChanged()
{
    emit requestImageUpdate(myImage, slider->value());
}

void MainWindow::addNewImage(QImage image)
{
    //update the image in your graphics view or do whatever you want to do with it
}

workerthread.h

public slots:
    void updateImage(QImage image, int sliderValue);

signals:
    void imageUpdated(QImage newImage);

workerthread.cpp

void WorkerThread::updateImage(QImage image, int sliderValue)
{
    QImage newImage; // you might no need this, this is just an example
    ....
    emit imageUpdated(newImage);
}

PS僅在主線程中使用QPixmap 在其他線程中使用QImage

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM