0

I read much about the JavaFX GUI Model, Plattform->RunLater and Threads, but I still do not figure out how to get this right. I had a JavaFX GUI which on a button click executed a process and updated a Progress Bar and Label. This was running well with Threading and Platform, but I had to Change this to an Observer Model.

I invoke a Progress Tracker in a Singleton Model, which gets updated by the class executing the process and is Observable. I implemented an Observer as well which should update the two UI Elements.

GUI Controller with Button Event

private void createKeyPressed(ActionEvent event) {

    // Make Progressbar visible
    pbKeyProgress.visibleProperty().set(true);

    if (!Check.keyFileExistant() || cbKeyOverwrite.selectedProperty().get()) {
        ProgressTracker.getTracker().addObserver(new ProgressObserver(pbKeyProgress, lblKeyProgress));
        Creator.createKey(cbKeyLength.getValue());
    } else {
    }
}

Progress Observer

public class ProgressObserver implements Observer {

    private final ProgressBar progressBar;
    private final Label statusLabel;

    public ProgressObserver(ProgressBar progressBar, Label statusLabel) {

        this.progressBar = progressBar;
        this.statusLabel = statusLabel;
    }

    @Override

    public void update(Observable o, Object o1) {
        Platform.runLater(() -> {
            System.out.println("Tracker set to "+ProgressTracker.getProgress() + " " + ProgressTracker.getStatus());
            progressBar.setProgress(ProgressTracker.getProgress());
            statusLabel.setText(ProgressTracker.getStatus());
        });

    }

}

Progress Tracker

    public synchronized void setTracker(int currentStep, String currentStatus) {

    checkInstance();
    instance.step = currentStep;
    instance.status = currentStatus;
    instance.notifyObservers();
    System.out.println(instance.countObservers());
}

Creator

public static void createKey(String length) {

    Task<Void> task;
    task = new Task<Void>() {
        @Override
        public Void call() throws Exception {
            initTracker(0,"Start");
            doStuff();
            ProgressTracker.getTracker().setTracker(1,"First");
            doStuff();
            ProgressTracker.getTracker().setTracker(2,"Second");
            // and so on

            return null;

        }
    };

    new Thread(task)
            .start();

}

The Print within the ProgressTracker gets executed. However, if I add a print within the update of the Observer nothing will be printed. If I check within the Progresstracker, the Observer Count is 1.

Why does the Observer not get notified or execute anything, even if the Notify is called? Did I get the Threading and Execution Modell wrong? The Progress Bar and the Label will also stay on their initial values.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
chenino
  • 454
  • 2
  • 7
  • 19
  • I recommend you to read [that](https://examples.javacodegeeks.com/desktop-java/javafx/javafx-concurrent-framework/) article. – mtrgn Mar 29 '17 at 14:09
  • 1
    The threading all looks correct. Probably the error is elsewhere in your code (try to create a [MCVE] if you can't figure it out). You are really reinventing the wheel, though: the [JavaFX properties pattern](http://www.oracle.com/pls/topic/lookup?ctx=javase80&id=JFXBD107) is a ready-made implementation of the Observer pattern, which is far more convenient to use than writing your own implementation. You can also call the task's `updateProgress()` and `updateMessage()` methods, and then just bind the progress bar and label to the appropriate properties of the task. – James_D Mar 29 '17 at 14:51

1 Answers1

1

Don't reinvent the wheel. The JavaFX Properties Pattern is a ready-made implementation of the Observable pattern: there is no need to implement it yourself. Additionally, Task already defines methods for updating various properties, which can be called from any thread but will schedule the actual updates on the FX Application Thread. See updateProgress() and updateMessage(), for example.

So you can do, for example:

public static Task<Void> createKey(String length) {

    Task<Void> task;
    task = new Task<Void>() {

        final int totalSteps = ... ;
        @Override
        public Void call() throws Exception {
            updateProgress(0, totalSteps);
            updateMessage("Start");
            doStuff();
            updateProgress(1, totalSteps);
            updateMessage("First");
            doStuff();
            updateProgress(2, totalSteps);
            updateMessage("Second");
            // and so on

            return null;

        }
    };

    new Thread(task)
            .start();

    return task ;

}

and

private void createKeyPressed(ActionEvent event) {

    // Make Progressbar visible
    pbKeyProgress.visibleProperty().set(true);

    if (!Check.keyFileExistant() || cbKeyOverwrite.selectedProperty().get()) {
        Task<Void> task = Creator.createKey(cbKeyLength.getValue());
        pbKeyProgress.progressProperty().bind(task.progressProperty());
        lblKeyProgress.textProperty().bind(task.messageProperty());
    } else {
    }
}
James_D
  • 201,275
  • 16
  • 291
  • 322
  • Well, this seems interesting, but i have to try and fully understand it before i can mark your answer. One Problem is, the creator has to work for CLI and GUI and i wanted to use the ProgressTracker to unify Status Updates and have 2 different Observers, one for CLI and one for GUI. I will check if this model with Propertys works for both modes and if mark this as correct. But thank you already for the help! – chenino Mar 29 '17 at 15:14
  • @chenino It should be reasonably straightforward to factor out the code that needs to be reusable in a sensible way. – James_D Mar 29 '17 at 15:15
  • yes, it seems to me like this. As said, i will verify this and will mark this as correct then. Thank You :) (Somehow @ James_D without the Space gets deleted) – chenino Mar 29 '17 at 15:19