I have created a LineChart in SceneBuilder and am trying to workout how to data to it, so that it will be drawn live. In my Controller
class, I have:
public class Controller {
...
@FXML private LineChart<Number, Number> chart;
@FXML private NumberAxis xAxis, yAxis;
private XYChart.Series<Number, Number> series;
private boolean run = true;
...
public init() {
xAxis.setAutoRanging(false);
xAxis.setTickLabelsVisible(false);
xAxis.setTickMarkVisible(false);
xAxis.setMinorTickVisible(false);
series = new XYChart.Series<Number, Number>();
series.setName("Data plot");
chart.setAnimated(false);
chart.getData().addAll(series);
}
...
//When a button is clicked, a signal is sent to my server that starts a
//tracking application. Then it enters the while loop. This code
//then makes requests to the server for data (getData()).
//This is the data that I would like to display.
@FXML
private track() {
connection.startTracking();
Platform.runLater(new Runnable() {
@Override
public void run() {
while(run) {
int[] data = connection.getData() //from somewhere else
dataManager.dataToSeries(data, series);
if(!series.getData.isEmpty()) {
chart.getData().retainAll();
chart.getData.addAll(series);
}
}
}
}
}
And here is the dataToSeries()
method from dataController
...
private void dataToSeries(int[] data, XYChart.Series<Number, Number> series) {
int yValue = 0;
int len = data.length;
for (int i = 0; i < len; i++) {
series.getData().add(new XYChart.Data<Number, Number>(data[i], yValue);
yValue++;
}
}
If I print out the series in Controller
, I get the data that i expect, however it is in the form:
Series: [Data[11,1,null], Data[16,2,null], Data[21,3,null]
Can anyone explain why this data isn't being displayed on my LineChart
?
You're blocking the UI thread with the Runnable
you post on the application thread using Platform.runLater
.
Since the track()
method is annotated with @FXML
I suspect this is used as a EventHandler
in the fxml file and therefore runs on the application thread anyways which removes the necessity of using Platform.runLater
to run the code on the application thread.
To not block the UI thread the loop should run on a non-application thread and only the UI updates should be done on the application thread. Furthermore a break small break between updates may not be wrong. In this case a ScheduledExecutorService
could provide suitable scheduling:
Example Application
code:
private final ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture future;
private Random random = new Random();
private int[] getData() {
int[] result = new int[10];
for (int i = 0; i < result.length; i++) {
result[i] = random.nextInt(10);
}
return result;
}
private static void dataToSeries(int[] data, XYChart.Series<Number, Number> series) {
int len = data.length;
int size = series.getData().size();
if (size > len) {
series.getData().subList(len, series.getData().size()).clear();
} else if (size < len) {
for (; size < len; size++) {
series.getData().add(new XYChart.Data<>(0, size));
}
}
for (int i = 0; i < len; i++) {
series.getData().get(i).setXValue(data[i]);
}
}
@Override
public void start(Stage primaryStage) {
ToggleButton btn = new ToggleButton("updating");
btn.setSelected(false);
XYChart.Series<Number, Number> series = new XYChart.Series<>();
LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis(), FXCollections.observableArrayList(series));
chart.setAnimated(false);
Runnable dataGetter = () -> {
try {
// simulate some delay caused by the io operation
Thread.sleep(100);
} catch (InterruptedException ex) {
}
int[] data = getData();
Platform.runLater(() -> {
// update ui
dataToSeries(data, series);
});
};
btn.selectedProperty().addListener((a, b, newValue) -> {
if (newValue) {
// update every second
future = service.scheduleWithFixedDelay(dataGetter, 0, 1, TimeUnit.SECONDS);
} else {
// stop updates
future.cancel(true);
future = null;
}
});
VBox root = new VBox(10, chart, btn);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
@Override
public void stop() throws Exception {
service.shutdownNow();
}
Based Java SE Development Demos and Samples Downloads in rootFoder -> jdk.xxx -> demo -> javafx_samples
execute Ensemble8.jar.
Within the application go to the section charts
and search for LineChart
You should see something like that Image from Ensembre Application
Then click in View Source
Here is an example of an implementation.
@FXML
private LineChart<Number, Number> lineChart;
@FXML
private NumberAxis xAxis;
@FXML
private NumberAxis yAxis;
@Override
public void initialize(URL location, ResourceBundle resources) {
lineChart.setTitle("Stock Monitoring, 2010");
ObservableList<XYChart.Series<Number,Number>> dataChart =
FXCollections.observableArrayList(
new LineChart.Series("Serie 1",FXCollections.observableArrayList(
new XYChart.Data<>(1,2),
new XYChart.Data<>(5,3),
new XYChart.Data<>(9,3),
new XYChart.Data<>(2,7),
new XYChart.Data<>(6,9),
new XYChart.Data<>(1,2)
)),
new LineChart.Series<>("Series 2",FXCollections.observableArrayList(
new XYChart.Data<>(5,7),
new XYChart.Data<>(3,4),
new XYChart.Data<>(8,2),
new XYChart.Data<>(6,9),
new XYChart.Data<>(1,3),
new XYChart.Data<>(9,7)
)));
lineChart.setData(dataChart);
For example to change the "y" axis
of the second point of the first list Serie 1
whose current value is (x: 5, y: 3) would look something like this
dataChart.get(0).getData().get(1).setYValue(9);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.