0

I'm trying to add tooltips to the different nodes on a JavaFX line chart. My chart has two different series; each series has multiple data points. Each data point relates to an object. I need to access the datapoint's associated object in order to add information to its tooltip.

From what I understand, the tooltips must be installed after the data has been added to the chart. I have created a HashMap that tracks the added data points against the relevant object so I can retrieve the object after the data points are added to the chart.

The following is an example of how I add data to a series:

dataToAdd = new XYChart.Data(Integer.toString(count), timeSpent);
addedData.put(dataToAdd, object); // Store a reference in a HashMap<XYChart.Data, DataObject>
series1.getData().add(new XYChart.Data(Integer.toString(count), timeSpent));

After running lineChart.getData().addAll(series1, series2); I loop through the chart's series and each series respective XYChart.Data. I then try to match this data with the data in my HashMap so I'm able to pull out the Question which contains data I want to provide to the tooltip. The following is a code snippet demonstrating this:

for (XYChart.Series<String, Number> s : lineChart.getData()) {
    for (XYChart.Data<String, Number> d : s.getData()) {
        // This if statement never evaluates to true
        if (addedData.containsKey(d)) {
            Tooltip tooltip = new Tooltip();
            tooltip.setText("Difficulty: " + addedData.get(d).difficulty.toString());
            Tooltip.install(d.getNode(), tooltip);
        }
    }
}

I have no idea why I'm unable to match the value against my Hashmap. I have tried all sorts of things, such as comparing against the XY.Data.getNode() method but this does not provide any matches either. I have confirmed that my HashMap is being populated with references to XY.Data objects. What am I doing wrong here?

Edit: As Sedrick pointed out, I was adding a new instance of an XYChart.Data object, rather than providing a reference to the object I was adding to the HashMap, don't know how I missed this!

Guybrush
  • 256
  • 4
  • 16
  • Possible duplicate of [JavaFX LineChart Hover Values](https://stackoverflow.com/questions/14615590/javafx-linechart-hover-values) – SedJ601 Sep 16 '18 at 15:55
  • Create an [MCVE](https://stackoverflow.com/help/mcve). – SedJ601 Sep 16 '18 at 15:57
  • Thanks @Sedrick but the answers to that question don't really help me. I don't think I require the listeners mentioned in that post, and even if I were to set them, I would not be able to retrieve the `Question` data associated with the data point. If I loop through all the `XY.Data` objects and install a tooltip with some hardcoded text, it display just fine. My question more relates to storing and then retrieving the `XY.Data` from a HashMap so I can display the data correlated by the `XY.Data` key in the HashMap. – Guybrush Sep 16 '18 at 16:13
  • Follow the second link I posted. I might be able to give more specific help after that. – SedJ601 Sep 16 '18 at 16:23
  • Also, If you don't need the listener, just use the code inside of it. – SedJ601 Sep 16 '18 at 16:25
  • You are right. the `mouseEntered` listener is not needed. I am updating the linked code. – SedJ601 Sep 16 '18 at 16:30
  • I updated my answer. – SedJ601 Sep 16 '18 at 20:34

1 Answers1

0

Looking at your code, your problem is

listeningSeries.getData().add(new XYChart.Data(Integer.toString(listeningQuestionCount), timeSpent));

with attention on

new XYChart.Data(Integer.toString(listeningQuestionCount), timeSpent)

This code should be

listeningSeries.getData().add(dataToAdd);

In your first case, you have this. In the rest, you don't. I personally would get rid of the HashMap and use the setExtraValue() like in the example below.

I am not sure if I am on the right track with your question, but one thing you can do is use setExtraValue() to add associated data to the node. That way you would not have to rely on the HashMap. The code below demos how to add extra data using setExtraValue()`.

import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.TreeMap;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.control.Tooltip;
import javafx.stage.Stage;

/**
 *
 * @author blj0011
 */
public class JavaFXApplication250 extends Application
{

    @Override
    public void start(Stage stage)
    {
        stage.setTitle("Line Chart Sample");
        //defining the axes
        final NumberAxis xAxis = new NumberAxis();
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("Number of Month");
        //creating the chart
        final LineChart<Number, Number> lineChart = new LineChart<>(xAxis, yAxis);

        lineChart.setTitle("Stock Monitoring, 2010");
        //defining a series
        XYChart.Series<Number, Number> series = new XYChart.Series();
        series.setName("My portfolio");
        //populating the series with data
        Random rand = new Random();

        Map<Integer, Integer> data = new TreeMap();
        //Create Chart data
        for (int i = 0; i < 3; i++) {
            data.put(rand.nextInt(51), rand.nextInt(51));
        }
        Set set = data.entrySet();

        //When you create the data points for the series, go ahead and add any assoicated data to the data point.
        int counter = 1;
        Iterator i = set.iterator();
        while (i.hasNext()) {
            Map.Entry me = (Map.Entry) i.next();
            System.out.println(me.getKey() + " - " + me.getValue());
            XYChart.Data tempData = new XYChart.Data(me.getKey(), me.getValue());
            tempData.setExtraValue("I am data point " + counter++);
            series.getData().add(tempData);//Add data to series
        }



        lineChart.getData().add(series);

        //loop through data and add tooltip
        //THIS MUST BE DONE AFTER ADDING THE DATA TO THE CHART!
        for (Data<Number, Number> entry : series.getData()) {
            System.out.println("Entered!");
            Tooltip t = new Tooltip(entry.getExtraValue().toString());
            Tooltip.install(entry.getNode(), t);
        }



        Scene scene = new Scene(lineChart, 800, 600);

        stage.setScene(scene);
        stage.show();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

}
SedJ601
  • 12,173
  • 3
  • 41
  • 59
  • Thanks for your help Sedrick. Unfortunately I have already tried this method but `getExtraValue` returns null for me. I have just created an MVCE though and am about to add it to my question. – Guybrush Sep 16 '18 at 17:01
  • 1
    Hi Sedrick, I've just read your edited answer and you are right, I was creating a new instance of `XY.Data` instead of referencing the one in the HashMap. I can't believe I missed this, had spent the entire day programmaing and was exhausted. I will follow your advice and take advantage of the `setExtraValue()` method. Thank you so much for your help. – Guybrush Sep 17 '18 at 01:00