JavaFX: ProgressBar with timeline; reset both if completed - javafx

I am building an application which is an exam. All the questions are shown on different screens, all with a progressbar showing how much time the user has left.
When the progressbar is full, the screen with another question is shown and the progressbar and timeline should be resetted.
When the time is up, the next screen is shown and the timeline resets, but the progressbar (shown in code as 'PRGB') remains full and it will not reset to an empty progressbar. This is my code:
public void timeBar() throws Exception{
Timeline timeline = new Timeline(
new KeyFrame(Duration.ONE, new KeyValue(PRGB.progressProperty(), 0)),
new KeyFrame(Duration.seconds(Main.time), e-> {
}, new KeyValue(PRGB.progressProperty(), 1))
);
timeline.setCycleCount(1);
timeline.play();
timeline.setOnFinished(event -> {
Main.setPane(questionNumber);
questionNumber++;
timeline.play();
//in here the progressbar has to be resetted
});
}
Update:
I have deleted my ProgressBar from SceneBuilder, and made a new one on which I then run the code below (with a few alterations taking by your suggestions). But now the progressbar does reset it self, but it does not show the progress: it stays empty.
public void timeBar() throws Exception{
Timeline timeline = new Timeline(
//I only use two keyframes instead of three
new KeyFrame(Duration.seconds(Main.time), e-> {
}, new KeyValue(PRGB.progressProperty(), 1))
);
timeline.setCycleCount(1);
timeline.play();
timeline.setOnFinished(event -> {
Main.setPane(questionNumber);
questionNumber++;
//The setProgress is added
PRGB.setProgress(0.0F);
timeline.play();
});
}

This can probably be done better. In this example, 3 seconds is given for each question with 1 second at the end of each question to load the next question. Code from here.
import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.PauseTransition;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application
{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage)
{
Label lblCurrentQuestion = new Label();
ProgressBar prgb = new ProgressBar(0);
int numberOfQuestions = 5;//Number of questions
int timeForEachQuestion = 3;//3 second to do each questions
AtomicInteger currentProgressCounter = new AtomicInteger(0);
AtomicInteger currentQuestionCounter = new AtomicInteger(1);
Timeline fiveSecondsWonder = new Timeline(new KeyFrame(Duration.seconds(1), (event) -> {
if (currentProgressCounter.get() == timeForEachQuestion + 1) {
//Go to next question!
prgb.setProgress(0);
currentProgressCounter.set(0);
currentQuestionCounter.incrementAndGet();
}
lblCurrentQuestion.setText("Question " + currentQuestionCounter.get());
prgb.setProgress(currentProgressCounter.getAndIncrement() / (double) timeForEachQuestion);
}));
fiveSecondsWonder.setCycleCount(numberOfQuestions * (timeForEachQuestion + 1));
fiveSecondsWonder.play();
fiveSecondsWonder.setOnFinished(event -> {
PauseTransition wait = new PauseTransition(Duration.seconds(1));
wait.setOnFinished((e) -> {
/*YOUR METHOD*/
lblCurrentQuestion.setText("Quiz done!");
prgb.setProgress(0);
wait.playFromStart();
});
wait.play();
});
Scene scene = new Scene(new StackPane(new VBox(lblCurrentQuestion, prgb)), 500, 500);
stage.setScene(scene);
stage.show();
}
}

You have a call to timeline.play() in your setOnFinished() call. Remove that call so that the progress bar doesn't get reset to full.
Also, you should reset your progress bar by doing progressBar.setProgress(0.0F); in the setOnFinished()

Related

JavaFX bounds issue?

So, using JAVAfx, which I painfully downloaded, I need to be able to move this ball, but not let it leave the bounds. Right now, I have it all set up, it just leaves the area. What am i doing wrong? Any tutors don't know anything about JavaFX, so I'm having trouble getting past this. Right now I tried to work it out, but keep getting this error Cannot Make a Static Reference to Bound
'''
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
import javafx.geometry.Bounds;
public class MoveBall extends Application {
// create buttons
Button left = new Button();
Button right = new Button();
Button up = new Button();
Button down = new Button();
Circle ball = new Circle(30, 30, 30);
// action event class
EventHandler<ActionEvent> event = new EventHandler<ActionEvent>() {
// handle them
#Override
public void handle(ActionEvent event) {
// movement of ball
if (event.getSource().equals(left) && ball.getLayoutX() >= (Bounds.getMaxX() + ball.getRadius())) {
ball.setCenterX(ball.getCenterX() - 5);
}
if (event.getSource().equals(right)) {
ball.setCenterX(ball.getCenterX() + 5);
}
if (event.getSource().equals(up)) {
ball.setCenterY(ball.getCenterY() - 5);
}
if (event.getSource().equals(down) && ball.getCenterY() < 400)) {
ball.setCenterY(ball.getCenterY() + 5);
}
}
};
// main method
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Ball Movement");
ball.setStroke(Color.BLACK);
ball.setFill(null);
// button location
left.setLayoutX(100);
left.setLayoutY(210);
left.setText("Left");
// button event
left.setOnAction(event);
// similar for all button
right.setLayoutX(150);
right.setLayoutY(210);
right.setText("Right");
right.setOnAction(event);
// button location
up.setLayoutX(200);
up.setLayoutY(210);
up.setText("Up");
// button event
up.setOnAction(event);
// similar for all button
down.setLayoutX(250);
down.setLayoutY(210);
down.setText("Down");
down.setOnAction(event);
Group root = new Group();
// add to a group
root.getChildren().add(left);
root.getChildren().add(right);
root.getChildren().add(up);
root.getChildren().add(down);
root.getChildren().add(ball);
primaryStage.setScene(new Scene(root, 400, 250, Color.WHITE));
// show the scene
primaryStage.show();
}
}
'''

Automatically resizing the window based on dynamic content

I am checking for a feature to automatically resize the window based on the content. I am already aware of Window.sizeToScene() method. But this is so cumbersome that I need to take care in every place to call the sizeToScene(). For example, when I add/remove nodes, when I expanded the titlePanes, etc...
Can someone let me know if it is possible to automatically adjust the window based on the content. I am looking this feature for both normal and transparent windows.
Any hints/suggestion is highly appreciated.
Please find below a quick demo of what I am looking for. Everything works as expected if I consider calling sizeToScene(). I am targeting for a way to get the same behavior without calling sizeToScene for every scenario.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TransparentWindowResizeDemo extends Application {
#Override
public void start(Stage stage) throws Exception {
VBox root = new VBox();
root.setSpacing(15);
root.setAlignment(Pos.CENTER);
Scene sc = new Scene(root, 300, 300);
stage.setScene(sc);
stage.show();
Button showNormal = new Button("Show Normal Window");
showNormal.setOnAction(e -> showWindow(false));
Button showTransparent = new Button("Show Transparent Window");
showTransparent.setOnAction(e -> showWindow(true));
root.getChildren().addAll(showNormal, showTransparent);
}
private void showWindow(boolean isTransparent) {
Stage window = new Stage();
VBox root = new VBox();
root.setStyle("-fx-background-color: grey; -fx-border-width:2px;-fx-border-color:black;");
Scene scene = new Scene(root, Color.TRANSPARENT);
window.setScene(scene);
VBox layout = new VBox();
layout.setSpacing(5);
layout.setPadding(new Insets(5));
CheckBox sizeToScene = new CheckBox("sizeToScene");
sizeToScene.setSelected(true);
StackPane box = new StackPane();
box.setStyle("-fx-background-color:yellow; -fx-border-width:1px;-fx-border-color:black;");
box.setMinSize(200, 100);
box.setMaxSize(200, 100);
Button add = new Button("Add Pane");
Button remove = new Button("Remove Pane");
remove.setDisable(true);
add.setOnAction(e -> {
layout.getChildren().add(box);
add.setDisable(true);
remove.setDisable(false);
if (sizeToScene.isSelected()) {
window.sizeToScene();
}
});
remove.setOnAction(e -> {
layout.getChildren().remove(box);
add.setDisable(false);
remove.setDisable(true);
if (sizeToScene.isSelected()) {
window.sizeToScene();
}
});
HBox btnLayout = new HBox();
btnLayout.setSpacing(5);
btnLayout.getChildren().addAll(add, remove);
StackPane tpContent = new StackPane();
tpContent.setStyle("-fx-background-color:pink; -fx-border-width:1px;-fx-border-color:black;");
tpContent.setMinSize(200, 100);
tpContent.setMaxSize(200, 100);
TitledPane tp = new TitledPane("Titled Pane", tpContent);
tp.setAnimated(false);
tp.expandedProperty().addListener((obs, oldVal, newVal) -> {
if (sizeToScene.isSelected()) {
window.sizeToScene();
}
});
if (isTransparent) {
window.initStyle(StageStyle.TRANSPARENT);
StackPane close = new StackPane();
close.setMaxWidth(30);
close.setStyle("-fx-background-color:red;");
close.getChildren().add(new Label("X"));
close.setOnMouseClicked(e -> window.hide());
DoubleProperty x = new SimpleDoubleProperty();
DoubleProperty y = new SimpleDoubleProperty();
StackPane header = new StackPane();
header.setOnMousePressed(e -> {
x.set(e.getSceneX());
y.set(e.getSceneY());
});
header.setOnMouseDragged(e -> {
window.setX(e.getScreenX() - x.get());
window.setY(e.getScreenY() - y.get());
});
header.setStyle("-fx-border-width:0px 0px 2px 0px;-fx-border-color:black;");
header.setMinHeight(30);
header.setAlignment(Pos.CENTER_RIGHT);
Label lbl = new Label("I am draggable !!");
lbl.setStyle("-fx-text-fill:white;-fx-font-weight:bold;");
StackPane.setAlignment(lbl,Pos.CENTER_LEFT);
header.getChildren().addAll(lbl,close);
root.getChildren().add(header);
}
layout.getChildren().addAll(sizeToScene, btnLayout, tp);
root.getChildren().add(layout);
window.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
Edit :: This is different from what I already found in other questions or with the link in the comment. The solution that is specified is what I am aware of and I already mentioned that in the question. I have a heavy screen where it contain many nodes in differnt hirerchy. I am checking for any generic solution to include in one place rather that calling from every node add/remove scenario.
I would say this is not a graceful solution (it's more like a hack), but at least it has worked using your example:
VBox root = new VBox() {
private boolean needsResize = false;
#Override
protected void layoutChildren()
{
super.layoutChildren();
if (!needsResize) {
needsResize = true;
Platform.runLater(() -> {
if (needsResize) {
window.sizeToScene();
needsResize = false;
}
});
}
}
};
Of course, you should add in the sizeToScene.isSelected() part, and you could also make this an actual subclass.

Alert box is not showing in the center of the application using JavaFX [duplicate]

This question already has answers here:
Center stage on parent stage
(2 answers)
Closed 4 years ago.
I am trying to pop-up the box in the center of the application when I resize the application or move it.
I tried with css alignment and also using Java. Is it possible if I don't use pane and directly add box in the scene?
Here is my code:
public Boolean call(String question) {
final Stage dialogStage = new Stage(StageStyle.UNDECORATED);
dialogStage.initModality(Modality.WINDOW_MODAL);
dialogStage.initOwner(owner);
dialogStage.setTitle("ConfirmTitle"); // WIP, waiting for the strings&trans
final Button ok = new Button(
nmsGuiContainer.getI18nService().getMessage("com.mlnms.gui.fmwk.main.container.ok")); // WIP,
// waiting
// for
// the
// strings&trans
ok.getStyleClass().add(HTML_POPUP_BUTTON_STYLE);
final Button cancel = new Button(
nmsGuiContainer.getI18nService().getMessage("com.mlnms.gui.fmwk.main.container.cancel")); // WIP,
// waiting
// for the
// strings&trans
cancel.getStyleClass().add(HTML_POPUP_BUTTON_STYLE);
final Text text = new Text(question);
text.getStyleClass().add(HTML_POPUP_STYLE);
final Insets ins = new Insets(10);
final VBox box = new VBox();
box.setAlignment(Pos.BOTTOM_CENTER);
box.setSpacing(10);
box.setPadding(ins);
final HBox buttons = new HBox(10);
buttons.getChildren().addAll(ok, cancel);
buttons.setAlignment(Pos.CENTER);
buttons.setPadding(ins);
box.getChildren().addAll(text, buttons);
box.getStyleClass().add(HTML_POPUP_STYLE);
StackPane pane = new StackPane();
pane.setAlignment(box, Pos.CENTER);
pane.getChildren().add(box);
Scene scene = new Scene(pane);
try {
URL javafxCss = nmsGuiContainer.getBundleContext().getBundle()
.getResource(NmsGuiContainer.JAVAFX_CSS_URL);
scene.getStylesheets().add(javafxCss.toExternalForm());
} catch (Exception e) {
LOGGER.error("Cannot load the CSS file for JavaFX components ", e);
}
dialogStage.setScene(scene);
ok.setCancelButton(false);
final boolean[] res = new boolean[1];
ok.setOnAction(new CloseDialogHandler(dialogStage, res));
cancel.setCancelButton(true);
cancel.setOnAction(new CloseDialogHandler(dialogStage, null));
dialogStage.centerOnScreen();
nmsGuiContainer.fadeContainer();
dialogStage.showAndWait();
nmsGuiContainer.unfadeContainer();
return res[0];
}
Here is a screenshot of the alertbox:
The Stage.initOwner() method does exactly what you need. While you do call it in your example code, I do not know what owner you are passing to it.
Here is a sample that demonstrates how to do this.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
Button btnShowAlert = new Button("Show Alert!");
// Set the action to show the alert
btnShowAlert.setOnAction(e -> {
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.setHeaderText("This is centered over the main window!");
alert.setContentText("Move the main window and show the alert again!");
alert.initOwner(primaryStage);
alert.showAndWait();
});
root.getChildren().add(btnShowAlert);
primaryStage.setTitle("Centered Alerts");
primaryStage.setScene(new Scene(root));
primaryStage.setWidth(500);
primaryStage.setHeight(300);
primaryStage.show();
}
}

JavaFX 8 Changing the opacity of the stage does not work with StageStyle.TRANSPARENT (bug or my fault?)

I'm trying to build a FadeIn/Out animation on a window (stage). If the mouse moves into the stage it should fade in and if the mouse leaves it should fade out.
I created a Timeline that modifies the stage.opacityProperty() to achieve this. I ran into problems when I set the stage style transparent like this stage.initStyle(StageStyle.TRANSPARENT);. If I do so, the fading will not be visible. The Timeline plays the animation, but the opacity change will not be rendered by JavaFX. When setting the stageStyle to default, everything works fine and the window plus its decoration will fade in and out.
I want this effect to work in TRANSPARENT stage style so i tried the following:
I put a label onto the scene and change its textproperty in another Timeline. I now update the label text every 400msecs. If i do so, the opacity change will be rendered on every label-change.
This brings me to the conclusion, that modifying the opacity in TRANSPARENT stage style, will not result in a repaint of the stage.
Modifying the label text will result in repaint. Does this mean, that i cannot fade a stage in TRANSPARENT stage style, if the content does not change?
Is this a bug or am I doing something wrong?
I've made an SSCCE that reproduces the problem. If you remove the line stage.initStyle(StageStyle.TRANSPARENT); the fadeIn/out animation will run smoothly.
package de.schuette.jfx.stage_opacity_bug;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class FadeApp extends Application {
public static void main(String[] args) {
Application.launch(args);
}
private Label label;
#Override
public void start(Stage stage) {
if (stage == null)
throw new IllegalArgumentException("No stage was set.");
this.label = new Label("HALLO WELT");
Scene scene = new Scene(label, 300, 300);
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE) {
stage.close();
}
});
stage.setScene(scene);
stage.setOpacity(1);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setTitle("Opacity change does result in repaint when stage style is transparent.");
stage.setAlwaysOnTop(true);
stage.show();
Platform.runLater(() -> {
Timeline t = new Timeline(new KeyFrame(Duration.millis(0),
new KeyValue(stage.opacityProperty(), 1)), new KeyFrame(
Duration.millis(500), new KeyValue(stage.opacityProperty(),
0)));
t.setAutoReverse(true);
t.setCycleCount(Timeline.INDEFINITE);
t.playFromStart();
});
Platform.runLater(() -> {
Timeline t = new Timeline(new KeyFrame(Duration.millis(400), e -> {
label.textProperty().set(String.valueOf(Math.random()));
}));
t.setCycleCount(Timeline.INDEFINITE);
t.playFromStart();
});
}
}
I'm currtently working with
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Windows 7 x64 Professional
With the help of the JavaFX developer team I was able to find a workaround for this problem. Using a custom linear interpolator that changes the scene's fill property and immediately change it back to its original value will cause a repaint on the stage. This is done by the "bugFixInterpolator" in the code below:
package de.schuette.jfx.stage_opacity_bug;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.input.KeyCode;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.util.Duration;
public class FadeApp extends Application {
public static void main(String[] args) {
Application.launch(args);
}
private Label label;
/*
* (non-Javadoc)
*
* #see javafx.application.Application#start(javafx.stage.Stage)
*/
#Override
public void start(Stage stage) {
if (stage == null)
throw new IllegalArgumentException("No stage was set.");
this.label = new Label("HELLO WORLD");
Scene scene = new Scene(label, 300, 300);
scene.setOnKeyPressed(e -> {
if (e.getCode() == KeyCode.ESCAPE) {
stage.close();
}
});
stage.setScene(scene);
stage.setOpacity(1);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setTitle("Opacity change does result in repaint when stage style is transparent.");
stage.setAlwaysOnTop(true);
stage.show();
Interpolator bugFixInterpolator = new Interpolator() {
#Override
protected double curve(double t) {
Paint fill = scene.getFill();
scene.setFill(Color.RED);
scene.setFill(fill);
return t;
}
#Override
public String toString() {
return "Interpolator.LINEAR";
}
};
Timeline t = new Timeline(new KeyFrame(Duration.millis(0),
new KeyValue(stage.opacityProperty(), 1, bugFixInterpolator)),
new KeyFrame(Duration.millis(500), new KeyValue(stage
.opacityProperty(), 0, bugFixInterpolator)));
t.setAutoReverse(true);
t.setCycleCount(Timeline.INDEFINITE);
t.playFromStart();
t = new Timeline(new KeyFrame(Duration.millis(400), e -> {
label.textProperty().set(String.valueOf(Math.random()));
}));
t.setCycleCount(Timeline.INDEFINITE);
t.playFromStart();
}
}

JavaFX button.arm() not working. Are there other ways to?

I am trying to auto press a button in my application so that it triggers a function.
As per the java docs, the .arm() button should do this. But it does not.
I also tried .fire(), but with no luck.
Any ideas on how to get a button be clicked on its own?
button.fire() works and is the right method to use to trigger an automated button press.
Here is a short sample which demonstrates it's use:
A timeline is started which automatically fires a button every second. When the button is fired it increments the counter label below the button.
import javafx.animation.*;
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Clicker extends Application {
#Override
public void start(Stage stage) throws Exception {
Label counter = new Label("0");
Button clicker = new Button("Auto-clicked");
clicker.setOnAction(event ->
counter.setText(
(1 + Integer.parseInt(counter.getText())) + ""
)
);
Timeline ticker = new Timeline(
new KeyFrame(
Duration.seconds(1),
event -> clicker.fire()
)
);
ticker.setCycleCount(1_000_000);
ticker.play();
VBox layout = new VBox(10, clicker, counter);
layout.setAlignment(Pos.CENTER);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(Clicker.class);
}
}

Resources