0

I am trying to get a reference to a dynamically created LineSeries when hovered over. After the LinesSeries is created I attach a signal handler to the hovered event.

The problem is: From the simplified example below, when I hover over the LineSeries it prints out the name of the last LineSeries that was added. When It should print the name of the series of each LineSeries added. For example if 3 LineSeries were created with names ["Line A", "Line B", "Line C" ], when hovering over each it should print each corresponding name, but instead it printing, "Line C", for all 3 LineSeries hovered event handlers. What am I doing wrong?

//dataset is a dictionary(QVariant) of items where each item is the name of the line series
for(var name in dataset) {
        var series = chart.createSeries(ChartView.SeriesTypeLine, name, xAxis, yAxis);
        series.name = name;

        series.hovered.connect(
                    function (point,state){
                        if (state){
                            console.log(">>>"+ name); // <- should print the name of each series
                        }

                    });

I have a feeling it has something to do with binding the current value of the name variable to the onhovered event handler but I am not sure how to do this. I know in regular JS they do something like

functionName.bind( { ... code ... }, this );

Thanks for your help.

--E

Edwin Fernandez
  • 89
  • 1
  • 12

2 Answers2

1

A possible solution is to add arguments similar to functools.partial() in Python, and we are lucky since there is an equivalent implementation in this post:

// https://stackoverflow.com/a/33261231/6622587
function partial() {
    var args = Array.prototype.slice.call(arguments);
    var fn = args.shift();
    return function() {
        var nextArgs = Array.prototype.slice.call(arguments);
        // replace null values with new arguments
        args.forEach(function(val, i) {
            if (val === null && nextArgs.length) {
                args[i] = nextArgs.shift();
            }
        });
        // if we have more supplied arguments than null values
        // then append to argument list
        if (nextArgs.length) {
            nextArgs.forEach(function(val) {
                args.push(val);
            });
        }
        return fn.apply(fn, args);
    }
}

// ...

for(var name in dataset) {
    var series = chart.createSeries(ChartView.SeriesTypeLine, name, xAxis, yAxis);
    var fun = function(name, point, state){
        if (state){
            console.log(">>>"+ name);
        }
    };
    series.hovered.connect(partial(fun, name));
}
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Im not sure I understand how it works but it does, I'll dig into it to understand it more. Thank you! The only thing is it does not trigger the handler when the mouse leaves the lineseries for example `for(var name in dataset) { var fun = function(name, point, state){ if (state){ console.log(">>>"+ name); } else { //THIS NEVER GETS CALLED console.log("EXITED: " + name); }; series.hovered.connect(partial(fun, name)); }` – Edwin Fernandez Mar 28 '19 at 14:04
1

name contains the last value of the array when the iteration finished so the connect handler always will take this value. To avoid this behavior you should use closure as following:

    Component.onCompleted: {
        var dataset = ["aaa","bbb","ccc"];

        for(var name in dataset) {
            var series = chart.createSeries(ChartView.SeriesTypeLine, dataset[name], xAxis, yAxis);

            (function(series){
                series.name = dataset[name];                    
                series.hovered.connect(function (point, state)
                {
                    if (state)
                    {
                        console.log(series.name);
                    }
                });
            })(series);

        }
    } 
folibis
  • 12,048
  • 6
  • 54
  • 97
  • Thank you! This is the most straightforward answer and with this solution the hovered event is also fired when the mouse leaves the LineSeries. So I can catch it with this `if (state) { console.log(series.name); } else { console.log('Exited line series: ' + series.name); }` – Edwin Fernandez Mar 28 '19 at 15:20