JavaFX:在进程运行时禁用所有组件并显示进度指示器
Posted
技术标签:
【中文标题】JavaFX:在进程运行时禁用所有组件并显示进度指示器【英文标题】:JavaFX: Disable all components while a process is running and show progress indicator 【发布时间】:2017-06-16 18:56:05 【问题描述】:我有一个从数据库中读取值并返回Map<Integer,String>.
的方法,此方法需要一些时间才能返回地图。
在读取时间值之前,我希望在屏幕上显示一个进度指示器(仅加载环形指示器就足够了,不需要进度条),并且所有其他组件都应禁用,直到显示时间进度条.
public void scanDevice()
ObservableList<TextField> list = FXCollections.observableArrayList(vehicleId, vehicleName, deviceType,
offboardBroker1, offboardBroker2, postfixQueue, pKIServer);
editedValuesMap.clear();
// devicePlugged = true;
if (cbChooseProject.getSelectionModel().getSelectedItem() != null)
try
devicePlugged = dsAdapter.getAdapter();
if (devicePlugged)
if (bScanDevice.isFocused())
readMap = new HashMap<Integer, String>();
//Process Start
readMap = dsAdapter.initScan();
//Process End
if (!readMap.isEmpty() && readMap != null)
isWritten = true;
isDeviceSideEnabled();
editDeviceContents.setDisable(false);
vehicleId.setText(readMap.get(0));
vehicleName.setText(readMap.get(1));
deviceType.setText(readMap.get(2));
offboardBroker1.setText(readMap.get(3));
offboardBroker2.setText(readMap.get(4));
postfixQueue.setText(readMap.get(5));
pKIServer.setText(readMap.get(6));
lContentsSerialNo.setText(readMap.get(7));
【问题讨论】:
【参考方案1】:这是SSCCE:
它使用可以多次启动的Service。它不完整,但可以开始。
public class Main extends Application
@Override
public void start(Stage primaryStage)
BorderPane root = new BorderPane();
Scene scene = new Scene(root, 400, 400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
Service<Void> serv = new Service<Void>()
@Override
protected Task<Void> createTask()
return new Task<Void>()
@Override
protected Void call() throws Exception
int maxWork = 10;
for (int i = 0; i < maxWork; i++)
Thread.sleep(1000);
updateProgress(i + 1, maxWork);
return null;
@Override
protected void succeeded()
super.succeeded();
updateProgress(1, 1);
@Override
protected void cancelled()
super.cancelled();
updateProgress(1, 1);
@Override
protected void failed()
super.failed();
updateProgress(1, 1);
;
;
ProgressIndicator pi = new ProgressIndicator();
pi.progressProperty().bind(serv.progressProperty());
Button bStart = new Button("Start");
bStart.setOnAction(e ->
serv.reset();
serv.start();
);
root.setCenter(bStart);
root.setBottom(pi);
primaryStage.setScene(scene);
primaryStage.show();
pi.getScene().getRoot().disableProperty().bind(serv.runningProperty());
public static void main(String[] args)
launch(args);
在我添加的 CSS 中:
.progress-indicator:disabled
-fx-opacity: 1;
【讨论】:
【参考方案2】:您可以使用以下方法禁用所有节点,但如果您还想在某些事情发生时等待,则使用 StackPane
s 的覆盖可能是首选。
public void setNodesDiabled(boolean disable, Node... nodes)
for(Node node : nodes)
node.setDisable(disable);
使用任意节点数,您可以禁用和重新启用与进程相关的尽可能多的节点。它还有助于清理,因为您不会有多个 node.setDisable(true); node2.setDisable(true);
等等。
在此示例中,您不需要 setNodesDisabled()
,因为 StackPane 叠加层可防止单击其中内容以外的任何内容。背景颜色是带有 70% alpha 的灰色,因此您可以判断它是一个叠加层。
public class ProgressExample extends Application
public StackPane layout, main, progress;
public StackPane createProgressPane()
ProgressIndicator indicator = new ProgressIndicator();
indicator.setMaxHeight(50);
indicator.setMaxWidth(50);
StackPane pane = new StackPane();
pane.setAlignment(Pos.CENTER);
pane.setStyle("-fx-background-color: rgba(160,160,160,0.7)");
pane.getChildren().add(indicator);
Task<Void> task = new Task<Void>()
protected Void call() throws Exception
// Your process here.
// Any changes to UI components must be inside Platform.runLater() or else it will hang.
Thread.sleep(2000);
Platform.runLater(() ->
layout.getChildren().remove(pane);
);
return null;
;
new Thread(task).start();
return pane;
public StackPane createMainPane()
Label label = new Label("Hello World!");
label.setFont(Font.font("Tahoma", FontWeight.SEMI_BOLD, 16));
Button start = new Button("Start Process");
start.setOnAction(action ->
progress = createProgressPane();
layout.getChildren().add(progress);
);
VBox vbox = new VBox(10);
vbox.setAlignment(Pos.CENTER);
vbox.getChildren().addAll(label, start);
vbox.setPadding(new Insets(10,10,10,10));
StackPane pane = new StackPane();
pane.getChildren().add(vbox);
return pane;
public void start(Stage stage) throws Exception
main = createMainPane();
layout = new StackPane();
layout.getChildren().add(main);
Scene scene = new Scene(layout, 900, 550);
stage.setScene(scene);
stage.setTitle("Progress Example");
stage.setOnCloseRequest(e ->
Platform.exit();
System.exit(0);
);
stage.show();
public static void main(String[] args)
launch(args);
我认为问题在于您试图更改 Task
内的 TextField
s 的值,这不是 FX 应用程序线程,这就是您获得 Not on FX application thread
的原因。要解决此问题,您需要将修改节点的行放在 Platform.runLater()
中,如下所示。
if (readMap != null && !readMap.isEmpty()) // Swap the order, can't check empty if it's null.
isWritten = true;
isDeviceSideEnabled();
Platform.runLater(() ->
editDeviceContents.setDisable(false);
vehicleId.setText(readMap.get(0));
vehicleName.setText(readMap.get(1));
deviceType.setText(readMap.get(2));
offboardBroker1.setText(readMap.get(3));
offboardBroker2.setText(readMap.get(4));
postfixQueue.setText(readMap.get(5));
pKIServer.setText(readMap.get(6));
lContentsSerialNo.setText(readMap.get(7));
);
【讨论】:
你能告诉我这个 Thread.sleep(2000) 有什么用吗?我是否需要计算我的方法所需的时间,然后在这段时间内休眠线程,并且由于项目包含,我必须使用 BorderPane 布局。我收到以下错误:线程“Thread-5”中的异常 java.lang.IllegalStateException:不在 FX 应用程序线程上; currentThread = Thread-5 示例中使用Thread.sleep(2000);
仅仅是因为它是一个示例。如果没有任何东西来花时间,进度窗格在测试时将不会显示足够长的时间。你不需要它。至于错误,您能否编辑问题以将这些行包含在Task
中?您需要将更改 FX 节点和窗格的行放在它们自己的 Platform.runLater()
中。
Mathews,我添加了包含我的流程的代码。该进程从设备中读取值并将其显示在 UI 上。以上是关于JavaFX:在进程运行时禁用所有组件并显示进度指示器的主要内容,如果未能解决你的问题,请参考以下文章
Python Multiprocessing 多进程,使用多核CPU计算 并使用tqdm显示进度条
错误:缺少 JavaFX 运行时组件,需要在 Eclipse 中运行此应用程序