Hi i want to show the progress of a background task.
#FXML
private void addOfficeBarrierSelect() {
App.getInstance().showProgressIndicator(myController);
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
ScreensController colllectScreenController = new ScreensController();
colllectScreenController.loadScreen(Screens.ADD_OFFICE_BARRIER);
colllectScreenController.setScreen(Screens.ADD_OFFICE_BARRIER);
content.setContent(null);
content.setContent(colllectScreenController);
return null;
}
#Override
protected void succeeded() {
App.getInstance().hideProgressIndicator(myController);
}
#Override
protected void failed() {
super.failed();
App.getInstance().hideProgressIndicator(myController);
}
};
new Thread(task).start();
}
And below code is the progress popup i have used in my application.
public void showProgressIndicator(ScreensController myController) {
myController.setDisable(true);
if (popupProgressIndicator == null) {
JFXSpinner spinner = new JFXSpinner();
spinner.setLayoutX(0);
spinner.setLayoutY(0);
spinner.setPrefHeight(200);
spinner.setPrefWidth(200);
popupProgressIndicator = new Popup();
popupProgressIndicator.getContent().add(spinner);
}
if (!popupProgressIndicator.isShowing()) {
popupProgressIndicator.show(primaryStage);
popupProgressIndicator.centerOnScreen();
}
}
public void hideProgressIndicator(ScreensController myController) {
myController.setDisable(false);
if (popupProgressIndicator.isShowing())
popupProgressIndicator.hide();
}
The problem is when i run the app , No FX app thread exception occuring..
Exception in thread "Thread-6" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-6 and pointing at void call() method inside Task thread.
Is there any solution for it..
James_D solution Actually works for me.But i got to change the code to this.
showProgressIndicator(myController);
Task<FXMLLoader> task = new Task<FXMLLoader>() {
#Override
protected FXMLLoader call() throws Exception {
FXMLLoader loader = new FXMLLoader(App.class.getResource(Screens.ADD_MEMBER));
return loader;
}
#Override
protected void succeeded() {
super.succeeded();
hideProgressIndicator(myController);
}
#Override
protected void failed() {
super.failed();
hideProgressIndicator(myController);
}
};
new Thread(task).start();
After changing the code again progress not showing.just followed #james_D anwser.
You are getting the exception because you are updating the UI from a background thread. You need to update the UI from the FX Application Thread. You can do this with a call to Platform.runLater() if you need to make incremental updates during your call() method execution, or (as it seems in this case: I'm not sure exactly what the loadScreen and setScreen methods do, but I am assuming they do not change the existing scene graph) if you only need to update the UI once the task is complete, update the UI in the succeeded method of the task:
#FXML
private void addOfficeBarrierSelect() {
App.getInstance().showProgressIndicator(myController);
Task<ScreensController> task = new Task<Void>() {
#Override
protected ScreensController call() throws Exception {
ScreensController colllectScreenController = new ScreensController();
colllectScreenController.loadScreen(Screens.ADD_OFFICE_BARRIER);
colllectScreenController.setScreen(Screens.ADD_OFFICE_BARRIER);
return colllectScreenController;
}
#Override
protected void succeeded() {
super.succeeded();
content.setContent(getValue());
App.getInstance().hideProgressIndicator(myController);
}
#Override
protected void failed() {
super.failed();
App.getInstance().hideProgressIndicator(myController);
}
};
new Thread(task).start();
}
Here's a simple example to illustrate how it could be done, using an AnimationTimer and some state (busy and labelText). The state of the button and the label are amended on every tick of the animation timer. And the task is the thing that updates the state (NOT the UI).
The state here would require to be synchronized, of course, so you'd perhaps wish to implement a postMessage() method that would allow the task to report state back to the UI, which would then add to a message queue, which would then in turn be consumed by the AnimationTimer. However, the code provided below is for ILLUSTRATIVE PURPOSES ONLY. The point is to highlight that you cannot update UI controls from a non-UI thread.
I've also added a ProgressBar and bound its progressProperty to the progressProperty of the task. So, there's TWO ways of managing updates for you :)
Using progressProperty() binding is probably the best here. No need for the AnimationTimer in that instance. But, if you do implement that way of doing it, then you should implement a message queue on the UI thread and synchronize state change via a postMessage() method.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Progress extends Application {
String labelText = "Foobar";
boolean busy = false;
#Override
public void start(Stage stage) {
Label label = new Label();
ProgressBar progress = new ProgressBar();
progress.setProgress(0);
Button button = new Button("Test");
button.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
System.out.println("Button has been pressed...");
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
busy = true;
System.out.println("Calling long running task...");
for (int i = 0; i <= 100; i++) {
labelText = "Progress: " + i;
updateProgress(i, 100);
Thread.sleep(50);
}
return null;
}
#Override
protected void succeeded() {
System.out.println("Succeeded");
busy = false;
}
#Override
protected void failed() {
System.out.println("Failed");
}
};
progress.progressProperty().bind(task.progressProperty());
Thread thread = new Thread(task);
thread.start();
}
});
AnimationTimer timer = new AnimationTimer() {
#Override
public void handle(long now) {
label.setText(labelText);
button.setDisable(busy);
}
};
timer.start();
VBox vbox = new VBox(label, progress, button);
Scene scene = new Scene(vbox, 400, 300);
stage.setTitle("Test");
stage.setScene(scene);
stage.show();
}
}
NB the task must do nothing with controls, only state.
See also here: https://docs.oracle.com/javafx/2/threads/jfxpub-threads.htm
And this SO question: JavaFX : How to pass value from background thread to JavaFX UI thread with Task.
Related
I have a JavaFX app that runs two threads at startup. One is the UI thread that must not be blocked. The other is a thread that prepares a large table (it takes about 20 seconds). I want to signal the UI thread when the second thread is done, so it can change the color of a rectangle from red to green. I have tried solutions using the synchronized keyword, but they all caused the UI thread to be blocked.
I used the following resources to obtain the below code.
Concurrency in JavaFX
Execute task in background in JavaFX
The below app simply displays a red rectangle which, after five seconds, turns to green. Explanations after the code.
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class JfxTask0 extends Application {
private Task<Void> task;
#Override
public void init() throws Exception {
task = new Task<Void>() {
#Override
protected Void call() throws Exception {
try {
Thread.sleep(5000L);
}
catch (InterruptedException xInterrupted) {
if (isCancelled()) {
System.out.println("CANCELLED!");
}
}
return null;
}
};
}
#Override
public void start(Stage primaryStage) throws Exception {
Rectangle rect = new Rectangle(25.0d, 25.0d, 50.0d, 50.0d);
rect.setFill(Color.RED);
task.stateProperty().addListener(new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends State> workerStateProperty,
Worker.State oldValue,
Worker.State newValue) {
if (newValue == Worker.State.SUCCEEDED) {
rect.setFill(Color.GREEN);
}
}
});
new Thread(task).start();
Group root = new Group();
ObservableList<Node> children = root.getChildren();
children.add(rect);
Scene scene = new Scene(root, 100.0D, 100.0D);
primaryStage.setScene(scene);
primaryStage.setTitle("Task");
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Method init() is declared in class javafx.application.Application. It is executed before method start() and, as its name suggests, is used to initialize the JavaFX application. In this method I create the background task. The background task merely sleeps for five seconds.
In method start() I create the red rectangle and then launch the background task but before launching the task, I register a listener with one of the task's properties. This property will be set to a particular value once the task completes.
After the task is launched, I build the rest of the GUI and display it.
Once the task terminates, then listener is invoked and it sets the rectangle color to green.
You can use a handler for this problem.
there is example
Add this in your main activity and create handler.
Handler h = new Handler(){
#Override public void handleMessage(Message msg){
switch(msg.what){
case 1:
// what you want when complete
break;
default:
break;
}
}
}
MyThread thread = new MyThread(new Messenger(h));
thread.start();
Now add this in your thread file.
public class MyThread{
Messenger m;
public MyThread(Messenger m){
this.m = m;
}
public void run(){
super.run();
// your codes
//
//when your task complete
Message msg = Message.obtain();
msg.what = 1;
msg.obj = "";
try{
m.send(msg);
}catch(IOException e){
e.printStackTrace();
}
}
}
I have initialized parseBtn onAction but because the work in parseFunction will be more than one minute I created Task to do the work in new Thread(task).start() the function is working in background will but after finishing parseBtn doesn't respond any more.
I think because the work in another thread the button doesn't notify that work finished
#FXML private Button parseBtn;
public void initialize(){
parseBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
new Thread(task).start();
}
});
}
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
//do something
return null;
}
};
the process inside the task is done but the button doesn't respond any more
I found the solution my mistake was that task can't be calling multiple time.
class parser extends Thread{
#Override
public void run() {
//work to do
}
}
public void initialize(){
parseBtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
parser p = new parser();
p.start();
}
});
}
I'm a beginner Java programmer trying to figure this out. I have a piece of code that does some calculation and updates a label in my JavaFX GUI. It runs every 100ms using a ScheduledExecutorService and a Runnable. The problem is it cannot update the Label of the GUI. I have spent yesterday looking for a way to do it and most of the topics seem to be solved with the use of Platform.runLater but even putting my code into the runLater runnable seems to still not work. Another thing I have found is using the Java concurrency framework, but I don't know how to use that for a repeating scheduled service like this. Here's how I wrote the code:
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Runnable loop = new Runnable() {
public void run() {
Platform.runLater(new Runnable() {
#Override public void run() {
double result = calculation();
labelResult.setText("" + result);
}
});
}
};
executor.scheduleAtFixedRate(loop, 0, 100, TimeUnit.MILLISECONDS);
How could I do this?
EDIT:
I'm including a full example.
Main class:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
public class Main{
private static long value = 0;
private static Gui gui;
public static void main(String[] args){
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Runnable loop = new Runnable() {
public void run() {
Platform.runLater(new Runnable() {
#Override public void run() {
calculate();
}
});
}
};
executor.scheduleAtFixedRate(loop, 0, 100, TimeUnit.MILLISECONDS);
Application.launch(Gui.class, args);
}
public static void calculate(){
double result = value++;
gui.setResult(result);
}
public static void setGui(Gui ref){
gui = ref;
}
}
Gui class:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Gui extends Application{
private Stage window;
private Scene scene;
private HBox layout = new HBox();
private Label result = new Label("TEST");
#Override
public void start(Stage stage) throws Exception {
window = stage;
layout.getChildren().addAll(result);
Main.setGui(this);
scene = new Scene(layout, 1280, 720);
window.setTitle("Example");
window.setResizable(false);
window.setScene(scene);
window.show();
}
public void setResult(double res){
result.setText("" + res);
}
}
The overall structure of your application is wrong. The reason that your scheduled executor service is failing is that you attempt to start it before you launch the JavaFX application, and consequently your first call to Platform.runLater(...) happens before the FX toolkit has been started and before the FX Application Thread is running.
If you wrap the call to Platform.runLater() in a try block and catch the exception:
Runnable loop = new Runnable() {
public void run() {
try {
Platform.runLater(new Runnable() {
#Override
public void run() {
calculate();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
};
you will see the exception:
java.lang.IllegalStateException: Toolkit not initialized
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:273)
at com.sun.javafx.application.PlatformImpl.runLater(PlatformImpl.java:268)
at javafx.application.Platform.runLater(Platform.java:83)
at Main$1.run(Main.java:17)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
(Incidentally, handling the exception will also allow the executor to continue, so eventually it will "recover" as the toolkit will be started at some point. You may also see other exceptions, because, e.g. there are race conditions on the gui field: some iterations of the executor may get called before gui is initialized.)
You should think of the Application.start() method essentially as the entry point for the application. When you call launch() (or when it is called for you, which happens in most final deployment scenarios), the FX Toolkit is started, then an instance of the Application subclass is created, and start() is invoked on that instance on the FX Application Thread.
So the way to structure this is to drive it all from the start() method. Create an instance of your GUI class there, create an instance of the class that is running your scheduled executor, tie them together, and then just display the UI in the provided stage. Here's one possible example of this refactoring:
Main.java:
import javafx.application.Application;
import javafx.stage.Stage;
public class Main extends Application{
private Stage window;
#Override
public void start(Stage stage) throws Exception {
window = stage;
Gui gui = new Gui();
UpdateService service = new UpdateService(gui);
service.startService();
window.setTitle("Example");
window.setResizable(false);
window.setScene(gui.getScene());
window.show();
}
public static void main(String[] args) {
launch(args);
}
}
UpdateService.java:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Platform;
public class UpdateService {
private long value = 0;
private final Gui gui;
public UpdateService(Gui gui) {
this.gui = gui;
}
public void startService() {
// create executor that uses daemon threads;
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t;
});
Runnable loop = new Runnable() {
public void run() {
Platform.runLater(new Runnable() {
#Override
public void run() {
calculate();
}
});
}
};
executor.scheduleAtFixedRate(loop, 0, 100, TimeUnit.MILLISECONDS);
}
public void calculate() {
double result = value++;
gui.setResult(result);
}
}
Gui.java:
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
public class Gui {
private Scene scene;
private HBox layout = new HBox();
private Label result = new Label("TEST");
public Gui() {
layout.getChildren().addAll(result);
scene = new Scene(layout, 1280, 720);
}
public Scene getScene() {
return scene ;
}
public void setResult(double res){
result.setText("" + res);
}
}
Finally, note that a cleaner way to get regularly-repeating functionality that runs on the FX Application Thread is to use the Animation API (as in JavaFX periodic background task):
public void startService() {
Timeline timeline = new Timeline(new KeyFrame(Duration.millis(100), e -> calculate()));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
}
I'm working on an Omaha online poker client written in javaFX+java.
I need to show an anchorPane containing 3 buttons after control.call() finishes executing. I know for a fact that control.call() finishes executing but for some reason task.setOnSucceeded(new EventHandler() {'s handle method does not update the User interface.
What am I doing wrong?
public void newRound() {
sog = StateOfGame.PREFLOP;
ControlGameOnNewThread control = new ControlGameOnNewThread();
Task task = new Task() {
#Override
protected Object call() throws Exception {
control.call();
return null;
}
};
task.setOnSucceeded(new EventHandler() {
#Override
public void handle(Event event) {
if (client.getAction() == FirstToAct.me) {
System.out.println("Task finsished");
showOptions(client.getToCall());
opponentBetField.setText(new Integer(opp.chipsInvested).toString());
myBetField.setText(new Integer(client.chipsInvested).toString());
System.out.println("Task finsished");
}
}
});
new Thread(task).start();
}
The problem is that you are updating the user interface in the other thread.. if you are in the other thread and you want to update the user interface
You need to call the
Platform.runLater(new Runnable() {
#Override public void run() {
//Update UI here
}
});
calling this will call the main thread and update all the necessary update to the user interface
EDIT
public void newRound() {
sog = StateOfGame.PREFLOP;
ControlGameOnNewThread control = new ControlGameOnNewThread();
Task task = new Task() {
#Override
protected Object call() throws Exception {
control.call();
return null;
}
};
task.setOnSucceeded(new EventHandler() {
#Override
public void handle(Event event) {
if (client.getAction() == FirstToAct.me) {
Platform.runLater(new Runnable() {
#Override public void run() {
System.out.println("Task finsished");
showOptions(client.getToCall());
opponentBetField.setText(new Integer(opp.chipsInvested).toString());
myBetField.setText(new Integer(client.chipsInvested).toString());
System.out.println("Task finsished");
}
});
}
}
});
new Thread(task).start();
}
As you override the call() method, you can override the succeeded() method: (See last example in the javadoc of Task.)
#Override protected void succeeded() {
super.succeeded();
updateMessage("Done!");
}
succeeded() is already called on the JavaFX Application Thread, so you do not need to do this for yourself.
Are you sure, that the code in your handle method isn't called? Maybe a NullPointerException is thrown, which you might not see? Is the comparison working as expected?
Try moving the code with if (client.getAction()... into the overridden succeeded() method and put a println before the if-statement in order to see whether it is called or not.
(EDIT: typos)
Your information has been helpful, but i think all methods suggested would have worked (including my initial one). The problem is that the task was never ended that's why the onTaskSucceded was never executed. In order to have the task exit after it has finished, i had to se the daemon to true:
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
I'm trying to simulate a basic thermostat in an application GUI.
I want to update a label box value every 2 secs with the new temperature value.
For example, my intial temperature will be displayed as 68 degrees and updated to 69, to 70, etc. till 75 every 2 seconds.
This is a piece of code I wrote in Java fx. controlpanel is object of te form where the label box is present. It updates only the final value as 75. It doesnt update it every 2 secs. I have written a method pause to cause a 2 secs delay. All labels are updated with their final values but not updated every 2 secs. When I debug, I can see that the values are increased by one every 2 secs. This code is written in button onClick event
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
int i=0;
Timer asd = new Timer(1000,null);
asd.setDelay(1000);
while(i < 10)
{
jTextField1.setText(Integer.toString(i));
i++;
asd.start();
}
}
To solve your task using Timer you need to implement TimerTask with your code and use Timer#scheduleAtFixedRate method to run that code repeatedly:
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
System.out.print("I would be called every 2 seconds");
}
}, 0, 2000);
Also note that calling any UI operations must be done on Swing UI thread (or FX UI thread if you are using JavaFX):
private int i = 0;
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
jTextField1.setText(Integer.toString(i++));
}
});
}
}, 0, 2000);
}
In case of JavaFX you need to update FX controls on "FX UI thread" instead of Swing one. To achieve that use javafx.application.Platform#runLater method instead of SwingUtilities
Here is an alternate solution which uses a JavaFX animation Timeline instead of a Timer.
I like this solution because the animation framework ensures that everything happens on the JavaFX application thread, so you don't need to worry about threading issues.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
public class ThermostatApp extends Application {
#Override public void start(final Stage stage) throws Exception {
final Thermostat thermostat = new Thermostat();
final TemperatureLabel temperatureLabel = new TemperatureLabel(thermostat);
VBox layout = new VBox(10);
layout.getChildren().addAll(temperatureLabel);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20; -fx-font-size: 20;");
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) throws Exception {
launch(args);
}
}
class TemperatureLabel extends Label {
public TemperatureLabel(final Thermostat thermostat) {
textProperty().bind(
Bindings.format(
"%3d \u00B0F",
thermostat.temperatureProperty()
)
);
}
}
class Thermostat {
private static final Duration PROBE_FREQUENCY = Duration.seconds(2);
private final ReadOnlyIntegerWrapper temperature;
private final TemperatureProbe probe;
private final Timeline timeline;
public ReadOnlyIntegerProperty temperatureProperty() {
return temperature.getReadOnlyProperty();
}
public Thermostat() {
probe = new TemperatureProbe();
temperature = new ReadOnlyIntegerWrapper(probe.readTemperature());
timeline = new Timeline(
new KeyFrame(
Duration.ZERO,
new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent actionEvent) {
temperature.set(probe.readTemperature());
}
}
),
new KeyFrame(
PROBE_FREQUENCY
)
);
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
}
class TemperatureProbe {
private static final Random random = new Random();
public int readTemperature() {
return 72 + random.nextInt(6);
}
}
The solution is based upon the countdown timer solution from: JavaFX: How to bind two values?
Calling Platform.runLater worked for me:
Platform.runLater(new Runnable() {
#Override
public void run() {
}
});