Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX: Update ProgressBar (@FXML) from Thread

I want to update a JavaFX ProgressBar defined in an FXML file by another class, initialized in a controller thread. Currently it just does not update.

test.fxml

<ProgressBar fx:id="progressBar" prefWidth="5000.0" progress="0.0">
    <VBox.margin>
        <Insets top="3.0" />
    </VBox.margin>
</ProgressBar>

Controller.java

@FXML
public static ProgressBar progressBar = new ProgressBar(0);

MyMain main;

@FXML
private void handleStartWork() throws Exception {
    new Thread() {
        @Override
        public void run() {
            try {
                main = new MyMain();
                main.doIt();
            } catch (final Exception v) {
                // ...
            }
        }
    }.start();
}

MyMain.java

public void doIt(){
    while(...){
        Platform.runLater(() -> PoCOverviewController.progressBar.setProgress((count / sum) * 100));
    }
}

I already tried different versions in consideration of posts like:

  • ProgressBar doesn't work with a fxml file and a controller
  • How to configure Progress Bar and Progress Indicator of javaFx?

I don't know if it's the right approach to make the ProgressBar static. I just did not want to pass the Object through the workflow.

Update (Xavier Lambros answer): Now i tried it with singleton but it's still not working:

Controller.java

@FXML
public ProgressBar progressBar = new ProgressBar(0);    

private static Controller INSTANCE = new Controller();

public static Controller getInstance() {
    return INSTANCE;
}

public ProgressBar getProgressBar() {
    return progressBar;
}

MyMain.java

public void doIt(){
    while(...){
        Platform.runLater(() -> Controller.getInstance().getProgressBar()
                .setProgress((count / sum) * 100));
    }
}
like image 610
Simon S. Avatar asked Dec 03 '25 17:12

Simon S.


1 Answers

As noted in javafx 8 compatibility issues - FXML static fields, you cannot make a @FXML-annotated field static (and it makes no sense to do so: these fields are inherently properties of the specific controller instance).

To allow the doIt() method access to the progress bar, you could just pass it directly as a parameter:

@FXML
public ProgressBar progressBar ;

MyMain main;

@FXML
private void handleStartWork() throws Exception {
    new Thread() {
        @Override
        public void run() {
            try {
                main = new MyMain();
                main.doIt(progressBar);
            } catch (final Exception v) {
                // ...
            }
        }
    }.start();
}

and then

public void doIt(ProgressBar progressBar){
    while(...){
        Platform.runLater(() -> progressBar.setProgress((count / sum) * 100));
    }
}

In some circumstances, it might not make sense for the Main class to have a dependency on the JavaFX API. In that case you could just pass a function that updates the progress bar:

@FXML
public ProgressBar progressBar ;

MyMain main;

@FXML
private void handleStartWork() throws Exception {
    new Thread() {
        @Override
        public void run() {
            try {
                main = new MyMain();
                main.doIt(progressBar::setProgress);
            } catch (final Exception v) {
                // ...
            }
        }
    }.start();
}

and

public void doIt(DoubleConsumer progressUpdate){
    while(...){
        Platform.runLater(() -> progressUpdate.accept((count / sum) * 100));
    }
}

Note that you haven't shown what's happening in your while loop: if you are submitting too many runnables to the FX Application Thread, you might "flood" it and prevent it from updating in a reasonable time. You might consider using a Task, which has specific API for updating a progress field to which the progress bar's progress property can be bound. If it's still not working, you should edit your question to include a MCVE.

like image 189
James_D Avatar answered Dec 06 '25 07:12

James_D