JavaFX ScreenCapture безголовый исключение на OSX
Я преобразовываю свое старое приложение Java из Swing в Java, и я столкнулся с проблемой.
Я использую следующий код для захвата скриншотов:
public ScreenCapper() {
ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
gs = ge.getScreenDevices();
try {
robot = new Robot(gs[gs.length-1]);
} catch (AWTException e) {
LOGGER.getInstance().ERROR("Error creating screenshot robot instance!");
}
}
public Color capture() {
Rectangle bounds;
mode = gs[0].getDisplayMode();
bounds = new Rectangle(0, 0, mode.getWidth(), mode.getHeight());
//......
}
Это прекрасно работает при запуске приложения под Windows. Однако при работе под OSX в получите следующее исключение:
Exception in Application start method
Exception in thread "main" java.lang.RuntimeException: Exception in Application start method
at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:403)
at com.sun.javafx.application.LauncherImpl.access$000(LauncherImpl.java:47)
at com.sun.javafx.application.LauncherImpl$1.run(LauncherImpl.java:115)
at java.lang.Thread.run(Thread.java:722)
Caused by: java.awt.HeadlessException
at sun.java2d.HeadlessGraphicsEnvironment.getScreenDevices(HeadlessGraphicsEnvironment.java:72)
at be.beeles_place.roggbiv.utils.ScreenCapper.<init>(ScreenCapper.java:33)
at be.beeles_place.roggbiv.modes.AverageColorMode.start(AverageColorMode.java:31)
at be.beeles_place.roggbiv.modes.ColorModeContext.startCurrentColorMode(ColorModeContext.java:28)
at be.beeles_place.roggbiv.controller.RoggbivController.<init>(RoggbivController.java:42)
at be.beeles_place.roggbiv.RoggbivMain.start(RoggbivMain.java:67)
at com.sun.javafx.application.LauncherImpl$5.run(LauncherImpl.java:319)
at com.sun.javafx.application.PlatformImpl$5.run(PlatformImpl.java:215)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:179)
at com.sun.javafx.application.PlatformImpl$4$1.run(PlatformImpl.java:176)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$4.run(PlatformImpl.java:176)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:76)
Я думаю, что это связано с тем, что javafx, по-видимому, работает в автономном режиме на OSX, как показывают следующие предупреждения об отладке:
013-03-10 10:44:03.795 java[1912:5903] *** WARNING: Method userSpaceScaleFactor in class NSView is deprecated on 10.7 and later. It should not be used in new applications. Use convertRectToBacking: instead.
2013-03-10 10:44:05.472 java[1912:707] [JRSAppKitAWT markAppIsDaemon]: Process manager already initialized: can't fully enable headless mode.
Есть ли способ заставить это работать? Или другой способ сделать снимок экрана, который не конфликтует с OSX?
полный код @ https://github.com/beele/Roggbiv
2 ответа
JavaFX не использует стек AWT, поэтому он не запускается в чистом приложении JavaFX. Из-за особенностей обработки потоков AWT запускается в автономном режиме на Mac, а затем запрашивается из JavaFX.
Есть следующие варианты решения этой проблемы:
Используйте магию вуду для инициализации AWT - при статической инициализации
java.awt.Toolkit.getDefaultToolkit();
РЕДАКТИРОВАТЬ это работало только в старых JavaFX, извинитеЛучше было бы отказаться от использования AWT от JavaFX. Вы можете использовать следующую функциональность для создания снимков экрана: http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#snapshot%28javafx.util.Callback,%20javafx.scene.SnapshotParameters,%20javafx.scene.image.WritableImage% 29
РЕДАКТИРОВАТЬ Как указал Александр, другой способ - запустить код AWT в отдельной виртуальной машине. Чтобы добиться этого, вы можете рефакторировать функциональность вашего скриншота в отдельный класс и вызывать его из приложения JavaFX:
new ProcessBuilder( System.getProperty("java.home") + "/bin/java", "-cp", "classpath", "my.apps.DoScreenshot" ).start();
Это приложение может хранить скриншот в файловой системе. Если вам нужно часто делать скриншоты и сталкиваться с проблемами производительности, вы можете запустить это отдельное приложение один раз и связаться с ним через сокет.
использование
com.sun.glass.ui.Robot
вместоAWTRobot
Я вижу следующие вещи, которые могут нуждаться во внимании:
В дополнение к 1-му набору Сергея Гринева
javafx.macosx.embedded
:System.setProperty("javafx.macosx.embedded", "true"); java.awt.Toolkit.getDefaultToolkit();
Позаботьтесь о том, чтобы AWT выполнялся в EDT, а JavaFX - в потоке приложений JavaFX.
Я недавно имел дело с проблемами JavaFX/Swing на Mac, поэтому это меня заинтересовало. Если вы попробуете код ниже, это работает для вас? (В качестве фона окна приложения следует поместить уменьшенный снимок экрана.)
import java.awt.AWTException;
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javax.swing.SwingUtilities;
public class SO extends Application {
@Override
public void start(Stage stage) throws Exception {
final Pane pane = new StackPane();
Scene scene = new Scene(pane, 600, 300);
stage.setScene(scene);
Button b = new Button("Snap");
final ImageView iv = new ImageView();
iv.fitWidthProperty().bind(pane.widthProperty());
iv.fitHeightProperty().bind(pane.heightProperty());
pane.getChildren().add(iv);
pane.getChildren().add(b);
b.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
doSnap(iv);
}
});
}
});
stage.show();
}
protected void doSnap(final ImageView iv) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice[] gs = ge.getScreenDevices();
Robot robot = null;
try {
robot = new Robot(gs[gs.length-1]);
} catch (AWTException e) {
e.printStackTrace();
return;
}
DisplayMode mode = gs[0].getDisplayMode();
Rectangle bounds = new Rectangle(0, 0, mode.getWidth(), mode.getHeight());
final BufferedImage bi = robot.createScreenCapture(bounds);
Platform.runLater(new Runnable() {
@Override
public void run() {
Image im = SwingFXUtils.toFXImage(bi, null);
iv.setImage(im);
}
});
}
public static void main(String[] args) {
System.setProperty("javafx.macosx.embedded", "true");
java.awt.Toolkit.getDefaultToolkit();
Application.launch(args);
}
}