I would like to export (.txt or .cvs) data plotted on a graph by left mouse click, highlight it then right mouse click opens a pop-up then a file chooser dialog to save data
Here is a picture example
and here is a sample code
public class BaseXYChart extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("Linear plot");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(0, 22, 0.5);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis){
#Override
public String toString(Number object){
return String.format("%7.2f", object);
}
});
final LineChart<String, Number>lineChart = new LineChart<String, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setLegendVisible(false);
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 1.5));
series1.getData().add(new XYChart.Data("Mar", 2));
series1.getData().add(new XYChart.Data("Apr", 2.5));
series1.getData().add(new XYChart.Data("May", 3));
series1.getData().add(new XYChart.Data("Jun", 4));
series1.getData().add(new XYChart.Data("Jul", 6));
series1.getData().add(new XYChart.Data("Aug", 9));
series1.getData().add(new XYChart.Data("Sep", 12));
series1.getData().add(new XYChart.Data("Oct", 15));
series1.getData().add(new XYChart.Data("Nov", 20));
series1.getData().add(new XYChart.Data("Dec", 22));
BorderPane pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
My question is then how to do to select the line data plotted in order to then open a pop-up.
Thanks
Check the modified code of yours below out.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.effect.DropShadow;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class BaseXYChart extends Application {
private DropShadow ds = new DropShadow();
private ContextMenu contextMenu;
private XYChart.Series selectedSeries;
#Override
public void start(Stage stage) {
stage.setTitle("Linear plot");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(0, 22, 0.5);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%7.2f", object);
}
});
final LineChart<String, Number> lineChart = new LineChart<String, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setLegendVisible(false);
final XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 1.5));
series1.getData().add(new XYChart.Data("Mar", 2));
series1.getData().add(new XYChart.Data("Apr", 2.5));
series1.getData().add(new XYChart.Data("May", 3));
series1.getData().add(new XYChart.Data("Jun", 4));
series1.getData().add(new XYChart.Data("Jul", 6));
series1.getData().add(new XYChart.Data("Aug", 9));
series1.getData().add(new XYChart.Data("Sep", 12));
series1.getData().add(new XYChart.Data("Oct", 15));
series1.getData().add(new XYChart.Data("Nov", 20));
series1.getData().add(new XYChart.Data("Dec", 22));
BorderPane pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);
contextMenu = new ContextMenu();
MenuItem menuItem = new MenuItem("Save data");
contextMenu.getItems().add(menuItem);
menuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
if (selectedSeries != null) {
System.out.println("Save data of " + selectedSeries.getData());
// Saving logic here
}
}
});
// for every series in linechart
applyMouseEvents(series1);
stage.setScene(scene);
stage.show();
}
private void applyMouseEvents(final XYChart.Series series) {
final Node node = series.getNode();
node.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
node.setEffect(ds);
node.setCursor(Cursor.HAND);
}
});
node.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
node.setEffect(null);
node.setCursor(Cursor.DEFAULT);
}
});
node.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getButton().equals(MouseButton.SECONDARY)) {
contextMenu.show(node, mouseEvent.getScreenX() + 1, mouseEvent.getScreenY() + 1);
// Set as selected
selectedSeries = series;
System.out.println("Selected Series data " + selectedSeries.getData());
}
}
});
}
public static void main(String[] args) {
launch(args);
}
}
Related
I want to stack two different XYCharts as done here.
In this example however, the bounds of the axes are the same and data is static.
In my case, I have dynamic data to plot: new values are added to the data series as they become available. So the y axis (for instance) gets updated when new data arrives.
Moreover, the two data sets are not exactly in the same range.
Here is a first attempt:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* Demonstrates how to draw layers of XYCharts.
* https://forums.oracle.com/forums/thread.jspa?threadID=2435995 "Using StackPane to layer more different type charts"
*/
public class LayeredXyChartsSample extends Application {
private XYChart.Series<String, Number> barSeries;
private XYChart.Series<String, Number> lineSeries;
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
initSeries();
// Close the application when the window is closed
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(0);
});
stage.setScene(
new Scene(
layerCharts(
createBarChart(),
createLineChart()
)
)
);
stage.show();
updateSeries();
}
#SuppressWarnings("unchecked")
private void initSeries() {
barSeries = new XYChart.Series(
FXCollections.observableArrayList(
new XYChart.Data("Jan", 2),
new XYChart.Data("Feb", 10),
new XYChart.Data("Mar", 8),
new XYChart.Data("Apr", 4),
new XYChart.Data("May", 7),
new XYChart.Data("Jun", 5),
new XYChart.Data("Jul", 4),
new XYChart.Data("Aug", 8),
new XYChart.Data("Sep", 16.5),
new XYChart.Data("Oct", 13.9),
new XYChart.Data("Nov", 17),
new XYChart.Data("Dec", 10)
)
);
lineSeries = new XYChart.Series(
FXCollections.observableArrayList(
new XYChart.Data("Jan", 1),
new XYChart.Data("Feb", 2),
new XYChart.Data("Mar", 1.5),
new XYChart.Data("Apr", 3),
new XYChart.Data("May", 2.5),
new XYChart.Data("Jun", 5),
new XYChart.Data("Jul", 4),
new XYChart.Data("Aug", 8),
new XYChart.Data("Sep", 6.5),
new XYChart.Data("Oct", 13),
new XYChart.Data("Nov", 10),
new XYChart.Data("Dec", 20)
)
);
}
private void updateSeries() {
new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
final int index = i;
final double value = 20 * Math.random();
Platform.runLater(() -> {
barSeries.getData().remove(0);
barSeries.getData().add(new XYChart.Data<>(String.valueOf(index), value));
lineSeries.getData().remove(0);
lineSeries.getData().add(new XYChart.Data<>(String.valueOf(index), value * 2));
});
}
Platform.exit();
System.exit(0);
}).start();
}
private NumberAxis createYaxis() {
final NumberAxis axis = new NumberAxis();
axis.setAutoRanging(true);
axis.setPrefWidth(35);
axis.setMinorTickCount(10);
axis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(axis) {
#Override public String toString(Number object) {
return String.format("%7.2f", object.floatValue());
}
});
return axis;
}
#SuppressWarnings("unchecked")
private BarChart<String, Number> createBarChart() {
final BarChart<String, Number> chart = new BarChart<>(new CategoryAxis(), createYaxis());
setDefaultChartProperties(chart);
chart.getData().addAll(barSeries);
return chart;
}
#SuppressWarnings("unchecked")
private LineChart<String, Number> createLineChart() {
final LineChart<String, Number> chart = new LineChart<>(new CategoryAxis(), createYaxis());
setDefaultChartProperties(chart);
chart.setCreateSymbols(false);
chart.getData().addAll(lineSeries);
return chart;
}
private void setDefaultChartProperties(final XYChart<String, Number> chart) {
chart.setLegendVisible(false);
chart.setAnimated(false);
}
#SafeVarargs
private final StackPane layerCharts(final XYChart<String, Number>... charts) {
for (int i = 1; i < charts.length; i++) {
configureOverlayChart(charts[i]);
}
StackPane stackpane = new StackPane();
stackpane.getChildren().addAll(charts);
return stackpane;
}
private void configureOverlayChart(final XYChart<String, Number> chart) {
chart.setAlternativeRowFillVisible(false);
chart.setAlternativeColumnFillVisible(false);
chart.setHorizontalGridLinesVisible(false);
chart.setVerticalGridLinesVisible(false);
chart.getXAxis().setVisible(false);
chart.getYAxis().setVisible(false);
chart.getStylesheets().addAll(getClass().getResource("/overlay-chart.css").toExternalForm());
}
}
The result looks like this:
The Y axis is not looking good: there are two axes and as they don't have the same bounds anymore, they don't overlay properly.
Next attempt consists in creating a single axis and assigning it to both charts. A few changes:
a class variable is created: private NumberAxis yAxis;
the createYaxis method is modified as follows (it's a void method and sets the variable):
private void createYaxis() {
yAxis = new NumberAxis();
yAxis.setAutoRanging(true);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override public String toString(Number object) {
return String.format("%7.2f", object.floatValue());
}
});
}
the createYaxis method is called at the end of the initSeries method.
the charts are created with the same yAxis, e.g.:
BarChart chart = new BarChart<>(new CategoryAxis(), yAxis);
Now, the yAxis looks good, but the graphs are not displayed on the same scale as soon as a new value for the line chart exceeds the axis bounds (note that the new line series values are 2x those of the bar series new values; the chart does not take it into account).
So my next move would be to create a BoundAxis class that takes a reference axis and update it's bound when the reference axis bounds are modified. Something like this:
public class BoundAxis<T> extends Axis<T> {
private final Axis<T> originalAxis;
public BoundAxis(Axis<T> originalAxis) {
this.originalAxis = originalAxis;
}
#Override
protected Object autoRange(double length) {
return originalAxis.autoRange(length); // Compilation error
}
#Override
protected void setRange(Object range, boolean animate) {
originalAxis.setRange(range, animate); // Compilation error
}
#Override
protected Object getRange() {
return originalAxis.getRange(); // Compilation error
}
#Override
public double getZeroPosition() {
return originalAxis.getZeroPosition();
}
#Override
public double getDisplayPosition(T value) {
return originalAxis.getDisplayPosition(value);
}
#Override
public T getValueForDisplay(double displayPosition) {
return originalAxis.getValueForDisplay(displayPosition);
}
#Override
public boolean isValueOnAxis(T value) {
return originalAxis.isValueOnAxis(value);
}
#Override
public double toNumericValue(T value) {
return originalAxis.toNumericValue(value);
}
#Override
public T toRealValue(double value) {
return originalAxis.toRealValue(value);
}
#Override
protected List<T> calculateTickValues(double length, Object range) {
return originalAxis.calculateTickValues(length, range); // Compilation error
}
#Override
protected String getTickMarkLabel(T value) {
return originalAxis.getTickMarkLabel(value); // Compilation error
}
}
But this does not compile, because there are protected methods I cannot call.
One last thing, I want something rather generic: the BoundAxis must be extend Axis so I can ue it with not only the NumberAxis.
Edit: This question is related to that one.
This is the solution I found. Basically, I set the upper and lower bounds manually.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* Demonstrates how to draw layers of XYCharts.
* https://forums.oracle.com/forums/thread.jspa?threadID=2435995 "Using StackPane to layer more different type charts"
*/
public class LayeredXyChartsSample extends Application {
private XYChart.Series<String, Number> barSeries;
private XYChart.Series<String, Number> lineSeries;
private NumberAxis yAxis;
private double lowerBound = Double.MAX_VALUE;
private double upperBound = Double.MIN_VALUE;
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
initSeries();
// Close the application when the window is closed
stage.setOnCloseRequest(t -> {
Platform.exit();
System.exit(0);
});
stage.setScene(
new Scene(
layerCharts(
createBarChart(),
createLineChart()
)
)
);
stage.show();
updateSeries();
}
#SuppressWarnings("unchecked")
private void initSeries() {
barSeries = new XYChart.Series(
FXCollections.observableArrayList(
new XYChart.Data("Jan", 2),
new XYChart.Data("Feb", 10),
new XYChart.Data("Mar", 8),
new XYChart.Data("Apr", 4),
new XYChart.Data("May", 7),
new XYChart.Data("Jun", 5),
new XYChart.Data("Jul", 4),
new XYChart.Data("Aug", 8),
new XYChart.Data("Sep", 16.5),
new XYChart.Data("Oct", 13.9),
new XYChart.Data("Nov", 17),
new XYChart.Data("Dec", 10)
)
);
lineSeries = new XYChart.Series(
FXCollections.observableArrayList(
new XYChart.Data("Jan", 1),
new XYChart.Data("Feb", 2),
new XYChart.Data("Mar", 1.5),
new XYChart.Data("Apr", 3),
new XYChart.Data("May", 2.5),
new XYChart.Data("Jun", 5),
new XYChart.Data("Jul", 4),
new XYChart.Data("Aug", 8),
new XYChart.Data("Sep", 6.5),
new XYChart.Data("Oct", 13),
new XYChart.Data("Nov", 10),
new XYChart.Data("Dec", 20)
)
);
createYaxis();
}
private void updateSeries() {
new Thread(() -> {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
final int index = i;
final double value = 20 * Math.random();
Platform.runLater(() -> {
barSeries.getData().remove(0);
barSeries.getData().add(new XYChart.Data<>(String.valueOf(index), value));
lineSeries.getData().remove(0);
lineSeries.getData().add(new XYChart.Data<>(String.valueOf(index), value * 2));
lowerBound = Double.MAX_VALUE;
upperBound = Double.MIN_VALUE;
for (int j = 0; j < barSeries.getData().size(); j++) {
lowerBound = Math.min(lowerBound, barSeries.getData().get(j).getYValue().doubleValue());
lowerBound = Math.min(lowerBound, lineSeries.getData().get(j).getYValue().doubleValue());
upperBound = Math.max(upperBound, barSeries.getData().get(j).getYValue().doubleValue());
upperBound = Math.max(upperBound, lineSeries.getData().get(j).getYValue().doubleValue());
}
yAxis.setLowerBound(lowerBound);
yAxis.setUpperBound(upperBound);
});
}
Platform.exit();
System.exit(0);
}).start();
}
private void createYaxis() {
yAxis = new NumberAxis();
yAxis.setAutoRanging(false);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
yAxis.setLowerBound(0);
yAxis.setUpperBound(20);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override public String toString(Number object) {
return String.format("%7.2f", object.floatValue());
}
});
}
#SuppressWarnings("unchecked")
private BarChart<String, Number> createBarChart() {
final BarChart<String, Number> chart = new BarChart<>(new CategoryAxis(), yAxis);
setDefaultChartProperties(chart);
chart.getData().addAll(barSeries);
return chart;
}
#SuppressWarnings("unchecked")
private LineChart<String, Number> createLineChart() {
final LineChart<String, Number> chart = new LineChart<>(new CategoryAxis(), yAxis);
setDefaultChartProperties(chart);
chart.setCreateSymbols(false);
chart.getData().addAll(lineSeries);
return chart;
}
private void setDefaultChartProperties(final XYChart<String, Number> chart) {
chart.setLegendVisible(false);
chart.setAnimated(false);
}
#SafeVarargs
private final StackPane layerCharts(final XYChart<String, Number>... charts) {
for (int i = 1; i < charts.length; i++) {
configureOverlayChart(charts[i]);
}
StackPane stackpane = new StackPane();
stackpane.getChildren().addAll(charts);
return stackpane;
}
private void configureOverlayChart(final XYChart<String, Number> chart) {
chart.setAlternativeRowFillVisible(false);
chart.setAlternativeColumnFillVisible(false);
chart.setHorizontalGridLinesVisible(false);
chart.setVerticalGridLinesVisible(false);
chart.getXAxis().setVisible(false);
chart.getYAxis().setVisible(false);
chart.getStylesheets().addAll(getClass().getResource("/overlay-chart.css").toExternalForm());
}
}
Here is the result:
Just a little note that one doesn't need to write source code to stack charts over eachother: You can also set the attributes without events and threading within the FXML file as shown below:
<BarChart fx:id="bc_stock_c" alternativeRowFillVisible="false" animated="false" horizontalZeroLineVisible="false" legendVisible="false" prefHeight="200.0" prefWidth="200.0" title="Produkt C" verticalGridLinesVisible="false" verticalZeroLineVisible="false" GridPane.rowIndex="1">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis animated="false" autoRanging="false" lowerBound="0.0" minorTickCount="100" prefHeight="135.0" prefWidth="35.0" side="LEFT" tickUnit="100.0" upperBound="1000.0" />
</yAxis>
</BarChart>
<LineChart fx:id="lc_demand_c" alternativeRowFillVisible="false" animated="false" createSymbols="false" horizontalGridLinesVisible="false" horizontalZeroLineVisible="false" legendVisible="false" prefHeight="200.0" prefWidth="200.0" stylesheets="#style.css" title=" " verticalGridLinesVisible="false" verticalZeroLineVisible="false" GridPane.rowIndex="1">
<xAxis>
<CategoryAxis side="BOTTOM" />
</xAxis>
<yAxis>
<NumberAxis animated="false" autoRanging="false" lowerBound="0.0" minorTickCount="100" prefHeight="135.0" prefWidth="35.0" side="LEFT" tickUnit="100.0" upperBound="1000.0" />
</yAxis>
</LineChart>
Just set pref_width, pref_height, autoRanging, minorTickCount, tickUnit, upperBound and lowerBound for each Y-axis and it should work. You also should add the following css style to make the chart area transparent for each line chart:
.chart-plot-background {
-fx-background-color: transparent;
}
hey guys i am an it student and i just started javafx and i need help.
i have created a class that allows me to generate a Menubar and a chart they both work in an independent way, but my probleme is how to add the Menu bar at every chart bar class, so i can navigate easly between the charts i have really been looking for it, and it most be done tonight
and this is the source code
package mto.cr.GUI;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class MenuMain extends Application {
Stage window;
BorderPane layout;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("Crowd Rise");
//statistique menu
Menu statisqueMenu = new Menu("Statistique ");
//Attribution Experience declaration setonaction et add dans le menu
MenuItem attributExperience = new MenuItem("Attribution Experience");
attributExperience.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Statistique1 st1=new Statistique1() ;
st1.start(window);
}
});
statisqueMenu.getItems().add(attributExperience);
//financement projet
MenuItem FinancementProjet = new MenuItem("Financement Projet");
FinancementProjet.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
}
}
);
statisqueMenu.getItems().add(FinancementProjet);
//projet
MenuItem projet = new MenuItem("projet");
projet.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
}
});
statisqueMenu.getItems().add(projet);
//separeteur avec un trait
statisqueMenu.getItems().add(new SeparatorMenuItem());
//user
MenuItem user = new MenuItem("User");
user.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
}
});
statisqueMenu.getItems().add(user);
statisqueMenu.getItems().add(new SeparatorMenuItem());
statisqueMenu.getItems().add(new MenuItem("Exit..."));
//recherche menu
Menu rechercheMenu = new Menu("Recherche");
//user
MenuItem userR = new MenuItem("User");
userR.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
}
});
rechercheMenu.getItems().add(userR);
//projet
MenuItem projetR = new MenuItem("Projet");
projetR.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
}
});
rechercheMenu.getItems().add(projetR);
//probleme
MenuItem problemeR = new MenuItem("Probleme recherche");
problemeR.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
}
});
rechercheMenu.getItems().add(problemeR);
//Main menu bar
MenuBar menuBar = new MenuBar();
menuBar.getMenus().addAll(statisqueMenu,rechercheMenu);
layout = new BorderPane();
layout.setTop(menuBar);
Scene scene = new Scene(layout, 400, 300);
window.setScene(scene);
window.show();
}
}
And i also have another class that allows me to create a chart and this is the source code
package mto.cr.GUI;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class Statistique1 extends Application {
#Override public void start(Stage stage) {
MenuMain menumain= new MenuMain();
menumain.start(stage);
stage.setTitle("Line Chart Sample");
//defining the axes
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("Number of Month");
//creating the chart
final LineChart<Number,Number> lineChart =
new LineChart<Number,Number>(xAxis,yAxis);
lineChart.setTitle("Stock Monitoring, 2010");
//defining a series
XYChart.Series series = new XYChart.Series();
series.setName("My portfolio");
//populating the series with data
series.getData().add(new XYChart.Data(1, 23));
series.getData().add(new XYChart.Data(2, 14));
series.getData().add(new XYChart.Data(3, 15));
series.getData().add(new XYChart.Data(4, 24));
series.getData().add(new XYChart.Data(5, 34));
series.getData().add(new XYChart.Data(6, 36));
series.getData().add(new XYChart.Data(7, 22));
series.getData().add(new XYChart.Data(8, 45));
series.getData().add(new XYChart.Data(9, 43));
series.getData().add(new XYChart.Data(10, 17));
series.getData().add(new XYChart.Data(11, 29));
series.getData().add(new XYChart.Data(12, 25));
Scene scene = new Scene(lineChart,800,600);
lineChart.getData().add(series);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
You cannot launch more than one Application. This is why you should seperate any code you want to use independent from the Application to classes not extending Application. Also you have repetitive code, which makes it harder to maintain the code (see DRY).
Since your intention seems to be to replace the center node of the BoderPane, I recommend creating factory classes for the content to be displayed in the center instead of Application subclasses and creating a function for creating the menus items:
MenuItem createMenuItem(String text, final Supplier<Node> factory) {
MenuItem item = new MenuItem(text);
item.setOnAction(evt -> {
setContent(factory);
});
}
void setContent(Supplier<Node> factory) {
Node content = factory.get();
layout.setCenter(content);
}
#Override
public void start(Stage primaryStage) {
window = primaryStage;
window.setTitle("Crowd Rise");
//statistique menu
Menu statisqueMenu = new Menu("Statistique", null,
createMenuItem("Attribution Experience", new Statistique1()),
createMenuItem("Financement Projet", new Statistique2()),
...);
...
public class Statistique1 implements Supplier<Node> {
#Override
public Node get() {
//defining the axes
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("Number of Month");
//creating the chart
final LineChart<Number,Number> lineChart =
new LineChart<Number,Number>(xAxis,yAxis);
lineChart.setTitle("Stock Monitoring, 2010");
//defining a series
XYChart.Series series = new XYChart.Series();
series.setName("My portfolio");
//populating the series with data
series.getData().add(new XYChart.Data(1, 23));
series.getData().add(new XYChart.Data(2, 14));
series.getData().add(new XYChart.Data(3, 15));
series.getData().add(new XYChart.Data(4, 24));
series.getData().add(new XYChart.Data(5, 34));
series.getData().add(new XYChart.Data(6, 36));
series.getData().add(new XYChart.Data(7, 22));
series.getData().add(new XYChart.Data(8, 45));
series.getData().add(new XYChart.Data(9, 43));
series.getData().add(new XYChart.Data(10, 17));
series.getData().add(new XYChart.Data(11, 29));
series.getData().add(new XYChart.Data(12, 25));
lineChart.getData().add(series);
return lineChart;
}
}
I want to implement zoom in Bar Chart but unfortunatly unsuccessful. Can you help me to fix this code?
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.geometry.Bounds;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Transform;
import javafx.scene.transform.Translate;
public class FreehandResize extends Application {
private CategoryAxis xAxis = new CategoryAxis();
private NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0001);
private LineChart<String, Number> lineChart = new LineChart<String, Number> (xAxis, yAxis);
private Path path;
private double initialWidth;
private double initialheight;
private Translate translate = new Translate(0, 0);
private Scale scale = new Scale(1, 1);
private ChangeListener<Number> changeListener = new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
Bounds y_axisBounds = yAxis.getBoundsInLocal();
double xOffset = y_axisBounds.getMaxX();
translate.setX(xOffset);
Bounds chartBounds = lineChart.getBoundsInLocal();
scale.setX((chartBounds.getWidth() - xOffset) / initialWidth);
scale.setY((chartBounds.getHeight() - xAxis.getBoundsInLocal().getHeight()) / initialheight);
}
};
private EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
double targetX = (mouseEvent.getX() - translate.getX()) / scale.getX();
double targetY = mouseEvent.getY() / scale.getY();
if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
path.getElements().add(new MoveTo(targetX, targetY));
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
path.getElements().add(new LineTo(targetX, targetY));
}
}
};
#Override
public void start(Stage stage) {
stage.setTitle("Resize line plot");
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%6.4f", object);
}
});
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 0.53185));
series1.getData().add(new XYChart.Data("Feb", 0.532235));
series1.getData().add(new XYChart.Data("Mar", 0.53234));
series1.getData().add(new XYChart.Data("Apr", 0.538765));
series1.getData().add(new XYChart.Data("May", 0.53442));
series1.getData().add(new XYChart.Data("Jun", 0.534658));
series1.getData().add(new XYChart.Data("Jul", 0.53023));
series1.getData().add(new XYChart.Data("Aug", 0.53001));
series1.getData().add(new XYChart.Data("Sep", 0.53589));
series1.getData().add(new XYChart.Data("Oct", 0.53476));
series1.getData().add(new XYChart.Data("Nov", 0.530123));
series1.getData().add(new XYChart.Data("Dec", 0.53035));
lineChart.getData().addAll(series1);
BorderPane pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
stage.setScene(scene);
path = new Path();
path.setStrokeWidth(2);
path.setStroke(Color.CHOCOLATE);
ObservableList<Transform> transforms = path.getTransforms();
transforms.add(0, translate);
transforms.add(1, scale);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
pane.getChildren().add(path);
scene.widthProperty().addListener(changeListener);
scene.heightProperty().addListener(changeListener);
stage.show();
Bounds axisBounds = yAxis.getBoundsInLocal();
double xOffset = axisBounds.getMaxX();
translate.setX(xOffset);
Bounds chartBounds = lineChart.getBoundsInLocal();
initialWidth = chartBounds.getWidth() - xOffset;
initialheight = chartBounds.getHeight() - xAxis.getBoundsInLocal().getHeight();
}
public static void main(String[] args) {
launch(args);
}
}
Zoom and Pan is implemented in this open source lib: https://github.com/gillius/jfxutils
You could use it or learn from its source.
In a code that plots a LineChart I have added a css string to choose either to display Y price scale to the right or left of the chart.
Here is the code
import java.util.Set;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class XyChart extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("Line plot");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis(1, 21,0.1);
yAxis.setTickUnit(1);
yAxis.setPrefWidth(35);
yAxis.setMinorTickCount(10);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis){
#Override
public String toString(Number object){
String label;
label = String.format("%7.2f", object.floatValue());
return label;
}
});
final LineChart<String, Number>lineChart = new LineChart<String, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setLegendVisible(false);
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data("Jan", 1));
series1.getData().add(new XYChart.Data("Feb", 4));
series1.getData().add(new XYChart.Data("Mar", 2.5));
series1.getData().add(new XYChart.Data("Apr", 5));
series1.getData().add(new XYChart.Data("May", 6));
series1.getData().add(new XYChart.Data("Jun", 8));
series1.getData().add(new XYChart.Data("Jul", 12));
series1.getData().add(new XYChart.Data("Aug", 8));
series1.getData().add(new XYChart.Data("Sep", 11));
series1.getData().add(new XYChart.Data("Oct", 13));
series1.getData().add(new XYChart.Data("Nov", 10));
series1.getData().add(new XYChart.Data("Dec", 20));
BorderPane pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.setAnimated(false);
lineChart.getData().addAll(series1);
String priceSide = "-fx-side: right;";
Set<Node> axisNode = lineChart.lookupAll(".axis");
for(final Node axis : axisNode){
axis.setStyle(priceSide);
}
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I think I am missing something because the plotted line it is now all collapsed to the left side of the chart.
Just comment this part
String priceSide = "-fx-side: right;";
Set<Node> axisNode = lineChart.lookupAll(".axis");
for(final Node axis : axisNode){
axis.setStyle(priceSide);
}
and it plots correctly: same problem happens if I use
"-fx-side: left;";
What's wrong with this code?
Thanks.
The code below plots a XYLineChart: by left mouse click and drag the plotted line can be translated left/right.
import javafx.application.Application;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.event.EventHandler;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
enter code here
public class GridMove extends Application {
BorderPane pane;
XYChart.Series series1 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectX = new SimpleDoubleProperty();
SimpleDoubleProperty rectY = new SimpleDoubleProperty();
#Override
public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis(1, 12, 1);
final NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0005);
xAxis.setAnimated(false);
yAxis.setAnimated(false);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
final LineChart<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(false);
lineChart.setLegendVisible(false);
series1.getData().add(new XYChart.Data(1, 0.53185));
series1.getData().add(new XYChart.Data(2, 0.532235));
series1.getData().add(new XYChart.Data(3, 0.53234));
series1.getData().add(new XYChart.Data(4, 0.538765));
series1.getData().add(new XYChart.Data(5, 0.53442));
series1.getData().add(new XYChart.Data(6, 0.534658));
series1.getData().add(new XYChart.Data(7, 0.53023));
series1.getData().add(new XYChart.Data(8, 0.53001));
series1.getData().add(new XYChart.Data(9, 0.53589));
series1.getData().add(new XYChart.Data(10, 0.53476));
pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
rectinitX.set(mouseEvent.getX());
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED || mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED) {
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
double Tgap = xAxis.getWidth() / (xAxis.getUpperBound() - xAxis.getLowerBound());
double newXlower = xAxis.getLowerBound(), newXupper = xAxis.getUpperBound();
double Delta = 0.3;
if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
if (rectinitX.get() < mouseEvent.getX()) {
newXlower = xAxis.getLowerBound() - Delta;
newXupper = xAxis.getUpperBound() - Delta;
} else if (rectinitX.get() > mouseEvent.getX()) {
newXlower = xAxis.getLowerBound() + Delta;
newXupper = xAxis.getUpperBound() + Delta;
}
xAxis.setLowerBound(newXlower);
xAxis.setUpperBound(newXupper);
}
rectinitX.set(mouseEvent.getX());
}
}
};
public static void main(String[] args) {
launch(args);
}
}
My questions are
1) Now by moving the Line left/right, Grid and X Ticks does not move: so, how to translate Line, Grid and X Ticks together?
2) Is it possible to accomplish this in JavaFx 2?
Thanks
Edit Nobody willing to help?
Edit 2: import statements added
Edit 3: Code improvements, now grid and line moves together. It only remains to move X axis ticks along with line and grid, and vertical grid lines are missing outside line range values
import java.util.Set;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.scene.chart.Axis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Series;
import javafx.stage.Stage;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
public class GridMove extends Application {
BorderPane pane;
XYChart.Series series1 = new XYChart.Series();
SimpleDoubleProperty rectinitX = new SimpleDoubleProperty();
SimpleDoubleProperty rectX = new SimpleDoubleProperty();
SimpleDoubleProperty rectY = new SimpleDoubleProperty();
LineChart<Number, Number> lineChart;
#Override
public void start(Stage stage) {
final NumberAxis xAxis = new NumberAxis(1, 12, 1);
final NumberAxis yAxis = new NumberAxis(0.53000, 0.53910, 0.0005);
xAxis.setAnimated(false);
yAxis.setAnimated(false);
yAxis.setTickLabelFormatter(new NumberAxis.DefaultFormatter(yAxis) {
#Override
public String toString(Number object) {
return String.format("%7.5f", object);
}
});
lineChart = new LineChart<Number, Number>(xAxis, yAxis);
lineChart.setCreateSymbols(false);
lineChart.setAlternativeRowFillVisible(false);
lineChart.setAnimated(false);
lineChart.setLegendVisible(false);
series1.getData().add(new XYChart.Data(1, 0.53185));
series1.getData().add(new XYChart.Data(2, 0.532235));
series1.getData().add(new XYChart.Data(3, 0.53234));
series1.getData().add(new XYChart.Data(4, 0.538765));
series1.getData().add(new XYChart.Data(5, 0.53442));
series1.getData().add(new XYChart.Data(6, 0.534658));
series1.getData().add(new XYChart.Data(7, 0.53023));
series1.getData().add(new XYChart.Data(8, 0.53001));
series1.getData().add(new XYChart.Data(9, 0.53589));
series1.getData().add(new XYChart.Data(10, 0.53476));
pane = new BorderPane();
pane.setCenter(lineChart);
Scene scene = new Scene(pane, 800, 600);
lineChart.getData().addAll(series1);
stage.setScene(scene);
scene.setOnMouseClicked(mouseHandler);
scene.setOnMouseDragged(mouseHandler);
scene.setOnMouseEntered(mouseHandler);
scene.setOnMouseExited(mouseHandler);
scene.setOnMouseMoved(mouseHandler);
scene.setOnMousePressed(mouseHandler);
scene.setOnMouseReleased(mouseHandler);
stage.show();
}
EventHandler<MouseEvent> mouseHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
if (mouseEvent.getEventType() == MouseEvent.MOUSE_PRESSED) {
rectinitX.set(mouseEvent.getX());
} else if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED || mouseEvent.getEventType() == MouseEvent.MOUSE_MOVED) {
LineChart<Number, Number> lineChart = (LineChart<Number, Number>) pane.getCenter();
NumberAxis xAxis = (NumberAxis) lineChart.getXAxis();
double newXlower = xAxis.getLowerBound(), newXupper = xAxis.getUpperBound();
double Delta = 0.3;
if (mouseEvent.getEventType() == MouseEvent.MOUSE_DRAGGED) {
if (rectinitX.get() < mouseEvent.getX()) {
Delta *= -1;
}
newXlower = xAxis.getLowerBound() + Delta;
newXupper = xAxis.getUpperBound() + Delta;
xAxis.setLowerBound(newXlower);
xAxis.setUpperBound(newXupper);
DoubleProperty p1 = xAxis.scaleXProperty();
DoubleProperty p2 = xAxis.translateXProperty();
double horizontalValueRange = xAxis.getUpperBound() - xAxis.getLowerBound();
double horizontalWidthPixels = xAxis.getWidth();
//pixels per unit
double xScale = horizontalWidthPixels / horizontalValueRange;
Set<Node> nodes = lineChart.lookupAll(".chart-vertical-grid-lines");
for (Node n: nodes) {
Path p = (Path) n;
double currLayoutX = p.getLayoutX();
p.setLayoutX(currLayoutX + (Delta*-1) * xScale);
}
double lox = xAxis.getLayoutX();
}
rectinitX.set(mouseEvent.getX());
}
}
};
public static void main(String[] args) {
launch(args);
}
}
Any help very much appreciated!
This is rather a comment but somehow im too new to comment.
I would approach this by embedding your chart in a ScrollPane with invisible scrollbars and set the chart's y-axis opacity to 0 as well. If it is not feasable to load all chart data into memory you have to manage data fetching in scroll events etc.
Additionally you can use a standalone NumberAxis for your y-axis. You have to do the propper scaling and positioning by hand.