Reputation: 964
I have written a program in java using JavaFX and the SceneBuilder application. I would like to add threading to my action handlers. This application runs a python script which discovers and sends a file to to appropriate devices on a BACnet network. The python script works as expected and my java application utilizes the script as expected. My issue is updating the UI. The script returns values showing its progress thus far. When running my upgrade action handler
code I want to output a message saying 'This should take xx minutes to complete' before executing the executeCommand()
code which will also have an output written to the UI. However all the updates are written at the same time, when the upgrade action handler
exits.
Main:
public class Main extends Application {
private Stage primaryStage;
private BorderPane rootLayout;
@Override
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("NODE-Sensor Configurator");
initRootLayout();
showNodeConfigurator();
}
public void initRootLayout( ){
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/RootLayout.fxml"));
rootLayout = (BorderPane) loader.load();
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
public void showNodeConfigurator(){
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(Main.class.getResource("view/NodeLayout.fxml"));
AnchorPane nodeConfig;
nodeConfig = (AnchorPane) loader.load();
rootLayout.setCenter(nodeConfig);
} catch (IOException e) {
e.printStackTrace();
}
}
public Stage getPrimaryStage(){
return this.primaryStage;
}
public static void main(String[] args) {
launch(args);
}
}
My code to send the commands and write the output:
@FXML
private Button upgrade;
upgrade.setOnAction((event) -> {
appendToOutputText("Reading in hex file");
python.executeCommand("readhexfile " + hexFile.getAbsolutePath());
appendToOutputText("Starting upgrade, this will take about 4 minutes to complete");
python.executeCommand("upgrade 158 158444");
)};
private void appendToOutputText(String message){
outputTextArea.appendText(message + "\n");
}
I have tried placing the appendToOutputText
method in a thread, I have tried using the runLater
JavaFX code, I even tried using a Task in the action handler. I can not get this output to thread properly. No examples I have found use the scenebuilder and fxloader.
Can anyone shed some light on how I can turn my action handlers into threaded tasks that can update the UI as needed rather than on exit?
This Questions looks like it could be of use. However I not understand how the FXLoader will work with the concept of tasks.
Upvotes: 0
Views: 185
Reputation: 13859
Given the limited info you shared, I think your code should look like:
upgrade.setOnAction((event) -> {
appendToOutputText("Reading in hex file");
Task<Void> task1 = new Task<Void>() {
@Override
protected Void call() throws Exception {
python.executeCommand("readhexfile " + hexFile.getAbsolutePath());
return null;
}
};
task1.setOnSucceeded((event) -> {
appendToOutputText("Starting upgrade, this will take about 4 minutes to complete");
});
Task<Void> task2 = new Task<Void>() {
@Override
protected Void call() throws Exception {
python.executeCommand("upgrade 158 158444");
return null;
}
};
task2.setOnSucceeded((event) -> {
System.out.println("task2 done!");
});
ExecutorService es = Executors.newSingleThreadExecutor();
es.submit(task1);
es.submit(task2);
es.shutdown();
)};
Or something like:
upgrade.setOnAction((event) -> {
appendToOutputText("Reading in hex file");
Task<Void> task1 = new Task<Void>() {
@Override
protected Void call() throws Exception {
python.executeCommand("readhexfile " + hexFile.getAbsolutePath());
Platform.runLater(()->{appendToOutputText("Starting upgrade, this will take about 4 minutes to complete");});
python.executeCommand("upgrade 158 158444");
return null;
}
};
task1.setOnSucceeded((event) -> {
System.out.println("task1 done!");
});
ExecutorService es = Executors.newSingleThreadExecutor();
es.submit(task1);
es.shutdown();
)};
Upvotes: 2
Reputation: 998
I think your event handler you pass in for setOnAction() will be invoked in the JavaFx application thread, so you will be able to manipulate the UI - but the two calls to executeCommand are probably blocking calls. You would need to create two background threads:
Upvotes: 1