2012-12-10 8 views
11

Pracuję nad aplikacją JavaFX, w moim scenariuszu jest pokazany monit hasła utworzony w JavaFX, który przyjmuje hasło z dwiema opcjami OK i Cancel. Zwróciłem hasło wprowadzone przez użytkownika.Powrót wynik z javafx platforma runlater

Moja klasa pokazując okno hasło to -

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; 
    } 

Mój kod gdzie pokazać pytanie o hasło jest poniżej, faktycznie ten komunikat zostanie pokazany na innym interfejsie, więc muszę ogrodzić to wewnątrz Platform.runlater(), inaczej rzuca Not on FX application thread. Potrzebuję tego hasła, aby wyświetlać się, dopóki nie otrzymam poprawnego. Jak mogę uzyskać wartość hasła, jeśli zamknąłem pokazując hasło wewnątrz runlater.

Czy istnieje inny lepszy sposób?

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("")); 

Odpowiedz

27

Polecam zawijanie kodu wewnątrz obiektu FutureTask. FutureTask jest konstrukcją użyteczną (między innymi) do wykonywania części kodu w jednym wątku (zwykle jest to robotnik, w twoim przypadku kolejka zdarzeń) i bezpieczne pobieranie go na innym. FutureTask#get zablokuje aż FutureTask#run został wywołany, więc Twoja prośba o podanie hasła może wyglądać następująco:

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

Jak FutureTask implementuje Runnable, można przekazać je bezpośrednio do Platform#runLater(...). queryPassword() zostanie wywołany w kolejce zdarzeń, a kolejne wywołanie otrzyma blok, dopóki ta metoda nie zostanie zakończona. Oczywiście, będziesz chciał wywołać ten kod w pętli, dopóki hasło faktycznie nie pasuje.

8

Ważne

Kod ten jest dla szczególnym przypadku, gdy masz kod, który nie znajduje się na tym wątku aplikacji JavaFX i chcesz wywołać kod, który znajduje się na tym wątku aplikacji JavaFX do wyświetlania GUI użytkownik, a następnie uzyskaj wynik z tego GUI przed kontynuowaniem przetwarzania wątku aplikacji JavaFX.

Nie możesz być w wątku aplikacji JavaFX, gdy wywołasz CountdownLatch.await w poniższym fragmencie kodu. Jeśli wywołasz CountDownLatch.await w wątku aplikacji JavaFX, zablokujesz aplikację. Poza tym, jeśli już jesteś w wątku aplikacji JavaFX, nie musisz wywoływać Platform.runLater, aby wykonać coś w wątku aplikacji JavaFX.

Zazwyczaj wiadomo, czy użytkownik jest w wątku aplikacji JavaFX, czy nie. Jeśli nie jesteś pewien, możesz sprawdzić swój wątek, dzwoniąc pod numer Platform.isFxApplicationThread().


Alternatywny sposób z użyciem CountDownLatch. Lubię sposób Sarcan jest lepiej choć ;-)

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()); 

Oto przykładowy kod wykonywalny demonstrując zastosowanie CountdownLatch o wstrzymanie wykonania non-JavaFX wątku aplikacji aż dialogowe JavaFX pobraniu wynik, który może następnie uzyskaj dostęp do wątku aplikacji innego niż JavaFX.

Aplikacja zapobiega wątkom uruchamiania aplikacji JavaFX dla aplikacji, dopóki użytkownik nie wprowadzi poprawnego hasła w oknie dialogowym JavaFX. Stopień przyznania dostępu nie jest wyświetlany, dopóki nie zostanie wprowadzone prawidłowe hasło.

enterpassword     access granted

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<WindowEvent>() { 
     @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<ActionEvent>() { 
     @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(); 
    } 
} 

Powyższy program wypisuje hasło na ekranie konsoli i wyłącznie do celów demonstracyjnych, wystawianie lub logujących haseł nie jest coś, co możesz zrobić w rzeczywistej aplikacji.

+0

Z jakiegoś dziwnego powodu nie działa zgodnie z oczekiwaniami. Utknął w oczekiwaniu i Runnable.run nigdy nie został wywołany. – xar

+0

To działa dla mnie, dodałem próbkę do wykonania w celu dalszego wykazania użycia. – jewelsea

+0

@xar Prawdopodobnie wywołałeś 'latch.await()' w wątku aplikacji JavaFX, co zablokowałoby twoją aplikację. Dodałem trochę tekstu do odpowiedzi, aby wyjaśnić. – jewelsea