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 (using Q_INVOKABLE) they are not being executed in separate thread.

    Would the answer be to move all logic out of the Bridgeclass 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?



  • @jramm

    Would the http://doc.qt.io/qt-5/qml-workerscript.html WorkerScript element help you with your problem?



  • @jramm, signals are methods. You can declare a signal as Q_INVOKABLE and call it from JS.



Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.