从 javafx 平台运行后返回结果
Posted
技术标签:
【中文标题】从 javafx 平台运行后返回结果【英文标题】:Return result from javafx platform runlater 【发布时间】:2012-11-27 14:37:22 【问题描述】:我正在开发 JavaFX 应用程序,在我的场景中是显示在 JavaFX 中创建的密码提示,它使用两个选项 OK
和 Cancel
输入密码。我已经返回了用户输入的密码。
我显示密码对话框的类是 -
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;
我的代码在哪里显示密码提示如下,实际上这个提示会显示在其他 UI 上,所以我需要将它包含在 Platform.runlater()
中,否则它会抛出 Not on FX application thread
。我需要显示此密码提示,直到我得到正确的提示。如果我在 runlater 中包含显示密码,如何获取密码值。
还有其他更好的方法吗?
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】:我建议将代码包装在 FutureTask
对象中。 FutureTask
是一种有用的构造(除其他外),用于在一个线程(通常是一个工作线程,在您的情况下为事件队列)上执行一部分代码并在另一个线程上安全地检索它。 FutureTask#get
将一直阻塞,直到调用 FutureTask#run
,因此您的密码提示可能如下所示:
final FutureTask query = new FutureTask(new Callable()
@Override
public Object call() throws Exception
return queryPassword();
);
Platform.runLater(query);
System.out.println(query.get());
由于FutureTask
实现了Runnable,您可以直接将其传递给Platform#runLater(...)
。 queryPassword()
将在事件队列中被调用,随后调用 get 阻塞直到该方法完成。当然,您会希望循环调用此代码,直到密码实际匹配为止。
【讨论】:
【参考方案2】:重要
此代码适用于 当您有代码不在 JavaFX 应用程序线程上的特定情况,并且您想调用 JavaFX 应用程序线程上的代码向用户显示 GUI,然后在继续处理 JavaFX 应用程序线程之前从该 GUI 获取结果。
当您在下面的代码 sn-p 中调用 CountdownLatch.await 时,您不能在 JavaFX 应用程序线程上。如果您在 JavaFX 应用程序线程上调用 CountDownLatch.await,您将死锁您的应用程序。除此之外,如果您已经在 JavaFX 应用程序线程上,则无需调用 Platform.runLater 来在 JavaFX 应用程序线程上执行某些操作。
大多数时候您都知道自己是否在 JavaFX 应用程序线程上。如果你不确定,你可以通过调用Platform.isFxApplicationThread()来检查你的线程。
使用CountDownLatch 的替代方法。不过我更喜欢 Sarcan 的方法 ;-)
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());
这里是一些可执行示例代码,演示如何使用 CountdownLatch 来暂停非 JavaFX 应用程序线程的执行,直到 JavaFX 对话框检索到结果,然后非 JavaFX 应用程序线程可以访问该结果。
在用户在 JavaFX 对话框中输入正确密码之前,应用程序会阻止应用程序的 JavaFX 启动器线程继续运行。在输入正确的密码之前,不会显示访问权限阶段。
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();
上述程序将密码打印到屏幕和控制台纯粹是出于演示目的,显示或记录密码不是您在真实应用程序中会做的事情。
【讨论】:
由于某种奇怪的原因,这不能按预期工作。它卡在 await 并且 Runnable.run 从未被调用过。 它适用于我,我添加了一个可执行示例以进一步演示用法。 @xar 您可能在 JavaFX 应用程序线程上调用了latch.await()
,这会使您的应用程序死锁。我在答案中添加了一些文字来解释。
谢谢@jewelsea,现在说得通了。感谢您花时间解释 JavaFX 应用程序线程。以上是关于从 javafx 平台运行后返回结果的主要内容,如果未能解决你的问题,请参考以下文章