Karan Bijani
Karan Bijani

Reputation: 85

JavaFX update progress bar and wait for threads to complete

I'm trying to update a progress bar in Java FX. My first problem was that the window said "not responding" instead of actually updating. It just froze and then after the tasks were done, the progress bar became full. So I found out that I had to use multithreading and implemented it like this.

overallList.clear();
progressbar.setprogress(0);

for(Object obj : list) {
    class ThreadProgress implements Runnable { // inner class
        public void run() {
            thisList = scrape(obj);
            overallList.add(thisList);
            progressbar.setProgress(progressbar.getProgress() + (double)1/size);
        }
    }

    Thread current = new Thread(new ThreadProgress());
    current.start();
}

textAreaConsole.setText("Total number of things:" + overallList.size());

But now the problem is the final line prints "Total number of things: 0" because the threads don't actually finish executing before the machine runs the final line. Then I found out multiple ways to fix this, specifically using join() or ExecutorService. I implemented join() like this.

overallList.clear();
progressbar.setprogress(0);
List<Thread> threads = new ArrayList<Thread>();

for(Object obj : list) {
    class ThreadProgress implements Runnable { // inner class
        public void run() {
            thisList = scrape(obj);
            overallList.add(thisList);
            progressbar.setProgress(progressbar.getProgress() + (double)1/size);
        }
    }

    Thread current = new Thread(new ThreadProgress());
    current.start();
    threads.add(current);
}

for(Thread thread : threads) thread.join(); // with a try-catch loop

textAreaConsole.setText("Total number of things:" + overallList.size());

But this brings me back to the original problem, the window says "not responding" again. Same thing happened with ExecutorService. I have no idea what to do now.

Upvotes: 4

Views: 5426

Answers (1)

Zephyr
Zephyr

Reputation: 10253

See the example application below. It provides a simple ProgressBar and a Label to demonstrate how to update the UI with the progress of a background Task.

The code is commented as well.

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
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.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ProgressBarExample extends Application {

    // Create our ProgressBar
    private ProgressBar progressBar = new ProgressBar(0.0);

    // Create a label to show current progress %
    private Label lblProgress = new Label();

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

    @Override
    public void start(Stage primaryStage) {

        // Simple interface
        VBox root = new VBox(5);
        root.setPadding(new Insets(10));
        root.setAlignment(Pos.CENTER);

        // Button to start the background task
        Button button = new Button("Start");
        button.setOnAction(event -> startProcess());

        // Add our controls to the scene
        root.getChildren().addAll(
                progressBar,
                new HBox(5) {{
                    setAlignment(Pos.CENTER);
                    getChildren().addAll(
                            new Label("Current Step:"),
                            lblProgress
                    );
                }},
                button
        );

        // Here we will

        // Show the Stage
        primaryStage.setWidth(300);
        primaryStage.setHeight(300);
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    private void startProcess() {

        // Create a background Task
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() throws Exception {

                // Set the total number of steps in our process
                int steps = 1000;

                // Simulate a long running task
                for (int i = 0; i < steps; i++) {

                    Thread.sleep(10); // Pause briefly

                    // Update our progress and message properties
                    updateProgress(i, steps);
                    updateMessage(String.valueOf(i));
                }
                return null;
            }
        };

        // This method allows us to handle any Exceptions thrown by the task
        task.setOnFailed(wse -> {
            wse.getSource().getException().printStackTrace();
        });

        // If the task completed successfully, perform other updates here
        task.setOnSucceeded(wse -> {
            System.out.println("Done!");
        });

        // Before starting our task, we need to bind our UI values to the properties on the task
        progressBar.progressProperty().bind(task.progressProperty());
        lblProgress.textProperty().bind(task.messageProperty());

        // Now, start the task on a background thread
        new Thread(task).start();
    }
}

Edit: Added the setOnFailed() and setOnSucceeded() methods.

Upvotes: 12

Related Questions