JavaFx 2.x: XYChart properties - javafx-2

Having a simple XY LineChart, I would like to set grid and line stroke (width), style (dotted, dashed and so on) and color as rgb as well as background color without using css.
Is this possibile? And if so, how to? I can't find any suitable method.
import javafx.application.Application;
import javafx.event.EventHandler;
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);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Yes, your case is simpler in logic than How to dynamically change line style in JavaFX 2.0 line chart? and you are also asking for a couple more features. Marking as duplicate was just an easy way to point to a similar question and answer.
I think the answer is the same (use css lookups) - there is no such api in JavaFX 2.2 as lineChart.setStroke.
Eventually some of the stuff which is only accessible via css lookups might be made available via Java API. For instance some of the region background stuff is API in JavaFX 8 so, once you get a reference to it, you could modify it via api - though, even then, I still don't think there is a way to get a reference to something like a chart background without a css lookup or some unthinkably ugly sequence of getChildren().get(idx) calls or hacking into the chart source code. Of those options, I think the css lookup approach is the most preferable most of the time.
Note that you are not really avoiding a css file, as JavaFX 2.2 ships with a default css file which is used to style charts. Also note that the linked sample solution to the dynamic line style question does not supply a user stylesheet - all user styling is done in code.

Related

How to dynamically resize an svg in javafx according to the size of the region

I'm keen on svg and would like to put many of them in my User Interface. But I have a problem with the size of svg. I would like to load any svg I retrieve as a parameter and resize it dynamically to the size of the control.
All the examples I found are resize thanks to the "rescale" method (as found in the following article JavaFX: How to resize button containing svg image.
But since I have no idea of the size of the original svg I don't know what factor to apply in the rescale method.
So, my question is how do I generify the following code:
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.shape.SVGPath;
import javafx.stage.Stage;
public class Main extends Application{
private final int MIN_BUTTON_SIZE = 10;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
HBox root = new HBox();
root.setAlignment(Pos.CENTER);
SVGPath svg = new SVGPath();
svg.setContent("M87.5,50.002C87.5,29.293,70.712,12.5,50,12.5c-20.712,0-37.5,16.793-37.5,37.502C12.5,70.712,29.288,87.5,50,87.5" +
"c6.668,0,12.918-1.756,18.342-4.809c0.61-0.22,1.049-0.799,1.049-1.486c0-0.622-0.361-1.153-0.882-1.413l0.003-0.004l-6.529-4.002" +
"L61.98,75.79c-0.274-0.227-0.621-0.369-1.005-0.369c-0.238,0-0.461,0.056-0.663,0.149l-0.014-0.012" +
"C57.115,76.847,53.64,77.561,50,77.561c-15.199,0-27.56-12.362-27.56-27.559c0-15.195,12.362-27.562,27.56-27.562" +
"c14.322,0,26.121,10.984,27.434,24.967C77.428,57.419,73.059,63,69.631,63c-1.847,0-3.254-1.23-3.254-3.957" +
"c0-0.527,0.176-1.672,0.264-2.111l4.163-19.918l-0.018,0c0.012-0.071,0.042-0.136,0.042-0.21c0-0.734-0.596-1.33-1.33-1.33h-7.23" +
"c-0.657,0-1.178,0.485-1.286,1.112l-0.025-0.001l-0.737,3.549c-1.847-3.342-5.629-5.893-10.994-5.893" +
"c-10.202,0-19.877,9.764-19.877,21.549c0,8.531,5.101,14.775,13.632,14.775c4.75,0,9.587-2.727,12.665-7.035l0.088,0.527" +
"c0.615,3.342,9.843,7.576,15.121,7.576c7.651,0,16.617-5.156,16.617-19.932l-0.022-0.009C87.477,51.13,87.5,50.569,87.5,50.002z" +
"M56.615,56.844c-1.935,2.727-5.101,5.805-9.763,5.805c-4.486,0-7.212-3.166-7.212-7.738c0-6.422,5.013-12.754,12.049-12.754" +
"c3.958,0,6.245,2.551,7.124,4.486L56.615,56.844z");
Button buttonWithGraphics = new Button();
buttonWithGraphics.setGraphic(svg);
// Bind the Image scale property to the buttons size
svg.scaleXProperty().bind(buttonWithGraphics.widthProperty().divide(100));
svg.scaleYProperty().bind(buttonWithGraphics.heightProperty().divide(100));
// Declare a minimum size for the button
buttonWithGraphics.setMinSize(MIN_BUTTON_SIZE, MIN_BUTTON_SIZE);
root.getChildren().addAll(buttonWithGraphics);
root.layoutBoundsProperty().addListener((observableValue, oldBounds, newBounds) -> {
double size = Math.max(MIN_BUTTON_SIZE, Math.min(newBounds.getWidth(), newBounds.getHeight()));
buttonWithGraphics.setPrefSize(size, size);
}
);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
I guess this is linked with the following lines:
svg.scaleXProperty().bind(buttonWithGraphics.widthProperty().divide(100));
create a resizable canvas, you can find details here
canvas can be scaled, and also graphic context can also be scale
var gc = canvas.getGraphicsContext2D();
gc.scale(0.1, 0.1);
use gc to draw the svg path, line, circle, etc.
sample code here:
var canvas = new ResizableCanvas() {
#Override
public void draw() {
var gc = getGraphicsContext2D();
gc.save();//make sure you save the status here and restore after all operations are finished
//System.out.print(getWidth()+" ");
//System.out.println(getHeight());
var width = getWidth();
var height = getHeight();
gc.clearRect(0,0,width, height);
gc.scale(width/512.002, height/512.002);
gc.beginPath();
gc.setFill(Color.web("#2D4961"));
gc.appendSVGPath("""
M399.994,0H112.008C94.337,0,80.009,14.327,80.009,31.999v342.624
c0.08,16.159,8.288,31.191,21.839,39.998l145.433,94.796c5.304,3.448,12.135,3.448,17.439,0l145.433-94.556
c13.551-8.808,21.759-23.839,21.839-39.998V31.999C431.993,14.327,417.665,0,399.994,0z M399.994,68.477
c-6.872-6.24-15.399-10.352-24.559-11.839c-1.496-9.2-5.64-17.759-11.919-24.639h36.478V68.477z M148.486,31.999
c-6.264,6.864-10.408,15.391-11.919,24.559c-9.168,1.512-17.695,5.656-24.559,11.919V31.999H148.486z M256.001,476.858
l-26.479-17.199c11.047-4.6,20.327-12.623,26.479-22.879c6.152,10.256,15.431,18.279,26.479,22.879L256.001,476.858z
M399.994,374.622c0.008,5.424-2.728,10.48-7.28,13.439l-91.756,59.917c-20.895-1.592-37.022-19.039-36.958-39.998
c0-4.416-3.584-8-8-8s-8,3.584-8,8c0.064,20.959-16.063,38.406-36.958,39.998l-91.756-59.917c-4.552-2.96-7.288-8.016-7.28-13.439
V103.995c0-17.671,14.327-31.998,31.998-31.998c4.416,0,8-3.584,8-8c0-17.671,14.327-31.999,31.999-31.999h143.993
c17.671,0,31.999,14.327,31.999,31.999c0,4.416,3.584,8,8,8c17.671,0,31.999,14.327,31.999,31.998L399.994,374.622L399.994,374.622z""");
gc.fill();
gc.setFill(Color.web("#44637F"));
gc.beginPath();
gc.appendSVGPath("""
M80.009,31.999v271.987l0,0c8.84,0,15.999-7.16,15.999-15.999V31.999
c0-8.84,7.16-15.999,15.999-15.999h271.987c8.84,0,15.999-7.16,15.999-15.999H112.008C94.329,0,80.009,14.327,80.009,31.999z""");
gc.fill();
gc.setFill(Color.web("#123247"));
gc.beginPath();
gc.appendSVGPath("""
M410.154,414.861c13.551-8.808,21.759-23.839,21.839-39.998V55.997l0,0
c-8.84,0-15.999,7.16-15.999,15.999v304.466c0,8.04-4.024,15.551-10.719,19.999L269.28,487.097
c-8.304,5.56-13.287,14.887-13.279,24.879l0,0c3.096,0.008,6.12-0.88,8.72-2.56L410.154,414.861z""");
gc.fill();
gc.restore();
}
};
canvas.widthProperty().bind(canvas.heightProperty());
canvas.heightProperty().bind(stage.getScene().heightProperty().multiply(0.1));
The solution above tries to do it dynamically, although (as you said) fails to do it in respect to the original SvgPath size.
I would recommend doing it like that:
double size = 30;
svg.setScaleX(size / svg.boundsInLocalProperty().get().getWidth());
svg.setScaleY(size / svg.boundsInLocalProperty().get().getHeight());
This will scale your SvgPath to size.

How to add space between menus in javafx

I am trying to implement a menu. This is my code :
Menu menuFile1 = new Menu("ADD");
Menu menuFile2 = new Menu("EDIT");
Menu menuFile3 = new Menu("VIEW");
Menu menuFile4 = new Menu("HELP");
How can I put some space between each menu (that is between ADD,EDIT,VIEW and HELP) ?
Answer
Space around menus is controlled by padding (see the Region css guide).
For example:
menu.setStyle("-fx-padding: 5 10 8 10;");
sets the padding around the menu to 5 pixels on the top, 10 pixels on the right, 8 pixels on the bottom and 10 pixels on the left.
Sample
The following is a bit overcomplicated for a code sample to demonstrate this effect, but you could run it to see the effect of varying padding values.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringExpression;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class SpacedOut extends Application {
#Override
public void start(final Stage stage) {
MenuBar menuBar = createMenuBar();
VBox controlPane = createControlPane(menuBar);
VBox layout = new VBox(10,
menuBar,
controlPane
);
VBox.setVgrow(controlPane, Priority.ALWAYS);
stage.setScene(new Scene(layout, 400, 200));
stage.show();
}
private MenuBar createMenuBar() {
MenuBar menuBar = new MenuBar();
menuBar.getMenus().addAll(
new Menu("ADD"),
new Menu("EDIT"),
new Menu("VIEW"),
new Menu("HELP")
);
return menuBar;
}
private VBox createControlPane(MenuBar menuBar) {
CheckBox useCustomPadding = new CheckBox("Use Custom Padding");
useCustomPadding.setSelected(false);
Slider padAmount = new Slider(0, 30, 15);
padAmount.setShowTickMarks(true);
padAmount.setShowTickLabels(true);
padAmount.setMajorTickUnit(10);
padAmount.setMaxWidth(200);
padAmount.disableProperty().bind(
useCustomPadding.selectedProperty().not()
);
VBox contentPane = new VBox(10,
useCustomPadding,
padAmount
);
contentPane.setPadding(new Insets(10));
StringExpression paddingExpression = Bindings.concat(
"-fx-padding: ", padAmount.valueProperty(), "px;"
);
menuBar.getMenus().forEach(
menu -> menu.styleProperty().bind(
Bindings
.when(useCustomPadding.selectedProperty())
.then(paddingExpression)
.otherwise("")
)
);
return contentPane;
}
public static void main(String[] args) {
launch(args);
}
}
With the setStyle() Method you can pass one or more css styles in one string.
Like menuFile1.setStyle("-fx-border-color: red; -fx-effect: dropshadow( one-pass-box , red , 10,0.5,0,0 );");
Alternatively you could put your style information inside a css file and add it to the Scene through.
Scene somescene = new Scene(root)
somescene.getStylesheets().add("your.css");
See the css reference of Java FX 2 or this tutorial.

Adding space between buttons in VBox

I have a collection of buttons:
VBox menuButtons = new VBox();
menuButtons.getChildren().addAll(addButton, editButton, exitButton);
I want to add some spacing between these buttons, without using a CSS style sheet. I think there should be a way to do this.
setPadding(); is for the Buttons in the VBox.
setMargin(); should be for the VBox itself. But I didn't find a way for the spacing between the buttons.
I'm glad for any ideas. :)
VBox supports spacing out of the box:
VBox menuButtons = new VBox(5);
or
menuButtons.setSpacing(5);
Just call setSpacing method and pass some value.
Example with HBox (it's same for VBox):
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.HBoxBuilder;
import javafx.stage.Stage;
public class SpacingDemo extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) {
stage.setTitle("Spacing demo");
Button btnSave = new Button("Save");
Button btnDelete = new Button("Delete");
HBox hBox = HBoxBuilder.create()
.spacing(30.0) //In case you are using HBoxBuilder
.padding(new Insets(5, 5, 5, 5))
.children(btnSave, btnDelete)
.build();
hBox.setSpacing(30.0); //In your case
stage.setScene(new Scene(hBox, 320, 240));
stage.show();
}
}
And this is how it looks:
Without of spacing:
With spacing:
If you're using FXML, use the spacing attribute:
<VBox spacing="5" />
As others have mentioned you can use setSpacing().
However, you can also use setMargin(), it is not for the pane (or box in your words), it is for individual Nodes. setPadding() method is for the pane itself. In fact, setMargin() takes a node as a parameter so you can guess what it's for.
For example:
HBox pane = new HBox();
Button buttonOK = new Button("OK");
Button buttonCancel = new Button("Cancel");
/************************************************/
pane.setMargin(buttonOK, new Insets(0, 10, 0, 0)); //This is where you should be looking at.
/************************************************/
pane.setPadding(new Insets(25));
pane.getChildren().addAll(buttonOK, buttonCancel);
Scene scene = new Scene(pane);
primaryStage.setTitle("Stage Title");
primaryStage.setScene(scene);
primaryStage.show();
You could get the same result if you replaced that line with
pane.setSpacing(10);
If you have several nodes that should be spaced, setSpacing() method is far more convenient because you need to call setMargin() for each individual node and that would be ridiculous. However, setMargin() is what you need if you need margins(duh) around a node that you can determine how much to each side because setSpacing() methods places spaces only in between nodes, not between the node and the edges of the window.
The same effect as the setSpacing method can also be achieved via css:
VBox {
-fx-spacing: 8;
}

Automatic resizing of LineChart fails if embedded in a pane

The following program fails to resize the line chart horizontally when embedded in a Pane (or borderpane of anchorpane for the matter)
If the line chart is directly parented to the VBox instead, then everything works as expected.
I found I needed to bind the chart size to the parent pane, which I assume must be done automatically by VBox and HBox.
After trying different combination of enclosing in HBox/VBox, setting growing and alignment policies, I am quite confused about how layouts work.
I observe that there are differences in how ui components behave wrt resizing.
Any clarification (or digest insight on javadoc unclear documentation) is appreciated.
Best regards.
Source edited and clarified
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.chart.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage stage) {
NumberAxis xAxis = new NumberAxis();
NumberAxis yAxis = new NumberAxis();
xAxis.setLabel("X");
yAxis.setLabel("Y");
final LineChart<Number, Number> lineChart = new LineChart<>(xAxis, yAxis);
lineChart.setTitle("x = f(y)");
XYChart.Series data = new XYChart.Series();
data.setName("Serie 1");
for (int i = 0; i < 10; i++) {
data.getData().add(new XYChart.Data(i, i * i));
}
lineChart.getData().add(data);
VBox vb = new VBox();
vb.setFillWidth(true);
HBox hb = new HBox();
hb.getChildren().add(lineChart);
hb.setFillHeight(true);
vb.getChildren().add(hb);
HBox.setHgrow(lineChart, Priority.ALWAYS);
VBox.setVgrow(hb, Priority.ALWAYS);
Scene scene = new Scene(vb);
stage.setScene(scene);
stage.centerOnScreen();
stage.setResizable(true);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
For the record, the problem of the provided example is solved after modifying this typo:
HBox.setHgrow(hb, Priority.ALWAYS);
to:
HBox.setHgrow(lineChart, Priority.ALWAYS);
This fixes resizing horizontally.
When embedded directly in VBox, the chart's size is recomputed on resize, as it is anchored to the VBox, which boundaries change.
When embedded in a HBox, we have to provide a hint for the HBox to grow horizontally, and vertically.
Vertically it's done with:
VBox.setVgrow(hb, Priority.ALWAYS);
Horizontally it's done by requesting its content to occupy all available space, which the fix above is about.

How to implement a transparent Pane with non-transparent children?

How can i implement a transparent panel with non-transparent children in JavaFX 2?
The effect i want to achieve is for example applied to menus in Blender:
The menu-panel / window is transparent, but the text items are not transparent which leads to a pretty effect.
Set the background of your pane to a color with an alpha component. You can use a stylesheet or an inline style for this.
For example, if your pane was named glass, then the following will give it a rounded, translucent cyan background:
glass.setStyle("-fx-background-color: rgba(0, 100, 100, 0.5); -fx-background-radius: 10;");
You could also accomplish similar effects using blends, stackpanes or groups of items with the opacity set for items at the back of the stackpane or group.
Here is an executable example using the css background method.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class TranslucentPane extends Application {
#Override public void start(final Stage stage) throws Exception {
final ImageView imageView = new ImageView(
new Image("https://upload.wikimedia.org/wikipedia/commons/b/b7/Idylls_of_the_King_3.jpg")
);
imageView.setFitHeight(300);
imageView.setFitWidth(228);
final Label label = new Label("The Once\nand\nFuture King");
label.setStyle("-fx-text-fill: goldenrod; -fx-font: italic 20 \"serif\"; -fx-padding: 0 0 20 0; -fx-text-alignment: center");
StackPane glass = new StackPane();
StackPane.setAlignment(label, Pos.BOTTOM_CENTER);
glass.getChildren().addAll(label);
glass.setStyle("-fx-background-color: rgba(0, 100, 100, 0.5); -fx-background-radius: 10;");
glass.setMaxWidth(imageView.getFitWidth() - 40);
glass.setMaxHeight(imageView.getFitHeight() - 40);
final StackPane layout = new StackPane();
layout.getChildren().addAll(imageView, glass);
layout.setStyle("-fx-background-color: silver; -fx-padding: 10;");
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) { launch(args); }
}
Sample program output:

Resources