1

In my JavaFX application, I would like to display live data from a background thread. Does anyone know how to update a linechart from a background thread? Thank you. Below some sample code.

TM

Preview image

Sample controller

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.chart.LineChart;
import javafx.scene.control.Button;

public class SampleController {

    @FXML
    Button btnStart;

    @FXML
    Button btnStop;

    @FXML
    LineChart myChart;

    Process process;

    @FXML
    public void initialize() {
        process = new Process();
    }


    public void start(ActionEvent event) {
        process.start();
    }

    public void stop(ActionEvent event) {
        process.stop();
    }

}

Process class. Launches the thread.

public class Process {

    private Task task = new Task();

    public void start(){
        task.start();
    }

    public void stop(){
        task.kill();
    }

}

Task class. The thread class which executes the tasks

public class Task extends Thread {

    private boolean isActive = true;


    @Override
    public void run() {

        while (isActive) {

            try {
                // Simulate heavy processing stuff
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // Add a new number to the linechart

            // Remove first number of linechart
        }
    }

    public void  kill(){
        isActive = false;
    }

}

Main class

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
        primaryStage.setTitle("Hello World");
        primaryStage.setScene(new Scene(root, 500, 500));
        primaryStage.show();
    }


    public static void main(String[] args) {
        launch(args);
    }
}

sample.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>

<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity"     
minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" 
xmlns="http://javafx.com/javafx/10.0.1"     
xmlns:fx="http://javafx.com/fxml/1"     
fx:controller="sample.SampleController">


   <children>
  <Button fx:id="btnStart" mnemonicParsing="false" onAction="#start" text="Start" />
  <LineChart fx:id="myChart">
    <xAxis>
      <CategoryAxis side="BOTTOM" />
    </xAxis>
    <yAxis>
      <NumberAxis side="LEFT" />
    </yAxis>
  </LineChart>
  <Button fx:id="btnStop" mnemonicParsing="false" onAction="#stop" 
text="Stop" />

</children>
    </VBox>
user10053673
  • 221
  • 1
  • 13
  • 1
    Possible duplicate of [Updating UI from different threads in JavaFX](https://stackoverflow.com/questions/22772379/updating-ui-from-different-threads-in-javafx) – Slaw Aug 20 '18 at 15:14
  • Also see https://stackoverflow.com/questions/20497845/constantly-update-ui-in-java-fx-worker-thread and https://stackoverflow.com/questions/44036735/live-update-linechart-in-javafx – Slaw Aug 20 '18 at 15:14
  • 1
    Possible duplicate of [How can I make a dynamic line chart with JavaFX using a socket input](https://stackoverflow.com/questions/28638173/how-can-i-make-a-dynamic-line-chart-with-javafx-using-a-socket-input) – SedJ601 Aug 20 '18 at 15:50

1 Answers1

0

You need to insert the values to display in the LineChart from the UI Thread. Therefore you can simply use something like this:

public class Task extends Thread {

    private boolean isActive = true;
    private LineChart<String, Number> chart;
    private Series<String, Number> series = new Series<>();


    public Task(LineChart<CategoryAxis, NumberAxis> chart) {
        this.chart.getData().add(series);
    }

    @Override
    public void run() {
        while (isActive) {
            try {
                // Simulate heavy processing stuff
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

           Platform.runLater(() -> {
               // Add a new number to the linechart
               this.series.getData().add(new Data<>("",0));

               // Remove first number of linechart
               this.series.getData().remove(0);
           });

        }
    }
    public void  kill(){
        isActive = false;
    }

}

or if you want to execute something after you added and removed the values use something like:

public class Task extends Thread {

    private boolean isActive = true;
    private LineChart<String, Number> chart;
    private Series<String, Number> series = new Series<>();


    public Task(LineChart<CategoryAxis, NumberAxis> chart) {
        this.chart.getData().add(series);
    }

    @Override
    public void run() {
        while (isActive) {
            try {
                // Simulate heavy processing stuff
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            FutureTask<Object> task = new FutureTask<Object>(new Callable<Object>()     {
                @Override
                public Object call() throws Exception {
                       // Add a new number to the linechart
                       series.getData().add(new Data<>("",0));

                       // Remove first number of linechart
                       series.getData().remove(0);
                    return true;
                }
            });
            Platform.runLater(task);

            try {
                System.out.println(task.get());
                //TODO after insertion
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
    public void  kill(){
        isActive = false;
    }

}