Adding dynamic data to StackedAreaChart in JavaFX - java

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));
}
});
}

Related

JAVAFX: Switching Between Panes in BorderPane

I have a desktop app that contains:
Main Class: that load the first fxml file -> SideBar.fxml
SideBar.fxml: contains BorderPane -> at the left of it, i create 2 buttons:
- the fist button: load sample fxml file
- the second button: load secondFxml file
sample.fxml: contains a tableView and a Button
secondFxml.fxml: contains a label
Controller: a class that control sample.fxml -> load random double values to a tableView
the issue is :
when i press the button (fill Table) in Pane 1 : it load the data to the tableView, untill now everything is going well
when i switch to the second pane and i return to the first pane the center border pane is reloaded again so the data of the tableView disappeared
what i want is when i return to the first pane the the table view stay as it was first
i try to hide the borderpane center but it doesn't work for me
i screenShot the issue:
Main:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("SideBar.fxml"));
primaryStage.setTitle("Hello World");
primaryStage.setScene(new Scene(root, 700, 500));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
SideBarController:
public class SideBarController implements Initializable {
#FXML BorderPane borderPane;
public void openPane1(ActionEvent event) throws Exception {
loadScene("Sample.fxml");
}
public void openPane2(ActionEvent event) throws Exception {
loadScene("secondFxml.fxml");
}
private void loadScene(String sc) throws IOException {
Parent root = FXMLLoader.load(getClass().getResource(sc));
borderPane.setCenter(root);
}
#Override
public void initialize(URL location, ResourceBundle resources) { }
}
Controller:
public class Controller implements Initializable {
double[][] data = new double[5][5];
Random random = new Random();
ObservableList<double[]> observableLists = FXCollections.observableArrayList();
#FXML
TableView<double []> tableView = new TableView<>(observableLists);
#FXML
public void fillTable(ActionEvent event) throws IOException {
//Random Values
for (int i = 0; i <data.length ; i++) {
for (int j = 0; j <data[0].length ; j++) {
data[i][j]= random.nextDouble();
}
}
//Add data to ObservableLists
for (int i = 0; i <data.length ; i++) {
observableLists.add(data[i]);
}
//Create Columns
for (int i = 0; i <data[0].length ; i++) {
TableColumn<double[], Double> column= null;
column = new TableColumn<>("column "+i);
int finalI = i;
column.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue()[finalI]));
tableView.getColumns().add(column);
}
// Fill TableView
tableView.setItems(observableLists);
}
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
SideBar.fxml
<BorderPane fx:id="borderPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.SideBarController">
<left>
<VBox prefHeight="400.0" prefWidth="173.0" style="-fx-background-color: black;" BorderPane.alignment="CENTER">
<children>
<Button mnemonicParsing="false" onAction="#openPane1" prefHeight="25.0" prefWidth="177.0" style="-fx-background-color: blue; -fx-border-color: white;" text="Pane 1" textFill="WHITE">
<VBox.margin>
<Insets top="50.0" />
</VBox.margin>
<font>
<Font name="System Bold" size="17.0" />
</font>
</Button>
<Button mnemonicParsing="false" onAction="#openPane2" prefHeight="25.0" prefWidth="176.0" style="-fx-background-color: blue; -fx-border-color: white;" text="Pane 2" textFill="WHITE">
<VBox.margin>
<Insets top="10.0" />
</VBox.margin>
<font>
<Font name="System Bold" size="17.0" />
</font>
</Button>
</children>
</VBox>
</left>
<center>
<Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
<Label layoutX="163.0" layoutY="152.0" prefHeight="68.0" prefWidth="131.0" text="Home">
<font>
<Font size="46.0" />
</font>
</Label>
</children>
</Pane>
</center>
</BorderPane>
Sample.fxml
<Pane prefHeight="395.0" prefWidth="597.0" style="-fx-background-color: white;" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<TableView fx:id="tableView" layoutX="77.0" layoutY="47.0" prefHeight="266.0" prefWidth="461.0" />
<Button layoutX="257.0" layoutY="329.0" mnemonicParsing="false" onAction="#fillTable" text="fill Table" />
</children>
</Pane>
SecondFxml.fxml
<Pane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label layoutX="232.0" layoutY="153.0" text="Pane 2">
<font>
<Font size="46.0" />
</font>
</Label>
</children>
</Pane>
Do not reload from fxml when button is clicked. Do it once in initialize:
public class SideBarController implements Initializable {
#FXML BorderPane borderPane;
private Parent sample, secondFxml;
public void openPane1(ActionEvent event) throws Exception {
borderPane.setCenter(sample);
}
public void openPane2(ActionEvent event) throws Exception {
borderPane.setCenter(secondFxml);
}
private Parent loadScene(String sc) throws IOException {
return FXMLLoader.load(getClass().getResource(sc));
}
#Override
public void initialize(URL location, ResourceBundle resources) {
try {
sample = loadScene("Sample.fxml");
secondFxml = loadScene("secondFxml.fxml");
} catch (IOException ex) {
ex.printStackTrace();
};
}
}

javafx - table view mouse event not catched

I have a TableView in my fxml. Within the controller this table is mapped with the #FXML annotation.
#FXML
TableView<BankMovement> table;
I also set, in fxml document, for table element
onMouseReleased = "#handleRowSelect"
finally always in the controller I have the following method
#FXML
private void handleRowSelect(MouseEvent event){
Document row = table.getSelectionModel().getSelectedItem();
if (row == null) return;
if(row != temp){
temp = row;
lastClickTime = new Date();
} else if(row == temp) {
Date now = new Date();
long diff = now.getTime() - lastClickTime.getTime();
if (diff < 300){
System.out.println("Edit dialog");
System.out.print(row.getDescription());
} else {
lastClickTime = new Date();
System.out.print(row.getFile());
}
}
}
here instead is where to bind the data to table
#Override
public void initializeTable() {
table.setItems(subList());
colId.setCellValueFactory(new PropertyValueFactory<>("idDocument"));
colIdCategory.setCellValueFactory(d -> new SimpleStringProperty(d.getValue().getCategory().getName()));
colIdCategoryChild.setCellValueFactory(d -> new SimpleStringProperty(d.getValue().getCategoryChild().getName()));
colIdDocument.setCellFactory(getFileCellFactory());
colIdDocument.setCellValueFactory(new PropertyValueFactory<>("file"));
colIdDescription.setCellValueFactory(new PropertyValueFactory<>("description"));
colIdDate.setCellValueFactory(d -> new SimpleStringProperty(Utils.localTimeToItalianDate(d.getValue().getDocumentDate())));
}
Even if I click on the table I never enter the handleRowSelect method.
I have the impression that when I click on the row the click on the cell is managed and not on the row. As if the event were not propagated.
This is fxml
<AnchorPane minWidth="-Infinity" prefHeight="900.0" prefWidth="1510.0" styleClass="background-white" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="MyController">
<children>
<VBox prefHeight="886.0" prefWidth="1465.0" style="-fx-min-width: 100%;" AnchorPane.bottomAnchor="4.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="330.0" AnchorPane.topAnchor="10.0">
<children>
<TitledPane animated="false" minHeight="-Infinity" minWidth="-Infinity" prefHeight="880.0" prefWidth="1465.0" text="%app.pane.document">
<content>
<VBox minWidth="-Infinity" prefHeight="815.0" prefWidth="1474.0">
<children>
<TableView onMouseReleased="#handleRowSelect" fx:id="table" minHeight="-Infinity" minWidth="-Infinity" prefHeight="746.0" prefWidth="1450.0">
.......
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</TableView>
<Pagination fx:id="pagination" prefHeight="73.0" prefWidth="1450.0" />
</children>
</VBox>
</content>
</TitledPane>
</children>
</VBox>
</children>
</AnchorPane>

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

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:

setVisible() does not hide the node [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I have the LineChart with 4 different series. There are checkboxes which should show/hide the correspondent series from chart. But when I pressed checkboxes for "Euler", "Improved Euler", "Runge-Kutta", they do not affect the chart at all, until I press the "Exact"'s checkbox. Can't get the problem absolutely.
Here is how I try to hide/show:
#FXML private NumberAxis xAxis = new NumberAxis();
#FXML private NumberAxis yAxis = new NumberAxis();
#FXML private LineChart<Number, Number> chartAll = new LineChart<>(xAxis, yAxis);
private XYChart.Series<Number, Number> exactSeries = new XYChart.Series<>();
private XYChart.Series<Number, Number> eulerSeries= new XYChart.Series<>();
private XYChart.Series<Number, Number> improvedEulerSeries = new XYChart.Series<>();
private XYChart.Series<Number, Number> rungeKuttaSeries = new XYChart.Series<>();
#FXML private CheckBox exactChartCheckbox;
#FXML private CheckBox eulerChartCheckbox;
#FXML private CheckBox improvedEulerChartCheckbox;
#FXML private CheckBox rungeKuttaChartCheckbox;
#FXML
protected void handlePlotButton(ActionEvent event) {
chartAll.getData().clear();
double x0 = Double.parseDouble(x0Input.getText());
double y0 = Double.parseDouble(y0Input.getText());
double x = Double.parseDouble(xInput.getText());
double step = Double.parseDouble(stepInput.getText());
IVP ivpForExact = new IVP(x0, y0, x, step);
Exact exact = new Exact(ivpForExact);
Euler euler = new Euler(ivpForExact);
ImprovedEuler improvedEuler = new ImprovedEuler(ivpForExact);
RungeKutta rungeKutta = new RungeKutta(ivpForExact);
exactSeries.setName("Exact");
eulerSeries.setName("Euler");
improvedEulerSeries.setName("Improved Euler");
rungeKuttaSeries.setName("Runge-Kutta");
for (int i = 0; i < exact.getGrid().getSize(); i++) {
exactSeries.getData().add(new XYChart.Data<>(exact.getGrid().getXat(i), exact.getGrid().getYat(i)));
eulerSeries.getData().add(new XYChart.Data<>(euler.getGrid().getXat(i), euler.getGrid().getYat(i)));
improvedEulerSeries.getData().add(new XYChart.Data<>(improvedEuler.getGrid().getXat(i), improvedEuler.getGrid().getYat(i)));
rungeKuttaSeries.getData().add(new XYChart.Data<>(rungeKutta.getGrid().getXat(i), rungeKutta.getGrid().getYat(i)));
}
chartAll.getData().add(exactSeries);
chartAll.getData().add(eulerSeries);
chartAll.getData().add(improvedEulerSeries);
chartAll.getData().add(rungeKuttaSeries);
stepInput.clear();
}
#FXML
protected void exactChartCheckboxPressed(ActionEvent event) {
if(!exactChartCheckbox.isSelected()) {
chartAll.getData().get(0).getNode().setVisible(false);
} else {
chartAll.getData().get(0).getNode().setVisible(true);
}
}
#FXML
protected void eulerChartCheckboxPressed(ActionEvent event) {
if(!eulerChartCheckbox.isSelected()) {
chartAll.getData().get(1).getNode().setVisible(false);
} else {
chartAll.getData().get(1).getNode().setVisible(true);
}
}
Here is FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.layout.*?>
<GridPane fx:id="scene" alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<columnConstraints>
<ColumnConstraints />
</columnConstraints>
<rowConstraints>
<RowConstraints />
</rowConstraints>
<children>
<Pane fx:id="pane" prefHeight="600.0" prefWidth="1080.0">
<children>
<LineChart fx:id="chartAll" createSymbols="false" layoutX="48.0" layoutY="135.0" prefHeight="427.0" prefWidth="698.0">
<xAxis>
<NumberAxis label="x" fx:id="xAxis" />
</xAxis>
<yAxis>
<NumberAxis fx:id="yAxis" autoRanging="false" label="y" lowerBound="-10.0" upperBound="40.0" />
</yAxis>
<opaqueInsets>
<Insets />
</opaqueInsets>
</LineChart>
<Button layoutX="48.0" layoutY="54.0" mnemonicParsing="false" prefHeight="41.0" prefWidth="120.0" text="Solutions" />
<TextField layoutX="883.0" layoutY="211.0" prefHeight="25.0" prefWidth="96.0" text="0" fx:id="x0Input" />
<TextField fx:id="y0Input" layoutX="883.0" layoutY="254.0" prefHeight="25.0" prefWidth="96.0" text="3" />
<TextField layoutX="883.0" layoutY="296.0" prefHeight="25.0" prefWidth="96.0" text="5.5" fx:id="xInput" />
<TextField fx:id="stepInput" layoutX="883.0" layoutY="336.0" prefHeight="25.0" prefWidth="96.0" text="0.1" />
<Label layoutX="841.0" layoutY="215.0" text="x0" />
<Label layoutX="841.0" layoutY="258.0" text="y0" />
<Label layoutX="841.0" layoutY="300.0" text="X" />
<Label layoutX="835.0" layoutY="340.0" text="step" />
<Button fx:id="plotButton" layoutX="896.0" layoutY="385.0" mnemonicParsing="false" onAction="#handlePlotButton" prefHeight="41.0" prefWidth="71.0" text="Plot" />
<CheckBox fx:id="exactChartCheckbox" layoutX="347.0" layoutY="562.0" mnemonicParsing="false" onAction="#exactChartCheckboxPressed" selected="true" text="Exact" />
<CheckBox fx:id="improvedEulerChartCheckbox" layoutX="523.0" layoutY="562.0" mnemonicParsing="false" onAction="#improvedEulerChartCheckboxPressed" selected="true" text="Improved Euler" />
<CheckBox fx:id="rungeKuttaChartCheckbox" layoutX="646.0" layoutY="562.0" mnemonicParsing="false" onAction="#rungeKuttaChartCheckboxPressed" selected="true" text="Runge-Kutta" />
<CheckBox fx:id="eulerChartCheckbox" layoutX="444.0" layoutY="562.0" mnemonicParsing="false" onAction="#eulerChartCheckboxPressed" selected="true" text="Euler" />
</children>
</Pane>
</children>
</GridPane>
Here is the result
after I hide firstly "Euler" and then "Exact":
after I show "Exact" then "Euler":
The problem actually is that chart is only updated when I check/uncheck "Exact" box, all three others do not affect the chart. But when I press the "Exact"'s checkbox, they are trying to be updated also but do it improperly.
To effectively hide the node try setting the managed property
series.getNode().setManaged(false);
Defines whether or not this node's layout will be managed by it's parent. If the node is managed, it's parent will factor the node's geometry into its own preferred size and layoutBounds calculations and will lay it out during the scene's layout pass. If a managed node's layoutBounds changes, it will automatically trigger relayout up the scene-graph to the nearest layout root (which is typically the scene's root node).
If the node is unmanaged, its parent will ignore the child in both preferred size computations and layout. Changes in layoutBounds will not trigger relayout above it. If an unmanaged node is of type Parent, it will act as a "layout root", meaning that calls to Parent.requestLayout() beneath it will cause only the branch rooted by the node to be relayed out, thereby isolating layout changes to that root and below. It's the application's responsibility to set the size and position of an unmanaged node.
Based on the length of my comment, I am posting it in answer rather than in comment.
Firstly, if you check the documentation of node in XYChart.Series
The node to display for this series. This is created by the chart if
it uses nodes to represent the whole series. For example line chart
uses this for the line but scatter chart does not use it. This node
will be set as soon as the series is added to the chart. You can then
get it to add mouse listeners etc.
From the above, it is clear that you can easily rely on node if it is line chart. And from your screenshot I can see that you are already using LineChart. Which is not a problem. If you inspect the node type, it will be of type Path. But things get a little messy if your line chart has data points displayed on it.Sometimes toggling the node(Path) visibility alone cannot get the desired behavior. For example check the below screen shot.
So to hide a series from the chart, I would recommend to add/remove the series from the data rather than toggling the visibility of the series node.
Coming to your problem, I gave a quick check to toggle the lines visiblity and it working fine for all series. Might be some issue in your code. As #Slaw mentioned, a Minimal, Complete, and Verifiable example can help us to understand the issue or even solve your problem by yourself.
It seems, like it depends on your luck or some legacy JavaFX code. When I changed each series to Array of series:
private ArrayList<XYChart.Series<Number, Number>> exactSeries = new ArrayList<>();
private ArrayList<XYChart.Series<Number, Number>> eulerSeries = new ArrayList<>();
private ArrayList<XYChart.Series<Number, Number>> improvedEulerSeries = new ArrayList<>();
private ArrayList<XYChart.Series<Number, Number>> rungeKuttaSeries = new ArrayList<>();
and then create the common function for all cases:
private void turnVisibility(CheckBox checkBox, ArrayList<XYChart.Series<Number, Number>> seriesList) {
if (!checkBox.isSelected()) {
for(XYChart.Series<Number, Number> series: seriesList) {
series.getNode().setVisible(false);
}
} else {
for(XYChart.Series<Number, Number> series: seriesList) {
series.getNode().setVisible(true);
}
}
}
visibility starts work correctly.

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.

Categories

Resources