Threads in JavaFX: Not on FX application thread - multithreading

I want study how to work with Threads in JavaFX. For example, 2 processes, which should change text on the lables every 100 ms, and updating information on the screen also every 100 ms.
But in this case it doesnt works. IDEA writes:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
I have read many examples with the same problem, but any of their solutions doesnt worked.
What I should to do?
Thanks.
sample.fxml
...
<Button fx:id="startBut" layoutX="100.0" layoutY="50.0" mnemonicParsing="false" onAction="#testingOfThread" prefHeight="25.0" prefWidth="65.0" text="Export" />
<Label fx:id="firstStatus" layoutX="100.0" layoutY="100" text="Status" />
<Label fx:id="secondStatus" layoutX="100.0" layoutY="150" text="Status" />
...
Main.java
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
primaryStage.setTitle("Sample");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
//Take control to Controller
public void initializeController(){
FXMLLoader loader = new FXMLLoader();
Controller controller = loader.getController();
controller.setMain(this);
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
package sample;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.*;
public class Controller {
#FXML
private Label firstStatus;
#FXML
private Label secondStatus;
#FXML
public Button startBut;
//Link to MainApp
private Main Main;
//Constructor
public Controller(){
}
//Link for himself
public void setMain(Main main){
this.Main = main;
}
#FXML
private void testingOfThread(){
Task<Void> task = new Task<Void>() {
#Override public Void call() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.interrupted();
break;
}
System.out.println(i + 1);
firstStatus.setText(i+"");
}
return null;
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
Task<Void> task2 = new Task<Void>() {
#Override public Void call() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.interrupted();
break;
}
System.out.println(i + 1);
secondStatus.setText(i+"");
}
return null;
}
};
Thread th2 = new Thread(task2);
th2.start();
}
}

Find the code which update the GUI from a thread other than the application thread,then put them in runLater().
Platform.runLater(new Runnable() {
#Override
public void run() {
//update application thread
}
});

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();
}

What is blocking my UI thread in a JavaFX app?

I have a simple JavaFX app that has two little circles that are supposed to change their location every 0.5 s. Later on this is supposed to become a planets simulation. At the moment the location of my space objects changes in a separate thread which is launched when the button "Start simulation" is pressed. Simultaneously I want my circles (representing the planets) to be drawn again and again always using the current location stored in the spaceObject objects. When I limit the re-drawing to three times (instead of an unlimited amount via a while ( true ) { which is what I actually want) I see that the GUI is not updating while the loop is running. But after the loop is finished the circles move to the new location while the calculations thread in the background is still running. Why is my GUI thread blocked for the time of the while ( i < 3 ) { and how can I simultaneously update my GUI with the current location of the circles? Here is my code:
Main.java
package plantenbahnen;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
package plantenbahnen;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.Pane;
public class Controller implements Initializable {
#FXML private Pane paneDraw;
#FXML private Pane paneControls;
private ArrayList<SpaceObject> universe = new ArrayList<>();
private Thread calcThread;
#Override
public void initialize(URL url, ResourceBundle rb) {
SpaceObject sun = new SpaceObject("sun", 600, 600);
universe.add(sun);
SpaceObject earth = new SpaceObject("earth", 450, 450);
universe.add(earth);
MyCalculations myCalc = new MyCalculations(universe);
calcThread = new Thread(myCalc);
Draw.drawPlanets(universe, paneDraw);
}
#FXML private void buttonStartSimulation(ActionEvent event) throws InterruptedException {
calcThread.start();
Platform.runLater(new Runnable() {
#Override public void run() {
int i = 0;
//while ( true ) { // this line is what I want
while ( i < 3 ) {
Draw.drawPlanets(universe, paneDraw);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e);
}
i++;
}
}
});
}
}
MyCalculations.java
package plantenbahnen;
import java.util.ArrayList;
public class MyCalculations implements Runnable {
ArrayList<SpaceObject> universe;
public MyCalculations (ArrayList<SpaceObject> universe) {
this.universe = universe;
}
#Override
public void run(){
double toAdd = 100.0;
while ( true ) {
for (SpaceObject so: universe) {
so.setx(so.getx() + toAdd);
so.sety(so.gety() + toAdd);
}
if ( toAdd > 0.0 ) {
toAdd = -300.0;
} else {
toAdd = 300.0;
}
}
}
}
Draw.java
package plantenbahnen;
import java.util.ArrayList;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
public class Draw {
public static void drawPlanets(ArrayList<SpaceObject> universe, Pane pane) {
for (Node child: pane.getChildren()) {
System.out.println(child);
}
// Clear objects first
for (SpaceObject so: universe) {
if ( pane.getChildren().contains(so) ) {
pane.getChildren().remove(so);
System.out.println("Removing ... " + so.getName());
}
}
double paneHalfWidth = pane.getPrefWidth() / 2.0;
double paneHalfHeight = pane.getPrefHeight() / 2.0;
double scaleFactor = 0.1;
for (SpaceObject so: universe) {
so.setCenterX(so.getx() * scaleFactor + paneHalfWidth);
so.setCenterY(so.gety() * scaleFactor + paneHalfHeight);
System.out.println("x=" + so.getCenterX() + " y=" + so.getCenterY());
so.setRadius(2);
//so.setColour(Color.BLACK);
pane.getChildren().add(so);
}
}
}
SpaceObject.java
package plantenbahnen;
import javafx.scene.shape.Circle;
public class SpaceObject extends Circle {
private double x,y;
private String name;
SpaceObject(String name, double x, double y) {
this.x = x;
this.y = y;
this.name = name;
}
public double getx(){
return this.x;
}
public void setx(double value){
this.x=value;
}
public double gety(){
return this.y;
}
public void sety(double value){
this.y=value;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
FXMLDocument.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="AnchorPane" prefHeight="700.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="plantenbahnen.Controller">
<children>
<Pane fx:id="paneDraw" prefHeight="700.0" prefWidth="800.0">
<children>
<Pane fx:id="paneControls" prefHeight="66.0" prefWidth="174.0">
<children>
<Button fx:id="buttonStartSimulation" layoutX="26.0" layoutY="21.0" mnemonicParsing="false" onAction="#buttonStartSimulation" text="Start simulation" />
</children>
</Pane>
</children></Pane>
</children>
</AnchorPane>
Thanks a lot in advance for your help.
Try something like this:
#FXML private void buttonStartSimulation(ActionEvent event) throws InterruptedException {
calcThread.start();
Thread updaterThread = new Thread( () -> {
#Override public void run () {
int i = 0;
while ( true ) { // this line is what I want
Platform.runLater( () -> Draw.drawPlanets(universe, paneDraw) );
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e);
}
i++;
}
}
}
updaterThread.setDaemon ( true );
updaterThread.start();
}
You want to make sure all of your calls to Platform.runLater() are short, have no sleeps involved, return quickly, and do minimal calculations -- all of these calls have to be done "in-between" other updates to the UI, like resizing windows, managing button presses, etc.
By the way -- I'm not sure if you need a "calcThread" and an "updaterThread". I suspect they should be one thread. But this is a good proof of concept.
Thanks everybody for your help. I ended up using Timeline. All I had to change is the Controller and it looks like this (only the relevant code is shown):
public class Controller implements Initializable {
// ...
private Thread calcThread;
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(0.5), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Draw.drawPlanets(universe, paneDraw);
}
}));
#Override
public void initialize(URL url, ResourceBundle rb) {
// ...
MyCalculations myCalc = new MyCalculations(universe);
calcThread = new Thread(myCalc);
// ...
}
#FXML private void buttonStartSimulation(ActionEvent event) throws InterruptedException {
calcThread.start();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
}

Running an infinite loop in a JavaFX(Embedded in javax.swing.JFrame) App

I am trying to run an Infinite loop in my JavaFX app.
An infinite while loop is present in my code in the Kulta.java file.
This loop actually freezes my app.
While the same thing works when I port the app to normal javax.swing.
Now since java.lang.Thread doesn't work for javafx, I came accross javafx.concurrent.Task,
which is not working as intended. As one of the main features of multithreading, i.e. running an infinite loop in a GUI app, is not served properly, please help me with the solution.
This is my code:
Urania.java
import java.awt.Toolkit;
import java.awt.Dimension;
import javax.swing.SwingUtilities;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
public class Urania {
public static final Dimension DIMENSION = Toolkit.getDefaultToolkit().getScreenSize();
public static void main(String[] args) {
SwingUtilities.invokeLater(
new Runnable() {
#Override
public void run() {
Kulta kulta = new Kulta();
kulta.setTitle("Abha K Pauri");
kulta.setSize(DIMENSION.width/2, DIMENSION.height/2);
kulta.setLocationRelativeTo(null);
kulta.setDefaultCloseOperation(EXIT_ON_CLOSE);
kulta.setVisible(true);
}
}
);
}
}
And here is my JFrame in which I have embedded my JavaFX app.
Kulta.java
import javax.swing.JFrame
import javafx.embed.swing.JFXPanel;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.control.Button;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.concurrent.Task;
public class Kulta extends JFrame {
private JFXPanel fxpanel;
private Scene scene;
private BorderPane borderpane;
private Button button;
public static final String INVOKE = "INVOKE";
public static final String INTERRUPT = "INTERRUPT";
public static final String[] COLORS = new String[]{"yellow", "pink", "green", "blue", "orange"};
public Kulta() {
fxpanel = new JFXPanel();
add(fxpanel);
Platform.runLater(
new Runnable() {
#Override
public void run() {
Kulta.this.setScene();
Kulta.this.setButton();
Kulta.this.setListener();
}
}
);
}
private void setScene() {
borderpane = new BorderPane();
scene = new Scene(borderpane);
fxpanel.setScene(scene);
}
private void setButton() {
button = new Button(INVOKE);
borderpane.setTop(button);
}
private void setListener() {
Event event = new Event();
button.setOnAction(event);
}
private class Event implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
boolean flag = true;
Task<Void> onInvoke = new Task<Void>() {
#Override
public Void call() {
int count = 0;
flag = true;
button.setText(INTERRUPT);
/* This loop freezes the app. */
while(flag) {
borderpane.setStyle("-fx-color: "+COLORS[count]+";");
count++;
if(count == COLORS.length)
count = 0;
}
return null;
}
};
Task<Void> onInterrupt = new Task<Void>() {
#Override
public Void call() {
button.setText(INVOKE);
if(flag)
flag = false; // This will stop the onInvoke thread
return null;
}
};
Task<Void> change = new Task<Void>() {
#Override
public Void call() {
if(button.getText().equals(INVOKE))
onInvoke().run();
else if(button.getText().equals(INTERRUPT))
onInterrupt().run();
}
};
change.run();
}
}
}
How should I write the loop in order to not let the app freeze.
Any code, solution, link or any help in any form will help a lot.
Thanks in advance.

How to update status and progress from a seperate thread in JavaFX

I need to be able to call a separate thread. The thread analysis a file and pull stats from that.
The analyzing of the file can take up to 2 minutes and during the analysis data is printed to the logs.
I would like to have a TextArea on the front end that needs to print out the analysis (as it analysis) and I would also like to have a progress bar to indicate the progress. All of this is determined inside the separate thread.
What I have done is creating a method in the UI class to add a string to the Text Area and pass in a reference of this class to the launched thread.
My Main Class
package trymutilthread;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class TryMutilThread extends Application {
TextArea ta;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Start");
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
startScheduledExecutorService();
}
});
ta = new TextArea();
VBox vBox = new VBox();
vBox.getChildren().addAll(btn, ta);
StackPane root = new StackPane();
root.getChildren().add(vBox);
Scene scene = new Scene(root, 300, 750);
primaryStage.setScene(scene);
primaryStage.show();
}
private void startScheduledExecutorService() {
final TryMutilThread classI = this;
Task<Void> task = new Task<Void>() {
#Override protected Void call() throws Exception {
try {
ta.appendText("Starting Thread\n");
new SomeProcess(classI).doTheLogic();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
public void appendText(String string) {
ta.appendText(string);
}
}
The class that is executed in the thread
package trymutilthread;
public class SomeProcess {
TryMutilThread taClass = null;
public SomeProcess (TryMutilThread taClass) {
this.taClass = taClass;
}
public void doTheLogic() throws Exception{
taClass.appendText("Staring Thread");
for (int i = 0; i < 5000; i++) {
taClass.appendText(String.valueOf(i));
}
taClass.appendText("Ending Thread");
}
}
Now when I execute this it still only output the text to the TextArea once the thread has ended.
I did had a look at the following 2 posts:
JavaFX update textArea
Java client / server thread null pointer exception when quickly communicating messages
I am not able to get the data printed to logs until the process has ended.
I updated my code to create a Task.
But now I am getting the following error when it executes
Executing com.javafx.main.Main from F:\DEV\Projects\TryMutilThread\dist\run404234128\TryMutilThread.jar using platform C:\Program Files\Java\jdk1.7.0_10/bin/java
java.lang.NullPointerException
at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.computeLinePadding(NGTextHelper.java:405)
at com.sun.javafx.sg.prism.NGTextHelper$TextAttributes.access$200(NGTextHelper.java:292)
at com.sun.javafx.sg.prism.NGTextHelper.buildTextLines(NGTextHelper.java:2357)
at com.sun.javafx.sg.prism.NGTextHelper.validateText(NGTextHelper.java:1847)
at com.sun.javafx.sg.prism.NGTextHelper.getCaretShape(NGTextHelper.java:1435)
at javafx.scene.text.Text.getDecorationShapes(Text.java:1150)
at javafx.scene.text.Text.impl_geomChanged(Text.java:757)
at javafx.scene.text.Text$1.invalidated(Text.java:214)
at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:127)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:161)
at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:67)
at javafx.scene.text.Text.setText(Text.java:188)
at com.sun.javafx.scene.control.skin.TextAreaSkin$17.invalidated(TextAreaSkin.java:610)
at com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:359)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.scene.control.TextInputControl$TextProperty.fireValueChangedEvent(TextInputControl.java:1034)
at javafx.scene.control.TextInputControl$TextProperty.markInvalid(TextInputControl.java:1038)
at javafx.scene.control.TextInputControl$TextProperty.invalidate(TextInputControl.java:978)
at javafx.scene.control.TextInputControl$TextProperty.access$200(TextInputControl.java:950)
at javafx.scene.control.TextInputControl$1.invalidated(TextInputControl.java:119)
at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.scene.control.TextArea$TextAreaContent.insert(TextArea.java:196)
at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:373)
at javafx.scene.control.TextInputControl.insertText(TextInputControl.java:308)
at javafx.scene.control.TextInputControl.appendText(TextInputControl.java:298)
at trymutilthread.TryMutilThread.appendText(TryMutilThread.java:80)
at trymutilthread.SomeProcess.doTheLogic(SomeProcess.java:26)
at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:66)
at trymutilthread.TryMutilThread$2.call(TryMutilThread.java:62)
at javafx.concurrent.Task$TaskCallable.call(Task.java:1259)
at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:334)
at java.util.concurrent.FutureTask.run(FutureTask.java:166)
at java.lang.Thread.run(Thread.java:722)
I then google'd the error and it seems that I need to put the interaction code in a Platform.runlater().
Java client / server thread null pointer exception when quickly communicating messages
I changed the class to execute the thread to be
package trymutilthread;
import javafx.application.Platform;
public class SomeProcess {
TryMutilThread taClass = null;
public SomeProcess(TryMutilThread taClass) {
this.taClass = taClass;
}
public void doTheLogic() throws Exception {
taClass.appendText("Staring Thread");
for (int i = 0; i < 5000; i++) {
//remove this append line
//taClass.appendText(i + "\n");
//And replaced it with platform.runlater
Platform.runLater(new Runnable() {
#Override
public void run() {
taClass.appendText("AGREED" + "\n");
}
});
}
taClass.appendText("Ending Thread");
}
}
It executes without any errors but now it seems to be back to the start... The UI is frozen until all is added to the TextArea
The problem is just that you're flooding the FX Application Thread with too many requests; there's no actual work happening between your Platform.runLater(...) calls. This problem probably goes away with your real application instead of this test, but to mimic the actual long-running work, you can just put a Thread.sleep(...) in there:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class TextAreaBackgroundUpdateExample extends Application {
#Override
public void start(Stage primaryStage) {
final BorderPane root = new BorderPane();
final TextArea textArea = new TextArea();
final ProgressBar progress = new ProgressBar();
final Button startButton = new Button("Start");
final int maxCount = 5000 ;
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
for (int i = 1; i <= maxCount; i++) {
Thread.sleep(10);
final int count = i ;
Platform.runLater(new Runnable() {
#Override
public void run() {
textArea.appendText("Processed part " + count + " (of "+maxCount+")\n");
}
});
updateProgress(i, maxCount);
}
return null;
}
};
progress.progressProperty().bind(task.progressProperty());
Thread t = new Thread(task);
t.setDaemon(true);
t.start();
}
});
root.setCenter(textArea);
root.setTop(progress);
root.setBottom(startButton);
final Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

To Hide JavaFx fxml or JavaFx swing application to System Tray

I want to develop a client app for website .
I want the app to reside in system tray when minimised.
I dont know how to accomplish this task .
Is their any example for this type of operation.
The key here is to set the implicit exit to false Platform.setImplicitExit(false);
Also is important to show and hide the stage in a new thread.
Platform.runLater(new Runnable() {
#Override
public void run() {
stage.show();
}
});
Platform.runLater(new Runnable() {
#Override
public void run() {
stage.hide();
}
});
Next, the whole code:
import java.awt.AWTException;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.URL;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javax.imageio.ImageIO;
/**
*
* #author alvaro
*/
public class TrayTest extends Application {
private boolean firstTime;
private TrayIcon trayIcon;
public static void main(String[] args)
{
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
createTrayIcon(stage);
firstTime = true;
Platform.setImplicitExit(false);
Scene scene = new Scene(new Group(), 800, 600);
stage.setScene(scene);
stage.show();
}
public void createTrayIcon(final Stage stage) {
if (SystemTray.isSupported()) {
// get the SystemTray instance
SystemTray tray = SystemTray.getSystemTray();
// load an image
java.awt.Image image = null;
try {
URL url = new URL("http://www.digitalphotoartistry.com/rose1.jpg");
image = ImageIO.read(url);
} catch (IOException ex) {
System.out.println(ex);
}
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
hide(stage);
}
});
// create a action listener to listen for default action executed on the tray icon
final ActionListener closeListener = new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
System.exit(0);
}
};
ActionListener showListener = new ActionListener() {
#Override
public void actionPerformed(java.awt.event.ActionEvent e) {
Platform.runLater(new Runnable() {
#Override
public void run() {
stage.show();
}
});
}
};
// create a popup menu
PopupMenu popup = new PopupMenu();
MenuItem showItem = new MenuItem("Show");
showItem.addActionListener(showListener);
popup.add(showItem);
MenuItem closeItem = new MenuItem("Close");
closeItem.addActionListener(closeListener);
popup.add(closeItem);
/// ... add other items
// construct a TrayIcon
trayIcon = new TrayIcon(image, "Title", popup);
// set the TrayIcon properties
trayIcon.addActionListener(showListener);
// ...
// add the tray image
try {
tray.add(trayIcon);
} catch (AWTException e) {
System.err.println(e);
}
// ...
}
}
public void showProgramIsMinimizedMsg() {
if (firstTime) {
trayIcon.displayMessage("Some message.",
"Some other message.",
TrayIcon.MessageType.INFO);
firstTime = false;
}
}
private void hide(final Stage stage) {
Platform.runLater(new Runnable() {
#Override
public void run() {
if (SystemTray.isSupported()) {
stage.hide();
showProgramIsMinimizedMsg();
} else {
System.exit(0);
}
}
});
}
}
As far as I know it will be possible in JFX 8. Right now the best solution is to embed your application into AWT and hide the AWT window itself.

Resources