3

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:

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));
    }
}
Community
  • 1
  • 1
Simon S.
  • 65
  • 2
  • 7
  • Possible duplicate of [javafx 8 compatibility issues - FXML static fields](http://stackoverflow.com/questions/23105433/javafx-8-compatibility-issues-fxml-static-fields) – fabian Jun 16 '16 at 09:09
  • @fabian Removed the static modifier from ProgressBar but is not working neither. – Simon S. Jun 16 '16 at 10:00
  • Are `count` and `sum` `int`s or `long`s? Then the following question should help: http://stackoverflow.com/questions/4685450/why-is-the-result-of-1-3-0 – fabian Jun 16 '16 at 10:18
  • @fabian in fact they are different data types, but i am already casting them. ((double) count / (double) sum) * 100) I wanted to make the example as short as possible. – Simon S. Jun 16 '16 at 11:09

2 Answers2

4

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.

Community
  • 1
  • 1
James_D
  • 201,275
  • 16
  • 291
  • 322
0

I don't think you can have ProgressBar static.

My way is to have an accessor on the ProgressBar inside your controller and init the controller like this :

FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/YourController.fxml");
loader.load();

After, you can access your ProgressBar with :

loader.<YourController>getController().getProgressBar();

If you need, to access it in different classes, many other possibilities, one is to make a Singleton :

public class Singleton
{   
    private ProgressBar progressBar; 

    private Singleton()
    {}

    private static Singleton INSTANCE = new Singleton();

    public static Singleton getInstance()
    {   
        return INSTANCE;
    }

    public ProgressBar getProgressBar() {
        return progressBar;
    }

    public ProgressBar setProgressBar() {
        return progressBar;
    }
}

To call it :

Singleton.getInstance().getProgressBar();
Xavier Lambros
  • 776
  • 11
  • 19
  • So is there a way to call "loader.getController().getProgressBar();" inside my MyMain.java class as there is no FXMLLoader object? – Simon S. Jun 16 '16 at 09:08
  • Put your MyMain.java – Xavier Lambros Jun 16 '16 at 09:18
  • As newbie on stackoverflow, I can't comment directly your question. ButI think you can't make your controller directly a singleton. Singleton should be another class. FXML Controller are specific and must respect some rules. Otherwise, I don't understand why you don't have a FXMLLoader in your MyMain because in javaFX project MyMain should contain scene init... Anyway, the idea of Singleton is to stock your progressBar setted in the init of your controller and make it accessible everywhere in your project accessing it throw the Singleton which is a design pattern. – Xavier Lambros Jun 16 '16 at 10:05
  • I hate to state the obvious, but why don't you just pass the progress bar to the `doIt()` method: `main.doIt(progressBar)` and `public void doIt(ProgressBar progressBar) { ... () -> progressBar.setProgress(...) }` ? – James_D Jun 16 '16 at 12:51