Reputation: 1121
I am writing a code that reads considerably large arxml file and stores data from it. I have a main class ARXMLParser and a class called ParsedDatabase. I am calling a function from main loadFromArxml and passing the path of file as parameter. The function would read and store that data. I want to update Progress bar in main as the function loadFromArxml is reading data.
I am using class Task and performing my function in it. However I don't know how would I update progress from it.
This is my main code
import java.io.File;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import parser.ParsedDatabase;
public class ARXMLParser extends Application {
private ProgressBar progress;
@Override
public void start(Stage primaryStage) {
TextField arxmlPath = new TextField("");
arxmlPath.setEditable(false);
arxmlPath.setFocusTraversable(false);
arxmlPath.setPromptText("ARXMl File");
Label arxmlLbl = new Label("No File Selected");
Button btnArxml = new Button("Browse");
btnArxml.setOnAction(e -> {
FileChooser chooser = new FileChooser();
chooser.setInitialDirectory(new File(System.getProperty("user.dir")));
FileChooser.ExtensionFilter fileExtensions = new FileChooser.ExtensionFilter("ARXML Files (*.arxml)",
"*.arxml");
chooser.getExtensionFilters().add(fileExtensions);
File file = chooser.showOpenDialog(primaryStage);
if (file != null) {
arxmlPath.setText(file.getAbsolutePath());
arxmlLbl.setText(file.getName());
}
});
HBox arxmlHbox = new HBox();
arxmlHbox.setSpacing(10);
arxmlHbox.getChildren().addAll(arxmlPath, btnArxml);
HBox.setHgrow(arxmlPath, Priority.ALWAYS);
HBox.setHgrow(btnArxml, Priority.ALWAYS);
progress = new ProgressBar();
progress.setPrefWidth(Double.MAX_VALUE);
Button btnParse = new Button("Parse");
btnParse.setOnAction(e -> {
ParsedDatabase parsedDatabase = new ParsedDatabase();
if (!arxmlPath.getText().isEmpty()) {
Task<Void> parse = new Task<Void>() {
@Override
protected Void call() throws Exception {
// Function that reads the file
parsedDatabase.loadFromArxml(arxmlPath.getText());
return null;
}
};
progress.progressProperty().bind(parse.progressProperty());
Thread thread = new Thread(parse);
thread.setDaemon(true);
thread.start();
}
});
VBox vBox = new VBox();
vBox.setFillWidth(true);
vBox.setSpacing(15);
vBox.getChildren().addAll(arxmlHbox, arxmlLbl, btnParse, progress);
vBox.setPadding(new Insets(15, 10, 15, 10));
Scene scene = new Scene(vBox, 500, 170);
primaryStage.setTitle("ARXML Parser");
primaryStage.setResizable(false);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public void setProgressBar(Double value) {
progress.setProgress(value);
}
public double getProgress() {
return progress.getProgress();
}
}
And my ParsedDatabase class
package parser;
import java.io.File;
import java.util.Scanner;
public class ParsedDatabase {
public void loadFromArxml(String arxmlPath) throws Exception {
File file = new File(arxmlPath);
Scanner scan = new Scanner(file);
while (scan.hasNext()) {
System.out.println(scan.nextLine());
// Update progress based on file length
}
scan.close();
// Update progress
exportToExcel();
// Update progress
}
public void exportToExcel() {
// Export to excel
// Update progress
}
}
I was wondering how to update progress from the method. I do know there is a way I can do it. That is if I call both functions from main and from there updating progress is easy. Something like this
updateProgress(0, 1.0);
parsedDatabase.loadFromArxml(arxmlPath.getText());
updateProgress(0.5, 1.0);
parsedDatabase.exportToExcel();
updateProgress(1, 1.0);
However, The progress bar then will not progress smoothly but will go from 0 to 50 and to 100. I want it to be continuous. If there is a better way to achieve the same thing please let me know. This isn't an assignment. I am just learning concurrency at my own.
Upvotes: 1
Views: 1248
Reputation: 10253
One possible solution is to have the code that you are running in loadFromArxml()
be moved to the Task
's call()
method.
Then you can update the progress from within the actual working method. Then just bind your ProgressBar
as usual.
Here is a complete example to demonstrate, with loadFromArxml()
in a separate class.
ProgressTaskExample.java:
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ProgressTaskExample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) {
// Simple Interface
VBox root = new VBox(10);
root.setAlignment(Pos.CENTER);
root.setPadding(new Insets(10));
// The ProgressBar
ProgressBar progressBar = new ProgressBar(0.0);
// Button to start the task
Button button = new Button("Run Task");
button.setOnAction(event -> {
System.out.println("Starting task ...");
// Get the loading Task
final Task<Void> task = Datasource.loadFromArxml();
// Catch any exceptions
task.setOnFailed(wse -> {
System.out.println("Error");
task.getException().printStackTrace();
});
// Print success when complete
task.setOnSucceeded(wse -> {
System.out.println("DONE!");
progressBar.setStyle("-fx-accent: #19ee15");
});
// Bind our ProgressBar's properties
progressBar.progressProperty().bind(task.progressProperty());
// Run the task
new Thread(task).start();
});
// Add our nodes to the scene
root.getChildren().addAll(button, progressBar);
// Show the stage
primaryStage.setScene(new Scene(root));
primaryStage.setTitle("Sample");
primaryStage.show();
}
}
Datasource.java:
import javafx.concurrent.Task;
public class Datasource {
public static Task<Void> loadFromArxml() {
return new Task<Void>() {
@Override
protected Void call() throws Exception {
// Do your processing here
updateProgress(0, 100);
// Run for 5 seconds
int i = 0;
while (i < 100) {
Thread.sleep(50);
i++;
updateProgress(i, 100);
}
return null;
}
};
}
}
Upvotes: 4