Return result from javafx platform runlater

匿名 (未验证) 提交于 2019-12-03 02:26:02

问题:

I am working on JavaFX application, in my scenario is to show a password prompt created in JavaFX which takes password with two option OK and Cancel. I have returned the password entered by user.

My class of showing password dialog is -

public static String showPasswordDialog(String title, String message, Stage parentStage, double w, double h) {     try {         Stage stage = new Stage();         PasswordDialogController controller = (PasswordDialogController) Utility.replaceScene("Password.fxml", stage);         passwordDialogController.init(stage, message, "/images/password.png");         if (parentStage != null) {             stage.initOwner(parentStage);         }         stage.initModality(Modality.WINDOW_MODAL);         stage.initStyle(StageStyle.UTILITY);         stage.setResizable(false);         stage.setWidth(w);         stage.setHeight(h);                         stage.showAndWait();         return controller.getPassword();      } catch (Exception ex) {          return null;     } 

My code where to show password prompt is below, actually this prompt will be shown over other UI, so I need to inclose this inside Platform.runlater(), otherwise it throws Not on FX application thread. I need this password prompt to be shown until I get correct one. How can I get value of password if I inclosed showing password inside runlater.

Is there any other better way?

final String sPassword = null;            do {             Platform.runLater(new Runnable() {                  @Override                 public void run() {                      sPassword = JavaFXDialog.showPasswordDialog(sTaskName + "Password", "Enter the password:", parentStage, 400.0, 160.0);                 }             });              if (sPassword == null) {                 System.out.println("Entering password cancelled.");                 throw new Exception("Cancel");             }         } while (sPassword.equalsIgnoreCase("")); 

回答1:

I'd recommend wrapping the code within a FutureTask object. FutureTask is a construct useful (among other things) for executing a portion of code on one thread (usually a worker, in your case the event queue) and safely retrieving it on another. FutureTask#get will block until FutureTask#run has been invoked, therefore your password prompt could look like this:

final FutureTask query = new FutureTask(new Callable() {     @Override     public Object call() throws Exception {         return queryPassword();     } }); Platform.runLater(query); System.out.println(query.get()); 

As FutureTask implements Runnable, you can pass it directly to Platform#runLater(...). queryPassword() will be inokved on the event queue, and the subsequent call to get block until that method completes. Of course, you will want to invoke this code in a loop until the password actually matches.



回答2:

Important

This code is for the specific case of when you have code which is not on the JavaFX application thread and you want to invoke code which is on the JavaFX application thread to display GUI to a user, then get a result from that GUI before continuing processing off the JavaFX application thread.

You must not be on the JavaFX application thread when you call CountdownLatch.await in the code snippet below. If you invoke CountDownLatch.await on the JavaFX Application thread, you will deadlock your application. Besides which, if you are already on the JavaFX application thread, you don't need to invoke Platform.runLater to execute something on the JavaFX application thread.

Most of the time you know if you are on the JavaFX application thread or not. If you are not sure, you can check your thread by calling Platform.isFxApplicationThread().


An alternate method using CountDownLatch. I like Sarcan's method better though ;-)

final CountDownLatch latch = new CountDownLatch(1); final StringProperty passwordProperty = new SimpleStringProperty(); Platform.runLater(new Runnable() {     @Override public void run() {         passwordProperty.set(queryPassword());         latch.countDown();     } }); latch.await();       System.out.println(passwordProperty.get()); 

Here is some executable sample code demonstrating use of a CountdownLatch to suspend execution of a non-JavaFX application thread until a JavaFX dialog has retrieved a result which can then be accessed by the non-JavaFX application thread.

The application prevents the JavaFX launcher thread for the application from continuing until the user has entered the correct password in a JavaFX dialog. The access granted stage is not shown until the correct password has been entered.

import javafx.application.*; import javafx.beans.property.*; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.layout.*; import javafx.scene.text.TextAlignment; import javafx.stage.*;  import java.util.concurrent.CountDownLatch;  public class PasswordPrompter extends Application {   final StringProperty passwordProperty = new SimpleStringProperty();   @Override public void init() {     final CountDownLatch latch = new CountDownLatch(1);      Platform.runLater(new Runnable() {       @Override public void run() {         passwordProperty.set(new PasswordPrompt(null).getPassword());         latch.countDown();       }     });      try {       latch.await();     } catch (InterruptedException e) {       Platform.exit();     }      System.out.println(passwordProperty.get());   }    @Override public void start(final Stage stage) {     Label welcomeMessage = new Label("Access Granted\nwith password\n" + passwordProperty.get());     welcomeMessage.setTextAlignment(TextAlignment.CENTER);      StackPane layout = new StackPane();     layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20px;");     layout.getChildren().setAll(welcomeMessage);     stage.setScene(new Scene(layout));      stage.show();   }    public static void main(String[] args) { launch(args); } }  class PasswordPrompt {   final Window owner;    PasswordPrompt(Window owner) {     this.owner = owner;   }    public String getPassword() {     final Stage dialog = new Stage();     dialog.setTitle("Pass is sesame");     dialog.initOwner(owner);     dialog.initStyle(StageStyle.UTILITY);     dialog.initModality(Modality.WINDOW_MODAL);     dialog.setOnCloseRequest(new EventHandler() {       @Override public void handle(WindowEvent windowEvent) {         Platform.exit();       }     });      final TextField textField = new TextField();     textField.setPromptText("Enter sesame");     final Button submitButton = new Button("Submit");     submitButton.setDefaultButton(true);     submitButton.setOnAction(new EventHandler() {       @Override public void handle(ActionEvent t) {         if ("sesame".equals(textField.getText())) {           dialog.close();         }       }     });      final VBox layout = new VBox(10);     layout.setAlignment(Pos.CENTER_RIGHT);     layout.setStyle("-fx-background-color: azure; -fx-padding: 10;");     layout.getChildren().setAll(textField, submitButton);      dialog.setScene(new Scene(layout));     dialog.showAndWait();      return textField.getText();   } } 

The above program prints password to the screen and console purely for demonstration purposes, displaying or logging passwords is not something you would do in a real application.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!