Search code examples
javafxpopupprogress-bar

Display Popup with ProgressBar in JavaFX


How can I display my progress bar through pop up and automatically close if process is finished. Here is my code.

 Task<ProgressForm> task = new Task<ProgressForm>() {
            @Override 
            public ProgressForm call() throws InterruptedException{
                ProgressForm pf = new ProgressForm();
                for (int i = 1; i <= 10; i++) {
                    pf.activateProgressBar(this);
                    updateProgress(i, 10);
                }
            return pf;                
            }
        };

        task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent t) {
                ProgressForm pf = (ProgressForm)task.getValue();
                pf.getDialogStage().close();
            }
        });

        Thread th = new Thread(task);
        th.run();

Progress form class:

private final Stage dialogStage;
private final ProgressBar pb = new ProgressBar();
private final ProgressIndicator pin = new ProgressIndicator();

public ProgressForm() {
    dialogStage = new Stage();
    dialogStage.initStyle(StageStyle.UTILITY);
    dialogStage.setResizable(false);
    dialogStage.initModality(Modality.APPLICATION_MODAL);

    // PROGRESS BAR
    final Label label = new Label();
    label.setText("alerto");

    pb.setProgress(-1F);
    pin.setProgress(-1F);

    final HBox hb = new HBox();
    hb.setSpacing(5);
    hb.setAlignment(Pos.CENTER);
    hb.getChildren().addAll(pb, pin);

    Scene scene = new Scene(hb);
    dialogStage.setScene(scene);
}

public void activateProgressBar(final Task task) throws InterruptedException {
    pb.progressProperty().bind(task.progressProperty());
    pin.progressProperty().bind(task.progressProperty());
    dialogStage.show();
}

public Stage getDialogStage() {
    return dialogStage;
}

The problem with this code is

  1. if i use .show(), displaying pop up is smooth but NO PROGRESS BAR.
  2. if i use .showAndWait(), displaying pop up requires manual exit for the pop up to close BUT Progress bar displays.

Any thoughts/ideas about this?


Solution

  • The two rules for multithreading in JavaFX are:

    1. Code which modifies the UI (creates a Stage or changes properties of nodes that are part of a scene graph) must be executed on the JavaFX Application thread. Violating this rule will either throw IllegalStateExceptions or result in unpredictable behavior.
    2. Code which takes a long time to execute should be executed in a background thread (i.e. not the FX Application Thread). Violating this rule will cause the UI to become unresponsive.

    Your code violates the first rule, because it calls the ProgressForm constructor in a background thread. You should set up the UI first, show the dialog, and then start the background thread.

    Note that there is no need to repeatedly bind the progress properties of the progress bar and indicator to the progress property of the task. Once it is bound, it will remain bound until and unless you unbind it.

    It's quite hard to fix your code as it stands, because your background task doesn't actually do anything that takes any time. Here's a version of what you're doing with just a pause:

    import javafx.application.Application;
    import javafx.concurrent.Task;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.ProgressBar;
    import javafx.scene.control.ProgressIndicator;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Modality;
    import javafx.stage.Stage;
    import javafx.stage.StageStyle;
    
    public class ProgressDialogExample extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            Button startButton = new Button("Start");
            startButton.setOnAction(e -> {
                ProgressForm pForm = new ProgressForm();
    
                // In real life this task would do something useful and return 
                // some meaningful result:
                Task<Void> task = new Task<Void>() {
                    @Override
                    public Void call() throws InterruptedException {
                        for (int i = 0; i < 10; i++) {
                            updateProgress(i, 10);
                            Thread.sleep(200);
                        }
                        updateProgress(10, 10);
                        return null ;
                    }
                };
    
                // binds progress of progress bars to progress of task:
                pForm.activateProgressBar(task);
    
                // in real life this method would get the result of the task
                // and update the UI based on its value:
                task.setOnSucceeded(event -> {
                    pForm.getDialogStage().close();
                    startButton.setDisable(false);
                });
    
                startButton.setDisable(true);
                pForm.getDialogStage().show();
    
                Thread thread = new Thread(task);
                thread.start();
            });
    
            StackPane root = new StackPane(startButton);
            Scene scene = new Scene(root, 350, 75);
            primaryStage.setScene(scene);
            primaryStage.show();
    
        }
    
        public static class ProgressForm {
            private final Stage dialogStage;
            private final ProgressBar pb = new ProgressBar();
            private final ProgressIndicator pin = new ProgressIndicator();
    
            public ProgressForm() {
                dialogStage = new Stage();
                dialogStage.initStyle(StageStyle.UTILITY);
                dialogStage.setResizable(false);
                dialogStage.initModality(Modality.APPLICATION_MODAL);
    
                // PROGRESS BAR
                final Label label = new Label();
                label.setText("alerto");
    
                pb.setProgress(-1F);
                pin.setProgress(-1F);
    
                final HBox hb = new HBox();
                hb.setSpacing(5);
                hb.setAlignment(Pos.CENTER);
                hb.getChildren().addAll(pb, pin);
    
                Scene scene = new Scene(hb);
                dialogStage.setScene(scene);
            }
    
            public void activateProgressBar(final Task<?> task)  {
                pb.progressProperty().bind(task.progressProperty());
                pin.progressProperty().bind(task.progressProperty());
                dialogStage.show();
            }
    
            public Stage getDialogStage() {
                return dialogStage;
            }
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }