Java Fx Splash screen with spring boot - java

JavaFX 11 and Spring Boot 2.0.
I want to display a splash screen until Spring inits all of its necessary beans, and in the spring.run() I want to close the splash stage(or at least after x amount of seconds). Such that connecting to the DB creating POJOs etc. But when I try to show my splash screen before FX thread kicks in so it throws:
Caused by: java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = main()
I even tried in the Platform.runLater() but still did not work out. Is there any work around for this problem? Thanks.
public class StartUp extends Application{
public static void main(String[] args) {
loadSplashScreen();
appContext = SpringApplication.run(StartUp.class);
launch(args);
}
#Override
public void start(Stage stage) {
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
stage.setScene(new Scene(root, 300, 275));
stage.show();
}
static void loadSplashScreen() {
Stage splashStage = new Stage();
try {
BorderPane splashPane = FXMLLoader.load(getClass().getResource("splash.fxml"));
Scene splashScene = new Scene(splashPane);
splashStage.setScene(splashScene);
splashStage.show();
setFadeInOut(splashPane, splashStage);
} catch (IOException e) {
e.printStackTrace();
}
}
static void setFadeInOut(Parent splashScene, Stage splashStage) {
FadeTransition fadeIn = new FadeTransition(Duration.seconds(3), splashScene);
fadeIn.setFromValue(0);
fadeIn.setToValue(1);
fadeIn.setCycleCount(1);
FadeTransition fadeOut = new FadeTransition(Duration.seconds(3), splashScene);
fadeOut.setFromValue(1);
fadeOut.setToValue(0);
fadeOut.setCycleCount(1);
fadeIn.play();
fadeIn.setOnFinished((e) -> fadeOut.play());
fadeOut.setOnFinished((e) -> splashStage.close());
}
}

In your code you have a method called loadSplashScreen() which you call before the Application.launch(). It will be the call to the launch method that starts the JavaFX thread which is why your loadSplashScreen() method fails i.e. the JavaFX thread hasn't even started when this method is called.
You might want to take a look here at this Oracle tutorial on PreLoaders to understand how to understand a basic example before you try work with starting JavaFX with Spring Boot.
Although I haven't booted JavaFX from Spring Boot, I have done similar in an OSGi bundle and you might like to take a look at my FlexFx GitHub repo here which might give you a few pointers on how to use a pre-loader with Spring Boot but note I currently do not have the ability to display a splash screen in my project.
Finally, your issue would happen on JavaFX-8, 9 or 10. It's not specific to JavaFX-11.

Related

ScalaFX (or JavaFX) - Calling GUI from another main class multiple times

I am following on from a previous question: link
I am writing a standalone visualization package for a simulation package
The simulation package is written by one of our team in Scala
What I want to do, is create a matplotlib like package for this simulation package. My envisioned end use would look something like matplotlib:
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
plt.show()
plt.plot([10, 20, 3000, 4121212])
plt.ylabel('some numbers')
plt.show()
For my package, I would do something analogous. Assume here that myFXPackage is a chart package written in ScalaFX. In my DRIVER class:
Import myFXPackage
// run some simulation code here...
myFXPackage.plot(results)
// run some MORE simulation code here...
myFXPackage.plot(results)
Now it seems that for ScalaFX, there can only be one entry point for the whole app; this is the JFXApp class. However, I want to import the package and simply run this code multiple times in my DRIVER class as shown above. So somehow how DRIVER class would call ScalaFX and run the plot, close it, then run another plot.
Is this feasible? If so how would I go about doing this?
Most JavaFX example code conflates the main method with the Application subclass, and in many cases even does the UI layout, etc., in the same class. There's not necessarily a reason to do this, and in the latter case it's not a particularly good design.
Assuming you separate concerns appropriately in your UI code, you might have a class like
public class MainUI {
private BorderPane root ;
public MainUI() {
root = new BorderPane();
// do layout, register event handlers, etc etc
}
public Pane getView() {
return root ;
}
}
Then if you want a standalone JavaFX application, you would do
public class JavaFxApp extends Application {
#Override
public void start(Stage stage) {
MainUI ui = new MainUI();
Scene scene = new Scene(ui.getView());
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
But this isn't the only place you can use the UI class. In JavaFX (as in most UI toolkits), you must create a new stage, and perform other UI tasks, on the UI thread (i.e. the FX Application Thread).
Additionally, in JavaFX you must explicitly start up the FX toolkit. In the code above, this is done by the Application.launch() method.
JavaFX 9 introduced a Platform.startup(Runnable), which starts the FX toolkit and executes the supplied Runnable on the FX Application Thread.
So, using JavaFX 9 and later, you can have code like
public class SomeApp {
public static void main(String[] args) {
// start FX toolkit
// Since we don't want it to exit when we close a window, set
// implicit exit to false:
Platform.startup(() -> Platform.setImplicitExit(false));
// do some other stuff...
// whenever you need to:
Platform.runLater(SomeApp::showStage);
// do other stuff...
}
private static void showStage() {
Scene scene = new Scene(new MainUI().getView());
Stage stage = new Stage();
stage.show();
}
}
Prior to JavaFX 9, you can still do this, but you need to launch a "blank" application:
public class FXStartup extends Application {
#Override
public void start(Stage ignored) {
// probably need this:
Platform.setImplicitExit(false);
}
}
Note that the launch() method blocks until the toolkit exits, so your application needs to start it in another thread:
public class SomeApp {
public static void main(String[] args) {
// start FX toolkit
new Thread(() -> Application.launch(FXStartup.class, args)).start();
// do some other stuff...
// whenever you need to:
Platform.runLater(SomeApp::showStage);
// do other stuff...
}
private static void showStage() {
Scene scene = new Scene(new MainUI().getView());
Stage stage = new Stage();
stage.show();
}
}

JavaFX Update Issue?

I'm working on a game in JavaFX, and right now I'm trying to create a loading screen, since loading the assets takes some time. I've created a LoadingPane class that displays several progress bars, and I know for sure that it works. However, in the below code, the loading pane will not be visible until after the loadAssets function, even though I'm adding it beforehand. When I run the below code, I get a blank stage for the time it takes for the assets to load, and then a screen with the completed progress bars.
I haven't been able to find anyone with similar issues, or any sort of refresh or update function to force the scene to display the loading pane before continuing with the program.
Note: I've deleted some irrelevant code setting up keyboard input handling.
public class Main extends Application{
Pane root = new Pane();
Scene mainScene = new Scene(root, Constants.WINDOW_WIDTH, Constants.WINDOW_HEIGHT, Color.BLACK);
static LoadingPane loadingPane = new LoadingPane(3);
private static int loadingIndex = 0;
public static void main(String[] args) {
if(Constants.DEBUG_MODE)
System.out.println("WARNING: Game has launched in debug mode!");
launch(args);
}
public static void updateProgress(double percent){
loadingPane.setBarLength(loadingIndex, percent);
}
public static void loadAssets(){
RoomLoader.createRooms();
updateProgress(1.0);
loadingIndex++;
ProjectileLoader.load("imgs/projectiles/");
ProjectileLoader.load(Constants.BATTLE_IMAGES_FILEPATH);
updateProgress(1.0);
loadingIndex++;
BattleLoader.createBattles();
updateProgress(1.0);
loadingIndex++;
}
public static void updateProgress(double percent){
loadingPane.setBarLength(loadingIndex, percent);
}
#Override
public void start(final Stage primaryStage) {
//root.getChildren().add(new javafx.scene.image.ImageView(new Image("imgs/loading.png")));
root.setLayoutX(0);
primaryStage.setScene(mainScene);
primaryStage.show();
primaryStage.toFront();
primaryStage.setTitle("Branch");
primaryStage.setResizable(false);
//primaryStage.getIcons().add(new Image("core/imgs/soul/red.png"));
//This allows the closing of the primaryStage to end the program
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>(){
#Override
public void handle(WindowEvent t) {
System.exit(0);
}
});
root.resize(Constants.WINDOW_WIDTH, Constants.WINDOW_HEIGHT);
primaryStage.getIcons().add(new Image("imgs/icon.png"));
//End GUI setup
//The problem lines
root.getChildren().add(loadingPane);
//refresh root?
loadAssets();
}
}
EDIT: Working Code
For anyone who arrives here with a similar issue, below is the code I used to get this to work:
I replaced this:
//The problem lines
root.getChildren().add(loadingPane);
//refresh root?
loadAssets();
With this:
root.getChildren().add (loadingPane);
Task<Integer> loadingTask = new Task<Integer>() {
#Override protected Integer call() throws Exception {
loadAssets();
return 1;
}
};
loadingTask.setOnSucceeded(new EventHandler<WorkerStateEvent>(){
#Override
public void handle(WorkerStateEvent t){
loadingPane.setVisible(false);
load(); //note: this function sets up the actual game
//updating the GUI, adding game elements, etc
}
});
new Thread(loadingTask).start();
I can't say that this is the best way to go about this, but I can say that it works. Good luck!
You need a separate thread for the update method.
Code runs in a linear fashion, one bit of code runs then the next. With a separate thread, the two “lines” of code can run side by side. The process runs and the GUI updates at the same time.
JavaFX application runs on specific thread called Application Thread, in other to make GUI responsive while doing some expensive operation, like in your case loading assets, you will need to load assets on another Thread that you create yourself or you can use Task which is one of JavaFX classes meant to be used in such use cases.
I suggest you to read about Task in official javadocs

Could not initialize class javafx.stage.screen

I'm trying to launch 2 javaFX applications, obviously Application#launch() can only be called once per JVM.
After some browsing one told me to manually create a Scene and call Application#start() for the second Application, and so I did:
public class Launcher extends Application {
private Stage primaryStage;
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setResizable(false);
this.primaryStage.setTitle("title");
initLayout();
}
public void initLayout() throws IOException {
Parent root = FXMLLoader.load(Launcher.class.getResource("myFile.fxml"));
Scene scene = new Scene(root, 450, 300);
this.primaryStage.setScene(scene);
this.primaryStage.show();
}
}
And loading it (from another class) with:
try {
Application launcher = new Launcher();
launcher.start(new Stage());
} catch (Exception e) {}
Though this results in an error saying
Exception in thread "Thread_number" java.lang.NoClassDefFoundError: Could not initialize class javafx.stage.Screen
at javafx.stage.Window.<init><Unknown Source>
at javafx.stage.Stage.<init><Unknown Source>
at javafx.stage.Stage.<init><Unknown Source>
at javafx.stage.Stage.<init><Unknown Source>
at classILaunchedFrom.methodLaunchedFrom<Main.java:lineNumber>
Does anyone know what I'm doing wrong, because I'm completely at a loss here. Been cracking my skull far too long with javaFX now.
Why do you want to completely separate applications? Just make a class that extends stage for your second window and then call it's .show() method from inside the main javafx application thread. Javafx is rather picky when doing things, only one application instance at a time, all work must be done on the same thread and so on.

JavaFX 8 JVM remains after exit

I have created a JavaFX application, and noticed that after I close the main stage, the following happens:
The Applications "stop" method is called
The main method continues
After the program leaves the main method, the JVM can not close
I do not create any threads (explicitly not, at least). The threads that are running at this point are (from the debug console):
InvokeLaterDispatcher
Prism Font Disposer
'pool-2-thread-1' (I don't know what this is - ThreadPoolExecutor parts are in its stacktrace)
HSQLDB Timer (I'm using a HSQLDB file db on the development/debug system)
FX Access Thread (Visual Debugger)
Abandoned connection cleanup thread
Which of these threads can stop the JVM from closing? I would think that all of these should be daemon threads...
Here is my Application code:
public class MainApp extends Application {
#Override
public void start(Stage stage) throws Exception {
final FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/Main.fxml"));
Parent root = fxmlLoader.load();
Scene scene = new Scene(root);
scene.getStylesheets().add("/styles/Styles.css");
//... Scene/stage setup here
stage.show();
}
#Override
public void stop() throws Exception {
super.stop();
}
public static void main(String[] args) {
launch(args);
}
}
Adding a System.exit(0); on the end of the stop() method fixes this, but I'm not sure if this is the best solution to the problem...
Any ideas?
Thx in advance
You say you are using a HSQLDB, you don't show the code where you initialize it. In your stop() method, close that connection.

JavaFX application still running after close

I'm having problem to close my javaFX application, when I click the close button from my stage, my application disappears but if I look for it in my task manager my application still there without close.
I've tried to use this code below to force it close the main thread and all childrens threads but the problem persists.
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
Platform.exit();
}
});
Does your application spawn any child threads? If so have you ensured that you terminate them (assuming that they're not daemon threads)?
If your application spawns non-daemon threads then they (and therefore your app) will continue to live on until such time you kill the process
The only way was to call System.exit(0);
primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
#Override
public void handle(WindowEvent t) {
Platform.exit();
System.exit(0);
}
});
[EDITED]
System.exit will just hide your application, if you open SO's manager task your application will be there. The correct way is to check your Threads, one by one and close all before close application.
First Look Here
public void start(Stage stage) {
Platform.setImplicitExit(true);
stage.setOnCloseRequest((ae) -> {
Platform.exit();
System.exit(0);
});
}
I currently had this problem while using an ThreadExecutor in the controller.
Application does not exit if the ThreadExecutor is not shutdown.
See here:
how-to-shut-down-all-executors-when-quitting-an-application
As it can be a problem to recognize an application exit in the controller, you can get a reference to the controller from your Application class like so (using the sample application from Eclipse):
public class Main extends Application {
private SampleController controller;
#Override
public void start(Stage primaryStage) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("MyFXML.fxml"));
BorderPane root = (BorderPane)loader.load(getClass().getResource("Sample.fxml").openStream());
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
controller = loader.<SampleController>getController();
}
catch(Exception e)
{
e.printStackTrace();
}
}
Your Application overrides the stop method, where you can call a housekeeping method of the controller (i use a method called startHousekeeping):
/**
* This method is called when the application should stop,
* and provides a convenient place to prepare for application exit and destroy resources.
*/
#Override
public void stop() throws Exception
{
super.stop();
if(controller != null)
{
controller.startHousekeeping();
}
Platform.exit();
System.exit(0);
}
I was able to fix this problem by calling com.sun.javafx.application.tkExit(). You can read more in my other answer here: https://stackoverflow.com/a/22997736/1768232 (these two questions really are duplicates).
Just a note:
Try checking if you use
Platform.setImplicitExit(false);
Had a similar problem and overflowing my tasks. The above line will not make the stage close, it will hide it.
To imitate pressing 'x' one can do:
stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST))

Categories