I am trying to terminate the thread that runs the JavaFX application when I close the window, without closing any other threads. This is my application class:
package testIt;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import java.io.IOException;
public class MemoryVisualizerApp extends Application{
public static void main(String[] args) {
launch(args);
}
//Setup the scene and launch with given properties
#Override
public void start(Stage primaryStage) throws IOException{
Parent root = FXMLLoader.load(getClass().getResource("/MemoryVisualizer.fxml"));
Scene scene = new Scene(root, 650, 340);
//Set whether the screen should be re-sizable (possibly best size = default)
primaryStage.setResizable(true);
primaryStage.setMinHeight(300);
primaryStage.setMinWidth(550);
primaryStage.setTitle("MINT Performance");
primaryStage.setScene(scene);
scene.getStylesheets().add("testIt/MemoryVisualizer.css");
primaryStage.show();
primaryStage.show();
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>()
{
public void handle(WindowEvent e){
System.out.println("test");
try {
stop();
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
}
}
For testing purposes, this app is the only thing running when I start the program, and so when I close the window, the entire program should terminate. But I still have the option to terminate the program (I'm using eclipse and the red square is still clickable), meaning the thread is still active.
How can I have it so that this thread terminates after closing the GUI window?
You can use Platform.exit() instead of stop()
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>()
{
public void handle(WindowEvent e){
System.out.println("test");
try {
Platform.exit();
}
catch (Exception e1) {
e1.printStackTrace();
}
}
});
Take a look at the JavaFX Application life cycle
Try overriding the stop() method:
#Override
public void stop(){
System.exit(0);
}
This will cause the application to stop running in the IDE.
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();
}
}
}
So, I'm getting a StackOverflowError (Unwrapped from a InvocationTargetException), and I can't for the life of me figure out why.
package gui;
import errorhandling.ErrorLogBook;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class GUIRouter extends Application{
public static void main(String[] args)
{
try
{
Application.launch(GUIRouter.class, (String[])null);
}
catch (Exception e)
{
System.out.println(e.getCause().toString());
}
}
#Override
public void start (Stage primaryStage)
{
try
{
StackPane page = (StackPane) FXMLLoader.load(GUIRouter.class.getResource("LoginScreen.fxml"));
Scene scene = new Scene(page);
primaryStage.setScene(scene);
primaryStage.show();
}
catch (Exception e)
{
ErrorLogBook.logReport(e);
}
}
It fails at the first try block with:
Application.launch(GUIRouter.class, (String[])null);
This is a FXML application that I'm building with NetBeans and JavaFX Scene Builder 2.0
Any ideas about why my code keeps crashing?
My guess would be that your .fxml in "LoginScreen.fxml" has GuiRouter defined as its controller, which it then creates through reflection. My guess is that during that creation it ends up calling start(..) creating a loop.
Try this:
package gui;
import errorhandling.ErrorLogBook;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class GUIRouter extends Application{
public static void main(String[] args)
{
launch(args);
}
#Override
public void start (Stage primaryStage)
{
try
{
StackPane page = FXMLLoader.load(getClass().getResource("/gui/LoginScreen.fxml"));
Scene scene = new Scene(page);
primaryStage.setScene(scene);
primaryStage.show();
}
catch (Exception e)
{
e.printStackTrace();
ErrorLogBook.logReport(e);
}
}
Without having the .fxml file and/or the stack trace, I (see no way but to ) guess the main cause of the exception in exactly the same way as Kiskae did.
However, why you do not replace the line
Application.launch(GUIRouter.class, (String[])null);
with the more simpler form
Application.launch(args);
Or at least
Application.launch(args, new String[0]);
To see if the exception still remains.
Thanks for all the responses!
However, after playing around with it, I have found the root of the problem.
I replaced
StackPane page = (StackPane) FXMLLoader.load(GUIRouter.class.getResource("LoginScreen.fxml"));
with
Parent root = FXMLLoader.load(getClass().getResource("LoginScreen.fxml"));
and now it's working just fine.
I am getting the follow error when I run a JavaFx application as "Run" only. Debug works fine...
Exception in thread "main" java.lang.ExceptionInInitializerError
at Goose.Program.<clinit>(Program.java:26)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:123)
Caused by: 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 com.sun.javafx.application.PlatformImpl.setPlatformUserAgentStylesheet(PlatformImpl.java:550)
at com.sun.javafx.application.PlatformImpl.setDefaultPlatformUserAgentStylesheet(PlatformImpl.java:512)
at javafx.scene.control.Control.<clinit>(Control.java:87)
... 4 more
I have read that you should subclass Application but I am already doing that so I am not sure why it doesn't work... It works fine if I debug but as soon as I try to run the application instead of debugging it, it throws that error message. Which is a little crazy.... Anyone have any idea what the heck is going on? Here is the code.
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class Program extends Application{
TextField input;
GameServer gm;
Player p = new Player();
/**
* Just starts our GameServer
*/
public static void main(String[] args) throws Exception {
launch(args);
}
public static final TextArea textArea = new TextArea();
#Override
public void start(Stage primaryStage) {
p.setState(Player.States.Ready);
p.setAccess(Player.AccessStatus.GameMaster);
input = new TextField();
input.setPrefWidth(500);
input.setOnKeyPressed(event -> {
if(event.getCode().equals(KeyCode.ENTER)){
textArea.appendText("Command: " + input.getText() + "\n");
handleEvent(input);
input.setText("");
}
});
GridPane gridPane = new GridPane();
gridPane.setAlignment(Pos.CENTER);
gridPane.setHgap(10);
gridPane.setVgap(10);
gridPane.add(input, 0, 0, 2, 1);
gridPane.add(textArea, 0,2, 2, 1);
Scene scene = new Scene(gridPane, 530, 250);
primaryStage.setMaxWidth(540);
primaryStage.setMaxHeight(280);
primaryStage.setMinWidth(540);
primaryStage.setMinHeight(280);
primaryStage.setTitle("My Server");
primaryStage.setScene(scene);
primaryStage.show();
primaryStage.setOnCloseRequest(we -> {
try {
textArea.appendText("Shutting down server...");
if(gm.gameworld.getRunning()) {
gm.gameworld.setRunning(false);
Thread.sleep(2000);
}
System.exit(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
run();
}
public void run(){
try {
GameServer gameServer = new GameServer();
this.gm = gameServer;
gameServer.start();
}catch (Exception e){
e.printStackTrace();
}
}
public void handleEvent(TextField textField){
try {
String eventKey = textField.getText().trim();
Event e = gm.gameworld.getEventHandler().stringToEvent.get(eventKey);
if(e != null) {
e.setPlayer(p);
e.ready(gm.gameworld);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
There is a 'public void init() throws Exception' method in Application class whose documentation says:
"The application initialization method. This method is called immediately after the Application class is loaded and constructed. An application may override this method to perform initialization prior to the actual starting of the application". "NOTE: This method is not called on the JavaFX Application Thread. An application must not construct a Scene or a Stage in this method. An application may construct other JavaFX objects in this method."
So, I suppose that you should move: p = new Player(); and
textArea = new TextArea(); to it.
I use a background-thread that should not stop immediately when JavaFX stops (as it does when no stage is open anymore as configured with setImplicitExit), so I do not use setDaemon for this one. But how can I check if JavaFX is shutting down? (This thread should just finish some things and stop itself)
I know I could put an setOnCloseRequest to all stages, but I'd prefer not doing that.
Runtime.getRuntime().addShutdownHook() does not work in this case as the machine does not go down as long as this thread is running).
Override Application.stop() and set a flag. Your thread will need to periodically check that flag.
SSCCE:
import java.util.concurrent.atomic.AtomicBoolean;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class ExitThreadGracefullyOnExit extends Application {
AtomicBoolean shutdownRequested = new AtomicBoolean();
#Override
public void start(Stage primaryStage) {
Label countLabel = new Label();
Thread thread = new Thread(() -> {
try {
int count = 0 ;
while (! shutdownRequested.get()) {
count++ ;
final String message = "Count = "+count ;
Platform.runLater(() -> countLabel.setText(message));
Thread.sleep(1000);
}
System.out.println("Shutdown... closing resources");
Thread.sleep(1000);
System.out.println("Almost done...");
Thread.sleep(1000);
System.out.println("Exiting thread");
} catch (InterruptedException exc) {
System.err.println("Unexpected Interruption");
}
});
VBox root = new VBox(10, countLabel);
root.setAlignment(Pos.CENTER);
thread.start();
Scene scene = new Scene(root, 350, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
#Override
public void stop() {
shutdownRequested.set(true);
}
public static void main(String[] args) {
launch(args);
}
}
i am trying to influence a UI-Element during an event in javaFX.
void buttonClicked(ActionEvent e) {
labelInfo.setText("restarting - might take a few seconds");
jBoss.restart();
labelInfo.setText("JBoss successfully restarted");
}
The action "jBoss.restart()" waits till the JBoss is restarted.
The problem:
the text "restarting - ..." is not displayed. The application waits till the JBoss is restarted and then it shows the Text "JBoss successfully restarted".
My thoughts:
the scene is refreshed AFTER the event is completed. So the first label-change will not happen.
How can i show a info message during an event?
The problem it's that the FX Thread has no safe operations. So I'm guessing that jBoss.restart() it's taking a lot of time. So you have to put this command in a Service. Also I recommend to you a progress indicator to show to the user you are making a long operation.
Here it is an example but I encourage you to go to Concurrency in JavaFX and take a deep look on it. Maybe there are other things that can help you.
import javafx.application.Application;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
private Label labelInfo;
private Button button;
private ProgressIndicator progressIndicator;
#Override
public void start(Stage stage) throws Exception {
VBox vbox = new VBox(5);
vbox.setAlignment(Pos.CENTER);
labelInfo = new Label();
button = new Button("Restart");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
buttonClicked(event);
}
});
progressIndicator = new ProgressIndicator(-1);
progressIndicator.setVisible(false);
vbox.getChildren().addAll(labelInfo, progressIndicator, button);
Scene scene = new Scene(vbox, 300, 200);
stage.setScene(scene);
stage.show();
}
void buttonClicked(ActionEvent e) {
Service<Void> service = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
updateMessage("restarting - might take a few seconds");
// Here the blocking operation
// jBoss.restart();
Thread.sleep(10000);
updateMessage("JBoss successfully restarted");
return null;
}
};
}
};
// Make the progress indicator visible while running
progressIndicator.visibleProperty().bind(service.runningProperty());
// Bind the message of the service to text of the label
labelInfo.textProperty().bind(service.messageProperty());
// Disable the button, to prevent more clicks during the execution of
// the service
button.disableProperty().bind(service.runningProperty());
service.start();
}
}