I have two XY charts and I want them to be placed one on the top and the other at the bottom of a JavaFX application.
Both graph widths should fill their "container" and the bottom graph should be x times smaller than the top graph.
For the sake of reproducibility, here is an example built from Oracle's tutorial:
import javafx.application.Application;
import javafx.geometry.Side;
import javafx.scene.Parent;
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.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Test extends Application {
#Override
public void start(Stage stage) {
stage.setTitle("Line Chart Sample");
final CategoryAxis xAxis1 = new CategoryAxis();
final NumberAxis yAxis1 = new NumberAxis();
xAxis1.setLabel("Month");
final LineChart<String, Number> lineChart1 =
new LineChart<>(xAxis1, yAxis1);
final CategoryAxis xAxis2 = new CategoryAxis();
xAxis2.setLabel("Month");
final NumberAxis yAxis2 = new NumberAxis();
final LineChart<String, Number> lineChart2 =
new LineChart<>(xAxis2, yAxis2);
lineChart1.setLegendVisible(false);
lineChart2.setLegendVisible(false);
XYChart.Series series1 = new XYChart.Series();
series1.setName("Portfolio 1");
series1.getData().add(new XYChart.Data("Jan", 23));
series1.getData().add(new XYChart.Data("Feb", 14));
series1.getData().add(new XYChart.Data("Mar", 15));
series1.getData().add(new XYChart.Data("Apr", 24));
series1.getData().add(new XYChart.Data("May", 34));
series1.getData().add(new XYChart.Data("Jun", 36));
series1.getData().add(new XYChart.Data("Jul", 22));
series1.getData().add(new XYChart.Data("Aug", 45));
series1.getData().add(new XYChart.Data("Sep", 43));
series1.getData().add(new XYChart.Data("Oct", 17));
series1.getData().add(new XYChart.Data("Nov", 29));
series1.getData().add(new XYChart.Data("Dec", 25));
XYChart.Series series2 = new XYChart.Series();
series2.setName("Portfolio 2");
series2.getData().add(new XYChart.Data("Jan", 33));
series2.getData().add(new XYChart.Data("Feb", 34));
series2.getData().add(new XYChart.Data("Mar", 25));
series2.getData().add(new XYChart.Data("Apr", 44));
series2.getData().add(new XYChart.Data("May", 39));
series2.getData().add(new XYChart.Data("Jun", 16));
series2.getData().add(new XYChart.Data("Jul", 55));
series2.getData().add(new XYChart.Data("Aug", 54));
series2.getData().add(new XYChart.Data("Sep", 48));
series2.getData().add(new XYChart.Data("Oct", 27));
series2.getData().add(new XYChart.Data("Nov", 37));
series2.getData().add(new XYChart.Data("Dec", 29));
lineChart1.getData().addAll(series1);
lineChart2.getData().addAll(series2);
Scene scene = new Scene(createVBoxLayout(lineChart1, lineChart2), 800, 600);
stage.setScene(scene);
stage.show();
}
private Parent createVBoxLayout(XYChart chart1, XYChart chart2) {
VBox vBox = new VBox(chart1, chart2);
chart1.prefHeight(200);
chart1.minHeight(200);
chart1.maxHeight(200);
chart2.prefHeight(400);
chart2.minHeight(400);
chart2.maxHeight(400);
return vBox;
}
private Parent createGridLayout(XYChart chart1, XYChart chart2) {
GridPane gridPane = new GridPane();
gridPane.add(chart1, 0, 0, 1, 1);
gridPane.add(chart2, 0, 1, 1, 2);
return gridPane;
}
public static void main(String[] args) {
launch(args);
}
}
The graph on the bottom should be twice as big as the one on the top.
I have tried two approaches:
Method createVBoxLayout creates a VBox and uses the prefered/min/max sizes to modify the chart size.
Here is the result:
Clearly, the bottom graph is not twice as big as the graph at the top. Moreover, the size ratio would not be kept when the window is resized.
The method createGridLayout creates a GridPane, here is the result:
Now, it's the top graph that is twice as big as the bottom one, which is the opposite of what is coded in the method! Moreover, the graphs do not fill the width of the window. Finally, when resizing the window, the charts overlap and don't keep their relative sizes (should be 1/3 - 2/3).
The methods prefHeight(...), minHeight(...), etc do not set the preferred height, etc. They return the computed values for those, with the argument being the width for the chart (i.e. chart1.prefHeight(200) means "tell me the preferred height if the width is 200"). See the documentation.
You need
chart1.setPrefHeight(200);
chart1.setMinHeight(200);
chart1.setMaxHeight(200);
etc.
For the grid pane version, use RowConstraints and ColumnConstraints to control the layout of the grid. The rows are simply resized to contain the nodes they contain, so making a chart span two rows will probably just give one of the rows zero height. (Again, see the documentation.)
private Parent createGridLayout(XYChart chart1, XYChart chart2) {
GridPane gridPane = new GridPane();
gridPane.add(chart1, 0, 0, 1, 1);
gridPane.add(chart2, 0, 1, 1, 2);
RowConstraints top = new RowConstraints(200);
RowConstraints bottom = new RowConstraints(400);
gridPane.getRowConstraints().addAll(top, bottom);
ColumnConstraints cc = new ColumnConstraints();
cc.setHgrow(Priority.ALWAYS);
gridPane.getColumnConstraints().add(cc);
return gridPane;
}
or
private Parent createGridLayout(XYChart chart1, XYChart chart2) {
GridPane gridPane = new GridPane();
gridPane.add(chart1, 0, 0);
gridPane.add(chart2, 0, 1);
RowConstraints top = new RowConstraints();
top.setPercentHeight(100.0 / 3.0);
RowConstraints bottom = new RowConstraints();
bottom.setPercentHeight(200.0 / 3.0);
gridPane.getRowConstraints().addAll(top, bottom);
ColumnConstraints cc = new ColumnConstraints();
cc.setHgrow(Priority.ALWAYS);
gridPane.getColumnConstraints().add(cc);
return gridPane;
}
Related
I want to create a scatter chart by fxml file.I put the code below in my fxml file but when it done the render,it did not like I expected.Do somebody know what's going on?
<BorderPane xmlns="http://javafx.com/javafx/8.0.121"
xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.ChartController">
<left>
<ScatterChart fx:id="memoryDistribution" prefWidth="360" title="Memory Distribution">
<xAxis>
<NumberAxis fx:id="xAxis" label="Memory Address/k" lowerBound="0" upperBound="7" tickUnit="1.00"/>
</xAxis>
<yAxis>
<NumberAxis fx:id="yAxis" label="Memory Address/k" lowerBound="1" upperBound="257" tickUnit="8.00"/>
</yAxis>
</ScatterChart>
</left>
what above code do like this:
wrong expectation
But what I expect is like this:
expectation
the second image code:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
primaryStage.setTitle("Scatter Chart Sample");
final NumberAxis xAxis = new NumberAxis(0, 8, 1);
final NumberAxis yAxis = new NumberAxis(1, 257, 8);
final ScatterChart<Number,Number> sc = new
ScatterChart<>(xAxis,yAxis);
xAxis.setLabel("Memory Address/k");
yAxis.setLabel("Memory Address/k");
sc.setTitle("Memory Distribution");
XYChart.Series series1 = new XYChart.Series();
series1.setName("job-1");
series1.getData().add(new XYChart.Data(0.5, 5));
series1.getData().add(new XYChart.Data(1.5, 5));
series1.getData().add(new XYChart.Data(2.5, 5));
series1.getData().add(new XYChart.Data(3.5, 5));
series1.getData().add(new XYChart.Data(4.5, 5));
series1.getData().add(new XYChart.Data(5.5, 5));
series1.getData().add(new XYChart.Data(6.5, 5));
series1.getData().add(new XYChart.Data(7.5, 5));
series1.getData().add(new XYChart.Data(0.5, 13));
series1.getData().add(new XYChart.Data(1.5, 13));
series1.getData().add(new XYChart.Data(2.5, 13));
series1.getData().add(new XYChart.Data(3.5, 13));
series1.getData().add(new XYChart.Data(4.5, 13));
series1.getData().add(new XYChart.Data(5.5, 13));
XYChart.Series series2 = new XYChart.Series();
series2.setName("job-2");
series2.getData().add(new XYChart.Data(0.5, 29));
series2.getData().add(new XYChart.Data(1.5, 29));
series2.getData().add(new XYChart.Data(2.5, 29));
series2.getData().add(new XYChart.Data(3.5, 29));
series2.getData().add(new XYChart.Data(4.5, 29));
series2.getData().add(new XYChart.Data(5.5, 29));
sc.getData().addAll(series1, series2);
Scene chartScene = new Scene(sc,360,736);//best lookup
primaryStage.setScene(chartScene);
primaryStage.setMaximized(true);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I already done my demand:the grid on,and the cell size is 256.
I put an any kind of Pane in the fxml,when the init start it should be render and put a blank pane in there.
<center>
<ScrollPane fx:id="chartPane" prefWidth="380" prefHeight="760">
</ScrollPane>
</center>
Then I write the code to init a scatter chart and put it to the above pane.
In the class sample.ChartController as the fxml configured,put the init code in this class,name a method initialize.
private void initialize(){
final NumberAxis xAxis = new NumberAxis(0,8,1);
final NumberAxis yAxis = new NumberAxis(0,256,8);
xAxis.setLabel("Memory Address/k");
yAxis.setLabel("Memory Address/k");
this.memoryDistributionChart = new ScatterChart(xAxis,yAxis);
memoryDistributionChart.setTitle("Memory Distribution");
memoryDistributionChart.setPrefHeight(736);
memoryDistributionChart.setPrefWidth(360);
chartPane.setContent(memoryDistributionChart);
}
memoryDistributionChart is a ScatterChart,a member property.
chartPane is another member property.
finally,the result like this
Because when the main stage render,the controller initialize method should be invoked first.
I can change the symbol shape, colour, padding, radius but I can't seem to find a CSS item that will change the size of the symbol.
I had thought that if I recreated the symbol with smaller SVG path, that might be a way to do it, but it appears that my smaller shape just gets scaled up anyway.
I need smaller symbols but so far have not found a way to scale them.
Here is the code, and the loop where you can find all symbols and change Width and Height. I know only this way. If you try this in CSS you will change also legend under chart.
public class Chart extends Application {
#Override
public void start(Stage stage) {
//set Axis
final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();
//chart
final ScatterChart<Number, Number> lineChart = new ScatterChart<Number, Number>(xAxis, yAxis);
//prepare series
XYChart.Series series1 = new XYChart.Series();
series1.getData().add(new XYChart.Data(1, 2));
series1.getData().add(new XYChart.Data(2, 2));
series1.getData().add(new XYChart.Data(3, 1));
series1.getData().add(new XYChart.Data(3, 3));
series1.getData().add(new XYChart.Data(5, 2));
HBox hbox = new HBox();
//add series to chart
lineChart.getData().addAll(series1);
//take all series
for (XYChart.Series<Number, Number> series : lineChart.getData()) {
//for all series, take date, each data has Node (symbol) for representing point
for (XYChart.Data<Number, Number> data : series.getData()) {
// this node is StackPane
StackPane stackPane = (StackPane) data.getNode();
stackPane.setPrefWidth(50);
stackPane.setPrefHeight(50);
}
}
hbox.getChildren().addAll(lineChart);
Scene scene = new Scene(hbox, 800, 600);
scene.getStylesheets().add(getClass().getResource("/resources/chart.css").toExternalForm());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Here is CSS wher you can set for this series radius
.default-color0.chart-symbol {
-fx-background-radius: 30px;
}
In this example symbols are bigger
This is how I created a rectangle in SVG path then changed the size in css.
.chart-symbol{
-fx-shape: "M 20.0 20.0 v24.0 h 10.0 v-24 Z";
-fx-padding: 7px 7px 7px 7px;
}
I have a problem I can not figure out. Please let me put the code first for better explanation:
public class TestXYChart extends Application {
NumberAxis xAxis = new NumberAxis(); // 1
NumberAxis yAxis = new NumberAxis(); // 2
XYChart.Series series1 = new XYChart.Series();
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Scene scene = new Scene(root, 300, 250);
LineChart lineChart = new LineChart<Number, Number>(xAxis, yAxis);
root.setOnScroll(scrollHandler);
root.getChildren().addAll(lineChart);
series1.getData().add(new XYChart.Data(1, 20));
series1.getData().add(new XYChart.Data(2, 40));
series1.getData().add(new XYChart.Data(3, 100));
series1.getData().add(new XYChart.Data(4, 50));
series1.getData().add(new XYChart.Data(5, 170));
series1.getData().add(new XYChart.Data(6, 190));
series1.getData().add(new XYChart.Data(7, 30));
series1.getData().add(new XYChart.Data(8, 39));
series1.getData().add(new XYChart.Data(9, 44));
series1.getData().add(new XYChart.Data(10, 78));
series1.getData().add(new XYChart.Data(11, 93));
series1.getData().add(new XYChart.Data(12, 75));
// this.loadIntoDataBaseFromFile();
lineChart.getData().addAll(series1);
lineChart.setAnimated(true);
lineChart.setAlternativeColumnFillVisible(false);
lineChart.setAlternativeRowFillVisible(false);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
EventHandler scrollHandler = new EventHandler<ScrollEvent>(){
#Override
public void handle(ScrollEvent event) {
System.out.println("Handling Scroll Event");
xAxis.setLowerBound(xAxis.getLowerBound() + 1);
xAxis.setUpperBound(xAxis.getUpperBound() - 1);
}
};
}
I use the code above to test whether I can operate the LineChart, especially when I scroll mouse, the LineChart will zoom in/out.
I found the fact that exactly as above code, it doesn't work. But if we just change the first two line codes, change from:
NumberAxis xAxis = new NumberAxis(); // 1
NumberAxis yAxis = new NumberAxis(); // 2
to:
NumberAxis xAxis = new NumberAxis(0,12,0.5); // 1
NumberAxis yAxis = new NumberAxis(0,300,10); // 2
Everything works well. So, why it happens like this?Thank you.
And, if I seperate the new NumberAxis(...) instance from the declaration NumberAxis xAxis/yAxis, it doesn't work too.
The difference between using an empty constructor or with arguments is clear if you have a look at NumberAxis JavaDoc:
public NumberAxis()
Create a auto-ranging NumberAxis
and
public NumberAxis(double lowerBound, double upperBound, double tickUnit)
Create a non-auto-ranging NumberAxis with the given upper bound, lower bound and tick unit
Basically, the first one has autoRanging set to true, what means it will try to show the data, no matter how you modify the bounds of the axis.
On the contrary, the second one has autoRanging set to false, so any change in the axis bounds will be reflected on the chart.
Note that you can set this in the first case to change the default behaviour:
xAxis.setAutoRanging(false);
This question already has an answer here:
How to change color of a single bar java fx
(1 answer)
Closed 9 years ago.
I want to create temperature chart where I can monitor in real time temperature.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedAreaChart;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class MainApp extends Application
{
final NumberAxis xAxis = new NumberAxis(1, 31, 1);
final NumberAxis yAxis = new NumberAxis();
final StackedAreaChart<Number, Number> sac
= new StackedAreaChart<Number, Number>(xAxis, yAxis);
#Override
public void start(Stage stage)
{
setUserAgentStylesheet(STYLESHEET_CASPIAN);
stage.setTitle("Area Chart Sample");
sac.setTitle("Temperature Monitoring (in Degrees C)");
XYChart.Series<Number, Number> seriesApril
= new XYChart.Series<Number, Number>();
seriesApril.setName("April");
seriesApril.getData().add(new XYChart.Data(1, 4));
seriesApril.getData().add(new XYChart.Data(3, 10));
seriesApril.getData().add(new XYChart.Data(6, 15));
seriesApril.getData().add(new XYChart.Data(9, 8));
seriesApril.getData().add(new XYChart.Data(12, 5));
seriesApril.getData().add(new XYChart.Data(15, 18));
seriesApril.getData().add(new XYChart.Data(18, 15));
seriesApril.getData().add(new XYChart.Data(21, 13));
seriesApril.getData().add(new XYChart.Data(24, 19));
seriesApril.getData().add(new XYChart.Data(27, 21));
seriesApril.getData().add(new XYChart.Data(30, 21));
XYChart.Series<Number, Number> seriesMay
= new XYChart.Series<Number, Number>();
seriesMay.setName("May");
seriesMay.getData().add(new XYChart.Data(1, 20));
seriesMay.getData().add(new XYChart.Data(3, 15));
seriesMay.getData().add(new XYChart.Data(6, 13));
seriesMay.getData().add(new XYChart.Data(9, 12));
seriesMay.getData().add(new XYChart.Data(12, 14));
seriesMay.getData().add(new XYChart.Data(15, 18));
seriesMay.getData().add(new XYChart.Data(18, 25));
seriesMay.getData().add(new XYChart.Data(21, 25));
seriesMay.getData().add(new XYChart.Data(24, 23));
seriesMay.getData().add(new XYChart.Data(27, 26));
seriesMay.getData().add(new XYChart.Data(31, 26));
Scene scene = new Scene(sac, 800, 600);
sac.getData().addAll(seriesApril);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}
I want to change the color of of the chart for height temperature. For example I want to see temperature between 10 and 20 in green and temperature between 40 and 50 in red. Can you hep me to implement this?
This is only possible with CSS.
Add the following lines to your CSS and give your chart the id temperatureChart.
#temperatureChart .series0.chart-series-area-fill {
-fx-fill: linear-gradient(to top, #0984df, #00d8ff 20%, #fff000 60%, #ff0000) ;
}
The right percent values you must figure out yourself.
Hope this helps,
Kalasch
EDIT(in code method):
To do this in Code, you can do the following(it is suggested that you check if the lookup is null):
sac.lookup("#temperatureChart .series0.chart-series-area-fill")
.setStyle("-fx-fill: linear-gradient(to top, #0984df, #00d8ff 20%, #fff000 60%, #ff0000);");
It is not recomended to do so, but if there is no other way...
I found many examples how to zoom in charts but I'm looking for basic example in which the user can scroll with the mouse wheel.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;
public class BarChartSample extends Application {
final static String austria = "Austria";
final static String brazil = "Brazil";
final static String france = "France";
final static String italy = "Italy";
final static String usa = "USA";
#Override public void start(Stage stage) {
stage.setTitle("Bar Chart Sample");
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final BarChart<String,Number> bc =
new BarChart<String,Number>(xAxis,yAxis);
bc.setTitle("Country Summary");
xAxis.setLabel("Country");
yAxis.setLabel("Value");
XYChart.Series series1 = new XYChart.Series();
series1.setName("2003");
series1.getData().add(new XYChart.Data(austria, 25601.34));
series1.getData().add(new XYChart.Data(brazil, 20148.82));
series1.getData().add(new XYChart.Data(france, 10000));
series1.getData().add(new XYChart.Data(italy, 35407.15));
series1.getData().add(new XYChart.Data(usa, 12000));
XYChart.Series series2 = new XYChart.Series();
series2.setName("2004");
series2.getData().add(new XYChart.Data(austria, 57401.85));
series2.getData().add(new XYChart.Data(brazil, 41941.19));
series2.getData().add(new XYChart.Data(france, 45263.37));
series2.getData().add(new XYChart.Data(italy, 117320.16));
series2.getData().add(new XYChart.Data(usa, 14845.27));
XYChart.Series series3 = new XYChart.Series();
series3.setName("2005");
series3.getData().add(new XYChart.Data(austria, 45000.65));
series3.getData().add(new XYChart.Data(brazil, 44835.76));
series3.getData().add(new XYChart.Data(france, 18722.18));
series3.getData().add(new XYChart.Data(italy, 17557.31));
series3.getData().add(new XYChart.Data(usa, 92633.68));
Scene scene = new Scene(bc,800,600);
bc.getData().addAll(series1, series2, series3);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Any help is welcome.
P.S I tested to insert the code into ScrollPane but I don't see any effect of using it.
ScrollPane s1 = new ScrollPane();
//s1.setPrefSize(620, 620);
s1.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED); // Horizontal scroll bar
s1.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED); // Vertical scroll bar
s1.setContent(bc);
Take a look at this answer.
I've got it working with this code:
final double SCALE_DELTA = 1.1;
bc.setOnScroll(new EventHandler<ScrollEvent>() {
public void handle(ScrollEvent event) {
event.consume();
if (event.getDeltaY() == 0) {
return;
}
double scaleFactor = (event.getDeltaY() > 0) ? SCALE_DELTA : 1 / SCALE_DELTA;
bc.setScaleX(bc.getScaleX() * scaleFactor);
bc.setScaleY(bc.getScaleY() * scaleFactor);
}
});
bc.setOnMousePressed(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
if (event.getClickCount() == 2) {
bc.setScaleX(1.0);
bc.setScaleY(1.0);
}
}
});
You should check out Jason Winnebeck's JFXUtils. He has developed a really nice library for chart zooming as well as panning (dragging the zoomed chart area like Google Maps).
You can also easily reset the chart. I wired it up to double click like you suggested.
You can specify the format of the axis labels on his custom StableTicksAxis (I display dates and times on my x axis).
Zooming works from a click-and-drag on the chart area or on the axis for zooming in to the entire span of either the x or y axis.
Mouse wheel zooming is baked right in.
He has a convenience method for super simple default setup, which wraps the chart in the components required to implement zooming.