Issue
I have a Project, where I need to start a GUI but in another thread that is not my main thread.
-> The first main thread starts...
-> If it decides to show a GUI it starts my GUI.
-> All other calculations should still happen in the main thread.
What I mean with this is, that i can't, ounder no circumstance, start the gui in the main thread. And i need to be able to comunicate with my Controller (no controller in the sample). But when i do the normal: .. Start extends Application { .. approach, I cant communicate with the controller anymore because the thread is occupied. The code below should allow me to do everything I need to do, so I hope there is a way I can make it work.
The problem, why my code doesn't work is a Exception:
Exception in thread "Thread-0" java.lang.IllegalStateException: Toolkit not initialized
When calling Platform.runLater() .. in my GuiThread class.
Thanks four your help in advance.
My main class:
public class Start{
private void start() {
GuiThread thread = new GuiThread();
thread.start();
System.out.println("here continues the thread, while the GUI is shown");
}
public static void main(String[] args) {
Start main = new Start();
main.start();
}
}
My custom thread class, where my GUI should be started:
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class GuiThread extends Thread {
@Override
public void run() {
super.run();
// Here i have objects i need to have in the controller
// Then i have to start my GUI
// When calling Platform.runLater()... this error shows : Exception in thread "Thread-0" java.lang.IllegalStateException: Toolkit not initialized
Platform.runLater(() -> {
Group root;
try {
Stage stage = new Stage();
root = new Group();
Scene scene = new Scene(root);
FXMLLoader loader = new FXMLLoader(getClass().getResource("myGui.fxml"));
Node node = loader.load();
// Controller stuff
root.getChildren().add(node);
stage.setScene(scene);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
Just a sample FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<StackPane prefHeight="150.0" prefWidth="200.0" HBox.hgrow="ALWAYS">
<children>
<Label text="left" />
</children>
</StackPane>
<StackPane prefHeight="150.0" prefWidth="200.0" HBox.hgrow="ALWAYS">
<children>
<Label text="right" />
</children>
</StackPane>
</children>
</HBox>
Solution
Usually you'd start any additional threads from the Application.init
or Application.start
methods, but in your case this doesn't seem to be an option.
Starting with JavaFX 9 you could use Platform.startup
the first time you need access to JavaFX. After the Runnable
passed to that method is executed you should be able to use Platfrom.runLater
as you're used to.
Using this approach you need to make sure to shut when all other logic has completed and you're sure you don't need to display any GUI.
Platform.startup(() -> {
Group root;
try {
Stage stage = new Stage();
root = new Group();
Scene scene = new Scene(root);
FXMLLoader loader = new FXMLLoader(getClass().getResource("myGui.fxml"));
Node node = loader.load();
// Controller stuff
root.getChildren().add(node);
stage.setScene(scene);
stage.setOnHidden(evt -> Platform.exit()); // make sure to completely shut down JavaFX when closing the window
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
});
Answered By - fabian