Aggiornare la gui in Qt da un altro thread

Aggiornare la gui in Qt da un altro thread

Supponiamo di voler eseguire un operazione in un thread secondario, e di voler aggiornare la nostra gui man mano che l'operazione va avanti: come possiamo fare questa cosa in Qt?

Molto semplicemente usando il metodo connect, i SIGNAL e gli SLOT.

Direi di cominciare dal nostro lavoro in background:

// workerthread.h
#ifndef WORKTHREAD_H
#define WORKTHREAD_H

#include <QMutex>
#include <QThread>

class WorkerThread : public QThread
{
    Q_OBJECT
public:
    explicit WorkerThread(QObject *parent = 0, bool b = false);
    void run();
    bool stop;

signals:
    void valueChanged(QString text);
    void finished();
};

#endif // WORKTHREAD_H

E questa l'implementazione:

// workerthread.cpp
#include "workerthread.h"

WorkerThread::WorkerThread(QObject *parent, bool b) :
    QThread(parent), stop(b)
{
}

void WorkerThread::run()
{
    for (int i = 0; i < 5; i++)
    {
        QMutex mutex;
        // PREVIENE CHE ALTRI THREAD IMPOSTINO LO STOP
        mutex.lock();
        if(this->stop) break;
        mutex.unlock();
        // EMETTE IL SEGNALE PER IMPOSTARE IL TESTO SULLA GUI
        emit valueChanged("VALORE: " + i);
        this->msleep(500);
    }
    emit finished();
}

Nel metodo run, eseguiamo un semplice ciclo for dimostrativo.

Ad ogni giro inviamo il testo e il valore corrente.

Alla fine del ciclo inviamo il segnale fiinished.

Passiamo adesso alla gui; non importa che tipo di finestra sia, quindi potete usare quello che volete al posto di QScrollArea:

// imageresize.h
#ifndef RESIZEWINDOW_H
#define RESIZEWINDOW_H

#include <QScrollArea>
#include "workerthread.h"

namespace Ui {
class ResizeWindow;
}

class ResizeWindow : public QScrollArea
{
    Q_OBJECT

public:
    explicit ResizeWindow(QWidget *parent = 0);
    ~ResizeWindow();
    WorkerThread *wt;

public slots:
    void onValueChanged(QString text);
    void onFinished();

private:
    Ui::ResizeWindow *ui;
};

#endif // RESIZEWINDOW_H

Abbiamo due SLOT, che verrano collegati ai due SIGNAL visti prima.

Questo il file di implementazione:

// imageresize.cpp
#include "resizewindow.h"
#include "ui_resizewindow.h"

ResizeWindow::ResizeWindow(QWidget *parent) :
    QScrollArea(parent),
    ui(new Ui::ResizeWindow)
{
    ui->setupUi(this);
    wt = new WorkerThread(this);
    connect(wt, SIGNAL(valueChanged(QString)), this, SLOT(onValueChanged(QString)));
    connect(wt, SIGNAL(finished()), this, SLOT(onFinished()));
    wt->start();
}

void ResizeWindow::onValueChanged(QString text)
{
    ui->txtArea->appendPlainText(text);
}

void ResizeWindow::onFinished()
{
   ui->txtArea->appendPlainText("OPERAZONI TERMINATE");
}

ResizeWindow::~ResizeWindow()
{
    delete ui;
}

Abbiamo un solo componente nella finestra, ed è una QPlainTextEdit.

Quello che dovete guardare sono le due invocazioni di connect, dove in pratica colleghiamo i SIGNAL del nostro worker thread agli SLOT della finestra.

Vi basta avviare questa finestra, o implementarne una vostra ovviamente, per vedere il tutto in azione.

Se volete fermare il lavoro, potreste, ad esempio, aggiungere un bottone e poi fare una cosa del genere:

wt->stop = true;

Direi che è tutto.

Come al solito, prendete spunto e migliorate / implementate il tutto come più vi serve / piace!

Enjoy!