Qt - Updating Main Window with Second Thread

Qt - updating main window with second thread


but the problem is that, i cannot reach the
ana->ui->horizontalLayout_4->addWidget(label);

Put your UI modifications in a slot in your main window, and connect a thread signal to that slot, chances are it will work. I think only the main thread has access to the UI in Qt. Thus if you want GUI functionality, it must be there, and can be only signaled from other threads.

OK, here is a simple example. BTW, your scenario doesn't really require to extend QThread - so you are better off not doing it, unless you really have to. That is why in this example I will use a normal QThread with a QObject based worker instead, but the concept is the same if you subclass QThread:

The main UI:

class MainUI : public QWidget
{
Q_OBJECT

public:
explicit MainUI(QWidget *parent = 0): QWidget(parent) {
layout = new QHBoxLayout(this);
setLayout(layout);
QThread *thread = new QThread(this);
GUIUpdater *updater = new GUIUpdater();
updater->moveToThread(thread);
connect(updater, SIGNAL(requestNewLabel(QString)), this, SLOT(createLabel(QString)));
connect(thread, SIGNAL(destroyed()), updater, SLOT(deleteLater()));

updater->newLabel("h:/test.png");
}

public slots:
void createLabel(const QString &imgSource) {
QPixmap i1(imgSource);
QLabel *label = new QLabel(this);
label->setPixmap(i1);
layout->addWidget(label);
}

private:
QHBoxLayout *layout;
};

... and the worker object:

class GUIUpdater : public QObject {
Q_OBJECT

public:
explicit GUIUpdater(QObject *parent = 0) : QObject(parent) {}
void newLabel(const QString &image) { emit requestNewLabel(image); }

signals:
void requestNewLabel(const QString &);
};

The worker object is created and moved to another thread, then connected to the slot that creates the labels, then its newLabel method is invoked, which is just a wrapper to emit the requestNewLabel signal and pass the path to the image. The signal is then passed from the worker object/thread to the main UI slot along with the image path parameter and a new label is added to the layout.

Since the worker object is created without parent in order to be able to move it to another thread, we also connect the thread destroyed signal to the worker deleteLater() slot.

QT Update variable of worker thread from mainwindow

You're trying to perform an operation periodically whilst still processing events -- that's simply calling out for a QTimer.

(Note that the following code is untested.)

Change your Worker class to make use of a QTimer rather than a loop that blocks the event queue...

class Worker: public QObject {
Q_OBJECT;
public:
Worker (QString path, int id);
~Worker();
public slots:
void readVideo(QString path = "");
void get_from_main(QString path);
signals:
// frame and index of label which frame will be displayed
void frameFinished(cv::Mat frame, int index);
void finished(int index);
private:
QString filepath;
int index;
QTimer timer;
cv::VideoCapture cap;
};

Worker::Worker (QString path, int id)
: filepath(path)
, index(id)
, timer(this)
, cap(filepath.toStdString())
{

/*
* Connect QTimer::timeout to the readVideo slot that will read a
* single frame on each signal at 30ms intervals.
*/
connect(&timer, &QTimer::timeout, this, &Worker::readVideo);
timer.start(30);
}

Worker::~Worker ()
{
}

void Worker::get_from_main (QString path)
{
qDebug() << "updating";
filepath = path;
cap = cv::VideoCapture(filepath);
if (!cap.isOpened()) {
qDebug() << "Can't open video file " << filepath;
emit finished(index);
}
}

void Worker::readVideo ()
{
cv::Mat frame;
cap >> frame;
if (frame.empty())
{
frame = cv::Mat(cv::Size(720, 576), CV_8UC3, cv::Scalar(192, 0, 0));
emit frameFinished(frame, index);
break;
}

emit frameFinished(frame.clone(), index);
}

Now Worker::readVideo simply reads a single frame from the capture and then returns to the event loop.

Also remove the line...

connect(threads[i], SIGNAL(started()), workers[i], SLOT(readVideo()));

from MainWindow::init. As I stated above this is untested and probably needs a lot more error checking. But it should give you a good idea as to what's required.

Qt How to update main windows from running thread , the right way

I suspect that since you create the timer in the QThread::run() method the slot the timer connects to is being called in the context of the main thread.

You don't need to subclass QThread to run code in its own thread.

Just subclass a QObject, add the functionality you want, create a QThread instance, start it and use the QObject::moveToThread() method to set the QObject's thread affinity to the new thread.

    worker = new WorkerClass;
connect(worker,SIGNAL(response(QString)),this,SLOT(response(QString)));
QThread *t = new QThread;
t->start();
worker->moveToThread(t);

//Start it either like this or by emitting a signal connected to the startWorking slot
QMetaObject::invokeMethod(worker,"startWorking",Qt::QueuedConnection);

Qt Gui not updated because of threading issue

Every GUI is executed in an infinite loop, so Qt also uses it, but the blocking tasks generate that the loop is not executed correctly, showing inadequate behaviors like the one you observe.

If one wants to execute blocking tasks it is advisable to execute it in another thread for it Qt provides several possibilities:

  • QThread

  • QThreadPool and QRunnable

  • QtConcurrent

I recommend the following link for you to choose the right option for your case.

If you want to update the GUI view with the information generated in another thread it is advisable to use signals and slots, or use QtConcurrent.

GUI Thread and Worker Thread

As mentioned, each program has one thread when it is started. This
thread is called the "main thread" (also known as the "GUI thread" in
Qt applications). The Qt GUI must run in this thread. All widgets and
several related classes, for example QPixmap, don't work in secondary
threads. A secondary thread is commonly referred to as a "worker
thread" because it is used to offload processing work from the main
thread.

Another way is to force the GUI to update for this we can use qApp->processEvents().

References:

  • http://doc.qt.io/qt-5/thread-basics.html
  • http://doc.qt.io/qt-5/threads-technologies.html

How to update value in progressBar in another thread in QT c++

Use a signal/slot combination, specifically the queued connection type (Qt::ConnectionType). So, along those lines:

void MainWindow::Somefunc()
{
emit computationProgress(progress);
}

void MainWindow::setProgress(int progress)
{
ui->progressBar->setValue(progress);
}

void MainWindow::on_pushButton_3_clicked()
{
auto futureWatcher = new QFutureWatcher<void>(this);
connect(futureWatcher, &QFutureWatcher<void>::finished, futureWatcher, &QFutureWatcher<void>::deleteLater);
auto future = QtConcurrent::run( [=]{ SomeFunc(); });

futureWatcher->setFuture(future);
connect(this, &MainWindow::computationProgress, this, &MainWindow::setProgress, Qt::QueuedConnection);
}

Updating GUI from different thread

Given you only need to run one function i would recommend QtConcurrent. Extract form the docs:

Running a Function in a Separate Thread

To run a function in another thread, use QtConcurrent::run():

extern void aFunction();
QFuture<void> future = QtConcurrent::run(aFunction);

This will run aFunction in a separate thread obtained from the default QThreadPool. You can use the QFuture and QFutureWatcher classes to monitor the status of the function.

Qt multithreading: How to update two QLabels?

You can use signals to communicate threads together.

define a signal in your second thread like this:

signals:
void changeLabelOnMain(QString text);

emit your signal in second thread:

emit changeLabelOnMain("some text");

connect your signal to a slot in your main :

 SecondClassName secondObject= new SecondClassName();
connect(secondObject, &SecondClassName::changeLabelOnMain, this, &MainClassName::YourSlotName);

this is a simple example of making threads communicate together.

How to manage mainwindow from QThread in Qt

You should use Qt's signal/slot mechanism. The thread will emit a signal, that new data has been read. Any interested object can connect to that signal, and perform actions depending on it.

This also works across thread-boundaries, as in your example. In Qt, it is required that only the main-thread interacts with UI elements.

Here is an outline:

// Your mainwindow:
class MyWindow : public QMainWindow {
Q_OBJECT
// as needed
private slots:
void setLabel(const QString &t) { m_label->setText(t); }
};


// Your thread
class MyThread: public QThread {
Q_OBJECT
// as needed
signals:
void statusUpdated(const QString &t);
};

// in your loop
if (status != 0) {
emit statusUpdated("New Status!");
}

// in your mainwindow
_thread = new MyThread;
connect(_thread, &MyThread::statusUpdated, this, &MyWindow::setLabel);
_thread->start();


Related Topics



Leave a reply



Submit