Wouter
Wouter

Reputation: 149

scenebuilder javafx linechart

So here's the deal, i'm trying to code a GUI that shows live data on a linechart. So far so good, i can get the linechart working but not into the GUI.

Using scenebuilder, i made a view with a linechart object, in order to link it to my generated chart. But for some reason this does not seem to work with this code in my mainApp.

public void showSes() {
    try {
        // Load the fxml file and set into the center of the main layout
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(MainApp.class.getResource("view/Session.fxml"));

        AnchorPane Session = (AnchorPane) loader.load();
        rootLayout.setCenter(Session);

        SessionController controller = loader.getController();
        controller.setMainApp(this);
        controller.initGraph();

    } catch (IOException e) {
        // Exception gets thrown if the fxml file could not be loaded
        e.printStackTrace();
    }
}

This simply shows the view with an empty linechart. I know the chart should know however, because i can use it to create a scene, and show that into the GUI, but the view i made in scenebuilder also has some other fields i want to show...

Does anybody have an idea?

FXML

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

<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.chart.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane prefHeight="900.0" prefWidth="1280.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.view.SessionController">
   <children>
      <Pane prefHeight="900.0" prefWidth="1280.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
         <children>
            <LineChart fx:id="linechart" layoutX="29.0" layoutY="194.0" prefHeight="416.0" prefWidth="1222.0" title="Temperature of session">
              <xAxis>
                <CategoryAxis label="Time (s)" fx:id="xAxis" />
              </xAxis>
              <yAxis>
                <NumberAxis fx:id="yAxis" label="Temperature (°C)" side="LEFT" upperBound="160.0" />
              </yAxis>
            </LineChart>
            <GridPane layoutX="254.0" layoutY="87.0" prefHeight="150.0" prefWidth="771.0">
              <columnConstraints>
                <ColumnConstraints hgrow="SOMETIMES" maxWidth="274.0" minWidth="10.0" prefWidth="274.0" />
                  <ColumnConstraints hgrow="SOMETIMES" maxWidth="273.0" minWidth="10.0" prefWidth="273.0" />
                <ColumnConstraints hgrow="SOMETIMES" maxWidth="273.0" minWidth="10.0" prefWidth="273.0" />
              </columnConstraints>
              <rowConstraints>
                <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
              </rowConstraints>
               <children>
                  <Label prefHeight="53.0" prefWidth="153.0" text="Temperature fluid:" GridPane.halignment="CENTER" GridPane.valignment="TOP">
                     <font>
                        <Font size="16.0" />
                     </font>
                  </Label>
                  <Label prefHeight="52.0" prefWidth="181.0" text="Temperature vapor:" GridPane.columnIndex="2" GridPane.halignment="CENTER" GridPane.valignment="TOP">
                     <font>
                        <Font size="16.0" />
                     </font>
                  </Label>
                  <TextField fx:id="fluidT" editable="false" />
                  <TextField fx:id="gasT" editable="false" GridPane.columnIndex="2" />
               </children>
            </GridPane>
            <Label layoutX="474.0" layoutY="14.0" text="TempTracker">
               <font>
                  <Font size="50.0" />
               </font>
            </Label>
            <TextArea editable="false" layoutX="190.0" layoutY="638.0" prefHeight="160.0" prefWidth="900.0" promptText="&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;&#9;------Warning log------- " wrapText="true" />
            <Button layoutX="540.0" layoutY="808.0" mnemonicParsing="false" onAction="#handleStop" prefHeight="65.0" prefWidth="200.0" text="STOP">
               <font>
                  <Font size="22.0" />
               </font>
            </Button>
         </children></Pane>
   </children>
</AnchorPane>

CONTROLLER

    private static final int MAX_DATA_POINTS = 50;
    private String xSeriesData = "";
    private XYChart.Series series1;
    private XYChart.Series series2;
    private ExecutorService executor;
    private BlockingQueue<Number> dataQ1 = new ArrayBlockingQueue<Number>(1024);
    private BlockingQueue<Number> dataQ2 = new ArrayBlockingQueue<Number>(1024);

    @FXML
    private CategoryAxis xAxis = new CategoryAxis();
    @FXML
    final NumberAxis yAxis = new NumberAxis();
    @FXML
    final LineChart<String, Number> linechart = new LineChart<String, Number>(xAxis, yAxis);


public void initGraph(){
    xAxis.setAutoRanging(false);

    xAxis.setTickLabelsVisible(false);
    xAxis.setTickMarkVisible(false);

    NumberAxis yAxis = new NumberAxis();
    yAxis.setAutoRanging(true);

    //Graph
    final LineChart<String, Number> lc = new LineChart<String, Number>(xAxis, yAxis){
            @Override 
            protected void dataItemAdded(Series<String, Number> series, int itemIndex, Data<String, Number> item){}
            };
    lc.setAnimated(false);
    lc.setId("liveLineChart");
    lc.setTitle("Animated Line Chart");

    //Graph Series
    series1 = new XYChart.Series<Number, Number>();
    series2 = new XYChart.Series<Number, Number>();
    linechart.getData().addAll(series1, series2);

    series1.setName("T1");
    series2.setName("T2");

    fluidT.setText("0000");
    gasT.setText("0000");

    prepareTimeline();

    Runnable con = new Consumer(this);
    Thread c = new Thread(con);
    c.start();

}

Upvotes: 3

Views: 9331

Answers (1)

jewelsea
jewelsea

Reputation: 159290

Don't create new objects for @FXML injected members

Never use new in conjunction with @FXML, i.e., never write:

@FXML 
private CategoryAxis xAxis = new CategoryAxis();

Instead, just write:

@FXML 
private CategoryAxis xAxis;

The FXMLLoader will automatically generate, i.e., create, a new object for each element in the FXML file and inject a reference to that into your controller where you provide an @FXML annotation. So if you reset the @FXML member reference to a new object you create in the controller there will be no association between that object and objects created by the loader.

Additionally, don't create another new LineChart within your initGraph() function. You already have a LineChart created by the FXMLLoader, just reference that. Same for NumberAxis and the other elements you are using @FXML injection with.

If you use an @FXML annotation also use <fx:id>

You have:

@FXML
private CategoryAxis xAxis;

So in your fxml, you should define:

<xAxis fx:id="xAxis">

Otherwise, the FXMLLoader will not be able to inject a reference to the axis you defined in your FXML.

Aside

You may have other errors in your code (e.g., around concurrency and threading). So the above might not be all of your errors. In general, when creating an mcve, try to eliminate anything which is not relevant to the question at hand (e.g. the threading code and non-linechart portions of the FXML), but include everything that somebody could use to copy and paste your code to compile and run it to replicate your issue.

Note: The Ensemble sample application contains a sample program which updates a graph in real-time based upon audio spectrum input data.

Upvotes: 3

Related Questions