【问题标题】:JavaFX screencapture headless exception on OSXOSX 上的 JavaFX 屏幕捕获无头异常
【发布时间】:2013-02-25 14:04:34
【问题描述】:

我正在将我的旧 Java 应用程序从 swing 转换为 javafx,但遇到了问题。

我正在使用以下代码来截取屏幕截图:

 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)

我认为这与在 OSX 上运行的 javafx 似乎是无头模式有关,如下调试警告所示:

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

【问题讨论】:

  • “我认为这与 javafx 有关,在 OSX 上似乎运行的是无头模式” 为什么它会这样做?它是一个 GUI 工具包,没有屏幕就没有多大用处。
  • 我不知道,我在 github 上的代码中添加了一个检查 GraphicsEnvironment.isHeadless() 并且返回 true,所以...

标签: java macos awt javafx-2


【解决方案1】:

JavaFX 不使用 AWT 堆栈,因此它不会在纯 JavaFX 应用程序中启动。由于线程处理细节,AWT 在 Mac 上以无头模式运行,然后从 JavaFX 请求。

有下一个选项可以解决这个问题:

  1. 使用一些巫术魔法来初始化 AWT——在静态初始化中运行 java.awt.Toolkit.getDefaultToolkit(); EDIT这仅适用于较旧的 JavaFX,抱歉

  2. 更好的选择是从 JavaFX 中选择不使用 AWT。您可以使用下一个功能来制作屏幕截图:http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#snapshot%28javafx.util.Callback,%20javafx.scene.SnapshotParameters,%20javafx.scene.image.WritableImage%29

  3. 编辑 正如 Alexander 指出的另一种方法是在单独的 VM 中运行 AWT 代码。为此,您可以将屏幕截图功能重构为一个单独的类,并通过以下方式从 JavaFX 应用程序中调用它:

        new ProcessBuilder(
              System.getProperty("java.home") + "/bin/java", 
              "-cp", "classpath", 
              "my.apps.DoScreenshot"
        ).start();
    

    此应用程序可以将屏幕截图存储到文件系统。 如果您需要经常截屏并遇到性能问题,您可以运行该单独的应用程序一次并通过套接字与其通信。

  4. 使用com.sun.glass.ui.Robot 而不是AWTRobot

【讨论】:

  • 选项 1 隐藏了我在启动时收到的警告,但现在整个 javafx 应用程序不会显示在屏幕上。我猜选项2不是我需要的。它将界面的一部分呈现为图像。我需要捕获整个桌面,而不是应用程序的一部分。我想现在没有选择让它在 osx 上工作......
  • mac os上可以截图,不用担心。答案是存在的。
  • @Beele 你用的是什么版本的JavaFX?
  • @SergeyGrinev javafx.runtime.version: 2.2.7-b01
  • 这些变通办法是否仅适用于 JavaFX 2.2-?我看到对于 Java 8,Mac: Headless environment issue, MacOSX,已解决为已修复,这可能意味着如果目标平台是 Java 8+,则不需要此答案中的解决方法? (或者可以列出新的替代解决方法 =>“使用 Java 8+”)。
【解决方案2】:

我看到以下几点可能需要注意:

  1. 除了 Sergey Grinev 的第 1 点。设置javafx.macosx.embedded

    System.setProperty("javafx.macosx.embedded", "true");
    java.awt.Toolkit.getDefaultToolkit();
    
  2. 注意 AWT 内容在 EDT 中完成,JavaFX 内容在 JavaFX 应用程序线程中完成。

我最近在 Mac 上处理 JavaFX/Swing 问题,所以这让我很感兴趣。如果您尝试下面的代码,它对您有用吗? (它应该将缩放的屏幕截图作为应用窗口的背景。)

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

}

【讨论】:

    猜你喜欢
    • 2010-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-15
    • 2012-04-19
    • 1970-01-01
    • 2014-03-07
    • 1970-01-01
    相关资源
    最近更新 更多