0

I'm trying to make my startCreation Button display a Text object that just says "Working..."

However, when I try and do it like this:

        startCreation.setOnAction(e ->
            {
                tWorking.setVisible(true);
                //This is where my method would be called
                tWorking.setVisible(false);
            });

the Text is never really visible, even though during debugging it changes to true.

I think this has something to do with the Thread not updating the Stage, but I am not sure about that either.

Does anyone have a solution?

Leftyx
  • 1
  • 1
  • `Platform.invokeLater(() -> { ... })` – Joop Eggen Jan 09 '18 at 10:31
  • If your method is "really fast", then the text will be not set to visible, as the `setVisible(false)` call is executed before the next layout-pulse. – DVarga Jan 09 '18 at 10:31
  • @Joop I have read a bit about Platform.invokeLater(), how exactly would I use this call? I'm fairly new to working with Java, and even newer to JFX. – Leftyx Jan 09 '18 at 10:46
  • @DVarga I thought that would be the case, but the method isn't really THAT fast. It's a method that connects to a server to produce a PDF, then displays it onscreen. I would also like to point out that, as long as the PDF is opened, the window does not respond. Is the method blocking the thread? – Leftyx Jan 09 '18 at 10:49
  • Theoretically if you wrap the call of your method together with the `setVisible(false)` call into a `Platform.runLater(...)` block, the first statement will be correctly executed - this is in the case when you run your method from the JavaFX Application Thread. As you connect to a remote server, that could be done on a separate thread (for example using a `Task`) for that, which would made also your GUI responsive during the long RMI. A good answer regarding this: https://stackoverflow.com/questions/19968012/javafx-update-ui-label-asynchronously-with-messages-while-application-different. – DVarga Jan 09 '18 at 10:57
  • @DVarga Similar to my comment on Joop's (now deleted) answer. *"the first statement will be correctly executed"*. The statement `tWorking.setVisible(true)` will be "correctly executed" regardless of what you do afterwards. However *all this does* is change the value of a boolean property. You will not see any actual effect on the UI until a rendering pulse occurs. This cannot happen during the execution of the event handler, and is pretty unlikely to happen before anything passed to `Platform.runLater(...)` gets executed either. You *must* use a background thread for this to work. – James_D Jan 09 '18 at 13:45

1 Answers1

0

I guess you want show loading text when something is processing. You need too use javafx.concurrent.Task like below

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextArea;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class AsyncLoadingExample extends Application{

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

@Override
public void start(Stage primaryStage) {
    try {
    HBox outerBox = new HBox(15) ;

    //Left Side
    VBox leftBox = new VBox(5);
    Button leftButton = new Button();
    leftButton.setText("Submit");

    StackPane leftPane = new StackPane();
    Text leftText = new Text();
    leftPane.getChildren().add(leftText);
    leftText.setText("Loading");
    leftText.setVisible(false);

    leftButton.setOnAction(loadContent(leftPane,leftText));
    leftBox.getChildren().addAll(leftButton, leftPane);

    outerBox.getChildren().addAll(leftBox);

    Scene scene = new Scene(outerBox);

    primaryStage.setScene(scene);
    primaryStage.show();
    }catch (Exception e) {
        e.printStackTrace();
    }
}

private EventHandler<ActionEvent> loadContent(StackPane pane,Text text) {
    return new EventHandler<ActionEvent>() {
        @Override public void handle(ActionEvent e) {
            createTask(pane, text);
        }
    };
}

private void createTask(StackPane pane, Text text){
    //VBox loading = createProgressIndicator();
     Task<String> task = new Task<String>(){
            @Override
            protected String call() throws Exception{
                Thread.sleep(4000);
                return " from Server";
            }
        };

        Thread th = new Thread(task);
        th.setDaemon(true);
        th.start();
        //pane.getChildren().add(loading);
        text.setVisible(true);


        task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {

            @Override
            public void handle(WorkerStateEvent t) {
                String value = task.getValue();
                //text.setText("Loading completed "+value);
                System.out.println("Loading completed "+value);
                text.setVisible(false);
            }
        });
        task.setOnFailed(new EventHandler<WorkerStateEvent>() {
            @Override
            public void handle(WorkerStateEvent evt) {
                text.setText("Error while getting from server");

                text.setVisible(false);
            }
        });
}
}

https://gist.github.com/sh9va/768049585e2eb8dbca487ebf9c5def84

Shiva Kumar
  • 242
  • 1
  • 4
  • 9
  • 1
    You should call `text.setVisible(true)` *before* calling `th.start()`, to avoid any possibility of the task completing before making the text visible (it is highly unlikely that would happen, but you should make it impossible). – James_D Jan 09 '18 at 12:06
  • Thanks for the response. I'll try to fit your solution to my needs and then report on the outcome. – Leftyx Jan 09 '18 at 12:13