Search code examples
multithreadingjavafxfilechooser

JavaFX wait FileChooser showSaveDialog to get selected file path


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.


Solution

  • 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.