Adjusting Height,Width of JavaFX Chart - charts

I'm new to Java with PHP, HTML, CSS experience. When I try to change the width and height my chart takes up in the window NetBeans gives me the error:
error: setWidth(double) has protected access in Region chart.setWidth(450);
I've searched through the javafx docs and found that width/height is bound to region, but I'm not sure what that is in my code, I tried a few things but haven't found it...
I'm sure this is simple..
thanks in advance, Brad.
package test;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.chart.ScatterChart;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.shape.Rectangle;
public class Test extends Application {
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root,1000,1000));
root.getStylesheets().add("test/Chart.css");
Rectangle rect = new Rectangle(35,70);
rect.setLayoutX(30);
rect.setLayoutY(30);
rect.getStyleClass().add("my-rect");
NumberAxis xAxis = new NumberAxis("X Axis", -24d, 24.0d, 2.0d);
NumberAxis yAxis = new NumberAxis("Y Axis", -24.0d, 24.0d, 1.0d);
ObservableList<XYChart.Series> data = FXCollections.observableArrayList(
new ScatterChart.Series("Series 1", FXCollections.<ScatterChart.Data>observableArrayList(
new XYChart.Data(0.2, 3.5),
new XYChart.Data(0.7, 4.6),
new XYChart.Data(1.8, 1.7),
new XYChart.Data(2.1, 2.8),
new XYChart.Data(4.0, 2.2),
new XYChart.Data(4.1, 2.6),
new XYChart.Data(4.5, 2.0),
new XYChart.Data(6.0, 3.0),
new XYChart.Data(7.0, 2.0),
new XYChart.Data(7.8, 4.0)
)),
new ScatterChart.Series("Series 2", FXCollections.<ScatterChart.Data>observableArrayList(
new XYChart.Data(6.2,3.0),
new XYChart.Data(6.0,4.0),
new XYChart.Data(5.8,5.0)
))
);
ScatterChart chart = new ScatterChart(xAxis, yAxis, data);
chart.setWidth(450);
chart.setHeight(450);
chart.setLayoutX(250);
chart.setLayoutY(250);
root.getChildren().addAll(chart,rect);
}
#Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}

The javadoc of ScatterChart.getHeight() (which in turn is Region.getHeight()) says
Gets the value of the property height.
Property description:
The height of this resizable node. This property is set by the region's
parent during layout and may not be set by the application. If an
application needs to explicitly control the size of a region, it
should override its preferred size range by setting the minHeight,
prefHeight, and maxHeight properties.
Namely you can adjust and constraint the size of any chart with:
ScatterChart.setPrefHeight(double)
ScatterChart.setMinHeight(double)
ScatterChart.setMaxHeight(double)
ScatterChart.setPrefWidth(double)
ScatterChart.setMinWidth(double)
ScatterChart.setMaxWidth(double)
ScatterChart.setPrefSize(double, double)
ScatterChart.setMinSize(double, double)
ScatterChart.setMaxSize(double, double)

Related

Can I select text within a label in JavaFX 8 [duplicate]

I just want to create copiable label in JavaFX.
I have tried to create TextField that have no background, have no focus border and default background color, but I have no success.
I have found a lot of questions how to remove focus background from control but all of that looks like "hacks".
Is there are any standard solution to implement copyable text?
You can create a TextField without the border and background color with css:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class CopyableLabel extends Application {
#Override
public void start(Stage primaryStage) {
TextField copyable = new TextField("Copy this");
copyable.setEditable(false);
copyable.getStyleClass().add("copyable-label");
TextField tf2 = new TextField();
VBox root = new VBox();
root.getChildren().addAll(copyable, tf2);
Scene scene = new Scene(root, 250, 150);
scene.getStylesheets().add(getClass().getResource("copyable-text.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
and
copyable-text.css:
.copyable-label, .copyable-label:focused {
-fx-background-color: transparent ;
-fx-background-insets: 0px ;
}
This is the solution I used, where there is a small button besides the label to be able to copy the text:
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import org.controlsfx.glyphfont.FontAwesome;
import org.controlsfx.glyphfont.Glyph;
import java.util.Locale;
public class CopiableLabel extends Label
{
public CopiableLabel()
{
addCopyButton();
}
public CopiableLabel(String text)
{
super(text);
addCopyButton();
}
public CopiableLabel(String text, Node graphic)
{
super(text, graphic);
}
private void addCopyButton()
{
Button button = new Button();
button.visibleProperty().bind(textProperty().isEmpty().not());
button.managedProperty().bind(textProperty().isEmpty().not());
button.setFocusTraversable(false);
button.setPadding(new Insets(0.0, 4.0, 0.0, 4.0));
button.setOnAction(actionEvent -> AppUtils.copyToClipboard(getText()));
Glyph clipboardIcon = AppUtils.createFontAwesomeIcon(FontAwesome.Glyph.CLIPBOARD);
clipboardIcon.setFontSize(8.0);
button.setGraphic(clipboardIcon);
setGraphic(button);
setContentDisplay(ContentDisplay.RIGHT);
}
}

How to make JavaFX gradually changing effect on a Image

In my JavaFX application I have a ImageView on the stage. I wanted to add both effects Glow and Sepia, and both should be gradually increasing (0 to 1) within duration 5 sec.
How do I do this with code?
Both effects should be applied in parallel. Would it make the animation slower?
Use a Timeline with one KeyFrame at the start and one at the end. For each KeyFrame just set the value of each effect:
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(glow.levelProperty(), 0),
new KeyValue(sepia.levelProperty(), 0)),
new KeyFrame(Duration.seconds(5),
new KeyValue(glow.levelProperty(), 1),
new KeyValue(sepia.levelProperty(),1))
);
SSCCE:
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.effect.Glow;
import javafx.scene.effect.SepiaTone;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class AnimatedEffects extends Application {
private static final String IMAGE_URL = "http://www.nasa.gov/sites/default/files/styles/full_width/public/thumbnails/image/nh-charon-neutral-bright-release.jpg?itok=20aE3TAH";
#Override
public void start(Stage primaryStage) {
ImageView image = new ImageView(new Image(IMAGE_URL, 400, 400, true, true));
Glow glow = new Glow(0);
SepiaTone sepia = new SepiaTone(0);
sepia.setInput(glow);
image.setEffect(sepia);
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(glow.levelProperty(), 0),
new KeyValue(sepia.levelProperty(), 0)),
new KeyFrame(Duration.seconds(5),
new KeyValue(glow.levelProperty(), 1),
new KeyValue(sepia.levelProperty(),1))
);
Button effectButton = new Button("Add effect");
effectButton.setOnAction(e -> timeline.play());
BorderPane root = new BorderPane(image, null, null, effectButton, null);
BorderPane.setAlignment(effectButton, Pos.CENTER);
BorderPane.setMargin(effectButton, new Insets(10));
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX - move window with effect

I have undecorated non-fullscreen window, which I like to move outside screen boundaries when mouse leaves it's area, but do so smoothly. I found some JavaFX functionality to do so - Timeline, but KeyValue for that Timeline doesn't supports stage.xProperty - because this property is readonlyProperty. Is there way to move my window smoothly using JavaFX functions?
You can setup proxy properties that you manipulate via KeyValues in a Timeline. A listener on the proxy can modify the actual stage location.
import javafx.animation.*;
import javafx.application.*;
import javafx.beans.property.*;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment;
import javafx.stage.*;
import javafx.util.Duration;
public class StageSwiper extends Application {
private static final int W = 350;
private static final Duration DURATION = Duration.seconds(0.5);
#Override
public void start(Stage stage) throws Exception {
Label instructions = new Label(
"Window will slide off-screen when the mouse exits it.\n" +
"Click the window to close the application."
);
instructions.setTextAlignment(TextAlignment.CENTER);
final StackPane root = new StackPane(instructions);
root.setStyle("-fx-background-color: null;");
DoubleProperty stageX = new SimpleDoubleProperty();
stageX.addListener((observable, oldValue, newValue) -> {
if (newValue != null && newValue.doubleValue() != Double.NaN) {
stage.setX(newValue.doubleValue());
}
});
final Timeline slideLeft = new Timeline(
new KeyFrame(
DURATION,
new KeyValue(
stageX,
-W,
Interpolator.EASE_BOTH
)
),
new KeyFrame(
DURATION.multiply(2)
)
);
slideLeft.setOnFinished(event -> {
slideLeft.jumpTo(Duration.ZERO);
stage.centerOnScreen();
stageX.setValue(stage.getX());
});
root.setOnMouseClicked(event -> Platform.exit());
root.setOnMouseExited(event -> slideLeft.play());
stage.setScene(new Scene(root, W, 100, Color.BURLYWOOD));
stage.initStyle(StageStyle.UNDECORATED);
stage.show();
stage.centerOnScreen();
stageX.set(stage.getX());
}
public static void main(String[] args) {
launch(args);
}
}

Javafx 8 drawing a line between translated nodes

How do you draw a line between the centers of translated nodes? Given for example the following code snippet:
public class Test extends Application{
#Override
public void start(Stage primaryStage) {
StackPane root = new StackPane();
Circle circle1=new Circle(10, Color.GREEN);
root.getChildren().add(circle1);
Circle circle2=new Circle(10, Color.RED);
root.getChildren().add(circle2);
Scene scene = new Scene(root, 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
circle1.setTranslateX(100);
Line line=new Line(circle1.getCenterX(), circle1.getCenterY(), circle2.getCenterX(), circle2.getCenterY());
root.getChildren().add(line);
}
public static void main(String[] args) {
launch(args);
}
}
Running this application will clearly show a red and a green circle. However, there won't be a line because each of the centers of the circles are at the coordinates (0,0). Nevertheless, the circles do not cover each other because one of the circles is translated. This doesn't work:
Line line=new Line(circle1.getCenterX()+circle1.getTranslateX(), circle1.getCenterY()+circle1.getTranslateY(), circle2.getCenterX()+circle2.getTranslateX(), circle2.getCenterY()+circle2.getTranslateY());
Finally, let's assume that there is an approach to draw a line connecting the centers of the two circles. If, after the line is drawn, I would invoke circle2.setTranslateX(50);, how do I ensure that the endpoint of the line on the side of circle2 moves accordingly?
A StackPane is a managed layout pane, meaning that it manages the positions of its child nodes (by default it centers them); the translation is applied after the StackPane positions the nodes. This is why the circles appear in different locations but the line is not where you expect. Using a Pane instead of a StackPane will make things work as you expect.
To keep the line in the correct position relative to the circles when the circles are repositioned dynamically, bind the startX, startY, endX, and endY properties, instead of just setting them.
import javafx.animation.Animation;
import javafx.animation.ParallelTransition;
import javafx.animation.SequentialTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class LineConnectingCircles extends Application{
#Override
public void start(Stage primaryStage) {
Pane root = new Pane();
Circle circle1=new Circle(10, Color.GREEN);
root.getChildren().add(circle1);
Circle circle2=new Circle(10, Color.RED);
root.getChildren().add(circle2);
// move circles so we can see them:
circle1.setTranslateX(100);
circle2.setTranslateY(50);
Line line = new Line();
// bind ends of line:
line.startXProperty().bind(circle1.centerXProperty().add(circle1.translateXProperty()));
line.startYProperty().bind(circle1.centerYProperty().add(circle1.translateYProperty()));
line.endXProperty().bind(circle2.centerXProperty().add(circle2.translateXProperty()));
line.endYProperty().bind(circle2.centerYProperty().add(circle2.translateYProperty()));
root.getChildren().add(line);
// create some animations for the circles to test the line binding:
Button button = new Button("Animate");
TranslateTransition circle1Animation = new TranslateTransition(Duration.seconds(1), circle1);
circle1Animation.setByY(150);
TranslateTransition circle2Animation = new TranslateTransition(Duration.seconds(1), circle2);
circle2Animation.setByX(150);
ParallelTransition animation = new ParallelTransition(circle1Animation, circle2Animation);
animation.setAutoReverse(true);
animation.setCycleCount(2);
button.disableProperty().bind(animation.statusProperty().isEqualTo(Animation.Status.RUNNING));
button.setOnAction(e -> animation.play());
BorderPane.setAlignment(button, Pos.CENTER);
BorderPane.setMargin(button, new Insets(10));
Scene scene = new Scene(new BorderPane(root, null, null, button, null), 300, 250);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Javafx - Rotate node for display in landscape/portrait orientation on handheld device

The javafx application I am developing will eventually be running on a handheld device with 800x480 resolution. The application will typically be running in portrait mode, but for some features (e.g. displaying charts and tables), it will need to switch to landscape mode to better display the data.
My question is, is there a straight forward way to operate with nodes that are rotated by multiples of 90 degrees?
I can rotate the table by calling setRotate(), although this introduces several new issues:
To resize column widths when rotated, the user has to drag the column dividers left to right (orthogonal to the row of headers),
The table still expands its width/height to the size of its parent, although this doesn't work as well when rotated -90 degrees (or other multiples of 90).
The other constraint is that the chart content is contained in the center of a BorderPane, where the top and bottom of the BorderPane contain toolbars, which prevents rotating the entire scene.
Here is my SSCCE; please correct me if there are any problems with the code below.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TablePanelTrial extends Application {
BorderPane root = new BorderPane();
private boolean isRotated=false;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage primaryStage) {
primaryStage.setTitle("Table Panel Trial");
final TablePanel tp = new TablePanel();
Button btnRotate = new Button("Rotate");
btnRotate.setOnAction(new EventHandler() {
#Override
public void handle(ActionEvent event) {
double r = -90;
if(isRotated){
r=0;
isRotated = !isRotated;
}
else{
r=-90;
tp.tv.setMinHeight(200);
isRotated = !isRotated;
}
tp.rotate(r);
}
});
root.setTop(btnRotate);
root.setCenter(tp.getVB());
primaryStage.setScene(new Scene(root, 480, 800));
primaryStage.show();
}
class TablePanel{
private VBox vbox = new VBox();
private TableView tv = new TableView();
private String[] labelVal = {"Column 1", "Element", "Difference", "File Name", "Report Number"};
public TablePanel(){
TableColumn column1 = new TableColumn(labelVal[0]);
TableColumn column2 = new TableColumn(labelVal[1]);
TableColumn column3 = new TableColumn(labelVal[2]);
TableColumn column4 = new TableColumn(labelVal[3]);
TableColumn column5 = new TableColumn(labelVal[4]);
tv.getColumns().addAll(column1, column2, column3, column4,column5);
vbox.getChildren().add(tv);
tv.setPrefHeight(2000);
}
public Pane getVB(){
return vbox;
}
public void rotate(double r){
vbox.setRotate(r);
}
}
}
Android does not perform a rotate function when the device goes to landscape mode, I think.
One has to redraw the layout again to display it.
With JavaFX, what you can do is add a Listener to the scene to listen for a change in width since that's what basically happens.
You can do it like this most probably:
scene.widthProperty().addListener((observable, oldValue, newValue) -> {
....initialize the changes to layout here....
);
});
May not be the best solution but I guess its something.