Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaFX 2.1: Toolkit not initialized

Tags:

javafx-2

My application is Swing-based. I would like to introduce JavaFX and configure it to render a Scene on a secondary display. I could use a JFrame to hold a JFXPanel which could hold a JFXPanel but I would like to achieve this with JavaFX API.

Subclassing com.sun.glass.ui.Application and using Application.launch(this) is not an option because the invoking thread would be blocked.

When instantiating a Stage from Swing EDT, the error I get is:

java.lang.IllegalStateException: Toolkit not initialized

Any pointers?


EDIT: Conclusions

Problem: Non-trivial Swing GUI application needs to run JavaFX components. Application's startup process initializes the GUI after starting up a dependent service layer.

Solutions

Subclass JavaFX Application class and run it in a separate thread e.g.:

public class JavaFXInitializer extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        // JavaFX should be initialized
        someGlobalVar.setInitialized(true);
    }
}

Sidenote: Because Application.launch() method takes a Class<? extends Application> as an argument, one has to use a global variable to signal JavaFX environment has been initialized.

Alternative approach: instantiate JFXPanel in Swing Event Dispatcher Thread:

final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        new JFXPanel(); // initializes JavaFX environment
        latch.countDown();
    }
});
latch.await();

By using this approach the calling thread will wait until JavaFX environment is set up.

Pick any solution you see fit. I went with the second one because it doesn't need a global variable to signal the initialization of JavaFX environment and also doesn't waste a thread.

like image 904
Dan Vulpe Avatar asked Jun 30 '12 11:06

Dan Vulpe


2 Answers

Found a solution. If I just create a JFXPanel from Swing EDT before invoking JavaFX Platform.runLater it works. I don't know how reliable this solution is, I might choose JFXPanel and JFrame if turns out to be unstable.

public class BootJavaFX {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new JFXPanel(); // this will prepare JavaFX toolkit and environment
                Platform.runLater(new Runnable() {
                    @Override
                    public void run() {
                        StageBuilder.create()
                                .scene(SceneBuilder.create()
                                        .width(320)
                                        .height(240)
                                        .root(LabelBuilder.create()
                                                .font(Font.font("Arial", 54))
                                                .text("JavaFX")
                                                .build())
                                        .build())
                                .onCloseRequest(new EventHandler<WindowEvent>() {
                                    @Override
                                    public void handle(WindowEvent windowEvent) {
                                        System.exit(0);
                                    }
                                })
                                .build()
                                .show();
                    }
                });
            }
        });
    }
}
like image 151
Dan Vulpe Avatar answered Sep 24 '22 20:09

Dan Vulpe


The only way to work with JavaFX is to subclass Application or use JFXPanel, exactly because they prepare env and toolkit.

Blocking thread can be solved by using new Thread(...).

Although I suggest to use JFXPanel if you are using JavaFX in the same VM as Swing/AWT, you can find more details here: Is it OK to use AWT with JavaFx?

like image 21
Sergey Grinev Avatar answered Sep 22 '22 20:09

Sergey Grinev