In JavaFX in my controller after clicking on Play button starting method 'createTTV()' creating nested TreeTableView(This treetableview will be even larger, but I don't want to paste too much unnecessary code). The method has many UI/not UI elements which are impossible to split for UI/not UI. Is there a way for running such method in background and when everything ready updating UI with not locking whole UI? My code below works, but the UI obviously doesn't respond until the createTTV() method finishes running.
public void buildUi() {
vBoxForProgressIndicator.setVisible(false);
fuses_ttv.setRoot(root_FRCs);
btnPlay.setOnAction(event ->{
clear_hook();
vBoxForTableView.setVisible(false);
vBoxForProgressIndicator.setVisible(true);
new Thread(()->{
createTTV();
Platform.runLater(new Runnable() {
@Override
public void run() {
vBoxForProgressIndicator.setVisible(false);
vBoxForTableView.setVisible(true);
}
});
}).start();
});
}
private void createTTV(){
tdoStructure = model.getDataStructure();
allFRCs = tdoStructure.getAllFRCsAndCandidates();
allWires = tdoStructure.getAllWires();
allParts = tdoStructure.collectAllParts();
allFusesAndRelays = tdoStructure.getAllFusesAndRelais();
Platform.runLater(()->{
root_FRCs.getChildren().clear();
partDescription_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("description"));
partIdentification_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("identification"));
partNo_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("partNumber"));
slot_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("slot"));
pin_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("pin"));
moduleFamilyName_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("module"));
partType_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("type"));
variants_ttc.setCellValueFactory(new TreeItemPropertyValueFactory<>("variants"));
if (frcElement != null) {
TreeItem frcTreeItem = new TreeItem<>(new FuseValidationTreeObject(frcElement));
if (!frcElement.getFrcSlots().isEmpty()) {
for (Map.Entry<String, ArrayList<String>> entrySlot : frcElement.getFrcSlots().entrySet()) {
String slot = entrySlot.getKey();
TreeItem slotItem = new TreeItem<>(new FuseValidationTreeObject(slot));
slotItem.setExpanded(true);
frcTreeItem.getChildren().add(slotItem);
for (Part fuseOrRelay : allFusesAndRelays) {
LinkedList<String> fuseOrRelayVariants = AppUtils.getAllVariantsForPart(fuseOrRelay);
if (fuseOrRelay.getReferencePartShortName().equals(frcElement.getShortName()) && AppUtils.getSlotIdFromFuseOrRelayName(fuseOrRelay.getShortName()).equals(slot)) {
TreeItem fuseRelayItem = new TreeItem(new FuseValidationTreeObject(fuseOrRelay));
boolean[] fuseOrRelayHasNoWires = {true};
//assign wires to fuse or relay
allWires.forEach(wire -> {
LinkedList<String> wireVariants = AppUtils.getAllVariantsForPart(wire);
if (fuseOrRelay.getReferencePartShortName().equals(wire.getWireToShortName())
&& entrySlot.getValue().contains(wire.getWireToPin()) && fuseOrRelay.getMemberModuleFamily().getDescription().equals(wire.getMemberModuleFamily().getDescription())) {
if (AppUtils.containsWireVariantsAnyOfFuseVariant(wireVariants, fuseOrRelayVariants)) {
TreeItem wireToItem = new TreeItem<>(new FuseValidationTreeObject(wire));
wireToItem.setExpanded(true);
fuseRelayItem.getChildren().add(wireToItem);
fuseOrRelayHasNoWires[0] = false;
if (!fuseOrRelayVariants.containsAll(wireVariants)) {
if (!wiresWithWrongVariants.contains(wire.getWireNumber())) {
wiresWithWrongVariants.add(wire.getWireNumber());
}
}
}
}
if (fuseOrRelay.getReferencePartShortName().equals(wire.getWireFromShortName())
&& entrySlot.getValue().contains(wire.getWireFromPin()) && fuseOrRelay.getMemberModuleFamily().getDescription().equals(wire.getMemberModuleFamily().getDescription())) {
if (AppUtils.containsWireVariantsAnyOfFuseVariant(wireVariants, fuseOrRelayVariants)) {
TreeItem wireFromItem = new TreeItem<>(new FuseValidationTreeObject(wire));
wireFromItem.setExpanded(true);
fuseRelayItem.getChildren().add(wireFromItem);
fuseOrRelayHasNoWires[0] = false;
if (!fuseOrRelayVariants.containsAll(wireVariants)) {
if (!wiresWithWrongVariants.contains(wire.getWireNumber())) {
wiresWithWrongVariants.add(wire.getWireNumber());
}
}
}
}
});
fuseRelayItem.setExpanded(true);
if (fuseOrRelayHasNoWires[0]) {
emptyFusesOrRelays.add(fuseOrRelay.getShortName());
}
slotItem.getChildren().add(fuseRelayItem);
}
}
if (slotItem.isLeaf()) {
//insert empty slot into emptySlots Map
if (emptySlots.containsKey(frcElement.getShortName())) {
emptySlots.get(frcElement.getShortName()).add(0, slot);
} else {
emptySlots.put(frcElement.getShortName(), new ArrayList<>(Arrays.asList(slot)));
}
//some more conditions
allWires.forEach(wire -> {
//if there is no fuse in the slot but there is still wire to fuse in this slot
if (wire.getWireToShortName().equals(frcElement.getShortName()) && entrySlot.getValue().contains(wire.getWireToPin())) {
wiresWithNoFuseOrRelay.add(wire);
TreeItem emptyWireTo = new TreeItem<>(new FuseValidationTreeObject(wire));
slotItem.getChildren().add(emptyWireTo);
}
if (wire.getWireFromShortName().equals(frcElement.getShortName()) && entrySlot.getValue().contains(wire.getWireFromPin())) {
wiresWithNoFuseOrRelay.add(wire);
TreeItem emptyWireFrom = new TreeItem<>(new FuseValidationTreeObject(wire));
slotItem.getChildren().add(emptyWireFrom);
}
});
}
}
}
frcTreeItem.setExpanded(true);
root_FRCs.getChildren().add(frcTreeItem);
}
}
}
UI elements can be created on another thread if they aren't attached to an active scene.
For most JavaFX controls (there are a few exceptions such as
WebView
), the rule to create or modify them only on the JavaFX thread only applies to modifications to a live scene graph that is attached to aStage
which is showing, or for which asnapshot
is being made. The controls which can only be created on the JavaFX thread are listed as such in their documentation.For example, it is perfectly fine to load an FXML file asynchronously on another thread (e.g. in a Task) and attach it to the scene graph on the JavaFX application thread later.
You can use
runLater
to add a createdTreeTableView
to an active scene.Quoting from the
Task
documentation, section: "A Task Which Modifies the Scene Graph".In the example provided, UI elements are created on the task thread, and
Platform.runLater()
is only used to add the created element to the scene graph.Alternately, you can create the
TreeTableView
in a task and add it to the sceneonSucceeded
So you could write this:
Or, you can create only the
TreeItems
in yourTask
If you are still uncomfortable with creating UI elements in another thread, then you can follow James_D's advice from comments: