PDA

View Full Version : Help with understanding QFutureWatcher::progressTextChanged(const QString & progressT



aubie83
25th February 2020, 19:28
I've been working on udpdating some old Qt 4.8 code I inherited. The application is fairly simple, it consists of a GUI that allows the user to select up to 1,000 files and apply some specialized filters to the files. Once eveything is set, you hit run and wait. Files are fairly large and take anywhere from 1 to 5 minutes to process. The GUI does them sequentially.

To speed this process up I'm using QtConcurrent::run to actually run the filter process and it works great. I connect the watcher to SIGNAL(started) and to SIGNAL(finished) to routines that keep up with when files start and finish. This work exactly as I would expect them to.

To make the GUI a little more user friendly I also want to use the progressTextChanged and progressValueChanged SIGNALS provided by QFutureWatcher. I cannot get these to work and have worn google out trying to find an example of how to use them. The only example I find is here (https://www.qtcentre.org/threads/21641-How-to-use-progressTextChanged()-signal-in-QFutureWatcher). I tried to emulate this but couldn't get it to work.

The Qt documentation for QFutureWatcher says this "Status changes are reported via the started(), finished(), canceled(), paused(), resumed(), resultReadyAt(), and resultsReadyAt() signals. Progress information is provided from the progressRangeChanged(), void progressValueChanged(), and progressTextChanged() signals." As I stated earlier the started() and finished() signals work great.

In the section on QFutureWather::progressText() it says "Be aware that not all computations provide a textual representation of the progress, and as such, this function may return an empty string." So I think this may be where my problem may be. How do I get the routine I'm threading to have a progressText? Like I said, I've searched everything possible looking for an example and can't find one.

d_stranz
26th February 2020, 17:01
I was curious about this, so I spent a good hour yesterday going through the source code for QtConcurrent and I still have no clue how you can create a process that will send text update signals. What I learned was that these signals originate deep in the bowels of QtConcurrent as customized QEvent events, namely a QFutureCallOutEvent derived type, which internally contain a flag the describes a specific sub-type of the event - start, progress, finished, etc. QFutureWatcher, with the help of a few other internal classes, maps these events onto signals.

These events are generated by the QtConcurrent thread execution mechanism which iterates over the list of things you want to process and which posts these events at various points. I could not find a case in the distributed Qt source code where the thread execution mechanism posts an event containing text. There is a setProgressValueAndText() method in one of the thread control classes, but the only place where I can find this being called is by another method, setProgressValue(), which simply calls it with an empty string. The QFutureWatcher::progressText() method seems to be an unused placeholder in case you want to go to the effort of actually implementing something that uses it.

There are a dozen or more internal ordinary and template C++ classes that implement the QtConcurrent framework. The only way I can imagine implementing something that could also send text events would be to either derive from or implement a new thread execution iterator that could create text-containing events in addition to the normal numeric-only update events. And even then, I don't know how you would interface a process to this mechanism to relay process status. Even in the standard framework, the processes being threaded do not talk to the QtConcurrent framework. It is the thread execution iterator that sends these events based on where the iterator is in processing the list of things to do.

Most of the discussion I have seen around this online say to implement your own set of signals if you need something that the standard QtConcurrent framework doesn't provide.

aubie83
26th February 2020, 17:25
Thanks so much for the reply,
I'm fairly new to Qt and and like I mentioned inherited this very old code. I was fully expecting a reply that really exposed my "Beginner" status. I've got something decent working just using the started and finished signal. I guess I need to remember "the enemy of good is better". I've burned enough hours on this one, and I'm moving on. Maybe someone from Qt has an answer/example.

d_stranz
26th February 2020, 17:50
I was fully expecting a reply that really exposed my "Beginner" status.

I've been using Qt for over 12 years, and on this one I am still at "beginner" status, too. Unlike the ordinary QThread framework, I think QtConcurrent is really designed for massively parallel MapReduce type problems and not generally the things we use QThread and QProcess for. Your use (or the use in your inherited code) is actually probably appropriate if you are indeed dealing with 1000 or more files that need processing.

As you've probably seen from the code online, most of these examples have no GUI at all - they are command line programs that fire off QtConcurrent to essentially run in the background and do a bunch of things in parallel. The feedback provided by QFutureWatcher is really only for applications which start off this processing and which need to provide feedback to an interactive user. It's nice that it is there, but it isn't easily customized.

So, for example, if you wanted to display a list of your files and put a check mark next to each one to show that it had been processed, I do not think that the QtConcurrent framework provides you any way to do that. You don't get any feedback on individual progress, only on the overall progress through the list. You would have to implement your own signalling mechanism to do that.

smyk
27th February 2020, 09:56
I was curious about this, so I spent a good hour yesterday going through the source code for QtConcurrent and I still have no clue how you can create a process that will send text update signals. What I learned was that these signals originate deep in the bowels of QtConcurrent as customized QEvent events, namely a QFutureCallOutEvent derived type, which internally contain a flag the describes a specific sub-type of the event - start, progress, finished, etc. QFutureWatcher, with the help of a few other internal classes, maps these events onto signals.

I found some tests in the Qt code base for QFutureWather (https://code.woboq.org/qt5/qtbase/tests/auto/corelib/thread/qfuturewatcher/tst_qfuturewatcher.cpp.html#_ZN18tst_QFutureWatche r12progressTextEv). I think, the key point is that QtCocurrent does not link directly (or not any time) the worker threads/functions/workers/... with the QFuture instance. It focuses on the whole task and not particular threads within the task. The above link to the test, shows that you should create your own implementation of QFuture (based on undocumented QFutureInterface (https://books.google.de/books?id=kiWGDwAAQBAJ&pg=PA151&lpg=PA151&dq=qfutureinterface&source=bl&ots=3u2L2K3wMV&sig=ACfU3U1hnpXezET2SyBQw4qN9KYnDR-xKg&hl=de&sa=X&ved=2ahUKEwjq9N_ftvHnAhWP5KQKHfz9AZAQ6AEwEnoECBQQA Q#v=onepage&q=qfutureinterface&f=false)) and then emits progress text changes with setProgressValueAndText() method.

d_stranz
27th February 2020, 16:58
shows that you should create your own implementation of QFuture (based on undocumented QFutureInterface)

The QFutureInterface class was exactly where I ended up in my research. It is key to the whole thread execution - QFuture link. But you are also correct that there is no direct connection between the QtConcurrent thread manager and the worker threads themselves. In order for that to happen, the workers would probably have to post QFutureCallOutEvent events to the event loop so the QFutureInterface could pick them up and map them into signals.

Seems like much more work than it is worth unless you are developing a reusable framework instead of a one-off solution for a given project. And I don't trust using undocumented Qt, even if it has been around and pretty stable since Qt4.

smyk
28th February 2020, 10:18
And I don't trust using undocumented Qt, even if it has been around and pretty stable since Qt4.
That's true. But as mentioned in the book i linked above, QFutureInterface is heavy used in QtCreator and the chances are good that it will be documented in Qt6. We will see ... :)

Does it work with posting QFutureCallOutEvent ? Did you tried it allready ?

d_stranz
28th February 2020, 17:37
chances are good that it will be documented in Qt6.

Good to know. Hopefully there will be some non-trivial examples too.



Does it work with posting QFutureCallOutEvent ? Did you tried it allready ?


Haven't tried it, so I don't know. But if you follow the source code, QFutureInterface / QFutureCallOutEvent / QFutureWatcher / QFuture are key components in the whole message relay architecture between QtConcurrent threading and the GUI.

Thanks for the pointer to the book. It looks like a good one, and I'll be ordering it.