I have a simple JavaFX app that has two little circles that are supposed to change their location every 0.5 s. Later on this is supposed to become a planets simulation. At the moment the location of my space objects changes in a separate thread which is launched when the button "Start simulation" is pressed. Simultaneously I want my circles (representing the planets) to be drawn again and again always using the current location stored in the spaceObject objects. When I limit the re-drawing to three times (instead of an unlimited amount via a while ( true ) {
which is what I actually want) I see that the GUI is not updating while the loop is running. But after the loop is finished the circles move to the new location while the calculations thread in the background is still running. Why is my GUI thread blocked for the time of the while ( i < 3 ) {
and how can I simultaneously update my GUI with the current location of the circles? Here is my code:
Main.java
package plantenbahnen;
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 stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
package plantenbahnen;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.Pane;
public class Controller implements Initializable {
@FXML private Pane paneDraw;
@FXML private Pane paneControls;
private ArrayList<SpaceObject> universe = new ArrayList<>();
private Thread calcThread;
@Override
public void initialize(URL url, ResourceBundle rb) {
SpaceObject sun = new SpaceObject("sun", 600, 600);
universe.add(sun);
SpaceObject earth = new SpaceObject("earth", 450, 450);
universe.add(earth);
MyCalculations myCalc = new MyCalculations(universe);
calcThread = new Thread(myCalc);
Draw.drawPlanets(universe, paneDraw);
}
@FXML private void buttonStartSimulation(ActionEvent event) throws InterruptedException {
calcThread.start();
Platform.runLater(new Runnable() {
@Override public void run() {
int i = 0;
//while ( true ) { // this line is what I want
while ( i < 3 ) {
Draw.drawPlanets(universe, paneDraw);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e);
}
i++;
}
}
});
}
}
MyCalculations.java
package plantenbahnen;
import java.util.ArrayList;
public class MyCalculations implements Runnable {
ArrayList<SpaceObject> universe;
public MyCalculations (ArrayList<SpaceObject> universe) {
this.universe = universe;
}
@Override
public void run(){
double toAdd = 100.0;
while ( true ) {
for (SpaceObject so: universe) {
so.setx(so.getx() + toAdd);
so.sety(so.gety() + toAdd);
}
if ( toAdd > 0.0 ) {
toAdd = -300.0;
} else {
toAdd = 300.0;
}
}
}
}
Draw.java
package plantenbahnen;
import java.util.ArrayList;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
public class Draw {
public static void drawPlanets(ArrayList<SpaceObject> universe, Pane pane) {
for (Node child: pane.getChildren()) {
System.out.println(child);
}
// Clear objects first
for (SpaceObject so: universe) {
if ( pane.getChildren().contains(so) ) {
pane.getChildren().remove(so);
System.out.println("Removing ... " + so.getName());
}
}
double paneHalfWidth = pane.getPrefWidth() / 2.0;
double paneHalfHeight = pane.getPrefHeight() / 2.0;
double scaleFactor = 0.1;
for (SpaceObject so: universe) {
so.setCenterX(so.getx() * scaleFactor + paneHalfWidth);
so.setCenterY(so.gety() * scaleFactor + paneHalfHeight);
System.out.println("x=" + so.getCenterX() + " y=" + so.getCenterY());
so.setRadius(2);
//so.setColour(Color.BLACK);
pane.getChildren().add(so);
}
}
}
SpaceObject.java
package plantenbahnen;
import javafx.scene.shape.Circle;
public class SpaceObject extends Circle {
private double x,y;
private String name;
SpaceObject(String name, double x, double y) {
this.x = x;
this.y = y;
this.name = name;
}
public double getx(){
return this.x;
}
public void setx(double value){
this.x=value;
}
public double gety(){
return this.y;
}
public void sety(double value){
this.y=value;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
FXMLDocument.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="AnchorPane" prefHeight="700.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="plantenbahnen.Controller">
<children>
<Pane fx:id="paneDraw" prefHeight="700.0" prefWidth="800.0">
<children>
<Pane fx:id="paneControls" prefHeight="66.0" prefWidth="174.0">
<children>
<Button fx:id="buttonStartSimulation" layoutX="26.0" layoutY="21.0" mnemonicParsing="false" onAction="#buttonStartSimulation" text="Start simulation" />
</children>
</Pane>
</children></Pane>
</children>
</AnchorPane>
Thanks a lot in advance for your help.