Javafx change text before and after run task - multithreading

I would like to update some text of my status bar label before and after I run a task.
This is the example code:
label.setText("please wait...");
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
veryLongOperation();
return null;
}
};
Thread thread = new Thread(task);
thread.start();
while (thread.isAlive()) {
System.out.println("waiting...");
Thread.currentThread().sleep(10);
}
System.out.println("work done!");
label.setText("work done!");
With this example the text value of label can't be changed... how can I update the text?

Have you tried to override the Task#suceeded method like this:
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
veryLongOperation();
return null;
}
#Override
protected void succeeded() {
System.out.println("work done!");
label.setText("work done!");
}
};
new Thread(task).start();

Related

Updating javafx textArea elment using separated thread or task

I'm trying to update text inside a javafx textArea element instantly to show execution information using both thread and task but nothing seems working, althought when I print something in console it works thus the thread is executing. The program prints all the messages once the program is executed, but i want show the messages as the same time as the program is executing.
Here I have my tsak and thread declarations
#Override
public void initialize(URL url, ResourceBundle rb) {
System.setProperty("webdriver.gecko.driver", "C:\\Users/lyesm/Downloads/geckodriver-v0.26.0-win64/geckodriver.exe");
try {
restoreValues();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
text = new Text(this.getLogs());
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Runnable updater = new Runnable() {
#Override
public void run() {
printMessages();
System.out.println(" working on ... \n");
}
};
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
//Platform.runLater(updater);
}
}
});
thread.setDaemon(true);
thread.start();
service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
Platform.runLater(() -> textArea.appendText(logs));
return null;
}
};
}
};
service.start();
}
I'm calling the service from this method
public void launchTest() {
this.setLogs("\n\n");
service.restart();
this.setLogs(" Test starting ...\n");
service.restart();
//this.setLogs(" Opening the navigator \n");
this.setDriver(new FirefoxDriver());
//this.setLogs(" Reaching http://127.0.0.1:8080/booksManager ... \n");
driver.get("http://127.0.0.1:8080/booksManager");
//this.setLogs(" Setting test data \n");
driver.findElement(By.id("lyes")).click();
driver.findElement(By.name("email")).sendKeys(pseudo.getText());
driver.findElement(By.name("password")).sendKeys(password.getText());
//this.setLogs(" Submitting ... \n");
driver.findElement(By.name("submit")).click();
if(driver.getCurrentUrl().equals("http://127.0.0.1:8080/booksManager/Views/index.jsp") == true) {
//InputStream input= getClass().getResourceAsStream("https://w0.pngwave.com/png/528/278/check-mark-computer-icons-check-tick-s-free-icon-png-clip-art-thumbnail.png");
//Image image = new Image(input);
//ImageView imageView = new ImageView(image);
Label label = new Label(" Test successed");
testsInfos.getChildren().add(label);
}else {
Text textRes = new Text("\n Test failed ");
textRes.setFill(javafx.scene.paint.Color.RED);
testsInfos.getChildren().add(textRes);
}
driver.close();
}
And here the printMessage method called from the thread
public void printMessages() {
String ll = this.getLogs();
this.text.setText(ll);
testsInfos.getChildren().remove(text);
testsInfos.getChildren().add(text);
textArea.clear();
textArea.setText(ll);
}
Neither method seems to work.
Does anybody have any idea how to fix it ?
Edited:
package application;
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
private Service<Void> service;
#Override
public void start(Stage primaryStage) throws InterruptedException {
StackPane root = new StackPane();
TextArea ta = new TextArea();
ta.setDisable(true);
root.getChildren().add(ta);
Scene scene = new Scene(root, 200, 200);
// longrunning operation runs on different thread
/*Thread thread = new Thread(new Runnable() {
#Override
public void run() {
Runnable updater = new Runnable() {
#Override
public void run() {
incrementCount();
}
};
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
}
// UI update is run on the Application thread
Platform.runLater(updater);
}
}
});
// don't let thread prevent JVM shutdown
thread.setDaemon(true);
thread.start();*/
primaryStage.setScene(scene);
primaryStage.show();
service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(new Runnable() {
#Override
public void run() {
try{
ta.appendText("\n Printed ");
}finally{
latch.countDown();
}
}
});
latch.await();
return null;
}
};
}
};
service.start();
showIT();
}
public static void main(String[] args) {
launch(args);
}
public void showIT() throws InterruptedException {
service.restart();
for(int i = 0;i<1000000;i++) {
System.out.println(i);
}
for(int i = 0;i<1000000;i++) {
System.out.println(i);
}
service.restart();
for(int i = 0;i<1000000;i++) {
System.out.println(i);
}
for(int i = 0;i<1000000;i++) {
System.out.println(i);
}
service.restart();
}
}
The two threading rules in JavaFX are:
Long-running code must not be executed on the FX Application Thread, and
Any code that updates the UI must be executed on the FX Application Thread.
The reason for the first rule is that the FX Application Thread is responsible for rendering the UI (among other things). So if you perform a long-running task on that thread, you prevent the UI from being rendered until your task is complete. This is why you only see the updates once everything is finished: you are running your long-running code on the FX Application Thread, preventing it from re-rendering the text area until everything is complete.
Conversely, the code you do run on a background thread (via the Task.call() method) doesn't do anything that takes a long time to run:
#Override
protected Void call() throws Exception {
final CountDownLatch latch = new CountDownLatch(1);
Platform.runLater(new Runnable() {
#Override
public void run() {
try{
ta.appendText("\n Printed ");
}finally{
latch.countDown();
}
}
});
latch.await();
return null;
}
The only thing you do here is schedule an update on the FX Application thread; the call to Platform.runLater() exits immediately. There's no long-running code at all, so no purpose for the background thread on which this runs. (Technically, the call to latch.await() is a blocking call, but it's redundant anyway, since you simply exit the method after waiting.) With this task implementation, there's no difference between calling service.restart();, and ta.appendText("\n Printed");.
So, your showIT() method should be called on a background thread, and can use Platform.runLater() to append text to the text area. Something like:
import java.util.concurrent.CountDownLatch;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Main extends Application {
private Service<Void> service;
#Override
public void start(Stage primaryStage) throws InterruptedException {
StackPane root = new StackPane();
TextArea ta = new TextArea();
ta.setDisable(true);
root.getChildren().add(ta);
Scene scene = new Scene(root, 200, 200);
primaryStage.setScene(scene);
primaryStage.show();
// run showIT() on a background thread:
Thread thread = new Thread(this::showIT);
thread.setDaemon(true);
thread.start();
}
public static void main(String[] args) {
launch(args);
}
public void showIT() {
try {
Platform.runLater(() -> ta.appendText("\nPrinted"));
Thread.sleep(1000);
Platform.runLater(() -> ta.appendText("\nPrinted"));
Thread.sleep(1000);
Platform.runLater(() -> ta.appendText("\nPrinted"));
Thread.sleep(1000);
} catch (InterruptedException exc) {
Thread.currentThread().interrupt();
}
}
}
For your original code, I have to make some guesses about which parts of the API you're using are long-running and which aren't. I would start by creating a utility log() method that you can call from any thread:
private void log(String message) {
Runnable update = () -> ta.appendText(message);
// if we're already on the FX application thread, just run the update:
if (Platform.isFxApplicationThread()) {
update.run();
}
// otherwise schedule it on the FX Application Thread:
else {
Platform.runLater(update);
}
}
And now you can do something like:
public void launchTest() {
log("\n\n");
log(" Test starting ...\n");
log(" Opening the navigator \n");
Task<Boolean> task = new Task<>() {
#Override
protected Boolean call() throws Exception {
this.setDriver(new FirefoxDriver());
log(" Reaching http://127.0.0.1:8080/booksManager ... \n");
driver.findElement(By.name("email")).sendKeys(pseudo.getText());
driver.findElement(By.name("password")).sendKeys(password.getText());
driver.get("http://127.0.0.1:8080/booksManager");
log(" Setting test data \n");
driver.findElement(By.id("lyes")).click();
log(" Submitting ... \n");
driver.findElement(By.name("submit")).click();
boolean result = driver.getCurrentUrl().equals("http://127.0.0.1:8080/booksManager/Views/index.jsp");
driver.close();
return result ;
}
};
task.setOnSucceeded(e -> {
if (task.getValue()) {
//InputStream input= getClass().getResourceAsStream("https://w0.pngwave.com/png/528/278/check-mark-computer-icons-check-tick-s-free-icon-png-clip-art-thumbnail.png");
//Image image = new Image(input);
//ImageView imageView = new ImageView(image);
Label label = new Label(" Test successed");
testsInfos.getChildren().add(label);
} else {
Text textRes = new Text("\n Test failed ");
textRes.setFill(javafx.scene.paint.Color.RED);
testsInfos.getChildren().add(textRes);
}
});
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}

JavaFX - Cancel Task doesn't work

In a JavaFX application, I have a method which takes a long time on large input. I'm opening a dialog when it is loading and I'd like the user to be able to cancel/close out the dialog and the task will quit. I created a task and added its cancellation in the cancel button handling. But the cancellation doesn't happen, the task doesn't stop executing.
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
// calling a function that does heavy calculations in another class
};
task.setOnSucceeded(e -> {
startButton.setDisable(false);
});
}
new Thread(task).start();
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
System.out.println("Button handled");
task.cancel();
}
);
Why isn't the task getting canceled when the button clicked?
You have to check on the cancel state (see Task's Javadoc). Have a look at this MCVE:
public class Example extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
new AnotherClass().doHeavyCalculations(this);
return null;
}
};
Button start = new Button("Start");
start.setOnMouseClicked(event -> new Thread(task).start());
Button cancel = new Button("Cancel");
cancel.setOnMouseClicked(event -> task.cancel());
primaryStage.setScene(new Scene(new HBox(start, cancel)));
primaryStage.show();
}
private class AnotherClass {
public void doHeavyCalculations(Task<Void> task) {
while (true) {
if (task.isCancelled()) {
System.out.println("Canceling...");
break;
} else {
System.out.println("Working...");
}
}
}
}
}
Note that…
You should use Task#updateMessage(String) rather than printing to System.out, here it's just for demonstration.
Directly injecting the Task object creates a cyclic dependency. However, you can use a proxy or something else that fits your situation.

Javafx Updating UI from a Thread Java 8

I interested in one interesting task. I have UI in JavaFx with another thread which updates UI. I started updates from Platform.runLater. Code:
private void startUpdateDaemon() {
updateUserStatus();
updateTable();
}
private void startUpdateDaemonTask() {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(() -> {
startUpdateDaemon();
});
Thread.sleep(1000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
#Override
public void initialize(URL location, ResourceBundle resources) {
startUpdateDaemonTask();
}
Also I have place in another class where I updates UI:
private void startUpdateDaemonTask() {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(new Runnable() {
#Override
public void run() {
updateGameStatus();
}
});
Thread.sleep(1000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
So, finally I have two places with call "Platform.runLater" and different methods inside.
My question is Can I create only "one" method with one time call "Platform.runLater" and send to this method different methods which will be call ?? May be I can write finish method with consumers and send to him methods 'startUpdateDaemon()' and 'updateGameStatus()'?
Thanks a lot.
You can add a Runnable parameter to your method. This parameter is given to you Platform.runLater:
private void startUpdateDaemonTask(Runnable runner) {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(runner);
Thread.sleep(1000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
Now you can invoke this method with your method references:
startUpdateDaemonTask(this::startUpdateDaemon);
startUpdateDaemonTask(this::updateGameStatus);

Binding Properties and using them during lengthy operations

In my JavaFX Application I want to disable a couple of Buttons during a refresh of the data from a database.
I am using the disableProperty of the Buttons I want to disable.
Here is the basic JavaFX Application, modefied to illustrate my point:
public class BindLengthy extends Application {
BooleanProperty disable = new SimpleBooleanProperty(false);
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.disableProperty().bind(disable);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
disable.set(true);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(BindLengthy.class.getName()).log(Level.SEVERE, null, ex);
}
btn.setText("Done");
}
});
//Do all the other stuff that needs to be done to launch the application
//Like adding btn to the scene and so on...
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
When executed, on the click the Button stays in the "fired" mode, waits for 5 Seconds and then changes text and disables. While I want the text to change later, I want to disableProperty Change to take effect immediately!
I tried putting the lengthy operation, represented by Thread.sleep(5000) into a task and start it on a new Thread(task), but then obviously the text is changes before the Thread awakens.
I can't put the btn.setText("Done")into the Threadas it wouldn't be executed on the JavaFX-Thread(which it needs to). So I tried joining the Thread, yet that gives the same result as not putting it into an extra Thread as well.
How can I force the diableProperty to register the new value before executing my long operation?
Use a Task and use its onSucceeded handler to update the UI:
public class BindLengthy extends Application {
BooleanProperty disable = new SimpleBooleanProperty(false);
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.disableProperty().bind(disable);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
disable.set(true);
Task<String> task = new Task<String>() {
#Override
public String call() throws Exception {
Thread.sleep(5000);
return "Done" ;
}
});
task.setOnFailed(e ->
Logger.getLogger(BindLengthy.class.getName())
.log(Level.SEVERE, null, task.getException()));
task.setOnSucceeded(e -> {
btn.setText(task.getValue());
disable.set(false);
});
Thread t = new Thread(task);
t.setDaemon(true);
t.start();
}
});
//Do all the other stuff that needs to be done to launch the application
//Like adding btn to the scene and so on...
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

Concurrency in JavaFX - task is not finished

I have two tasks, one to change screen opacity, the other to perform a complex computation. I wanted the computation to begin only after the screen fading has actually changed so I wrote the following:
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
stage.getScene().getRoot().setOpacity(0.2);
stage.show();
return null;
}
};
Task<Void> task2 = new Task<Void>() {
#Override
protected Void call() throws Exception {
// Complex computation
return null;
}
};
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent paramT) {
// this busy waiting line was added in attempt to force the
// fading before beginning second task
while (stage.getScene().getRoot().getOpacity() != 0.2) {}
stage.show();
Platform.runLater(task2);
}
});
task2.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent paramT) {
stage.getScene().getRoot().setOpacity(1);
stage.show();
}
});
task.run();
However, sometimes I see the computation beginning and the screen fading only after it's done. Sometimes it does work. So I think there's a threading issue here. Note: When I'm on debug mode and stop at each task it really works everytime. What can be done to force the screen fade before the second task begins?
EDIT:
As suggested, I changed the code to the following:
Task<Void> task1 = new Task<Void>() {
#Override
protected Void call() throws Exception {
// Long computation
return null;
}
};
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
FadeTransition ft = new FadeTransition(Duration.millis(1), stage.getScene().getRoot());
ft.setFromValue(1.0);
ft.setToValue(0.1);
ft.setCycleCount(1);
//ft.setAutoReverse(true);
ft.play();
ft.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
new Thread(task1).run();
}
});
return null;
}
};
task1.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent paramT) {
FadeTransition ft1 = new FadeTransition(new Duration(0.1), stage.getScene().getRoot());
ft1.setFromValue(0.1);
ft1.setToValue(1.0);
ft1.setCycleCount(1);
//ft.setAutoReverse(true);
ft1.play();
//stage.show();
}
});
new Thread(task).run();
Still I see the same behavior.
Replace
new Thread(task).run();
with
new Thread(task).start();

Resources