Calling JS from a loop in C++ and keeping it responsive
-
I'm using Qt Webkit to show a map (with leaflet.js). When a JS event happens (a certain button is clicked), I call some C++ code (by directly calling a function in a C++ object that has been added to the JS environment).
This code runs a loop and on each iteration fires two signals. One signal provides the location of a marker and the other signal provides a percentage update.This invokes some javascript code which creates a marker and should print out the percentage to the console. However, the
console.log
calls are queued until the end of the loop and only then processed. How can I make this happen async?javascript code:
MT.Progress = function(){ BRIDGE.progressUpdated.connect(this.update.bind(this)); //BRIDGE is the C++ object that has been added to the javascript window } MT.Progress.prototype.update = function(perc){ console.log(perc); } MT.Markers = function(){ var pb = new MT.Progress(); BRIDGE.markerUpdated.connect(this.addMarker.bind(this)); // Call code to loop through markers BRIDGE.getMarkers(); } MT.Markers.prototype.addMarker = function(){ var marker = new PruneCluster.Marker(lat, lon); this.clusterLayer.RegisterMarker(marker); }
C++ function:
void Bridge::getMarkers() { QSqlQuery query(db); query.prepare("SELECT lat, lon FROM locations"); query.exec(); int size = query.size(); if (size > 0) { int increment = 0.01 * size; int percent = 0; int countdown = increment; while(query.next()){ countdown--; if (countdown==0) { percent++; emit progressUpdated(percent); countdown = increment; } double lat = query.value(1).toDouble(); double lng = query.value(2).toDouble(); emit markerUpdated(lat, lng); } emit updatesFinished(); } }
-
Hi. The correct solution would be to use threads, but you can try adding
QCoreApplication::processEvents();
http://doc.qt.io/qt-5/qcoreapplication.html#processEvents
to your loop and see whether it gives some satisfying performance.
-
@Leonardo
Thanks for the reply.
I had moved the Bridge class to a separate thread with no luck - but I suspect that because the JS is calling the functions directly (usingQ_INVOKABLE
) they are not being executed in separate thread.Would the answer be to move all logic out of the
Bridge
class and simply use that class to pass signals along (from javascript to the logic class and back again?). It feels a bit clunky to have a class that just regurgitates signals; would it affect performance? -
Hi. You're right. You should use signals. When you call the method directly, it blocks the UI's thread. You need to keep it free to perform the rendering. Take a look at this sample:
http://doc.qt.io/qt-5/qthread.html#details
You should do something like that.
-
As far as I can see, there is no way for the JS to send a signal, so it seems as though I will need this intemediary message passer class? JS calls the method on this class directly which sends a signal...
I wonder if using the newer QWebEngine provides a more elegant solution around this?