QFutureWatcher not watching???
-
I can't get get hold of signals (resultReadyAt, finished) coming from a QFutureWatcher. The different tasks running in different thread run allright, but I get no feedback from the overseeing QFutureWatcher, despite having connected the appropriate signals.
My demo class, MyClass, is very simple. It holds an integer _n that is initialised in the constructor, and another integer _ms that is initialised to -1:class MyClass { private: int _n; int _ms = -1; public: MyClass() = default; MyClass( int n ) : _n( n ) {} public: int n() const { return _n; } void setms( int ival ) { _ms = ival; } };
Then I have a worker function, that operates on an object of class MyClass. It reads the integer _n after which it sets a value for the other integer, _ms. It sleeps for _ms milliseconds, just to emulate a real computation.
void worker( MyClass& myclass ) { qDebug() << "line:" << __LINE__ << "task" << myclass.n() << "..."; MyClass newobj( myclass.n() ); int ms = 1000 * myclass.n(); std::this_thread::sleep_for( std::chrono::milliseconds( ms ) ); newobj.setms( ms ); qDebug() << "line:" << __LINE__ << "task" << myclass.n() << "done"; }
Finally, the class Scheduler below has a vector with various MyClass objects as well as a QFutureWatcher. The method called "runAll" connect two signals from the watcher to two verbose routines in the Scheduler after which the job is launched.
class Scheduler : public QObject { Q_OBJECT using watchertype = QFutureWatcher<void>; public: std::vector<MyClass> _taskvec; // QList<MyClass*> gives the same result watchertype _watcher; public: void runAll() { QObject::connect( &_watcher, &watchertype::resultReadyAt, this, &Scheduler::taskReady ); QObject::connect( &_watcher, &watchertype::finished, this, &Scheduler::finished ); // Launch the job. _watcher.setFuture( QtConcurrent::map( _taskvec, worker ) ); qDebug() << "line:" << __LINE__ << "wait for finish ..."; _watcher.waitForFinished(); qDebug() << "line:" << __LINE__ << "... all done!"; } public slots: void taskReady( int n ) { std::cerr << "taskReady: n=" << n << std::endl; } void finished() { std::cerr << "finished!" << std::endl; } };
The main program:
int main( int argc, char* argv[] ) { // QCoreApplication qappl( argc, argv ); // has no effect Scheduler scheduler; scheduler._taskvec.push_back( MyClass( 1 ) ); scheduler._taskvec.push_back( MyClass( 7 ) ); scheduler._taskvec.push_back( MyClass( 3 ) ); scheduler._taskvec.push_back( MyClass( 2 ) ); scheduler._taskvec.push_back( MyClass( 0 ) ); scheduler.runAll(); }
The output: no message from Scheduler::finished() or Scheduler::taskReady().
line: 68 wait for finish ... line: 39 task 1 ... line: 39 task 7 ... line: 39 task 2 ... line: 39 task 3 ... line: 39 task 0 ... line: 46 task 0 done line: 46 task 1 done line: 46 task 2 done line: 46 task 3 done line: 46 task 7 done line: 70 ... all done!
All tasks seems to be ok. But not any printing coming from the routines finished and taskReady, despite being connected to the watcher. What am I doing wrong?
For completeness, in case you want to see it yourself, I will copy below the full source file (one file, main.cpp) as well as a CMakeList.txt. You might need to change the Qt6-location.
main.cpp://///////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// #include <chrono> #include <iostream> #include <string> #include <thread> #include <vector> #include <QtConcurrent/QtConcurrent> #include <QtCore/QCoreApplication> #include <QtCore/QDebug> #include <QtCore/QObject> /*############################################################################*/ /*############################################################################*/ class MyClass { private: int _n; int _ms = -1; public: MyClass() = default; MyClass( int n ) : _n( n ) {} public: int n() const { return _n; } void setms( int ival ) { _ms = ival; } }; /*############################################################################*/ /*############################################################################*/ void worker( MyClass& myclass ) { qDebug() << "line:" << __LINE__ << "task" << myclass.n() << "..."; MyClass newobj( myclass.n() ); int ms = 1000 * myclass.n(); std::this_thread::sleep_for( std::chrono::milliseconds( ms ) ); newobj.setms( ms ); qDebug() << "line:" << __LINE__ << "task" << myclass.n() << "done"; } /*############################################################################*/ /*############################################################################*/ class Scheduler : public QObject { Q_OBJECT using watchertype = QFutureWatcher<void>; public: std::vector<MyClass> _taskvec; // QList<MyClass*> gives the same result watchertype _watcher; public: void runAll() { QObject::connect( &_watcher, &watchertype::resultReadyAt, this, &Scheduler::taskReady ); QObject::connect( &_watcher, &watchertype::finished, this, &Scheduler::finished ); // Launch the job. _watcher.setFuture( QtConcurrent::map( _taskvec, worker ) ); qDebug() << "line:" << __LINE__ << "wait for finish ..."; _watcher.waitForFinished(); qDebug() << "line:" << __LINE__ << "... all done!"; } public slots: void taskReady( int n ) { std::cerr << "taskReady: n=" << n << std::endl; } void finished() { std::cerr << "finished!" << std::endl; } }; #include "main.moc" /*############################################################################*/ /*############################################################################*/ int main( int argc, char* argv[] ) { // QCoreApplication qappl( argc, argv ); // has no effect Scheduler scheduler; scheduler._taskvec.push_back( MyClass( 1 ) ); scheduler._taskvec.push_back( MyClass( 7 ) ); scheduler._taskvec.push_back( MyClass( 3 ) ); scheduler._taskvec.push_back( MyClass( 2 ) ); scheduler._taskvec.push_back( MyClass( 0 ) ); scheduler.runAll(); } // -- eof --
CMakeList.txt:
cmake_minimum_required(VERSION 3.17) project( qfuturewatcher VERSION 1.0 DESCRIPTION "minimul program using QFeatureWatcher" LANGUAGES CXX ) # qt components find_package( Qt6 COMPONENTS Core Concurrent REQUIRED ) set(CMAKE_AUTOMOC ON) # For meta object compiler set(CMAKE_AUTORCC ON) # Resource files set(CMAKE_AUTOUIC ON) # UI files FILE( GLOB sources "*.cpp" "*.qrc" ) add_executable( ${PROJECT_NAME} ${sources} ) target_link_libraries( ${PROJECT_NAME} Qt::Concurrent Qt::Core ) #===end-of-file===
-
@BwvB said in QFutureWatcher not watching???:
The other slot, supposedly being triggered by the resultReadyAt(int) signal, remains silent however.
_watcher.setFuture( QtConcurrent::map( _taskvec, worker ) );
QtConcurrent::mapped()
takes an input sequence and a map function. This map function is then called for each item in the sequence, and a new sequence containing the return values from the map function is returned.If you want to modify a sequence in-place, use
QtConcurrent::map()
.
Note that the return value and return type of the map function are not used.
UsingQtConcurrent::map()
is similar to usingQtConcurrent::mapped()
:
Since the sequence is modified in place,QtConcurrent::map()
does not return any results viaQFuture
. However, you can still useQFuture
andQFutureWatcher
to monitor the status of the map.So with
map()
you can only getfinished
, notresultReadyAt
. Try usingmapped()
for the latter?@JonB You are right! When I tried with QtConcurrrent::mapped (a few other changes are then needed as well) the behaviour is as expected, with the resultReadyAt() signal being emitted. Thanks very much for the suggestion. I had read the manual page as well, but never interpreted the way you did!
I will close the issue, but I wonder if this is not just a bug in Qt. After all, whether it is map or mapped, one still wants to follow progress, I would say!Thanks again.
Bertwim -
Hi,
Two things that jumps to my eyes: the use of waitForFinished -> you are blocking the event loop so it can't process anything and you don't have an event loop running since you don't have a QCoreApplication instance nor call exec on it so it's even surprising that it does not crash.
-
Hi,
Two things that jumps to my eyes: the use of waitForFinished -> you are blocking the event loop so it can't process anything and you don't have an event loop running since you don't have a QCoreApplication instance nor call exec on it so it's even surprising that it does not crash.
@SGaist Thanks for your response. It partly helped. I have now embedded the main code in an event loop, as you suggested. I also put, in the finished() slot, a call to quit the application.
The call to waitForFinished() seems unneccessary (no difference with or without it) so I left it out.
This partly worked! The output now becomes:line: 39 task 7 ... line: 39 task 3 ... line: 39 task 1 ... line: 39 task 2 ... line: 39 task 0 ... line: 46 task 0 done line: 46 task 1 done line: 46 task 2 done line: 46 task 3 done line: 46 task 7 done ==>finished!
That is, the finished() slot is being called indeed. The other slot, supposedly being triggered by the resultReadyAt(int) signal, remains silent however.
Full code follows now:#include <chrono> #include <iostream> #include <string> #include <thread> #include <vector> #include <QtConcurrent/QtConcurrent> #include <QtCore/QCoreApplication> #include <QtCore/QDebug> #include <QtCore/QObject> /*############################################################################*/ /*############################################################################*/ class MyClass { private: int _n; int _ms = -1; public: MyClass() = default; MyClass( int n ) : _n( n ) {} public: int n() const { return _n; } void setms( int ival ) { _ms = ival; } }; /*############################################################################*/ /*############################################################################*/ void worker( MyClass& myclass ) { qDebug() << "line:" << __LINE__ << "task" << myclass.n() << "..."; MyClass newobj( myclass.n() ); int ms = 1000 * myclass.n(); std::this_thread::sleep_for( std::chrono::milliseconds( ms ) ); newobj.setms( ms ); qDebug() << "line:" << __LINE__ << "task" << myclass.n() << "done"; } /*############################################################################*/ /*############################################################################*/ class Scheduler : public QObject { Q_OBJECT using watchertype = QFutureWatcher<void>; public: std::vector<MyClass> _taskvec; // QList<MyClass*> gives the same result watchertype _watcher; public: void runAll() { QObject::connect( &_watcher, &watchertype::resultReadyAt, this, &Scheduler::taskReady ); QObject::connect( &_watcher, &watchertype::finished, this, &Scheduler::finished ); // Launch the job. _watcher.setFuture( QtConcurrent::map( _taskvec, worker ) ); } public slots: void taskReady( int n ) { std::cerr << "==>taskReady: n=" << n << std::endl; } void finished() { std::cerr << "==>finished!" << std::endl; QCoreApplication::instance()->quit(); } }; #include "main.moc" /*############################################################################*/ /*############################################################################*/ int main( int argc, char* argv[] ) { QCoreApplication qappl( argc, argv ); Scheduler scheduler; scheduler._taskvec.push_back( MyClass( 1 ) ); scheduler._taskvec.push_back( MyClass( 7 ) ); scheduler._taskvec.push_back( MyClass( 3 ) ); scheduler._taskvec.push_back( MyClass( 2 ) ); scheduler._taskvec.push_back( MyClass( 0 ) ); scheduler.runAll(); qappl.exec(); } // -- eof --
-
@SGaist Thanks for your response. It partly helped. I have now embedded the main code in an event loop, as you suggested. I also put, in the finished() slot, a call to quit the application.
The call to waitForFinished() seems unneccessary (no difference with or without it) so I left it out.
This partly worked! The output now becomes:line: 39 task 7 ... line: 39 task 3 ... line: 39 task 1 ... line: 39 task 2 ... line: 39 task 0 ... line: 46 task 0 done line: 46 task 1 done line: 46 task 2 done line: 46 task 3 done line: 46 task 7 done ==>finished!
That is, the finished() slot is being called indeed. The other slot, supposedly being triggered by the resultReadyAt(int) signal, remains silent however.
Full code follows now:#include <chrono> #include <iostream> #include <string> #include <thread> #include <vector> #include <QtConcurrent/QtConcurrent> #include <QtCore/QCoreApplication> #include <QtCore/QDebug> #include <QtCore/QObject> /*############################################################################*/ /*############################################################################*/ class MyClass { private: int _n; int _ms = -1; public: MyClass() = default; MyClass( int n ) : _n( n ) {} public: int n() const { return _n; } void setms( int ival ) { _ms = ival; } }; /*############################################################################*/ /*############################################################################*/ void worker( MyClass& myclass ) { qDebug() << "line:" << __LINE__ << "task" << myclass.n() << "..."; MyClass newobj( myclass.n() ); int ms = 1000 * myclass.n(); std::this_thread::sleep_for( std::chrono::milliseconds( ms ) ); newobj.setms( ms ); qDebug() << "line:" << __LINE__ << "task" << myclass.n() << "done"; } /*############################################################################*/ /*############################################################################*/ class Scheduler : public QObject { Q_OBJECT using watchertype = QFutureWatcher<void>; public: std::vector<MyClass> _taskvec; // QList<MyClass*> gives the same result watchertype _watcher; public: void runAll() { QObject::connect( &_watcher, &watchertype::resultReadyAt, this, &Scheduler::taskReady ); QObject::connect( &_watcher, &watchertype::finished, this, &Scheduler::finished ); // Launch the job. _watcher.setFuture( QtConcurrent::map( _taskvec, worker ) ); } public slots: void taskReady( int n ) { std::cerr << "==>taskReady: n=" << n << std::endl; } void finished() { std::cerr << "==>finished!" << std::endl; QCoreApplication::instance()->quit(); } }; #include "main.moc" /*############################################################################*/ /*############################################################################*/ int main( int argc, char* argv[] ) { QCoreApplication qappl( argc, argv ); Scheduler scheduler; scheduler._taskvec.push_back( MyClass( 1 ) ); scheduler._taskvec.push_back( MyClass( 7 ) ); scheduler._taskvec.push_back( MyClass( 3 ) ); scheduler._taskvec.push_back( MyClass( 2 ) ); scheduler._taskvec.push_back( MyClass( 0 ) ); scheduler.runAll(); qappl.exec(); } // -- eof --
@BwvB said in QFutureWatcher not watching???:
The other slot, supposedly being triggered by the resultReadyAt(int) signal, remains silent however.
_watcher.setFuture( QtConcurrent::map( _taskvec, worker ) );
QtConcurrent::mapped()
takes an input sequence and a map function. This map function is then called for each item in the sequence, and a new sequence containing the return values from the map function is returned.If you want to modify a sequence in-place, use
QtConcurrent::map()
.
Note that the return value and return type of the map function are not used.
UsingQtConcurrent::map()
is similar to usingQtConcurrent::mapped()
:
Since the sequence is modified in place,QtConcurrent::map()
does not return any results viaQFuture
. However, you can still useQFuture
andQFutureWatcher
to monitor the status of the map.So with
map()
you can only getfinished
, notresultReadyAt
. Try usingmapped()
for the latter? -
@BwvB said in QFutureWatcher not watching???:
The other slot, supposedly being triggered by the resultReadyAt(int) signal, remains silent however.
_watcher.setFuture( QtConcurrent::map( _taskvec, worker ) );
QtConcurrent::mapped()
takes an input sequence and a map function. This map function is then called for each item in the sequence, and a new sequence containing the return values from the map function is returned.If you want to modify a sequence in-place, use
QtConcurrent::map()
.
Note that the return value and return type of the map function are not used.
UsingQtConcurrent::map()
is similar to usingQtConcurrent::mapped()
:
Since the sequence is modified in place,QtConcurrent::map()
does not return any results viaQFuture
. However, you can still useQFuture
andQFutureWatcher
to monitor the status of the map.So with
map()
you can only getfinished
, notresultReadyAt
. Try usingmapped()
for the latter?@JonB You are right! When I tried with QtConcurrrent::mapped (a few other changes are then needed as well) the behaviour is as expected, with the resultReadyAt() signal being emitted. Thanks very much for the suggestion. I had read the manual page as well, but never interpreted the way you did!
I will close the issue, but I wonder if this is not just a bug in Qt. After all, whether it is map or mapped, one still wants to follow progress, I would say!Thanks again.
Bertwim