I'm a new JavaFX programmer and my question is I'm getting multiple decimal points in the slider and I only want one.
I'm getting like 99.99999999999 for each value within but I would love to get only one like 99.9
The thing is printf is not applicable here I guess and I only know that way >.<
private void createView() {
final GridPane radioGridPane = new GridPane();
final Label frequencyLabel = new Label("something");
final Slider sliderFrequency = new Slider(87.9, 107.9, 90);
frequencyLabel.textProperty().bind(sliderFrequency.valueProperty().asString());
radioGridPane.add(frequencyLabel, 2, 1);
radioGridPane.add(sliderFrequency, 3, 1);
this.setCenter(radioGridPane);
radioGridPane.setGridLinesVisible(true);
}
You were actually quite close. You can use asString method with the following signature:
javafx.beans.binding.NumberExpressionBase#asString(java.lang.String)
that accepts a format parameter.
Example usage:
public class FormatBindingSSCCE extends Application {
#Override
public void start(Stage stage) {
final Label frequencyLabel = new Label();
final Slider sliderFrequency = new Slider(87.9, 107.9, 90);
frequencyLabel.textProperty().bind(sliderFrequency.valueProperty().asString("%.1f"));
final GridPane radioGridPane = new GridPane();
radioGridPane.add(frequencyLabel, 2, 1);
radioGridPane.add(sliderFrequency, 3, 1);
final Scene scene = new Scene(radioGridPane);
stage.setScene(scene);
stage.show();
}
}
Related
I am currently trying to write a simple application in Java using JavaFX.
In the application I want to have a pop up window that prompts the user for input. This works fine, as long as the user doesn't try to open the pop up window again.
If he does that the following error occurs:
Exception in thread "JavaFX Application Thread" java.lang.IllegalArgumentException: Grid hgap=5.0, vgap=0.0, alignment=TOP_LEFTis already set as root of another scene
And here is the code that opens the window:
In the main view controller:
btAddChat.setOnAction(e -> lvItems.add(PopUpVC.display()));
And in the view controller for the pop up:
public class PopUpVC
{
private static final GridPane root = new GridPane();
private static final TextField tfInput = new TextField();
private static final Button btOK = new Button("OK");
private static String result;
private static Stage primaryStage = new Stage();
public static String display()
{
Scene scene = new Scene(root, 200, 50);
MainVC mvc = new MainVC();
root.setPadding(new Insets(10));
root.setHgap(5);
tfInput.setPrefWidth(scene.getWidth()*0.65);
root.add(btOK, 0, 0, 1, 1);
root.add(tfInput, 1, 0, 1, 1);
btOK.setOnAction(e ->
{
if(!tfInput.getText().equals(""))
{
primaryStage.close();
}
});
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.showAndWait();
return tfInput.getText();
}
I only copied the most important part, the actual error is a lot longer. I know what is causing the error (trying to open a window with the same root throws an error because the root is already in use), I just can't figure out how to resolve it.
Here is a picture of the application:
The top left button opens the pop up.
The pop up:
If there are any suggestions on how to resolve this issue I would be very glad to hear them, if there should be any another way to open a pop up window that prompts the user for text input, I would also be glad to hear about those!
Thanks a lot in advance!
The OP's solution:
I figured it out, easy thing really, I added a parameter to the start function so when I call it I just give it a new GridPane() and it works perfectly fine.
Is really the wrong approach. As #James_D pointed out, static is not a good idea for anything like this. Trying to keep with the original design as much as possible, I'd suggest this, which builds the PopUp just once, and re-displays it:
public class PopUp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button button = new Button("Click Me");
PopUpVC popUpVC = new PopUpVC();
button.setOnAction(evt -> {
popUpVC.display();
});
primaryStage.setScene(new Scene(button));
primaryStage.show();
}
public class PopUpVC {
private final TextField tfInput = new TextField();
private Stage primaryStage = new Stage();
public PopUpVC() {
GridPane root = new GridPane();
Scene scene = new Scene(root, 200, 50);
root.setPadding(new Insets(10));
root.setHgap(5);
tfInput.prefWidthProperty().bind(scene.widthProperty().multiply(0.65));
Button btOK = new Button("OK");
root.add(btOK, 0, 0, 1, 1);
root.add(tfInput, 1, 0, 1, 1);
btOK.setOnAction(e -> {
if (!tfInput.getText().equals("")) {
primaryStage.close();
}
});
primaryStage.setResizable(false);
primaryStage.setScene(scene);
}
public String display() {
primaryStage.showAndWait();
return tfInput.getText();
}
}
}
When you have something that's completely static, like PopUpVC, that's a hint that it's just code that you've moved out of somewhere else. This is useful, and a good practice, if you are calling the methods from a variety of other classes, when it saves code duplication. However, you shouldn't have JavaFX elements as static fields in such a class.
In this case, you can do away with PopUpVC and move all of the code into a local method:
public class PopUp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button button = new Button("Click Me");
button.setOnAction(evt -> {
displayPopUpVC();
});
primaryStage.setScene(new Scene(button));
primaryStage.show();
}
public String displayPopUpVC() {
TextField tfInput = new TextField();
Stage primaryStage = new Stage();
GridPane root = new GridPane();
Scene scene = new Scene(root, 200, 50);
root.setPadding(new Insets(10));
root.setHgap(5);
tfInput.prefWidthProperty().bind(scene.widthProperty().multiply(0.65));
Button btOK = new Button("OK");
root.add(btOK, 0, 0, 1, 1);
root.add(tfInput, 1, 0, 1, 1);
btOK.setOnAction(e -> {
if (!tfInput.getText().equals("")) {
primaryStage.close();
}
});
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.showAndWait();
return tfInput.getText();
}
}
This behaves slightly different from the first solution because it doesn't retain the text in the TextField between calls. But the default text could be passed in a parameter if you wanted.
But, also as #James_D has pointed out, TextInputDialog does exactly what you want. Plus it does it with about 4 lines of code:
public class PopUp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button button = new Button("Click Me");
TextInputDialog dialog = new TextInputDialog();
dialog.setHeaderText(null);
dialog.setTitle("Chat");
dialog.setGraphic(null);
button.setOnAction(evt -> {
dialog.showAndWait();
});
primaryStage.setScene(new Scene(button));
primaryStage.show();
}
}
I am building an application that has 2 SubScenes, one for GUI and one for displaying 3D models (in my case OBJ).
For the import of the OBJ file I use the ObjModelImporterJFX library by InteractiveMesh.
Like the title says and as seen in this picture the models start to glitch when they are handled by a SubScene. It seems like some covered parts that should not be visible are rendered like they were. Just for comparison this is how it looks normally, when the models are handled direcly by the scene.
Here is my code:
public class Main extends Application{
#Override
public void start(Stage stage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
PerspectiveCamera camera = new PerspectiveCamera(true);
Group model = loadOBJ("course.obj");
Group course = new Group(model);
BorderPane borderPane = new BorderPane();
Pane pane1 = new Pane(course);
Pane pane2 = new Pane(root);
SubScene subScene1 = new SubScene(pane1, 1000, 720);
subScene1.setCamera(camera);
SubScene subScene2 = new SubScene(pane2,500,500);
borderPane.setLeft(subScene1);
borderPane.setRight(subScene2);
Scene scene = new Scene(borderPane, 1280,720, true);
stage.setScene(scene);
stage.show();
}
private Group loadOBJ(String fileName){
URL url = getClass().getResource(fileName);
Group modelRoot = new Group();
ObjModelImporter importer = new ObjModelImporter();
importer.read(url);
for (MeshView view : importer.getImport()){
modelRoot.getChildren().add(view);
}
return modelRoot;
}
public static void main(String[] args){
launch(args);
}
}
I hope that someone has an idea why this is. Thanks in advance :)
UPDATE: To fix this enable depthBuffer by changing the constructor of the SubScene from
SubScene subScene1 = new SubScene(pane1, 1000, 720);
to
SubScene subScene1 = new SubScene(pane1, 1000, 720, true, null);
In below code, I have a box added in scene. I am printing the mouse event coordinate. Coordinates are fine when I am not clicking on the box but as soon as I click on the box I get some different set of coordinate relative to some different node. I am trying to get the normalized coordinate, relative to scene only, even if I click on the box. I cant use me.getSceneX()/Y() and cant set box as mouse transparent. So how to carry out this transformation. Thanks.
public class PointConversion extends Application
{
private final static Group worldRoot = new Group();
private final PerspectiveCamera camera = new PerspectiveCamera(true);
private final static Scene scene = new Scene(worldRoot, 1100, 800);
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage primaryStage)
{
camera.setNearClip(1.0);
camera.setTranslateZ(-6000);
camera.setFarClip(100000.0);
worldRoot.getChildren().add(camera);
scene.setCamera(camera);
primaryStage.setScene(scene);
primaryStage.show();
scene.setOnMousePressed(me ->
{
Point2D mousePoint = new Point2D(me.getX(), me.getY());
if(!(me.getTarget() instanceof Scene))
{
PickResult pickResult = me.getPickResult();
mousePoint = pickResult.getIntersectedNode().localToScene(mousePoint);
}
System.out.println("me.getSource() : " + me.getTarget() + " mousePoint:" + mousePoint);
});
final PhongMaterial blueMaterial = new PhongMaterial();
blueMaterial.setDiffuseColor(Color.web(Color.CRIMSON.toString(), 0.25));
blueMaterial.setSpecularColor(Color.web(Color.CRIMSON.toString(), 0.25));
Box cubiod = new Box(500, 500, 500);
cubiod.setMaterial(blueMaterial);
worldRoot.getChildren().add(cubiod);
}
}
Is it possible to do a simple background "flash" effect with a gradual fade on an arbitrary Node/Region/Pane?
I just want to show a subtle/brief red/white "flash" effect on a VBox (containing a label) to draw attention to it when the label's value changes.
Edit: All examples of this nature I've found so far seem to use a "Shape" (which is a Node), but of course a VBox or a Pane aren't a Shape - so that doesn't help me too much. Calling getShape() on the VBox just returns a null, so that's no help (I guess layout code hasn't been executed yet).
Edit 2:
This ALMOST works, but this dang effect seems to be completely overwriting (I think) everything in the VBox, including the text Label.
ColorInput effect = new ColorInput(0, 0, 900, 25, Paint.valueOf("#FFDDDD"));
Timeline flash = new Timeline(
new KeyFrame(Duration.seconds(0.4), new KeyValue(effect.paintProperty(), Paint.valueOf("#EED9D9"))),
new KeyFrame(Duration.seconds(0.8), new KeyValue(effect.paintProperty(), Paint.valueOf("#E0DDDD"))),
new KeyFrame(Duration.seconds(1.0), new KeyValue(effect.paintProperty(), Paint.valueOf("#DDDDDD"))));
vbox.setEffect(effect);
flash.setOnFinished(e -> vbox.setEffect(null));
flash.play();
Best way would be to provide a custom animation, like this (elaborating on fabian's answer):
#Override
public void start(Stage primaryStage) {
Label label = new Label("Bla bla bla bla");
Button btn = new Button("flash");
VBox box = new VBox(10, label, btn);
box.setPadding(new Insets(10));
btn.setOnAction((ActionEvent event) -> {
//**************************
//this animation changes the background color
//of the VBox from red with opacity=1
//to red with opacity=0
//**************************
final Animation animation = new Transition() {
{
setCycleDuration(Duration.millis(1000));
setInterpolator(Interpolator.EASE_OUT);
}
#Override
protected void interpolate(double frac) {
Color vColor = new Color(1, 0, 0, 1 - frac);
box.setBackground(new Background(new BackgroundFill(vColor, CornerRadii.EMPTY, Insets.EMPTY)));
}
};
animation.play();
});
Scene scene = new Scene(box, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
You could animate a effect, e.g. DropShadow:
#Override
public void start(Stage primaryStage) {
Label label = new Label("Bla bla bla bla");
DropShadow shadow = new DropShadow();
shadow.setColor(Color.RED);
shadow.setSpread(0.75);
Timeline shadowAnimation = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(shadow.radiusProperty(), 0d)),
new KeyFrame(Duration.seconds(0.15), new KeyValue(shadow.radiusProperty(), 20d)));
shadowAnimation.setAutoReverse(true);
shadowAnimation.setCycleCount(2);
Button btn = new Button("flash");
btn.setOnAction((ActionEvent event) -> {
Node target = label;
target.setEffect(shadow);
shadowAnimation.setOnFinished(evt -> target.setEffect(null));
shadowAnimation.play();
});
VBox box = new VBox(10, label, btn);
box.setPadding(new Insets(10));
Scene scene = new Scene(box, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
You can create a fake shape and use the FillTransition Interpolator to apply the shape's fill to the control background.
public static void AnimateBackgroundColor(Control control, Color fromColor,Color toColor,int duration)
{
Rectangle rect = new Rectangle();
rect.setFill(fromColor);
FillTransition tr = new FillTransition();
tr.setShape(rect);
tr.setDuration(Duration.millis(duration));
tr.setFromValue(fromColor);
tr.setToValue(toColor);
tr.setInterpolator(new Interpolator() {
#Override
protected double curve(double t) {
control.setBackground(new Background(new BackgroundFill(rect.getFill(), CornerRadii.EMPTY, Insets.EMPTY)));
return t;
}
});
tr.play();
}
I need to invert a Slider in javafx.
This is how I built the slider:
Slider slide = new Slider();
slide.setPrefHeight(height);
slide.setMin(0);
slide.setMax(100);
slide.setOrientation(javafx.geometry.Orientation.HORIZONTAL);
slide.setShowTickLabels(true);
slide.setShowTickMarks(true);
slide.setSnapToTicks(true);
This code creates a horizontally aligned slider from value 0 to 100.
But I would like to invert it. As in, place it horizontally but display values from 100 to 0. and not 0 to 100.
Any help? Thanks in advance.
One easy way to invert the slider, without actually doing it, is by using a custom label formatter. Then you just need to take the value and revert it too.
Something like this:
private final DecimalFormat df = new DecimalFormat( "#,##0.00" );
private final double MIN = 0d;
private final double MAX = 100d;
#Override
public void start(Stage primaryStage) {
VBox vbox=new VBox(20);
vbox.setPadding(new Insets(20));
Slider slide = new Slider();
slide.setPrefSize(300,100);
slide.setMin(MIN);
slide.setMax(MAX);
slide.setOrientation(javafx.geometry.Orientation.HORIZONTAL);
slide.setShowTickLabels(true);
slide.setShowTickMarks(true);
slide.setSnapToTicks(true);
slide.setLabelFormatter(new StringConverter<Double>(){
#Override
public String toString(Double object) {
return df.format(MAX-object+MIN);
}
#Override
public Double fromString(String string) {
throw new UnsupportedOperationException("Not supported yet.");
}
});
Label label = new Label("Value: ");
slide.valueProperty().addListener((ov,n,n1)->
label.setText("Value: "+(MAX-n1.doubleValue()+MIN)));
vbox.getChildren().addAll(slide, label);
Scene scene = new Scene(vbox, 400, 200);
primaryStage.setScene(scene);
primaryStage.show();
}