How to solve problem with drawing Chart in Scene Builder with JavaFX - java

I have problem with my program.
I want to draw chart after providing data (coefficients of the equation).
I tried to change my import (it helped with some variables)
I changed java.awt* for javafx.scene ... (few imports).
FXML file:
<?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.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="grahps.Controller">
<children>
<TextField fx:id="factorA" layoutX="24.0" layoutY="598.0" prefHeight="33.0" prefWidth="106.0" text="a=" AnchorPane.bottomAnchor="75.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" />
<TextField fx:id="factorB" layoutX="24.0" layoutY="630.0" prefHeight="33.0" prefWidth="106.0" text="b=" AnchorPane.bottomAnchor="37.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" />
<TextField fx:id="factorC" layoutX="24.0" layoutY="674.0" prefHeight="33.0" prefWidth="106.0" text="c=" AnchorPane.bottomAnchor="1.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" />
<TextField layoutX="158.0" layoutY="592.0" prefHeight="47.0" prefWidth="120.0" text="xMin=" AnchorPane.bottomAnchor="61.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" fx:id="xMin" />
<TextField layoutX="158.0" layoutY="650.0" prefHeight="47.0" prefWidth="120.0" text="xMax=" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" fx:id="xMax" />
<Label fx:id="label" layoutX="468.0" layoutY="629.0" prefHeight="61.0" prefWidth="276.0" text="f(x)=" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="468.0" AnchorPane.rightAnchor="56.0">
<font>
<Font size="18.0" />
</font>
</Label>
<LineChart fx:id="drawChart" prefHeight="598.0" prefWidth="800.0" title="Chart">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</LineChart>
<Button fx:id="button" layoutX="317.0" layoutY="612.0" mnemonicParsing="false" prefHeight="61.0" prefWidth="98.0" text="Rysuj wykres" />
</children>
</AnchorPane>
The part with LineChart (fx:id="drawChart") generated communicate: "Unresolved fx:id reference
My main class:
package grahps;
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 {
private Controller controller;//
#Override
public void start(Stage stage) throws Exception {
stage.show();
System.out.println(getClass().getResource("/fxml/sample.fxml").getPath());
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/sample.fxml"));
loader.setController(controller);
Parent root = FXMLLoader.load(getClass().getResource("/fxml/sample.fxml"));
Scene scene = new Scene(root, 800, 800);
controller.drawChart(stage);
stage.setScene(scene);
}
public static void main(String[] args) {
launch(args);
}
}
And finally my controller:
package grahps;
import javafx.fxml.FXML;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.control.TextField;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
public class Controller {
#FXML
TextField factorA;
#FXML
TextField factorB;
#FXML
TextField factorC;
#FXML
TextField xMin;
#FXML
TextField xMax;
#FXML
Label label;
#FXML
Button button;
#FXML
XYChart.Series<Number, Number> chart;
//Parser Text Field -> double
double xMax1 = Double.parseDouble(xMax.getText());
double xMin1 = Double.parseDouble(xMin.getText());
double a = Double.parseDouble(factorA.getText());
double b = Double.parseDouble(factorB.getText());
double c = Double.parseDouble(factorC.getText());
#FXML
public void drawChart(Stage stage) {
XYChart.Series<Number, Number> series = chart;
series.setName("Chart");
final NumberAxis xAxis = new NumberAxis(xMin1, xMax1, 1);
final NumberAxis yAxis = new NumberAxis();
yAxis.setTickUnit(1);
xAxis.setLabel("X Label");
yAxis.setLabel("Y Label");
final javafx.scene.chart.LineChart<Number, Number> lineChart = new javafx.scene.chart.LineChart<Number, Number>(xAxis, yAxis);
double y;
String pattern;
if (a == 0 && c == 0) {
pattern = "f(x)=" + factorB;
label.setText(pattern);
} else if (c == 0) {
pattern = "f(x)=" + factorA + "x+" + factorB;
label.setText(pattern);
for (double i = xMin1; i <= xMax1; i++) {
y = a * i + b;
series.getData().add(new XYChart.Data(i, y));
}
} else {
pattern = "f(x)=" + factorC + "x^2" + factorA + "x+" + factorB;
label.setText(pattern);
for (double i = xMin1; i < xMax1; i++) {
y = a * i * i + b * i + c;
series.getData().add(new XYChart.Data(i, y));
}
}
lineChart.getData().add(series);
Scene scene = new Scene(lineChart, 800, 800);
stage.setScene(scene);
stage.setResizable(false);
stage.show();
}
}
Please notice one thing: this is my first JavaFX project.
Simply I want to fill in coefficients of the equation and generate equation pattern + draw chart after clicking button.
I'll be greaftul for help.
When I deleted #FXML annotation above method and compiled code I got those errors:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:389)
at com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:328)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$154(LauncherImpl.java:182)
at java.lang.Thread.run(Thread.java:748)
Caused by: javafx.fxml.LoadException:
/C:/Users/Damian/IdeaProjects/Graphs/target/classes/fxml/sample.fxml:14
at javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2601)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2579)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2441)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3214)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3175)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3148)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3124)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3104)
at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3097)
at grahps.Main.start(Main.java:22)
at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$161(LauncherImpl.java:863)
at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$174(PlatformImpl.java:326)
at com.sun.javafx.application.PlatformImpl.lambda$null$172(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$173(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$147(WinApplication.java:177)
... 1 more
Caused by: java.lang.NullPointerException
at grahps.Controller.<init>(Controller.java:31)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at sun.reflect.misc.ReflectUtil.newInstance(ReflectUtil.java:51)
at javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:927)
at javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:971)
at javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:220)
at javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:744)
at javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2707)
at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2527)
... 17 more
Exception running application grahps.Main
Process finished with exit code 1
Also I changed onAction="drawChart" for onAction="chart" and now I can see highlighted communicate: "cannot set javafx.scene.control.LineChart to chart"

There are several bugs in the code, e.g.:
The //Parser Text Field -> double-block in the controller is executed too early => NullPointerException.
When calling controller.drawChart(stage) in the start-method controller equals null => NullPointerException.
In the drawChart-method in the controller series equals null, because chart equals null => NullPointerException.
A reference to the chart is missing in the controller. This has already been noted in the comments.
CategoryAxis is used as the type of the x-axis, although the x-data are numerical values.
Before fixing these bugs, the architecture should be improved (this will automatically fix some of the bugs):
Controller-class: Since the chart has to be initialized and then updated with every click on the button, the following changes in the controller would be useful:
Implement an initialize-method, in which the necessary initializations can be made.
Implement an updateChart-method, which is called when the button is clicked and which updates the chart.
Define a reference to the LineChart.
Define references to both axes.
Thus, the Controller-class looks as follows:
public class Controller {
#FXML
TextField factorA;
#FXML
TextField factorB;
#FXML
TextField factorC;
#FXML
TextField xMin;
#FXML
TextField xMax;
#FXML
Label label;
#FXML
Button button;
#FXML
LineChart<Number, Number> chart;
#FXML
NumberAxis xAxis;
#FXML
NumberAxis yAxis;
#FXML
public void updateChart() {/*ToDo*/}
public void initialize(){/*ToDo*/}
}
Main-class: In the start-method only the FXML needs to be loaded:
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("/fxml/sample.fxml"));
Scene scene = new Scene(root, 800, 800);
stage.setScene(scene);
stage.show();
}
FXML: The following changes should be made:
Change the ID of the LineChart to fx:id="chart"
Change the type of the x-axis of the LineChart to NumberAxis
Add onAction="#updateChart" to the button. This calls the updateChart-method when the button is clicked.
Define an ID for both axes (fx:id="xAxis" and fx:id="yAxis").
Remove all initializations with non-numeric characters (e.g. text="a=") for all text fields, otherwise there are problems with parsing (it makes more sense to use labels or watermarks, e.g. promptText="a").
Then, the FXML becomes:
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="grahps.Controller">
<children>
<TextField fx:id="factorA" prefHeight="33.0" prefWidth="106.0" AnchorPane.bottomAnchor="75.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" promptText="a" />
<TextField fx:id="factorB" prefHeight="33.0" prefWidth="106.0" AnchorPane.bottomAnchor="37.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" promptText="b" />
<TextField fx:id="factorC" prefHeight="33.0" prefWidth="106.0" AnchorPane.bottomAnchor="1.0" AnchorPane.leftAnchor="24.0" AnchorPane.rightAnchor="670.0" promptText="c" />
<TextField fx:id="xMin" prefHeight="47.0" prefWidth="120.0" AnchorPane.bottomAnchor="61.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" promptText="xMin" />
<TextField fx:id="xMax" prefHeight="47.0" prefWidth="120.0" AnchorPane.bottomAnchor="3.0" AnchorPane.leftAnchor="158.0" AnchorPane.rightAnchor="522.0" promptText="xMax" />
<Label fx:id="label" prefHeight="61.0" prefWidth="276.0" AnchorPane.bottomAnchor="30.0" AnchorPane.leftAnchor="468.0" AnchorPane.rightAnchor="56.0" text="f(x)=" >
<font>
<Font size="18.0" />
</font>
</Label>
<LineChart fx:id="chart" prefHeight="598.0" prefWidth="800.0" title="Chart">
<xAxis>
<NumberAxis fx:id="xAxis" side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis fx:id="yAxis" side="LEFT" />
</yAxis>
</LineChart>
<Button fx:id="button" prefHeight="61.0" prefWidth="98.0" layoutX="317.0" layoutY="612.0" mnemonicParsing="false" text="Rysuj wykres" onAction="#updateChart" />
</children>
</AnchorPane>
With these changes, an empty chart is displayed when the application is started (since no initialization has yet been implemented). Clicking on the button has no effect (since no updating has yet been implemented).
Initialization:
public void initialize(){
initChartProperties();
initInputControls();
XYChart.Series<Number, Number> series = getSeries();
chart.getData().add(series);
}
The getSeries-method essentially contains the logic of the drawChart-method:
private XYChart.Series<Number, Number> getSeries() {
double xMax1 = Double.parseDouble(xMax.getText());
double xMin1 = Double.parseDouble(xMin.getText());
double a = Double.parseDouble(factorA.getText());
double b = Double.parseDouble(factorB.getText());
double c = Double.parseDouble(factorC.getText());
XYChart.Series<Number,Number> series = new XYChart.Series<Number, Number>();
series.setName("Chart");
String pattern;
if (a == 0 && c == 0) {
pattern = "f(x)=" + factorB.getText();
label.setText(pattern);
} else if (c == 0) {
pattern = "f(x)=" + factorA.getText() + "x+" + factorB.getText();
label.setText(pattern);
for (double i = xMin1; i <= xMax1; i++) {
double y = a * i + b;
series.getData().add(new Data<Number, Number>(i, y));
}
} else {
pattern = "f(x)=" + factorA.getText() + "x^2+" + factorB.getText() + "x+" + factorC.getText();
label.setText(pattern);
for (double i = xMin1; i < xMax1; i++) {
double y = a * i * i + b * i + c;
series.getData().add(new Data<Number, Number>(i, y));
}
}
return series;
}
The initInputControls-method initializes the input controls, e.g.:
private void initInputControls() {
xMax.setText("100.0");
xMin.setText("10.0");
factorA.setText("1.0");
factorB.setText("2.0");
factorC.setText("3.0");
}
The initChartProperties-method initializes the chart:
private void initChartProperties() {
chart.setAnimated(true);
xAxis.setLabel("X Label");
yAxis.setLabel("Y Label");
}
If you want to display an empty chart at startup, simply remove the last three lines in the initialize-method.
Updating: Updating simply deletes the old series and adds the new series to the chart:
#FXML
public void updateChart() {
XYChart.Series<Number, Number> series = getSeries();
chart.getData().clear();
chart.getData().add(series);
}
After these changes, the application behaves as expected. The figure on the left shows the application after startup, the figure on the right after updating the input values.
Some things can still be improved, e.g. the application does not scale properly if the window size is changed. In addition, the validation of the input fields is missing. Chart animation can be disabled in the initChartProperties-method.
UPDATE:
If no symbols are to be displayed, add in the initChartProperties-method:
chart.setCreateSymbols(false);
The result is:

Related

Issues with setting bounds of NumberAxis based on user input in FXML JavaFX application

SUMMARY: I am trying to design an application where the user inputs into TextFields, and upon clicking a button ONCE:
The values are retrieved
The scene switches to one containing a lineChart
The number axes' bounds are set corresponding to the values entered before.
However I'm finding it very hard to understand how to make the program flow correctly for this to work. My main issues are with switching the scene then accessing x_axis and y_axis, where for some reason "this.x_axis" is null.
Main Class:
public class Main extends Application {
#Override
public void start(Stage stage) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource("menuScene.fxml"));
Scene scene = new Scene(root);
stage.setTitle("FT_Tool");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
Controller: (calculate is called after clicking button)
public class MenuController {
private Stage stage;
private Scene scene;
private Parent root;
#FXML
private TextField funcBox;
#FXML
private TextField sampBox;
#FXML
private TextField limitBox;
Expression func;
double sampFreq;
double limit;
#FXML
public void calculate(ActionEvent event) throws IOException{
func = new ExpressionBuilder(funcBox.getText()).variables("x").build();
sampFreq = Double.parseDouble(sampBox.getText());
limit = Double.parseDouble(limitBox.getText());
//=== SWITCHING SCENES ===//
root = FXMLLoader.load(getClass().getResource("graphScene.fxml"));
stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
scene = new Scene(root);
stage.setScene(scene);
stage.show();
graphing();
}
#FXML
private LineChart sampleGraph;
#FXML
private NumberAxis x_axis;
#FXML
private NumberAxis y_axis;
public void graphing(){
x_axis.setUpperBound(limit);
}
}
2nd Scene:
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1">
<children>
<LineChart fx:id="sampleGraph" layoutX="24.0" layoutY="16.0" prefHeight="368.0" prefWidth="552.0" title="Samples taken">
<xAxis>
<NumberAxis fx:id="x_axis" autoRanging="false" label="Time (s)" tickUnit="1.0"/>
</xAxis>
<yAxis>
<NumberAxis fx:id="y_axis" autoRanging="false" label="Amplitude (m)"/>
</yAxis>
</LineChart>
</children>
</AnchorPane>
When I run it, application runs fine and after clicking button the scene switches and linechart is shown but without the right bounds.
This line of code: x_axis.setUpperBound(limit) causes the following error:
Caused by: java.lang.NullPointerException: Cannot invoke "javafx.scene.chart.NumberAxis.setUpperBound(double)" because "this.x_axis" is null
Despite not having the fx:controller attribute defined in your FXML file, it looks like—based on your FXML controller class—you're trying to use the same FXML controller class with multiple FXML files. This is strongly discouraged. By default, a new controller instance is created every time the FXML file is loaded. Consequently, it quickly becomes difficult to reason about which fields should have been injected (i.e., non-null) at any given time. An FXML file should have a one-to-one mapping with an FXML controller class (or possibly no controller).
You have two views, which suggests two FXML files and thus two FXML controller classes. When you load the second view, you should communicate to its controller the needed information. For trivial applications you can do this directly, but for more complex applications it would be better to communicate via a model shared between the two controllers.
Example
Here's an example (based on your code but simplified for demonstration). It only asks for the upper bounds of the X and Y axes and then graphs random data within those bounds. It also makes use of TextFormatter to make it easier to grab the values out of the TextFields, and to ensure the user only enters positive integers.
FXML files are assumed to be located in the com.example package.
Main.java:
package com.example;
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 {
var root = FXMLLoader.<Parent>load(Main.class.getResource("Form.fxml"));
primaryStage.setScene(new Scene(root, 1000, 650));
primaryStage.show();
}
}
Form.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.GridPane?>
<GridPane xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.FormController" alignment="CENTER" vgap="20" hgap="15">
<Label text="Upper X Bound:" GridPane.rowIndex="0" GridPane.columnIndex="0"/>
<TextField fx:id="xField" GridPane.rowIndex="0" GridPane.columnIndex="1" GridPane.columnSpan="2"/>
<Label text="Upper Y Bound:" GridPane.rowIndex="1" GridPane.columnIndex="0"/>
<TextField fx:id="yField" GridPane.rowIndex="1" GridPane.columnIndex="1" GridPane.columnSpan="2"/>
<Button text="Graph" onAction="#handleOnGraph" defaultButton="true" GridPane.rowIndex="2"
GridPane.columnIndex="2" GridPane.halignment="RIGHT"/>
</GridPane>
FormController.java:
package com.example;
import java.io.IOException;
import java.util.function.UnaryOperator;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.util.converter.IntegerStringConverter;
public class FormController {
#FXML private TextField xField;
#FXML private TextField yField;
private TextFormatter<Integer> xFieldFormatter;
private TextFormatter<Integer> yFieldFormatter;
#FXML
private void initialize() {
UnaryOperator<TextFormatter.Change> filter =
change -> {
if (change.getControlNewText().matches("\\d*")) {
return change;
}
return null;
};
xFieldFormatter = new TextFormatter<>(new IntegerStringConverter(), 50, filter);
xField.setTextFormatter(xFieldFormatter);
yFieldFormatter = new TextFormatter<>(new IntegerStringConverter(), 50, filter);
yField.setTextFormatter(yFieldFormatter);
}
#FXML
private void handleOnGraph(ActionEvent event) throws IOException {
event.consume();
int upperX = xFieldFormatter.getValue();
int upperY = yFieldFormatter.getValue();
var loader = new FXMLLoader(FormController.class.getResource("Chart.fxml"));
xField.getScene().setRoot(loader.load());
loader.<ChartController>getController().graph(upperX, upperY);
}
}
Chart.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<LineChart fx:id="chart" xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.ChartController">
<xAxis>
<NumberAxis fx:id="xAxis" upperBound="50" autoRanging="false"/>
</xAxis>
<yAxis>
<NumberAxis fx:id="yAxis" upperBound="50" autoRanging="false"/>
</yAxis>
</LineChart>
ChartController.java:
package com.example;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
public class ChartController {
#FXML private LineChart<Number, Number> chart;
#FXML private NumberAxis xAxis;
#FXML private NumberAxis yAxis;
void graph(int upperX, int upperY) {
xAxis.setUpperBound(upperX);
yAxis.setUpperBound(upperY);
generateAndGraphRandomData();
}
private void generateAndGraphRandomData() {
var data = FXCollections.<XYChart.Data<Number, Number>>observableArrayList();
for (int x = 0; x <= xAxis.getUpperBound(); x++) {
int y = (int) (Math.random() * yAxis.getUpperBound());
data.add(new XYChart.Data<>(x, y));
}
var series = new XYChart.Series<>("Random Demo Data", data);
chart.getData().add(series);
}
}

Unknown cause of javafx.fxml.LoadException

I have a three stages JavaFX app (right now only the login/register/blank view) and I tried to communicate between them. I've created a super class and all stages extend that super class.
Now, when I run the main class like below, I get the runtime errors:
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
conn = DBUtils.connectToDB(DB_SCHEMA);
new LoginController(primaryStage);
}
Exception:
Exception in Application start method
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplicationWithArgs(LauncherImpl.java:464)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication(LauncherImpl.java:363)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at java.base/sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:1051)
Caused by: java.lang.RuntimeException: Exception in Application start method
at javafx.graphics/com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:900)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: javafx.fxml.LoadException:
/C:/Mihai/Faculta/Anul%203/Licenta/Aplicatie/licenta-mihai-laza/out/production/licenta-mihai-laza/application/login.fxml:7
at javafx.fxml/javafx.fxml.FXMLLoader.constructLoadException(FXMLLoader.java:2625)
at javafx.fxml/javafx.fxml.FXMLLoader.access$700(FXMLLoader.java:105)
at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:941)
at javafx.fxml/javafx.fxml.FXMLLoader$InstanceDeclarationElement.processAttribute(FXMLLoader.java:980)
at javafx.fxml/javafx.fxml.FXMLLoader$Element.processStartElement(FXMLLoader.java:227)
at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processStartElement(FXMLLoader.java:752)
at javafx.fxml/javafx.fxml.FXMLLoader.processStartElement(FXMLLoader.java:2722)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2552)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:2466)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3237)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3194)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3163)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3136)
at javafx.fxml/javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3113)
at javafx.fxml/javafx.fxml.FXMLLoader.load(FXMLLoader.java:3106)
at licenta.mihai.laza/application.superclasses.StageSuperclass.<init>(StageSuperclass.java:20)
at licenta.mihai.laza/application.LoginController.<init>(LoginController.java:45)
at licenta.mihai.laza/application.Main.start(Main.java:21)
at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
... 1 more
Caused by: java.lang.InstantiationException: application.LoginController
at java.base/java.lang.Class.newInstance(Class.java:571)
at javafx.fxml/javafx.fxml.FXMLLoader$ValueElement.processAttribute(FXMLLoader.java:936)
... 24 more
Caused by: java.lang.NoSuchMethodException: application.LoginController.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3350)
at java.base/java.lang.Class.newInstance(Class.java:556)
... 25 more
[INFO] Connection to database closed
Exception running application application.Main
StageSuperclass.java:
public class StageSuperclass extends Stage {
// Frame window
private Stage stage;
private double xOffset = 0;
private double yOffset = 0;
public StageSuperclass(Stage stage, String fxmlFilePath) throws java.io.IOException {
this.stage = stage;
Parent root = FXMLLoader.load(getClass().getResource(fxmlFilePath));
Scene scene = new Scene(root, Color.TRANSPARENT);
root.setStyle("-fx-background-color: transparent;");
this.initStyle(StageStyle.UNDECORATED);
root.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
xOffset = mouseEvent.getSceneX();
yOffset = mouseEvent.getSceneY();
}
});
root.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
stage.setX(mouseEvent.getScreenX() - xOffset);
stage.setY(mouseEvent.getScreenY() - yOffset);
}
});
this.setScene(scene);
this.show();
}
public Stage getStage() {
return this.stage;
}
LoginController.java:
public class LoginController extends StageSuperclass {
// Constants
private static final String LOGIN_FXML_PATH = "/application/login.fxml";
private static final String WRONG_CREDENTIALS_MSG = "Datele de logare sunt gresite!";
private static final String BLANK_CREDENTIALS_MSG = "Introduceti username si parola!";
// PageObjects
RegisterController registerPage;
// FXML Objects
#FXML
private Button btnLogin;
#FXML
private Button btnIesire;
#FXML
private TextField tfdUsername;
#FXML
private TextField tfdParola;
#FXML
private Label lblInvalid;
#FXML
private Hyperlink lnkInregistrare;
// Constructor
public LoginController(Stage stage) throws IOException {
super(stage, LOGIN_FXML_PATH);
}
public void inregistrareLinkOnAction(ActionEvent event) throws IOException {
new RegisterController(this.getStage());
}
public void loginButtonOnAction(ActionEvent event) throws IOException {
if (!tfdUsername.getText().isBlank() && !tfdParola.getText().isBlank()) {
if (validareLogin()) {
new MainWindowController(this.getStage());
}
} else {
lblInvalid.setText(BLANK_CREDENTIALS_MSG);
}
}
public void iesireButtonOnAction(ActionEvent event) {
Stage stage = (Stage) btnIesire.getScene().getWindow();
stage.close();
}
private boolean validareLogin() {
List<String> columns = Arrays.asList(COL_USERNAME, COL_PAROLA);
List<String> whereValues = Arrays.asList(tfdUsername.getText(), tfdParola.getText());
if (DBUtils.select(columns, columns, whereValues, DBUtils.TABEL_UTILIZATORI.TABLE_NAME, Main.conn).size() == 1) {
return true;
} else {
lblInvalid.setText(WRONG_CREDENTIALS_MSG);
return false;
}
}
}
login.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.*?>
<AnchorPane prefHeight="300.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/11.0.1"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.LoginController">
<AnchorPane prefHeight="60.0" prefWidth="300.0" style="-fx-background-color: #0084b8;">
<children>
<Label alignment="CENTER" contentDisplay="CENTER" layoutY="17.0" prefHeight="17.0" prefWidth="300.0"
text="LOGIN" textAlignment="CENTER" textFill="WHITE">
<font>
<Font name="System Bold" size="18.0"/>
</font>
</Label>
</children>
</AnchorPane>
<Button id="btnLogin" fx:id="btnLogin" layoutX="33.0" layoutY="216.0" mnemonicParsing="false"
onAction="#loginButtonOnAction" prefHeight="25.0" prefWidth="233.0" style="-fx-background-color: #0084b8;"
text="Login" textFill="WHITE">
<font>
<Font size="13.0"/>
</font>
</Button>
<TextField id="tfdUsername" fx:id="tfdUsername" layoutX="89.0" layoutY="82.0" promptText="User">
<font>
<Font size="14.0"/>
</font>
</TextField>
<PasswordField id="tfdParola" fx:id="tfdParola" layoutX="89.0" layoutY="123.0" promptText="Parola">
<font>
<Font size="14.0"/>
</font>
</PasswordField>
<Hyperlink id="lnkInregistrare" fx:id="lnkInregistrare" layoutX="118.0" layoutY="153.0"
onAction="#inregistrareLinkOnAction" text="Nu ai cont? Inregistreaza-te!">
<font>
<Font size="11.0"/>
</font>
</Hyperlink>
<Label layoutX="33.0" layoutY="87.0" text="User">
<font>
<Font size="14.0"/>
</font>
</Label>
<Label layoutX="32.0" layoutY="128.0" text="Parola">
<font>
<Font size="14.0"/>
</font>
</Label>
<Button id="btnCancel" fx:id="btnIesire" layoutX="33.0" layoutY="254.0" mnemonicParsing="false"
onAction="#iesireButtonOnAction" prefHeight="25.0" prefWidth="233.0" style="-fx-background-color: #0084b8;"
text="Iesire" textFill="WHITE">
<font>
<Font size="13.0"/>
</font>
</Button>
<Label id="lblInvalid" fx:id="lblInvalid" alignment="TOP_CENTER" contentDisplay="CENTER" layoutX="44.0"
layoutY="194.0" prefHeight="16.0" prefWidth="233.0" textAlignment="CENTER" textFill="RED">
<font>
<Font size="11.0"/>
</font>
</Label>
</AnchorPane>
Do you have any suggestions solving this exception?
The exception is caused because your FXML file specifies fx:controller="application.LoginController". This will cause the FXMLLoader to create an instance of application.LoginController by (effectively) calling its no-argument constructor. However, your LoginController class doesn't have a no-argument constructor: hence the exception:
Caused by: java.lang.NoSuchMethodException: application.LoginController.<init>()
The typical approach is to load the FXML file, and let the FXMLLoader create the controller for you. Except in quite advanced usage, you should not instantiate the controller class yourself, because the FXMLLoader does that for you.
Get rid of the inheritance in LoginController (i.e. it doesn't need to be a subclass of your other controller class, and certainly should not be a subclass of Stage). Then just do
public class MyApplication extends Application {
private double xOffset = 0 ;
private double yOffset = 0 ;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource(LOGIN_FXML_PATH));
Parent root = loader.load();
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.UNDECORATED);
root.setOnMousePressed(mouseEvent -> {
xOffset = mouseEvent.getSceneX();
yOffset = mouseEvent.getSceneY();
});
root.setOnMouseDragged(mouseEvent -> {
stage.setX(mouseEvent.getScreenX() - xOffset);
stage.setY(mouseEvent.getScreenY() - yOffset);
});
primaryStage.show();
}
}
If you need to reuse the same functionality repeatedly, just create a separate class in the normal way:
public class StageDragHandler {
private double xOffset = 0 ;
private double yOffset = 0 ;
public Stage createStage(Parent root) {
Stage stage = new Stage();
configureStage(root, stage);
return stage ;
}
public void configureStage(Parent root, Stage stage) {
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.initStyle(StageStyle.UNDECORATED);
root.setOnMousePressed(mouseEvent -> {
xOffset = mouseEvent.getSceneX();
yOffset = mouseEvent.getSceneY();
});
root.setOnMouseDragged(mouseEvent -> {
stage.setX(mouseEvent.getScreenX() - xOffset);
stage.setY(mouseEvent.getScreenY() - yOffset);
});
}
}
Then your application class reduces to:
public class MyApplication extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource(LOGIN_FXML_PATH));
Parent root = loader.load();
new StageDragHandler().configureStage(root, primaryStage);
primaryStage.show();
}
}
and you can use similar code anywhere you need it.

JAVAFX application UI become unresponsive

I have an desktop application based on javafx.The only requirement is that it has to run continuously for 5-6 days.I am in testing phase.
UI component used in this:
6 Gauges (Medusa)
Line chart that is stacked on JFXDrawer and is controlled by a hamburger-button.
Out of 6 gauges 3 gauges and line chart has to be updated on a period of a sec. For this i have used a thread which update this component per second.
I have tested this application for almost 3 time:
For Three days
For 26 hours(In which graph is working fine and values are updating in the gauges but the gauge needle is stuck at a particular value)
For 24 hours now the gauge needle is moving but gauge value is stuck and the Line chart drawer is not open only the hamburger-icon is changing.
There is no Exception in log and the UI is not hanging, Just Ui components are not responsive.
I did profiling but everything seems OK.I have read this post also but in his case the ui is hanging and i have no issue regarding this, all the button are clicky and i am able to change the screens with not issue.
After reloading the Screen All gets back to normal.
sample code i've used to update the component.
Controller
public class testGaugeController implements Initializable {
// Component
private static final int MAX_DATA_POINTS = 1000;
private int xSeriesData = 0;
private final XYChart.Series<Number, Number> series1 = new XYChart.Series<>();
private ExecutorService executor;
private final ConcurrentLinkedQueue<Number> dataQ1 = new ConcurrentLinkedQueue<>();
private NumberAxis xAxis,yAxis;
LineChart<Number, Number> lineChart;
DatabaseHandler dh = new DatabaseHandler();
Connection connect = dh.MakeConnection();
#FXML
private JFXDrawer drawer;
#FXML
private JFXHamburger burger;
#FXML
private Gauge Gauge;
/**
* Initializes the controller class.
*
* #param url
* #param rb
*/
public void initialize(URL url, ResourceBundle rb) throws IOException {
initializeRecorder();
start_recording();
}
private void initializeRecorder() throws IOException {
try {
xAxis = new NumberAxis(0, MAX_DATA_POINTS, MAX_DATA_POINTS / 100);
xAxis.setForceZeroInRange(false);
xAxis.setAutoRanging(true);
xAxis.setTickLabelsVisible(true);
xAxis.setTickMarkVisible(true);
xAxis.setMinorTickVisible(true);
yAxis = new NumberAxis();
// Create a LineChart
lineChart = new LineChart<Number, Number>(xAxis, yAxis) {
// Override to remove symbols on each data point
#Override
protected void dataItemAdded(XYChart.Series<Number, Number> series, int itemIndex, XYChart.Data<Number, Number> item) {
}
};
lineChart.setAnimated(false);
lineChart.setTitle("");
lineChart.setHorizontalGridLinesVisible(true);
series1.setName("Test Value");
lineChart.getData().addAll(series1);
drawer.setSidePane(lineChart);
drawer.setOverLayVisible(false);
} catch (Exception e) {
}
HamburgerBackArrowBasicTransition burgermove = new HamburgerBackArrowBasicTransition(burger);
burgermove.setRate(-1);
burger.addEventHandler(MouseEvent.MOUSE_PRESSED, (evt) -> {
burgermove.setRate(burgermove.getRate() * -1);
burgermove.play();
if (drawer.isShown()) {
drawer.close();
} else {
drawer.open();
}
});
}
int count_executer_status = 0;
boolean initial_start_trend = true;
private void start_recording() {
if (initial_start_trend) {
initial_start_trend = false;
} else {
xSeriesData = 0;
System.out.println("Clearing dataQue");
dataQ1.clear();
series1.getData().clear();
}
xAxis.setLowerBound(0);
count_executer_status++;
System.out.println("Cleared dataQue");
executor = Executors.newCachedThreadPool((Runnable r) -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
});
count_executer_status = 0;
AddToQueue addToQueue = new AddToQueue();
executor.execute(addToQueue);
//-- Prepare Timeline
prepareTimeline();
}
private class AddToQueue implements Runnable {
String query = "SELECT test_value FROM test_data_reader ORDER BY test_data_reader_id DESC LIMIT 1";
ResultSet rs;
#Override
public void run() {
try {
// add a item of random data to queue.
rs = dh.getData(query, connect);
if (rs.next()) {
double test_value = Double.parseDouble(rs.getString("test_value"));
dataQ1.add(test_value);
String Record_data = "INSERT INTO `test_data_record` (`test_value`, `date_time`) VALUES( '" + rs.getString("test_value") + "', NOW());";
dh.execute(Record_data, connect);
Platform.runLater(() -> {
Gauge.setValue(test_value);
});
}
xaxis_count++;
Thread.sleep(1000);
executor.execute(this);
} catch (Exception ex) {
}
}
}}
//-- Timeline gets called in the JavaFX Main thread
private void prepareTimeline() {
// Every frame to take any data from queue and add to chart
new AnimationTimer() {
#Override
public void handle(long now) {
addDataToSeries();
}
}.start();
}
int xaxis_count = 0;
private void addDataToSeries() {
try {
for (int i = 0; i < 20; i++) {
//-- add 20 numbers to the plot+
if (dataQ1.isEmpty()) {
break;
}
series1.getData().add(new XYChart.Data<>(xaxis_count, dataQ1.remove()));
}
if (series1.getData().size() > MAX_DATA_POINTS) {
series1.getData().remove(0, series1.getData().size() - MAX_DATA_POINTS);
}
// update
xAxis.setLowerBound(xSeriesData - MAX_DATA_POINTS);
xAxis.setUpperBound(xSeriesData - 1);
} catch (Exception e) {
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import com.jfoenix.controls.JFXDrawer?>
<?import com.jfoenix.controls.JFXHamburger?>
<?import eu.hansolo.medusa.Gauge?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="e913_300mt.testGaugeController">
<children>
<VBox prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<HBox fx:id="sectionHeader" minHeight="-Infinity" prefHeight="50.0" prefWidth="600.0" style="-fx-background-color: #2F333E;">
<children>
<VBox alignment="CENTER" layoutX="10.0" layoutY="10.0" HBox.hgrow="ALWAYS" />
<VBox alignment="CENTER" HBox.hgrow="ALWAYS" />
<VBox alignment="CENTER" HBox.hgrow="ALWAYS" />
<VBox alignment="CENTER" HBox.hgrow="ALWAYS" />
<VBox alignment="CENTER" HBox.hgrow="ALWAYS">
<children>
<JFXHamburger fx:id="burger" />
</children>
</VBox>
</children>
</HBox>
<HBox prefHeight="100.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
<children>
<Gauge fx:id="gauge" autoScale="false" borderPaint="#0099ff" borderWidth="3.0" decimals="0" foregroundPaint="#0000000b" highlightSections="true" innerShadowEnabled="true" knobType="METAL" lcdDesign="RED" lcdFont="LCD" majorTickMarkType="PILL" majorTickSpace="250.0" markersVisible="true" maxValue="3000.0" mediumTickMarkType="TRAPEZOID" mediumTickMarksVisible="false" minorTickSpace="50.0" needleSize="THIN" needleType="VARIOMETER" shadowsEnabled="true" threshold="300.0" title="Test Gauge" unit="unit" HBox.hgrow="ALWAYS">
<HBox.margin>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</HBox.margin>
</Gauge>
</children>
</HBox>
</children>
</VBox>
<JFXDrawer fx:id="drawer" defaultDrawerSize="600.0" direction="RIGHT" layoutY="56.0" prefHeight="344.0" prefWidth="600.0" />
</children>
</AnchorPane>
Libraries
jfoenix-8.0.1-for-java8.jar Github
fontawesomefx-8.9.jar bitbucket
Medusa-8.0.jar jar-download
For testing purpose i am only updating one gauge.And system Configuration is :
OS : Ubuntu 18.04 lts
Processor : Intel core i5-8400
Ram : 8Gb
Any Idea on this.

passing a variable string through a textfield for a digital clock in java fxml

im building a stop watch with a digital and analog clock but having trouble with printing the digital clock value mainly because im unsure how to pass the string to the FXML.
why is it that when i try to us id="digitalText" for the textfield in the fxml the program fails but if i were to write it using a text="00:00" it will work properly(just with a text with 00:00 though so no updating the digital clock of course). i would have hoped giving it the id would print the value inside it.
the controller
package tds7xbstopwatchfxml;
import java.awt.TextField;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.util.Duration;
public class FXMLDocumentController implements Initializable {
#FXML
private ImageView clockFace;
#FXML
private ImageView hand;
#FXML
private TextField digitalText;
String digital;
Double rotation = 0.0;
Integer ms = 00;
Integer mm = 00;
Timeline timeline = new Timeline(
new KeyFrame(Duration.millis(1000), (ActionEvent actionEvent) -> {
updateTimer();
updateDigital();
})
);
#FXML
private void stopButton(ActionEvent event) {
timeline.stop();
}
#FXML
private void startButton(ActionEvent event) {
timeline.play();
}
#FXML
private void resetButton(ActionEvent event) {
rotation = 0.0;
ms = 0;
mm = 0;
hand.setRotate(0);
digital="00:00";
timeline.stop();
}
public void updateTimer() {
ms += 1000;
if (ms >= 60000) {
ms = 00;
mm++;
}
rotation = (ms / 60000.0) * 360.0;
hand.setRotate(rotation);
}
public void updateDigital(){
if(ms/1000<10 && mm<10){
digital=("0" + String.valueOf(mm) + ":" + "0" + String.valueOf(ms/1000));
}
if(ms/1000>=10 && mm <10){
digital=("0" + String.valueOf(mm) + ":" + "" + String.valueOf(ms/1000));
}
if(mm>=10 && ms/1000<10){
digital=("" + String.valueOf(mm) + ":" + "0" + String.valueOf(ms/1000));
}
if(mm>=10 && ms/1000>=10){
digital=("" + String.valueOf(mm) + ":" + "" + String.valueOf(ms/1000));
}
}
#Override
public void initialize(URL url, ResourceBundle rb) {
clockFace.setImage(new Image(this.getClass().getResourceAsStream("clockface.png")));
hand.setImage(new Image(this.getClass().getResourceAsStream("hand.png")));
timeline.setCycleCount(Animation.INDEFINITE);
digitalText.setText(digital);
}
}
i know there are better ways to implement the code for the digital clock but mainly i want to know about passing a changing string to be printed. the digitalText.setText(digital); will cause it to crash
and the fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="600.0" prefWidth="600.0" styleClass="mainFxmlClass" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="tds7xbstopwatchfxml.FXMLDocumentController">
<stylesheets>
<URL value="#stopwatchui.css" />
</stylesheets>
<children>
<StackPane layoutY="94.0" prefHeight="150.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="175.0">
<children>
<ImageView fx:id="clockFace" fitHeight="400.0" fitWidth="400.0" pickOnBounds="true" preserveRatio="true" />
<ImageView fx:id="hand" fitHeight="400.0" fitWidth="400.0" pickOnBounds="true" preserveRatio="true" />
</children>
</StackPane>
<TextField fx:id="digitalText" alignment="CENTER" editable="false" layoutX="204.0" layoutY="123.0" prefHeight="43.0" prefWidth="194.0">
<font>
<Font name="Arial" size="23.0" />
</font>
</TextField>
<Button fx:id="stopbutton" layoutX="280.0" layoutY="75.0" onAction="#stopButton" text="Stop" />
<Button fx:id="startbutton" layoutX="204.0" layoutY="75.0" onAction="#startButton" text="Start" />
<Button fx:id="resetbutton" layoutX="353.0" layoutY="75.0" onAction="#resetButton" text="Reset" />
</children>
</AnchorPane>
You imported the Swing TextField, not the JavaFX text field
Change
import java.awt.TextField
to
import java.scene.control.TextField;

Adding dynamic data to StackedAreaChart in JavaFX

I am trying to change the data of the StackedAreaChart dynamically. So I have created an fxml file and a controller for it.
fig.fxml
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="495.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.gollahalli.test.controller">
<children>
<StackedAreaChart fx:id="graph" layoutX="24.0" layoutY="95.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
<xAxis>
<NumberAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis side="LEFT" />
</yAxis>
</StackedAreaChart>
<TextField fx:id="number" layoutX="57.0" layoutY="39.0" />
<Button fx:id="button" layoutX="330.0" layoutY="39.0" mnemonicParsing="false" text="Button" />
</children>
</AnchorPane>
Controller.java
public class controller {
#FXML
private Button button;
#FXML
private TextField number;
#FXML
private StackedAreaChart<Number, Number> graph;
public void initialize(){
XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
button.setOnAction(event -> {
int number1 = Integer.parseInt(number.getText());
System.out.println(number1);
for (int i = 0; i < number1; i++) {
series.getData().addAll(new XYChart.Data(i, i));
}
graph.getData().add(series);
});
}
}
When I enter a number say 100, I am able to get the graph correctly, when I change the data say 101 and click on the button I get an error as Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Duplicate series added.
I do know that to overcome this I would have to use ObservableList, but I am not sure how to use it.
Now the question is, how should I change/refresh the data every time I click on the button?
The exception message is clear. To avoid it add series only once:
public void initialize(){
XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>();
// add series only once at init
graph.getData().add(series);
button.setOnAction(event -> {
int number1 = Integer.parseInt(number.getText());
System.out.println(number1);
// clear current data
series.getData().clear();
// add new data
for (int i = 0; i < number1; i++) {
series.getData().add(new XYChart.Data(i, i));
}
});
}

Categories

Resources