Javafx Several GUI Threads - Progress Indicator

Zon picture Zon · Nov 10, 2013 · Viewed 7.3k times · Source

In javafx gui processes are done on a separate thread. Putting progress indicator on a background thread with Service and Task is not allowed as it is not an FX thread and indicator is FX element. Is it possible to create multiple gui threads in javafx?

Or is there another way to make progress indicator keep rolling, when other gui elements are being loaded? Currently it starts rolling, then stucks, until the pane is loaded.

@FXML
public void budgetShow(ActionEvent event) {
    progressIndicator = new ProgressIndicator(-1.0);
    rootPane.getChildren().add(progressIndicator);
    progressIndicator.setVisible(true);
    progressIndicator.toFront();

    threadBudgetShow().start();
}

public Service<Void> threadBudgetShow() {
Service<Void> service = new Service<Void>() {
    @Override
    protected Task<Void> createTask() {
        return new Task<Void>() {
            @Override
            protected Void call() throws Exception {

                // Background Thread operations.                    
                final CountDownLatch latch = new CountDownLatch(1);

                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            // FX Thread opeartions.
                            // budgetAnchorPane - reload.
                            if (budgetAnchorPane == null || !budgetAnchorPane.isVisible()) {
                                budgetAnchorPane = new BudgetAnchorPane();
                                rootPane.getChildren().add(budgetAnchorPane);
                                budgetAnchorPane.setVisible(true);
                                budgetAnchorPane.getChildren().remove(budgetAnchorPane.budgetTypeComboBox);
                                budgetAnchorPane.budgetTypeComboBox = new BudgetTypeCombobox();
                                budgetAnchorPane.getChildren().add(budgetAnchorPane.budgetTypeComboBox);
                            }
                        } finally {
                            rootPane.getChildren().remove(progressIndicator);
                            latch.countDown();
                        }
                    }
                });
                latch.await();
                // Other background Thread operations.
                return null;
            }
        };
    }
};
return service;
}

Answer

jewelsea picture jewelsea · Nov 12, 2013

Indeterminate Progress Indicators

progress indicator keep rolling

I think by this you mean an indeterminate progress indicator.

Progress indicators start in an indeterminate state by default and you can change the indicator back to an indeterminate state at any time by setting it's progress to indeterminate:

progressIndicator.setProgress(ProgressIndicator.INDETERMINATE);

Indeterminate Progress Indicators and Tasks

As progress by default is indeterminate, if you don't update the progress of a task until the task is done, then a progress indicator bound to task progress will remain indeterminate while the task is running.

Sample

The progress indicator in this sample will just be a set of spinning dots indicating indeterminate progress until the task is complete.

progress

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class ProgressTracker extends Application {

    final int N_SECS = 10;

    @Override
    public void start(Stage stage) throws Exception {
        Task task = createTask();

        stage.setScene(
            new Scene(
                createLayout(
                    task
                )
            )
        );
        stage.show();

        new Thread(task).start();
    }

    private Task<Void> createTask() {
        return new Task<Void>() {
            @Override public Void call() {
                for (int i=0; i < N_SECS; i++) {
                    if (isCancelled()) {
                        break;
                    }
                    // uncomment updateProgress call if you want to show progress
                    // rather than let progress remain indeterminate.
                    // updateProgress(i, N_SECS);
                    updateMessage((N_SECS - i) + "");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        return null;
                    }
                }

                updateMessage(0 + "");
                updateProgress(N_SECS, N_SECS);

                return null;
            }
        };
    }

    private HBox createLayout(Task task) {
        HBox layout = new HBox(10);

        layout.getChildren().setAll(
            createProgressIndicator(task),
            createCounter(task)
        );

        layout.setAlignment(Pos.CENTER_RIGHT);
        layout.setPadding(new Insets(10));

        return layout;
    }

    private ProgressIndicator createProgressIndicator(Task task) {
        ProgressIndicator progress = new ProgressIndicator();

        progress.progressProperty().bind(task.progressProperty());

        return progress;
    }

    private Label createCounter(Task task) {
        Label counter = new Label();

        counter.setMinWidth(20);
        counter.setAlignment(Pos.CENTER_RIGHT);
        counter.textProperty().bind(task.messageProperty());
        counter.setStyle("-fx-border-color: forestgreen;");

        return counter;
    }

    public static void main(String[] args) {
        launch(args);
    }
}