JavaFX 2.1: Инструментарий не инициализирован
Мое приложение основано на Swing. Я хотел бы представить JavaFX и настроить его для рендеринга сцены на вторичном дисплее.
Я мог бы использовать JFrame для хранения JFXPanel, который мог бы содержать JFXPanel, но я хотел бы достичь этого с помощью API JavaFX.
Подклассификация com.sun.glass.ui.Приложение и использование Application.launch(это) не является опцией, потому что вызывающий поток будет заблокирован.
При создании экземпляра Stage из Swing EDT появляется ошибка:
java.lang.IllegalStateException: Toolkit not initialized
Любые указатели?
EDIT: Выводы
Проблема: нетривиальное приложение GUI Swing должно запускать компоненты JavaFX. Процесс запуска приложения инициализирует графический интерфейс после запуска зависимого служебного уровня.
Решения
Подкласс JavaFX Application class и запустите его в отдельном потоке, например:
public class JavaFXInitializer extends Application {
@Override
public void start(Stage stage) throws Exception {
// JavaFX should be initialized
someGlobalVar.setInitialized(true);
}
}
Sidenote: поскольку метод Application.launch() принимает в качестве аргумента Class<? extends Application>
, необходимо использовать глобальную переменную для сигнатуры среды JavaFX.
Альтернативный подход: создать экземпляр JFXPanel в потоке диспетчера событий Swing:
final CountDownLatch latch = new CountDownLatch(1);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new JFXPanel(); // initializes JavaFX environment
latch.countDown();
}
});
latch.await();
Используя этот подход, вызывающий поток будет ждать настройки среды JavaFX.
Выберите любое решение, которое вам подходит. Я пошел со вторым, потому что ему не нужна глобальная переменная, чтобы сигнализировать инициализацию среды JavaFX, а также не тратить поток.
Ответы
Ответ 1
Найден решение. Если я просто создаю JFXPanel из Swing EDT перед вызовом JavaFX Platform.runLater, он работает.
Я не знаю, насколько это надежное решение, я могу выбрать 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();
}
});
}
});
}
}
Ответ 2
Единственный способ работать с JavaFX - это подкласс Application или использовать JFXPanel, именно потому, что они готовят env и toolkit.
Блокирующая нить может быть решена с помощью new Thread(...)
.
Хотя я предлагаю использовать JFXPanel, если вы используете JavaFX в той же виртуальной машине, что и Swing/AWT, вы можете найти более подробную информацию здесь: Можно ли использовать AWT с JavaFx?
Ответ 3
Я проверил исходный код, чтобы инициализировать его
com.sun.javafx.application.PlatformImpl.startup(()->{});
и выйти из него
com.sun.javafx.application.PlatformImpl.exit();
Ответ 4
Я использовал следующее при создании unittests для тестирования обновлений 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
}
}
хотя этот пост не связан с тестированием, тогда он помог мне заставить мой unittest работать
- без initToolkit BeforeClass, тогда экземпляр TableView в unittest даст сообщение с отсутствующим набором инструментов
Ответ 5
Также можно явно инициализировать инструментарий, вызвав:
com.sun.javafx.application.PlatformImpl#startup(Runnable)
Немного взломанный, из-за использования * Impl, но полезен, если вы не хотите использовать Application
или JXFPanel
по какой-либо причине.
повторно отправить себя из этого сообщения
Ответ 6
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)
является ключевым.
Ответ 7
Начиная с JavaFX 9, вы можете запускать приложение JavaFX без расширения класса Application
, вызывая Platform.startup()
:
Platform.startup(() ->
{
// This block will be executed on JavaFX Thread
}
Этот метод запускает среду выполнения JavaFX.