Starting JavaFX from random Java code
I write a lot of prototype routines - too many to keep track of in separate projects so I end up with a ton of mains(). Best practice? Who gives a shit: its a prototype, played with for a day or a week and then forgotten.
So far for graphical output i've just been using Swing: actually there's probably not much reason not to use it for that because it does the job but once you need to add some interactivity it becomes a bit of a pain if you've been playing with JavaFX. I might add a 'display intermediate image' anywhere in the code and up it comes.
But JavaFX doesn't let you just call Platform.runLater() or new Stage() from anywhere as with Swing: the system needs initialising within an Application context.
Here's a solution. I have no claims it's a good one but it works for me so far.
// This code is placed in the public domain public class FXUtils { static FXApplication app; static Semaphore sem = new Semaphore(0); public static void startFX(Runnable r) { if (app == null) { try { Thread t = new Thread(() -> { FXApplication.start(r); }); t.start(); sem.acquire(); } catch (InterruptedException ex) { } } else { Platform.runLater(r); } } public static class FXApplication extends Application { WritableImage image; static Runnable run; public FXApplication() { } @Override public void start(Stage stage) throws Exception { app = this; run.run(); sem.release(); } public static void start(Runnable r) { run = r; // Application.launch() can only be called from a static // method from a class that extends Application Application.launch(); // no windows - no app! System.exit(0); } } }
Whether start() calls System.exit() or not is up to you - personally when I close a window i'm prototyping stuff on I want everything else to fuck off for good.
And this is how to use it:
public static void main(String[] args) { FXApplication.start(() -> { // Now on javafx thread Stage s = new Stage(); s.setScene(new Scene(new VBox(new Label("foobar!")))); s.show(); }); // Will wait for javafx to start, but then continue here // exiting will leave the windows open, till they're closed }
This uses a thread to launch javafx so that the original main thread can continue; Application.launch() doesn't return until the last window is closed so would otherwise block. The thread could be made a daemon too for some different behaviours.
If you just want to launch a full JavaFX application from multiple mains then none of this is required, just create a static start() method which calls Application.launch().