'Not on FX application thread' 从以加载的 FXML 为内容的对话框启动线程时出错
'Not on FX application thread' error when starting thread from dialog with loaded FXML as content
我目前的情况是:
我创建了一个 JavaFX 应用程序,其中包含一个屏幕,我可以从该屏幕打开一个对话框(只需单击屏幕上的一个按钮)。然后,用户提供输入并单击应用按钮。然后将用户输入发送到一个方法,该方法打开某种进度对话框(用于向用户显示同步状态,这对问题并不重要)。我将此对话框称为 'MyDialog'。 MyDialog 使用以下代码构建:
Dialog<Void> dialog = new Dialog<>();
dialog.initOwner(null);
dialog.initStyle(StageStyle.UNDECORATED);
dialog.setHeaderText("Nieuw product synchroniseren...");
dialog.setResizable(false);
//Load dialog FXML file into the Pane
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setLocation(getClass().getResource("dialogs/MyDialogContent.fxml"));
try {
dialog.getDialogPane().setContent(fxmlloader.load());
} catch (IOException e) {
Functions.createExceptionDialog(e);
}
MyDialogContentController childController = fxmlloader.getController();
final ButtonType canceledButtonType = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
dialog.getDialogPane().getButtonTypes().add(canceledButtonType);
这很好用。 MyDialog 中显示的 ProgressBar 指示 任务 的进度。该任务从 线程 开始。这个线程是从上屏的controller开始的。在任务中,我想在某个时候显示一个额外的对话框,以获得一些用户验证。我将此对话框称为 'AlertDialog'。这是该部分的代码(放在上屏的Controller中,不在MyDialog的Controller中):
Task<Object> task = new Task<Object>() {
@Override
protected Object call() {
//Show choice dialog
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.initOwner(null);
alert.initStyle(StageStyle.UNDECORATED);
ButtonType buttonTypeOne = new ButtonType("One");
ButtonType buttonTypeTwo = new ButtonType("Two");
ButtonType buttonTypeThree = new ButtonType("Three");
ButtonType buttonTypeCancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeTwo, buttonTypeThree, buttonTypeCancel);
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == buttonTypeOne){
//User chose "One";
} else if (result.get() == buttonTypeTwo) {
// ... user chose "Two"
} else if (result.get() == buttonTypeThree) {
// ... user chose "Three"
} else {
// ... user chose CANCEL or closed the dialog
}
}
}
不幸的是,AlertDialog 没有显示,我收到以下错误:
Exception in thread "Thread-10" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-10
我已经尝试过以下解决方案:
- 将 AlertDialog 代码放在 MyDialogController 中,然后从任务中调用它。
- 从MyDialogController 启动线程,而不是从上层屏幕控制器启动。然后通过'childController.thread'.
调用
这两种解决方案均无效。我希望它与在 DialogPane 中加载 FXML 文件有关,因此与线程及其启动位置有关。
所以这种情况下的问题是:
为什么会出现这个错误?
错误是否与 AlertDialog 未显示有关?
我对这部分代码的处理方式是否应该有所不同? (例如,不在对话框中加载外部 FXML 文件)
非常感谢任何帮助!
我觉得我已经在 SO 上写了 100 遍了。再说一遍:JavaFX 是一个单线程 GUI,因此与 GUI 相关的所有事情都必须在主 JavaFX 线程上完成。
如果您尝试在 JavaFX 线程之外做一些与 GUI 相关的事情,您将得到 IllegalStateException: Not on FX application thread
。
Optional<ButtonType> result = alert.showAndWait();
是一个 GUI 操作,因为您初始化并显示 Dialog
。因此,您应该在其他地方检索您的用户输入,并且只在后台进行 long-运行 计算。
用户输入的好处是 Task
class(如 succeeded()
或 failed()
)的各种生命周期挂钩。
我目前的情况是:
我创建了一个 JavaFX 应用程序,其中包含一个屏幕,我可以从该屏幕打开一个对话框(只需单击屏幕上的一个按钮)。然后,用户提供输入并单击应用按钮。然后将用户输入发送到一个方法,该方法打开某种进度对话框(用于向用户显示同步状态,这对问题并不重要)。我将此对话框称为 'MyDialog'。 MyDialog 使用以下代码构建:
Dialog<Void> dialog = new Dialog<>();
dialog.initOwner(null);
dialog.initStyle(StageStyle.UNDECORATED);
dialog.setHeaderText("Nieuw product synchroniseren...");
dialog.setResizable(false);
//Load dialog FXML file into the Pane
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setLocation(getClass().getResource("dialogs/MyDialogContent.fxml"));
try {
dialog.getDialogPane().setContent(fxmlloader.load());
} catch (IOException e) {
Functions.createExceptionDialog(e);
}
MyDialogContentController childController = fxmlloader.getController();
final ButtonType canceledButtonType = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
dialog.getDialogPane().getButtonTypes().add(canceledButtonType);
这很好用。 MyDialog 中显示的 ProgressBar 指示 任务 的进度。该任务从 线程 开始。这个线程是从上屏的controller开始的。在任务中,我想在某个时候显示一个额外的对话框,以获得一些用户验证。我将此对话框称为 'AlertDialog'。这是该部分的代码(放在上屏的Controller中,不在MyDialog的Controller中):
Task<Object> task = new Task<Object>() {
@Override
protected Object call() {
//Show choice dialog
Alert alert = new Alert(AlertType.CONFIRMATION);
alert.initOwner(null);
alert.initStyle(StageStyle.UNDECORATED);
ButtonType buttonTypeOne = new ButtonType("One");
ButtonType buttonTypeTwo = new ButtonType("Two");
ButtonType buttonTypeThree = new ButtonType("Three");
ButtonType buttonTypeCancel = new ButtonType("Cancel", ButtonData.CANCEL_CLOSE);
alert.getButtonTypes().setAll(buttonTypeOne, buttonTypeTwo, buttonTypeThree, buttonTypeCancel);
Optional<ButtonType> result = alert.showAndWait();
if (result.get() == buttonTypeOne){
//User chose "One";
} else if (result.get() == buttonTypeTwo) {
// ... user chose "Two"
} else if (result.get() == buttonTypeThree) {
// ... user chose "Three"
} else {
// ... user chose CANCEL or closed the dialog
}
}
}
不幸的是,AlertDialog 没有显示,我收到以下错误:
Exception in thread "Thread-10" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-10
我已经尝试过以下解决方案:
- 将 AlertDialog 代码放在 MyDialogController 中,然后从任务中调用它。
- 从MyDialogController 启动线程,而不是从上层屏幕控制器启动。然后通过'childController.thread'. 调用
这两种解决方案均无效。我希望它与在 DialogPane 中加载 FXML 文件有关,因此与线程及其启动位置有关。
所以这种情况下的问题是:
为什么会出现这个错误?
错误是否与 AlertDialog 未显示有关?
我对这部分代码的处理方式是否应该有所不同? (例如,不在对话框中加载外部 FXML 文件)
非常感谢任何帮助!
我觉得我已经在 SO 上写了 100 遍了。再说一遍:JavaFX 是一个单线程 GUI,因此与 GUI 相关的所有事情都必须在主 JavaFX 线程上完成。
如果您尝试在 JavaFX 线程之外做一些与 GUI 相关的事情,您将得到 IllegalStateException: Not on FX application thread
。
Optional<ButtonType> result = alert.showAndWait();
是一个 GUI 操作,因为您初始化并显示 Dialog
。因此,您应该在其他地方检索您的用户输入,并且只在后台进行 long-运行 计算。
用户输入的好处是 Task
class(如 succeeded()
或 failed()
)的各种生命周期挂钩。