我的问题是,我需要第二只眼睛指出我在做什么,以使我的代码能够在后台线程中运行,而且还更新了GUI而不冻结它。
最初,使用线程中的任务将PDF文件上载到应用程序。
这很好。
显示进度条,其动画效果没有问题:
public void uploadFile(File fileToProcess) {
fileBeingProcessed = fileToProcess;
Task<Parent> uploadingFileTask = new Task<Parent>() {
@Override
public Parent call() {
try {
progressBarStackPane.setVisible(true);
pdfPath = loadPDF(fileBeingProcessed.getAbsolutePath());
createPDFViewer();
openDocument();
} catch (IOException ex) {
java.util.logging.Logger.getLogger(MainSceneController.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
};
uploadingFileTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
fileHasBeenUploaded = true;
progressBarStackPane.setVisible(false);
uploadFilePane.setVisible(false);
tabPane.setVisible(true);
/* This is where I am getting issue, more so in createThumbnailPanels() */
setupThumbnailFlowPane();
createThumbnailPanels();
/****** ^^^^^^^^^^^^ ******/
}
});
uploadingFileTask.setOnFailed(evt -> {
uploadingFileTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(uploadingFileTask.getException().getSuppressed()));
});
Thread uploadingFileThread = new Thread(uploadingFileTask);
uploadingFileThread.start();
}
文档上载后,它将显示在选项卡中,允许用户查看文档。
有一个二级标签,上传后,该二级标签被禁用,直到另一个名为createThumbnailPanelsTask
的任务完成;
但是,在运行此任务之前,已创建缩略图面板的FlowPane
。这似乎没有问题,似乎也不是GUI挂起的原因(这显然是createThumbnailPanelsTask
中的循环,但为清楚起见,我将显示setupThumbnailFlowPane()
):
public void setupThumbnailFlowPane() {
stage = model.getStage();
root = model.getRoot();
secondaryTabScrollPane.setFitToWidth(true);
secondaryTabScrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
/**
This will be removed from here when refactored but for now it is here,
I don't think this is anything to do with my issue
**/
Set<Node> nodes = secondaryTabScrollPane.lookupAll(".scroll-bar");
for (final Node node : nodes) {
if (node instanceof ScrollBar) {
ScrollBar sb = (ScrollBar) node;
if (sb.getOrientation() == Orientation.VERTICAL) {
sb.setUnitIncrement(30.0);
}
if (sb.getOrientation() == Orientation.HORIZONTAL) {
sb.setVisible(false);
}
}
}
secondaryTab = new FlowPane();
secondaryTab.setId("secondaryTab");
secondaryTab.setBackground(new Background(new BackgroundFill(Color.LIGHTSLATEGRAY, new CornerRadii(0), new Insets(0))));
secondaryTab.prefWidthProperty().bind(stage.widthProperty());
secondaryTab.prefHeightProperty().bind(stage.heightProperty());
secondaryTab.setPrefWrapLength(stage.widthProperty().intValue() - 150);
secondaryTab.setHgap(5);
secondaryTab.setVgap(30);
secondaryTab.setBorder(new Border(new BorderStroke(Color.TRANSPARENT, BorderStrokeStyle.NONE, CornerRadii.EMPTY, new BorderWidths(8, 10, 20, 10))));
secondaryTab.setAlignment(Pos.CENTER);
}
最后,调用createThumbnailPanels()
,我认为这是我遇到的问题。
假定发生的情况是,在文档上载后,隐藏了上载文件窗格,其中显示了“查看器”选项卡以及“辅助”选项卡。
这时二级标签被禁用,并且左侧还有一个加载图像(gif
)。
预期的行为是createThumbnailPanels()
任务将在后台运行,并且在完成之前,该选项卡将保持禁用状态,但是在此期间,gif
图像将旋转,给人的印象是正在发生一些负载。
加载完成后,gif
将被删除,并启用选项卡,从而允许用户导航到该选项卡,并查看生成的缩略图面板。
这一切正常,但是,如上所述,该任务正在挂起GUI:
public void createThumbnailPanels() {
Task<Void> createThumbnailPanelsTask = new Task<Void>() {
@Override
public Void call() {
if (model.getIcePdfDoc() != null) {
numberOfPagesInDocument = model.getIcePdfDoc().getNumberOfPages();
for (int thumbIndex = 0; thumbIndex < numberOfPagesInDocument; thumbIndex++) {
ThumbnailPanel tb = new ThumbnailPanel(thumbIndex, main, model);
Thumbnail tn = new Thumbnail(tb);
model.setThumbnailAt(tn, thumbIndex);
eventHandlers.setMouseEventsForThumbnails(tb);
/*
I have added this in as I am under the impression that a task runs in a background thread,
and then to update the GUI, I need to call this:
*/
Platform.runLater(() -> {
secondaryTab.getChildren().add(tb);
});
}
}
return null;
}
};
createThumbnailPanelsTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
/*
Further GUI modification run in setOnSucceeded so it runs on main GUI thread(?)
*/
secondaryTabScrollPane.setContent(secondaryTab);
secondaryTab.setDisable(false);
secondaryTab.setGraphic(null);
}
});
createThumbnailPanelsTask.setOnFailed(evt -> {
createThumbnailPanelsTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(createThumbnailPanelsTask.getException().getSuppressed()));
});
Thread createThumbnailPanelsThread = new Thread(createThumbnailPanelsTask);
createThumbnailPanelsThread.start();
}
所有操作都可以阻止GUI在创建面板时挂起,并且工作正常。
创建它们后,可以再次控制GUI,已删除加载gif
,启用了选项卡,并且用户可以导航到该视图并查看面板。
很显然,在这里我缺少关于并发性的东西。
如前所述,我的印象是Task在后台线程中运行,所以我对为什么它似乎没有这样做感到困惑。再次,显然我缺少了一些东西。
任何帮助将不胜感激……朝着正确的方向推进,或者对我理解上的错误之处进行澄清。
在此先感谢您,毫无疑问,一旦整理,这将有助于我将来避免此问题!
public void createThumbnailPanels() {
Task<Void> createThumbnailPanelsTask = new Task<Void>() {
//TODO: Need to check that it's a PDF
@Override
public Void call() {
if (model.getIcePdfDoc() != null) {
numberOfPagesInDocument = model.getIcePdfDoc().getNumberOfPages();
for (int thumbIndex= 0; thumbIndex< numberOfPagesInDocument; thumbIndex++) {
ThumbnailPanel tb = new ThumbnailPanel(thumbIndex, main, model);
Thumbnail tn = new Thumbnail(tb);
eventHandlers.setMouseEventsForThumbnails(tb);
model.setThumbnailAt(tn, thumbIndex);
model.setThumbnailPanels(tb);
}
setThumbnailPanelsToScrollPane();
}
return null;
}
};
createThumbnailPanelsTask.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent event) {
// setThumbnailPanelsToScrollPane();
}
});
createThumbnailPanelsTask.setOnFailed(evt -> {
createThumbnailPanelsTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(createThumbnailPanelsTask.getException().getSuppressed()));
});
Thread createThumbnailPanelsThread = new Thread(createThumbnailPanelsTask);
createThumbnailPanelsThread.start();
}
public void setThumbnailPanelsToScrollPane() {
Task<Void> setThumbnailPanelsToScrollPaneTask = new Task<Void>() {
//TODO: Need to check that it's a PDF
@Override
public Void call() {
Platform.runLater(() -> {
secondaryTab.getChildren().addAll(model.getThumbnailPanels());
secondaryTabScrollPane.setContent(main.informationExtractionPanel);
secondaryTab.setDisable(false);
secondaryTab.setGraphic(null);
});
return null;
}
};
setThumbnailPanelsToScrollPaneTask.setOnFailed(evt -> {
setThumbnailPanelsToScrollPaneTask.getException().printStackTrace(System.err);
System.err.println(Arrays.toString(setThumbnailPanelsToScrollPaneTask.getException().getSuppressed()));
});
Thread setThumbnailPanelsToScrollPaneThread = new Thread(setThumbnailPanelsToScrollPaneTask);
setThumbnailPanelsToScrollPaneThread.start();
}
仅供参考:如果我在setOnSucceeded中调用setThumbnailPanelsToScrollPane();
,它似乎不起作用。
答案 0 :(得分:1)
getChildren().add
在 JavaFX GUI线程上运行(这就是Platform.runLater
的作用),但是只有在父级Platform.runLater
中运行它才需要您将子代添加到的用户已连接到显示的gui的根,这意味着您应该能够将子代添加到未连接到任何根的父代,并在子代添加末尾将整个父代添加到根流程中,如果您在任何异步代码中执行Platform.runLater
,它将在gui线程上运行(在您的情况下)是在您的异步for
循环中添加ThumbnailPanels
,并且它们的数量是大gui将挂起。