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

Ale*_*lex 7 c++ qt multithreading

我已经在网上阅读了有关如何在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;
Run Code Online (Sandbox Code Playgroud)

};

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);
}
Run Code Online (Sandbox Code Playgroud)

希望上面的评论能够传达我想要做的事情,将新创建的Pixmap发送回主线程以绘制到GUI.

我遇到麻烦的步骤是编写逻辑来桥接我的主线程和工作线程之间的连接,到目前为止,我已经创建了一个QThread名为'thread' 的对象mainwindow.h,然后在我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();
}
Run Code Online (Sandbox Code Playgroud)

根据我的阅读,我应该在开始任务时生成一个新线程,但我怎么能:

  1. 重用这个线程用于多种方法(改变饱和度,改变亮度,改变色调......),我是否需要为每个不同的任务创建一个新线程(这看起来有点复杂)?
  2. 如何连接value changed饱和度滑块的方法以在新线程上启动计算然后返回以使用OnSaturationChanged主窗口中的插槽更新GUI ?

The*_*ght 8

正如你刚才提到的那篇伟大的文章如何真正使用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();
Run Code Online (Sandbox Code Playgroud)

这段代码通常放在主线程上的一个对象中,也许在MainWindow中:

创建一个新的线程对象 - QThread实际上更多的是线程控制器而不是线程

QThread* thread = new QThread;
Run Code Online (Sandbox Code Playgroud)

创建一个新的工作对象.这是一个可以在不同的线程上工作的对象.由于这可以移动到不同的线程,请注意您可以创建多个对象并将它们移动到同一个线程

Worker* worker = new Worker();
Run Code Online (Sandbox Code Playgroud)

将对象及其子对象移动到新线程

worker->moveToThread(thread);
Run Code Online (Sandbox Code Playgroud)

设置有用的连接以监视和控制工作人员.让我们从任何错误开始,所以我们知道工人是否有问题

connect(worker, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
Run Code Online (Sandbox Code Playgroud)

为了启动worker对象处理,我们将线程的started()信号连接到worker的process()槽.在您的示例中,process将是modifySaturation插槽

connect(thread, SIGNAL(started()), worker, SLOT(process()));
Run Code Online (Sandbox Code Playgroud)

当worker完成处理后,如果它是唯一的对象,则需要退出并清理,因此线程应该退出

connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
Run Code Online (Sandbox Code Playgroud)

为了整理,现在不再需要工人和线程,确保他们自己整理

connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
Run Code Online (Sandbox Code Playgroud)

最后,让我们开始调用thread-> start(),它将触发初始的started()信号,我们之前连接到worker的process()函数

thread->start();
Run Code Online (Sandbox Code Playgroud)

考虑到所有这些,让我们解决提出的问题: -

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);
Run Code Online (Sandbox Code Playgroud)

这里的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 ) );
Run Code Online (Sandbox Code Playgroud)

因此,正如您在此处所看到的,您并不总是需要连接信号,但这很方便.