Error "Not in the FX application thread" when starting a thread from a dialog with loaded FXML as content

I currently have the following situation:

I have created a JavaFX application that contains a screen from which I open a dialog (just by clicking a button on the screen). The user then enters input and clicks the "Apply" button. The user input is then sent to the method, which opens some kind of progress dialog (to show the user the sync status, which is not important for the problem). I'll call this dialog "MyDialog" . MyDialog is built with the following code:

Dialog<Void> dialog = new Dialog<>();
dialog.initOwner(null);
dialog.initStyle(StageStyle.UNDECORATED);
dialog.setHeaderText("Nieuw product synchroniseren...");
dialog.setResizable(false);

//Load dialog FXML file into the Pane
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setLocation(getClass().getResource("dialogs/MyDialogContent.fxml"));
try {
    dialog.getDialogPane().setContent(fxmlloader.load());
} catch (IOException e) {
    Functions.createExceptionDialog(e);
}
MyDialogContentController childController = fxmlloader.getController();

final ButtonType canceledButtonType = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
dialog.getDialogPane().getButtonTypes().add(canceledButtonType);

      

This works great. The ProgressBar, shown in MyDialog, indicates the progress of the task . The task is started from a thread . This thread starts from the top screen controller. From within the task, I would like to show an additional dialog at some point to get some user validation. I'll call this dialog "AlertDialog" . This is the code for this part (which fits into the top screen controller, not the MyDialog controller):

Task<Object> task = new Task<Object>() {

    @Override
    protected Object call() {
        //Show choice dialog
        Alert alert = new Alert(AlertType.CONFIRMATION);
        alert.initOwner(null);
        alert.initStyle(StageStyle.UNDECORATED);

        ButtonType buttonTypeOne = new ButtonType("One");
        ButtonType buttonTypeTwo = new ButtonType("Two");
        ButtonType buttonTypeThree = new ButtonType("Three");
        ButtonType buttonTypeCancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);

        alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeTwo, buttonTypeThree, buttonTypeCancel);

        Optional<ButtonType> result = alert.showAndWait();

        if (result.get() == buttonTypeOne){
            //User chose "One";
        } else if (result.get() == buttonTypeTwo) {
            // ... user chose "Two"
        } else if (result.get() == buttonTypeThree) {
            // ... user chose "Three"
        } else {
            // ... user chose CANCEL or closed the dialog
        }
    }
}

      

Unfortunately, the AlertDialog is not showing and I am getting the following error:

Exception in thread "Thread-10" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-10

      

I've already tried the following solutions:

  • Place the AlertDialog code in MyDialogController and then call it from the task.
  • Start the stream from MyDialogController, not start from the top screen controller. And then call it "childController.thread".

Both of these solutions don't work. I expect it to have something to do with loading the FXML file into the DialogPane and hence the thread and from which it starts.

So the questions in this situation:

  • Why am I getting this error?

  • An error related to AlertDialog not showing?

  • Should my approach to this piece of code be different? (for example without loading external FXML file in dialog)

Any help is greatly appreciated!

+3


source to share


1 answer


It looks to me like I've written this 100 times on SO .. once again: JavaFX is a single threaded GUI, so every associated GUI must execute on the main JavaFX thread.

If you try to do something GUI from JavaFX Thread, you get yours IllegalStateException: Not on FX application thread

.



Optional<ButtonType> result = alert.showAndWait();

is a GUI action because you are initializing and showing Dialog

. So you have to get your user login somewhere else and only do long running calculations in the background.

Good points for user input are, for example, various class lifecycle hooks Task

(like succeeded()

or failed()

).

+4


source







All Articles