How to get a scheduled event to interact with JavaFX GUI
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);
}
}
java user-interface javafx concurrency scheduled-tasks
add a comment |
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);
}
}
java user-interface javafx concurrency scheduled-tasks
add a comment |
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);
}
}
java user-interface javafx concurrency scheduled-tasks
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);
}
}
java user-interface javafx concurrency scheduled-tasks
java user-interface javafx concurrency scheduled-tasks
edited Jun 10 '17 at 15:06
Blasanka
3,54642134
3,54642134
asked Jun 10 '17 at 14:22
Kamil MarkiewiczKamil Markiewicz
1517
1517
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
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();
}
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f44474391%2fhow-to-get-a-scheduled-event-to-interact-with-javafx-gui%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
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();
}
add a comment |
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();
}
add a comment |
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();
}
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();
}
edited Nov 21 '18 at 12:36
Matthias Wimmer
2,085925
2,085925
answered Jun 10 '17 at 15:16
James_DJames_D
141k9161204
141k9161204
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f44474391%2fhow-to-get-a-scheduled-event-to-interact-with-javafx-gui%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown