0

I want to get the selected file path from a FileChooser showSaveDialog() dialog in JavaFX in order to export a tableview to a file. The code is running in a Runnable so I have to run the showSaveDialog in a JavaFX main thread (Platform.runLater)

public class ExportUtils {
  ...
  private File file = null;
  private String outputPath = null;

  public void Export(){
   ...
   ChooseDirectory(stage);
   if (outputPath != null{
      ...   //export to the selected path
   }
  }

  public void ChooseDirectory(Stage stage) {
      ...
      FileChooser newFileChooser = new FileChooser();
      ...

      Platform.runLater(new Runnable() {
        public void run() {
            file = newFileChooser.showSaveDialog(stage);
            if (file != null) {
                outputPath = file.getPath();
            }
        }
    });
}

I would like to know the best solution for this situation where I have to wait for the user to choose the path and filename before I evaluate the value of the outputPath variable in the Export() method.

LazyTurtle
  • 131
  • 1
  • 3
  • 16

2 Answers2

2

Unless you need to keep the thread running I suggest handling the file choices by starting a new thread instead of posting a task on a ExecutorService.

If you do need to do it like this, you could use a CompletableFuture to retrieve the result:

private static void open(Stage stage, CompletableFuture<File> future) {
    Platform.runLater(() -> {
        FileChooser fileChooser = new FileChooser();
        future.complete(fileChooser.showSaveDialog(stage)); // fill future with result
    });
}

@Override
public void start(Stage primaryStage) throws Exception {
    Button button = new Button("start");

    button.setOnAction(evt -> {
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                CompletableFuture<File> future = new CompletableFuture<>();
                open(primaryStage, future);
                try {
                    File file = future.get(); // wait for future to be assigned a result and retrieve it
                    System.out.println(file == null ? "no file chosen" : file.toString());
                } catch (InterruptedException | ExecutionException ex) {
                    ex.printStackTrace();
                }
            }
        }).start();
    });

    primaryStage.setScene(new Scene(new StackPane(button)));
    primaryStage.show();

}

Note: If you access data from the ui in a seperate thread, you may get into trouble if the data is modified concurrently.

fabian
  • 80,457
  • 12
  • 86
  • 114
0

Don't split the method up like this. There is no way your export() can continue off after you have a Platform.runLater().

Option 1

Combine everything into one method.

public void export() {
    ...

    Platform.runLater(new Runnable() {
        public void run() {
            file = new FileChooser().showSaveDialog(stage);
            if (file != null) {
                outputPath = file.getPath();

                // Export to the path
            }
        }
    });
}

Option 2

You can move the Platform.runLater() to the start of export().

public void export() {
    ...
    Platform.runLater(() -> {
        String outputPath = chooseDirectory(stage);
        if (outputPath != null) {
            // Export to the path
        }
    });
}

private String chooseDirectory(Stage stage) {
    ...
    file = new FileChooser().showSaveDialog(stage);
    if (file != null) {
        return file.getPath();
    }
    else return null;
}
Jai
  • 8,165
  • 2
  • 21
  • 52
  • Thank you for your answer. Is there a way to wait for the platform.runlater? Such as CompletableFuture or Future? – LazyTurtle Jul 23 '18 at 10:02
  • @user6127833 There is, but it's a lot more tedious to do so. I wouldn't want to do that unless there is really a need to do so. Options include returning a future, to using advanced concurrency classes in concurrency API. – Jai Jul 23 '18 at 10:16
  • I see, maybe for low processing tasks this is too heavy to code, but I understand that at the end it will be the programmer´s choice, to use concurrency or simply wrap all the export process in the platform run later...Thank you – LazyTurtle Jul 23 '18 at 10:19