Emil Grigore Emil Grigore - 6 months ago 96
Linux Question

Modify Qt GUI from background worker thread

I work in Qt and when I press the button GO I need to continuously send packages to the network and modify the interface with the information I receive.

The problem is that I have a

while(1)
in the button so the button never finishes so the interface is never updated. I thought to create a thread in the button and put the
while(){}
code there.

My question is how can I modify the interface from the thread? (For example how can I modify a textBox from the thread ?

NIA NIA
Answer

Important thing about Qt is that you must work with Qt GUI only from GUI thread, that is main thread.

That's why the proper way to do this is to notify main thread from worker, and the code in main thread will actually update text box, progress bar or something else.

The best way to do this, I think, is use QThread instead of posix thread, and use Qt signals for communicating between threads. This will be your worker, a replacer of thread_func:

class WorkerThread : public QThread {
    void run() {
        while(1) {
             // ... hard work
             // Now want to notify main thread:
             emit progressChanged("Some info");
        }
    }
    // Define signal:
    signals:
    void progressChanged(QString info);
};

In your widget, define a slot with same prototype as signal in .h:

class MyWidget : public QWidget {
    // Your gui code

    // Define slot:
    public slots:
    void onProgressChanged(QString info);
};

In .cpp implement this function:

void MyWidget::onProgressChanged(QString info) {
    // Processing code
    textBox->setText("Latest info: " + info);
}

Now in that place where you want to spawn a thread (on button click):

void MyWidget::startWorkInAThread() {
    // Create an instance of your woker
    WorkerThread *workerThread = new WorkerThread;
    // Connect our signal and slot
    connect(workerThread, SIGNAL(progressChanged(QString)),
                          SLOT(onProgressChanged(QString)));
    // Setup callback for cleanup when it finishes
    connect(workerThread, SIGNAL(finished()),
            workerThread, SLOT(deleteLater()));
    // Run, Forest, run!
    workerThread->start(); // This invokes WorkerThread::run in a new thread
}

After you connect signal and slot, emiting slot with emit progressChanged(...) in worker thread will send message to main thread and main thread will call the slot that is connected to that signal, onProgressChanged here.

P.s. I haven't tested the code yet so feel free to suggest an edit if I'm wrong somewhere