Important: Please read the Qt Code of Conduct -

Updating a QProgressBar from within map function in QtConcurrent::mappedReduce

  • We're using Qt 5.3.1 on Windows.

    I'm refactoring a file importer to run using "QtConcurrent::mappedReduce": and it's going quite well, however, I'm having difficulty with our progress bar.

    We've got a "QProgressBar ": (originally) would update as the importer read through the file it was reading. However, I am not memory mapping the file and then splitting up the work of reading through it in my map function.

    I would like the threads to occasionally increment the position of the progress bar (say, every 100 lines). I discovered that I can't simply keep a pointer to the progress bar and set the position as I'm running in a thread other than the gui thread. Everywhere I've read says that I should use signals and slots.

    I pass in a list of QObject subclasses to the map function that have a signal connected to a slot on our progress bar handler. Every 100 lines or so I emit the signal to update the progress bar. The debugger is showing that i'm emitting the signal. However, the slot is only running after the reduce function has been called (once it's no longer relevant!). I believe that these are being set up as queued connections and they are only scheduled to run once control returns to the main thread which is after the threads have completed their work.

    Is there something specific I must do in order to get a somewhat responsive/accurate progress bar? Should I simply set the progress bar to 0 so it displays a continuously moving bar (ie: no indication how far we are in processing)?

  • Lifetime Qt Champion


    I may be wrong but isn't "QFutureWatcher": what you are looking for ?

  • If I do something like this :

    @QFuture<Thing> results = QtConcurrent::mappedReduced(data, map, reduce, QtConcurrent::OrderedReduce);
    QFutureWatcher<Thing> watcher;
    while (!watcher.isFinished())

    My position updates to the progress bar don't appear. However, if I set the progress bar to a range of 0 then I do see it moving at the very least.

  • No, no no... Don't do this syncrhronously. The whole idea of using a QFutures is asychronic programming. And now you are bussy-looping the application! Don't do that.

    You want to keep your QFuture in a member variable somewhere. Then, you assign the result of your mappedReduced to that member , and create a QFutureWatcher for it (on the heap). Then, you connect to the signals of the watcher.

    Something like:
    //in declaration
    QFuture<Thing> m_results;

    //in implementation
    m_results = QtConcurrent::mappedReduced(data, map, reduce, QtConcurrent::OrderedReduce);
    QFutureWatcher* watcher = new QFutureWatcher(this);
    connect(watcher, SIGNAL(progressValueChanged(int),
    m_ui->progressBar, SLOT(setValue(int)));
    //also hook up range and text signals
    connect(watcher, SIGNAL(finished()),
    this, SLOT(handleMapReduceFinished()));
    connect(watcher, SIGNAL(finished()),
    watcher, SLOT(deleteLater()));

    Of course, you can also use the Qt5 syntax with the connects, and you can choose to use C++/11 lambda's instead of separate slots for handling the result of the operation on the finished() signal.

Log in to reply