ApAp
ApAp

Reputation: 1

QThreading in QT and update GUI at the same time

Hello Im trying to print my result from a thread to textBrower in qtwidget QT but I cant, either I get error or the program wont compile is there another way ?? how can I change the textBrowser outside of the class's function??

PS BTW I need to keep looping inside the thread cuz actually im getting some data from uart in final program (in this code i just wanna print "lol" which eventually I wanna change it with some other code which take data ) so I cant come out of the loop eventually i want use some process from another library and show the resault REAL TIME

bool stop;

out::out(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::out)
{
    ui->setupUi(this);
    ui->textBrowser->setText("lol");
}

out::~out()
{
    delete ui;
}
///////////////////////////////////////////////

class t : public out, public QThread
{
public:
    void run()
    {
        while(1){
            qDebug()<<"hi"<<i;
           // ui->textBrowser->setText("lol"); I tried to use this but it didnt worked
            if(stop==true){
            QThread::exec();
                    }
        }
    }
};


void mamad1(void){   //this function get called from another cpp file and its for starting the thread
    stop=false;
    t *b = new t;
    b->start();


}

void out::on_pushButton_clicked() // a push button to stop the thread
{
    stop=true;
} 

I tried make ui in out.h file a public property and use ui->textBrowser->setText("lol"); but it didn't worked the program freezed and i got this error : (Parent is QTextDocument(0x208d812a510), parent's thread is QThread(0x208d6816de0), current thread is QThread(0x208dac94e10)

I tried to use connect() also and didn't worked or I didn't use it right

Upvotes: 0

Views: 379

Answers (1)

siadynamic
siadynamic

Reputation: 41

The concept of QT the GUI thread is called the master thread. This thread has an event queue. In general, this event queue is populated by internal and external events. Moreover, the queue between threads is an efficient approach for thread communication. The same is true for the worker threads as well. When you call the start method through the worker threads their signal queue is created and ready to be consumed by the corresponding thread.

Let's come back to your question. The solution is simple. From your worker threads, you can just send a signal to the master thread. The master thread will see this event that modifies the GUI item and forwards this signal/event to the slot that is responsible to take action accordingly. Just define a signal in your worker thread and connect your master thread to this signal with a given slot.

UPDATE FOR SOLUTION

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <MyWorkerThread.h>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void setWorkerThread(MyWorkerThread* thread);
    void connectToSignals();

public slots:
    void handleTextBoxSignal(const QString& text);

private:
    Ui::MainWindow *ui;
    MyWorkerThread* m_worker_thread{nullptr};
};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

void MainWindow::setWorkerThread(MyWorkerThread* thread)
{
    m_worker_thread = thread;
}

void MainWindow::connectToSignals()
{
    QObject::connect(m_worker_thread,
                     SIGNAL(changeTextOnUI(QString)),
                     this,
                     SLOT(handleTextBoxSignal(QString)));
}

void MainWindow::handleTextBoxSignal(const QString& text)
{
    qDebug() << "Text box change signal received with text: " << text;
    auto text_box = findChild<QTextEdit*>("myTxtEdit");
    if(text_box != nullptr)
    {
        text_box->setText(text);
    }
}

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

MyWorkerThread.h

#ifndef MYWORKERTHREAD_H
#define MYWORKERTHREAD_H

#include <QObject>
#include <QThread>

class MyWorkerThread : public QThread
{
    Q_OBJECT

public:
    MyWorkerThread(QObject* parent = nullptr);
    void stopThread();

signals:
    void changeTextOnUI(const QString& text);

private:
    void run() override;

    bool m_exit{false};
};

#endif // MYWORKERTHREAD_H

MyWorkerThread.cpp

#ifndef MYWORKERTHREAD_H
#define MYWORKERTHREAD_H

#include <QObject>
#include <QThread>

class MyWorkerThread : public QThread
{
    Q_OBJECT

public:
    MyWorkerThread(QObject* parent = nullptr);
    void stopThread();

signals:
    void changeTextOnUI(const QString& text);

private:
    void run() override;

    bool m_exit{false};
};

#endif // MYWORKERTHREAD_H

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;

    MyWorkerThread* worker_thread = new MyWorkerThread();
    w.setWorkerThread(worker_thread);
    w.connectToSignals();
    worker_thread->start();

    w.show();
    return a.exec();
}

In the above code, you see the full solution to your problem. In the worker thread, we increment a static counter and concatenate it with a string and send it to the master thread that manages the UI elements. We emit a signal and this signal is landed on the slot. This way the master thread updates the text box on the screen per 5 seconds. The worker thread sleeps for 5 seconds at each iteration.

Upvotes: 2

Related Questions