【问题标题】:JavaFX 2.1: Toolkit not initializedJavaFX 2.1:工具包未初始化
【发布时间】:2012-07-01 16:37:30
【问题描述】:

我的应用程序是基于 Swing 的。我想介绍 JavaFX 并将其配置为在辅助显示器上呈现场景。 我可以使用 JFrame 来保存可以保存 JFXPanel 的 JFXPanel,但我想使用 JavaFX API 来实现这一点。

子类化 com.sun.glass.ui.Application 并使用 Application.launch(this) 不是一个选项,因为调用线程会被阻塞。

从 Swing EDT 实例化舞台时,我得到的错误是:

java.lang.IllegalStateException: Toolkit not initialized

任何指针?


编辑:结论

问题:重要的 Swing GUI 应用程序需要运行 JavaFX 组件。应用程序的启动过程在启动依赖服务层后初始化 GUI。

解决方案

继承 JavaFX 应用程序类并在单独的线程中运行它,例如:

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

旁注:由于 Application.launch() 方法将 Class<? extends Application> 作为参数,因此必须使用全局变量来表示 JavaFX 环境已初始化。

替代方法:在 Swing Event Dispatcher 线程中实例化 JFXPanel

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

通过使用这种方法,调用线程将等待 JavaFX 环境设置完成。

选择您认为合适的任何解决方案。我选择了第二个,因为它不需要全局变量来表示 JavaFX 环境的初始化,也不会浪费线程。

【问题讨论】:

  • 注:从 Swing 应用程序中使用 JavaFX 的推荐解决方案是创建 JFXPanel 并将 JavaFX 场景传递给 jfxPanel.setScene() 方法。见docs.oracle.com/javafx/2/api/javafx/embed/swing/JFXPanel.html
  • @SergeyGrinev:嗯,但是由于在 JFXPanel 中使用某些组件存在问题,因此非常感谢 Swing 中使用 JavaFX 2 的另一种方式。
  • 嗨。哪些组件有问题?
  • HTMLEditor 组件在 JFXPanel 内部时不接受 Enter-Key - 在我看来,这是编辑器组件的一个重要问题。也可以在这里查看:javafx-jira.kenai.com/browse/RT-20887
  • 这是使用 Java 8 进行的简化,将其变成单线:SwingUtilities.invokeAndWait(() -> new JFXPanel());

标签: javafx-2


【解决方案1】:

找到了解决办法。如果我只是在调用 JavaFX Platform.runLater 之前从 Swing EDT 创建一个 JFXPanel,它就可以工作。 我不知道这个方案的可靠性如何,如果结果不稳定,我可能会选择 JFXPanel 和 JFrame。

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();
                    }
                });
            }
        });
    }
}

【讨论】:

  • 如果您已经拥有 JFXPanel,为什么不想将 FX 代码放入其中?
  • 在这个解决方案中,我只使用 JFXPanel 作为一种解决方法来运行 JavaFX 环境。我会喜欢 JavaFX 中的 Platform.initialize() 之类的东西。
  • 你需要做所有这些才能让它工作?,荒谬。
  • 在演示中,看起来总是那么简单
【解决方案2】:

使用 JavaFX 的唯一方法是继承 Application 或使用 JFXPanel,这正是因为它们准备了 env 和工具包。

使用new Thread(...)可以解决阻塞线程。

虽然如果您在与 Swing/AWT 相同的 VM 中使用 JavaFX,我建议使用 JFXPanel,但您可以在此处找到更多详细信息:Is it OK to use AWT with JavaFx?

【讨论】:

  • 我避免使用新的 Thread(...) 东西,因为我的应用程序是基于 Swing 的。感谢您的提示。
  • 我的应用程序有一个特定的启动例程。 GUI 初始化发生在稍后阶段,从主线程调用。我正在研究启动 JavaFX 环境并在完成后收到通知。使用 Application.launch 对我来说不是解决方案,因为它会阻塞调用线程。
  • 我仍然没有看到与使用 Thread 有任何冲突。如果您想坚持使用这种方法,请在生产前在 Mac 上进行测试。
  • 我主要在 Mac 上开发,只要主线程不退出,这种方法就可以正常工作。我并不是说使用新线程会产生冲突,只是我不喜欢阻塞线程。在我看来,这两种解决方案都是 JavaFX 环境/工具包中缺少的 api 调用的解决方法。
  • 在第一篇文章中总结了所有内容。感谢您的反馈。
【解决方案3】:

从 JavaFX 9 开始,您无需扩展 Application 类即可运行 JavaFX 应用程序,只需调用 Platform.startup()

Platform.startup(() ->
{
    // This block will be executed on JavaFX Thread
});

此方法启动 JavaFX 运行时。

【讨论】:

    【解决方案4】:

    我检查了源代码,这是为了初始化它

    com.sun.javafx.application.PlatformImpl.startup(()->{});
    

    然后退出

    com.sun.javafx.application.PlatformImpl.exit();
    

    【讨论】:

    • 像这样调用内部 com.sun 类在 Java 9+ 上不起作用,因为包没有导出。
    • 在 Windows 10 64 位上使用 JDK 11(特别是 Azul Community JDK 11.0.11+9 x86_64 和 OpenJFX 16)构建和执行测试时,我能够成功地使用此答案中的方法。我在@BeforeClass setUpOnce 方法中调用PlatformImpl.startup,在我的JUnit 4 测试套件中的tearDownOnce 方法中调用PlatformImpl.exit。这些com.sun 方法调用只会在运行时产生警告
    【解决方案5】:

    我在创建单元测试以测试 javaFX tableview 更新时使用了以下内容

    public class testingTableView {
            @BeforeClass
            public static void initToolkit() throws InterruptedException
            {
                final CountDownLatch latch = new CountDownLatch(1);
                SwingUtilities.invokeLater(() -> {
                    new JFXPanel(); // initializes JavaFX environment
                    latch.countDown();
                });
    
                if (!latch.await(5L, TimeUnit.SECONDS))
                    throw new ExceptionInInitializerError();
            }
    
            @Test
            public void updateTableView() throws Exception {
    
                TableView<yourclassDefiningEntries> yourTable = new TableView<>();
                .... do your testing stuff
    
            }
        }
    

    尽管这篇文章与测试无关,但它帮助我让我的单元测试工作

    • 如果没有 BeforeClass initToolkit,那么单元测试中 TableView 的实例化会产生缺少工具包的消息

    【讨论】:

    • 谢谢你...我也在尝试弄清楚 JavaFX 和测试。但是大多数重要的 JavaFX 操作都会产生“IllegalStateException:仅在事件线程上允许此操作;”。在 Plaform.runLater() Runnable 内运行不起作用:对于 Swing,始终需要使用 EventQueue.invokeAndWait() - 否则测试将在 Runnable 运行之前结束。是不是你几乎必须使用 TestFX 之类的东西来进行 JavaFX 测试?
    • PS 当然,您可以使用闩锁来实现这种“等待 Runnable 结束”......但这并不优雅。
    • @mikerodent,我同意使用锁存器等待实用程序出现并不优雅,但是当涉及到依赖于外部实体时,这可能并不像它可能的那样笨拙是
    【解决方案6】:

    还有一种方法可以显式初始化工具包,方法是调用: com.sun.javafx.application.PlatformImpl#startup(Runnable)

    由于使用了 *Impl,所以有点 hacky,但如果您出于某种原因不想使用 ApplicationJXFPanel,它很有用。

    this post重新发布我自己

    【讨论】:

    • 我想给这个答案起立鼓掌!我正在编写一个应用程序,目的是拥有两个 UI,第一个在 FX 中,第二个是非图形的。我的模型使用 FX 集合。没有这个,你就不能使用集合,也没有图形元素。
    • 这也解决了在图形之前启动模型时的竞争条件。所以感谢没有结束!
    • 消除了对子类的需求,并在 UI 线程之前运行。很明显,在 GUI-Thread 之前我需要准备什么(请谷歌!记住我......)。非常感谢!
    • 像这样调用内部 com.sun 类在 Java 9+ 上不起作用,因为包没有导出。
    【解决方案7】:
    private static Thread thread;
    
    public static void main(String[] args) {
    
        Main main = new Main();
        startup(main);
        thread = new Thread(main);
        thread.start();
    }
    
    public static void startup(Runnable r) {
        com.sun.javafx.application.PlatformImpl.startup(r);
    }
    
    @Override
    public void run() {
        SoundPlayer.play("BelievexBelieve.mp3");
    }
    

    这是我的解决方案。该类名为 Main 并实现 Runnable。方法startup(Runnable r)是关键。

    【讨论】:

    • 像这样调用内部 com.sun 类在 Java 9+ 上不起作用,因为包没有导出。
    【解决方案8】:

    使用 Jack Lin 的回答,我发现它触发了 run() 两次。通过一些修改也使答案更加简洁,我提供以下内容;

    import com.sun.javafx.application.PlatformImpl;
    
    public class MyFxTest implements Runnable {
    
        public static void main(String[] args) {
    
            MyFxTest main = new MyFxTest();
            PlatformImpl.startup((Runnable) main);
        }
    
        @Override
        public void run() {
    
            // do your testing;
            System.out.println("Here 'tis");
            System.exit(0); // Optional
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-10-27
      • 2012-08-25
      • 1970-01-01
      • 2012-12-11
      • 2014-08-26
      • 1970-01-01
      相关资源
      最近更新 更多