0

When a user clicks a button on the UI I want the button to be hidden and then I want a background task to launch that does a bunch of stuff.

EDIT: How do I ensure that the button is hidden before any other blocking task starts?

class startTask implements Callable<Void>{
    @Override
    public Void call() {
        //.....Do a bunch of stuff.....
        return null;
    }
}

 public void hitTheButton() throws ExecutionException, InterruptedException {
    hideTheButton();
    //this only happens first IF i remove the .get()

    StartTask startTask = new StartTask();
    ExecutorService ex = Executors.newSingleThreadExecutor();
    Future<?> exFuture = ex.submit(startSyncTask);
    exFuture.get();
    //.... Now we can continue with the program....        
}

public void hideThebutton(){
    FadeTransition ft = new FadeTransition(Duration.millis(10), myButton);
    ft.setFromValue(1);
    ft.setToValue(0);
    ft.setCycleCount(1);
    ft.setAutoReverse(false);
}

I have also tried putting .isDone() in a while loop to no avail.

I understand that the UI is blocked by the .get() but why wouldn't the hide method be completed before I even instantiate the Executor??

Daniel H.
  • 422
  • 2
  • 15
  • 2
    What's the point of running `StartTask` in a separate thread if you immediately block on its result using `get`? You might have just as well run the task in the same thread. – Malt Oct 29 '17 at 20:36
  • because I am trying to figure out how to hide the button. – Daniel H. Oct 29 '17 at 22:32
  • So just remove `exFuture.get()`. – James_D Oct 30 '17 at 01:18
  • So If i unblock in that one spot then the program will continue on without waiting for network calls and database items to be set. The program has to stop here and get data. No matter how I try though any blocking at this point also blocks the hideTheButton method even though I call it first. I am so confused. – Daniel H. Oct 30 '17 at 01:24
  • "The program has to stop here." That just doesn't really make sense. What you're stopping is the FX Application thread, and if that stops, then the UI doesn't get rendered. I think what you mean is that there is code you want to execute when the work done by your task completes. Either put that code at the end of the task's `call()` method (wrapped in `Platform.runLater(...)` if it updates the UI), or use a `javafx.concurrent.Task` and use the `onSucceeded` handler. (Basically, you should never block the FX Application Thread, as you do by calling `exFuture.get()`.) – James_D Oct 30 '17 at 01:32
  • Maybe see https://stackoverflow.com/questions/30249493/using-threads-to-make-database-requests – James_D Oct 30 '17 at 01:35
  • 1
    I guess one other point, maybe this helps: *"No matter how I try though any blocking at this point also blocks the `hideTheButton` method even though I call it first."* is not correct. Your `hideTheButton()` method ***does*** complete before you launch the task, whether or not you call `exFuture.get()`. However, nothing in `hideTheButton()` forces the UI to be rendered immediately (and, before you ask, there is no API to force the UI to be rendered, by design). All you do is change some properties of the button, which will have a visual effect *next time the UI is rendered*. – James_D Oct 30 '17 at 13:11
  • @James_D Thanks for all of the input. I grew up coding Javascript and PHP so am use to the two layers being separate and fully controlled. This is so odd to me that I can not hide a UI element and then execute a portion of code. – Daniel H. Oct 30 '17 at 14:43

1 Answers1

3

You wrote:

I understand that the UI is blocked by the .get() but why wouldn't the hide method be completed before I even instantiate the Executor??

The JavaFX UI is updated "in between" the code you call. What you actually do is ask the JavaFX runtime to execute some change, like hiding, enabling, part of a transition. The runtime will mark at that point that it has to do something with that node, but will wait until the next UI tick is executed.

With your exFuture.get(); you prevent the next UI tick to be executed, and thus no update of your user interface. If you use the concurrency framework you can create a Task and use its various state moments to manipulate your button node.

Button button;
Task<?> task;
task.setOnSucceeded(e -> showButton(button));
task.setOnFailed(e -> showButton(button));
hideButton(button);
executor.submit(task);
M. le Rutte
  • 3,525
  • 3
  • 18
  • 31
  • so how do I hide the button and then execute the background thread wait for it to complete and then continue with the program? – Daniel H. Oct 29 '17 at 22:32
  • 1
    @DanielH. You don't. You hide the button; you start the task; you have the *task* unhide it when it's done. You might want to disable some other UI elements as well. – user207421 Oct 30 '17 at 00:21
  • Thanks for trying to help. It's hard for me to believe I can not hide a node before a blocking Task takes over and prevents updates to the UI. – Daniel H. Oct 30 '17 at 01:30
  • 1
    @DanielH. Your `hitTheButton()` method, assuming it is an event handler method (or invoked from one), is executed on the FX Application Thread. That is the thread that is responsible for rendering the UI. If you block that thread, the UI can't be rendered until the thread is unblocked. As you've actually already discovered, you can hide the node before the task completes, since the task runs on a background thread. You just have to refrain from blocking the FX Application Thread. – James_D Oct 30 '17 at 01:48
  • @M. le Rutte. Yes! That pattern did it. And it improves my design by accounting for failure, which I had not done. Thank you!! button hides right when clicked and background thread does its thing! – Daniel H. Oct 30 '17 at 15:29