JavaFX: Prompt user based on Task, and pass back r

2020-04-21 07:51发布

问题:

I have a simple JavaFX GUI that fires a background task on button click. This task continuously updates a TextArea with its latest progress messages. I have demonstrated how I solved this below. The issue arises when the task runs into an error, and requires a decision from the user on how to proceed. My goal is to have this decision made via an Alert, with the user choosing Yes or No. I've been unable to achieve this functionality, though. Here is what I have attempted so far:

  • Create an Alert in the JavaFX main thread, pass it to the script, and call showAndWait. This resulted in the error indicating I am not in a JavaFX thread.
  • UpdateMessage() etc. Extending the script as a Task, I keep running into a NullPointerException.
  • Creating a new JavaFX instance from the script.

Thank you for your help!

Button creation with EventHandler:

private Button createButton() {
    Button btn = new Button();
    btn.setText("Run");
    btn.setPrefWidth(100);
    EventHandler<ActionEvent> buildWindow = new EventHandler<ActionEvent>() {
        @Override
        public void handle(ActionEvent e) {
            TextArea output = buildCenterTextArea();
            Task task = new Task<Void>() {
                @Override public Void call() {
                    callScript(output); // Calls script
                    return null;
                }
            };
            new Thread(task).start();
        }
    };
    btn.setOnAction(buildWindow);
    return btn;
}

private void buildCenterTextArea() {
    // Builds a text area which the script updates with status
    TextArea output = new TextArea();
    output.setEditable(false);
    this.borderpane.setCenter(output);
    return output
}

In my script, I update the text by doing the following:

output.setText(statusText+ "\n" + newStatus);

回答1:

The background thread can be kept busy waiting. This means you can create a CompletableFuture, use Platform.runLater to create an alert and displaying it using showAndWait and after that filling the future with the results. Just after this call on the background thread wait for the result using Future.get.

The following example generates random numbers between 0 and 9 (inclusive) and prints 0-8 to the TextArea. 9 is a simulated error and the user is asked, if the task should be continued.

@Override
public void start(Stage stage) throws IOException {
    TextArea ta = new TextArea();

    Thread thread = new Thread(() -> {
        Random rand = new Random();
        while (true) {
            int i = rand.nextInt(10);
            if (i == 9) {
                CompletableFuture<ButtonType> future = new CompletableFuture<>();

                // ask for user input
                Platform.runLater(() -> {
                    Alert alert = new Alert(AlertType.CONFIRMATION);
                    alert.setContentText("An error occured. Continue?");
                    future.complete(alert.showAndWait().orElse(ButtonType.CANCEL)); // publish result
                });
                try {
                    if (future.get() == ButtonType.CANCEL) { // wait for user input on background thread
                        break;
                    }
                } catch (InterruptedException | ExecutionException e) {
                    e.printStackTrace();
                    break;
                }
            } else {
                Platform.runLater(() ->ta.appendText(Integer.toString(i) + "\n"));
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
        }
    });
    thread.setDaemon(true);
    thread.start();

    Scene scene = new Scene(new VBox(ta));


    stage.setScene(scene);
    stage.show();
}


标签: java javafx