QFuture and QFuturewatcher to update progress
I have a UI form which displays the directory size of some folders, like size of Desktop contents , size of Documents contents etc. Sometimes it is possible for the size of a folder to be in Gbs, so it might take time to compute the size in UI thread, doing which will freeze the UI for long time. So I want the computation to be done in an independent thread and update the UI. My requirement from the independent thread is to do 2 things:
- For each file in the directory when the size is computed, it must indicate progress to UI. This I call it as progressive.
- When the size of all files in the directory is computed, it must notify UI. This I call as final result.
I went through documentation and examples of QFuture and QFutureWatcher, QtConcurrent::run. But I could only achieve the 2nd objective, that is, get the final result. What is that I need to do to get progressive update as well as final result. Documents says, future created with QtConcurrent does not give progress information. So what do I need to do and What do I need to use?
there is a full example here:
However, I have the same problema, I can't update my progress bar, but I think I'm not using QFuture in the proper way.
In the given example, he uses
@const QFuture<void> future = QtConcurrent::map(vector, spin);@
and this is my "unproperly?" way:
@ QFuture<QStandardItemModel *> future = QtConcurrent::run(myLongTask);@
How can I updated the progress "inside" my task function? With signals?
you are right sandeepsastry, it is stated in the "documentation":http://qt-project.org/doc/qt-5.1/qtconcurrent/qtconcurrentrun.html#run: "the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting". If you must use QtConcurrent::run(), signals could indeed be a way to report progress, but if possible you should consider using another function of QtConcurrent or maybe design a worker and move it in a QThread.
Take a look at the docs on QtConcurrent::run:
bq. Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.
Since in this case, only a single function is called in a separate thread, QtConcurrent simply doesn't know enough to give you progress indication. What you want is a structure where you process a lot of objects using a Map or Filter method.
There's basically three modes of usage:
- map means a function is called for every item in your sequence. The map-function you provide can modify the item in-place (no copy of the item is created)
- mapped means a function is called for every item in your sequence. In this case, the map function you provide creates a copy of the item, and modifies it before returning it.
- mappedReduced is similar to mapped, but it may reduce the number of items in the final sequence by filtering out some of them, based on the ReduceFunction.
For your example, I guess it would make sense to have one object instance per folder within your whole folder tree.
First, you collect the folder objects by going through your folder tree, and append them to a "todo" list.
Then, you write a static function (global or class member), which collects and saves the accumulated size of all files within that folder (but without looking into subfolders).
You use QtConcurrent::map with the list and function. The returned QFuture will indicate progress. Use a QFutureWatcher to get signals when progress values change, and when the task is complete.
When all folders are processed, you can calculate the size of the whole folder tree by adding up the saved sizes within each folder. This should be quick even with huge trees.