Question

I want to set the text of a label on main window when a function in code is running. But the label does not change until the function ends.

for (int i = 0; i < files.size(); ++i) {

    ui->label->setText(files[i]);
    myfoo();

}
Was it helpful?

Solution

The reason the label isn't seen to update is that the main thread, which updates the GUI, is busy in your for loop and not getting time to process events which, amongst other things, causes the GUI widgets to be redrawn.

While you could use QApplication::processEvents, this is not ideal, especially if many events are received during the processing of your function.

The proper way to handle this is to create a separate thread (QThread) and an object, derived from QObject, which will do work while sending messages to the main (GUI) thread to update the label.

Rather than repeating the code, I suggest you read this article on how to use QThread properly. It's not that difficult.

Then you would change your function, which is now in the object on the 2nd thread, to something like this: -

for (int i = 0; i < files.size(); ++i) 
{
    // calling UpdateLabel signal, which connects to an object on the main thread
    UpdateLabel(files[i]);        
    myfoo();  
}

Assuming the signal from the Worker class has been connected to a slot in an object on the main thread, such as QMainWindow, you'd receive the text in the slot and update the label: -

void QMainWindow::UpdateLabel(const QString text)
{
    ui->label->setText(text);
}

OTHER TIPS

This happens because your loop is blocking the event loop - you never return to the event loop. When most any slot is called in your code, it is called from the event loop. The event loop can only proceed when you return from the slot, or from the QObject::event method.

With Qt 5 and C++11, it's very easy to update the file list from a separate thread.

QtConcurrent::run([this] {
  // This code runs in a separate thread
  for (int i = 0; i < files.size(); ++i) {
    // This is safe for cross-thread use
    QMetaObject::invokeMethod(ui->label, "setText", files[i]);
    myfoo();
  }
});

The implementation of myfoo() must be thread safe, and cannot access any widgets directly. It can still safely "write" to widgets by using the invokeMethod mechanism, as it is cross-thread.

A reasonably good starting point in demonstrating how to write fully multithreaded file access that doesn't block the gui is given in my photomosaic Qt example, implemented in a mere 300 lines.

You could execute your for-loop in a separate thread that only sends a signal to the gui thread, which then calls label->setText() in the appropriate slot. (Use Qt::QueuedConnection)

Or try this:

for (int i = 0; i < files.size(); ++i) {
    ui->label->setText(files[i]);
    QApplication::processEvents();
    myfoo();  
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top