Thursday, June 12, 2014

Using WorkerScript from QML

As you might know, I am contributing to Ubuntu Touch Calendar core application. Those application are pure QML application with no C++ backend or we aim to avoid it as much as possible. This is done to make sure application are portable across device.

For simple use-case you can just use javascript code as backend logic and it should work fine without any problem. but if you need to some intensive calculation in application then its better to move those calculation to WorkerScript.

While working on one calendar feature, Calculating layout for overlapping events, I also needed to use WorkerScript. But one major problem with WorkerScript is that, it does not have access to QML Engine. So you will not be able to send your Declarative object to worker script to offload intensive task to threads.

In calendar application I am getting Events declarative object from model and I needed to process events time to decide position and size of events in view.

As such I can not send those Events to WorkerScript, but to overcome that limitation, I created temporary Javascript object which has required data like start time, end time and id, which I can use to decide position, size and Id can be used to map temp object to original object from model and rest of logic then can be implemented in WorkerScript.

Code wise using WorkerScript is quite easy. Following code creates WorkerScript object and sends array of events to script.
    WorkerScript {
        id: eventLayoutHelper
        source: "EventLayoutHelper.js"

        onMessage: {
            //recevied process data, which we can use to layout events
            layoutEvents(messageObject.schedules);
        }
    }

    function createEvents() {

        var eventMap = {};
        var allSchs = [];

        var startDate = new Date().midnight();
        var endDate = new Date().endOfDay();
        var items = model.getItems(startDate,endDate);
        for(var i = 0; i < items.length; ++i) {
            var event = items[i];
            var schedule = {"startDateTime": event.startDateTime, "endDateTime": event.endDateTime,"id":event.itemId };
            allSchs.push(schedule);
            eventMap[event.itemId] = event;
        }

        intern.eventMap = eventMap;
        //here we are asking worker script to process all the events
        eventLayoutHelper.sendMessage(allSchs);
    }


Following is script code, it will process these events and will send back location and size information back.
WorkerScript.onMessage = function(events) {

    //do some pre-processing like sorting and converting the data type more comfortable to algorithm
    var allSchs = processEvents(events);

    while( allSchs.length > 0) {
        var sch = allSchs.shift();

        //finds all schedules overlapping with current schedule and remove from original array
        var schs = findOverlappingSchedules(sch, allSchs);

        //insert original schedule first, so array remain sorted
        schs.unshift(sch);

        //send processed events back to GUI thread so we can render it
        WorkerScript.sendMessage({ 'schedules': schs});
    }
}

No comments:

Post a Comment