Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX refresh Progress Bar within a method

I have a javafx Progressbar in my UI and then I have a for loop. In every iteration of the for loop this happens:

progressVal += 5.0;
progress.setProgress(progressVal);

And after the whole loop, this happens:

progress.setProgress(100);

I now have recognized that the UI controls are only refreshed after the method has finished, so the progress bar wont update until the end and than would be set to 100 imidiatly.

So is it possible so somehow force the UI update, even when the method isn`t finished completly? Or how else could I do this?

like image 702
Jakob Abfalter Avatar asked Jun 20 '14 11:06

Jakob Abfalter


People also ask

How to load Progress Bar in JavaFX?

Use the code fragment in Example 17-1 to insert the progress controls in your JavaFX application. ProgressBar pb = new ProgressBar(0.6); ProgressIndicator pi = new ProgressIndicator(0.6); You can also create the progress controls without parameters by using an empty constructor.

How do you refresh a scene in JavaFX?

The JavaFX scene can be forcibly refreshed by changing the width or height of the Scene by a fractional number of pixels (for example, 0.001). This change forces the background windowing and rendering services to recalculate layout and rendering requirements for the scene graph.

Does JavaFX provide a progress monitor control?

ProgressIndicator is a part of JavaFX package. It's a circular control which is used for indicating progress, either infinite or finite.


2 Answers

If what you do in your loop takes a long time and should not be executed on the JavaFX application thread, (which it probably does or you probably wouldn't have this question), then you should probably run the loop in a Task, on a different thread, updating the task's progress as the loop progresses and binding the progress bar's value to the task's progress.

progress

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ProgressFeedback extends Application {
    private static final double EPSILON = 0.0000005;
    @Override
    public void start(Stage stage) throws Exception {
        final Task<Void> task = new Task<Void>() {
            final int N_ITERATIONS = 100;

            @Override
            protected Void call() throws Exception {
                for (int i = 0; i < N_ITERATIONS; i++) {
                    updateProgress(i + 1, N_ITERATIONS);
                    // sleep is used to simulate doing some work which takes some time....
                    Thread.sleep(10);
                }

                return null;
            }
        };

        final ProgressBar progress = new ProgressBar();
        progress.progressProperty().bind(
                task.progressProperty()
        );
        // color the bar green when the work is complete.
        progress.progressProperty().addListener(observable -> {
            if (progress.getProgress() >= 1 - EPSILON) {
                progress.setStyle("-fx-accent: forestgreen;");
            }
        });

        // layout the app
        final StackPane layout = new StackPane(progress);
        layout.setPadding(new Insets(10));
        stage.setScene(new Scene(layout));
        stage.show();

        final Thread thread = new Thread(task, "task-thread");
        thread.setDaemon(true);
        thread.start();
    }

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

what if the task makes call to a method that does only a single job , and do not want it to be called again and again?

Then remove the loop and invoke updateProgress through multiple calls within the job as the job progresses. The loop is only present in this sample for demonstration purposes and because this is what the original question specifically asked about.

For example lets say your job has 3 stages: fetch from database, perform calculation and accumulate results. Each stage taking an estimated 30 percent, 60 percent and 10 percent of total time respectively, then you can do something like this within the call() body of your task:

updateProgress(0, 1.0);
Data data = db.fetchData(query);
updateProgress(0.3, 1.0);
ProcessedData processed = calculator.process(data);
updateProgress(0.9, 1.0);
Result result = accumulator.reduce(processed);
updateProgress(1.0, 1.0);
like image 174
jewelsea Avatar answered Oct 12 '22 17:10

jewelsea


Quotation from Java Doc:

The actual progress of the ProgressIndicator. A negative value for progress indicates that the progress is indeterminate. A positive value between 0 and 1 indicates the percentage of progress where 0 is 0% and 1 is 100%. Any value greater than 1 is interpreted as 100%.

That means in simple words, you must divide you value with 100, or you just increment the value by 0.05 instead of 0.5.

Happy Coding,
Kalasch

like image 30
Kalaschni Avatar answered Oct 12 '22 15:10

Kalaschni