使用快照方法创建WritableImage对象,无需在javafx中扩展javafx.application.application

使用快照方法创建WritableImage对象,无需在javafx中扩展javafx.application.application,javafx,Javafx,如何在javafx.scene.shape.Rectangle或javafx.scene.chart.BarChart上使用快照方法创建WritableImage对象,而不扩展javafx.application.application 获取以下异常 线程“main”java.lang.IllegalStateException中的异常:不在FX应用程序线程上;currentThread=main 在com.sun.javafx.tk.Toolkit.checkFxUserThread(Tool

如何在
javafx.scene.shape.Rectangle
javafx.scene.chart.BarChart
上使用快照方法创建
WritableImage
对象,而不扩展
javafx.application.application

获取以下异常

线程“main”java.lang.IllegalStateException中的异常:不在FX应用程序线程上;currentThread=main
在com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:238)上
位于com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:400)
位于javafx.scene.Node.snapshot(Node.java:1698)
公共类示例1{
公共静态void main(字符串[]args)引发异常{
新示例1().manipulaepdf();
}
受保护的void manufacturePDF()引发异常{
图像1();
图像2();
}
私有可写映像映像1(){

矩形=新矩形(); 矩形。setX(50); 矩形。setY(50); 矩形。设置宽度(300); 矩形。设置高度(20); 矩形。设定行程(颜色。白色); LinearGradient linearGrad=新的LinearGradient(0,0,1,0,true,CycleMethod.NO_CYCLE, 新挡块(0f,Color.rgb(255,0,0,1)),新挡块(0.25f, Color.rgb(255,255,0,1)),新停止(0.5f,Color.rgb( 255,255,255,1),新挡块(0.75f,颜色.rgb(124252, 新的停止(1.0f,Color.rgb(0,255,0,1)); 矩形.setFill(linearGrad); WritableImage img=新的WritableImage((int)rectangle.getWidth(), (int)rectangle.getHeight()); 矩形快照(null,img); 返回img; } 私有可写映像映像2(){ 条形图=新条形图(新类别轴(),新编号轴()); 随机rng=新随机(); 系列=新系列(); series.setName(“数据”);
对于(int i=1;i我不知道您的代码,但根据您得到的异常,您的代码在错误的线程中运行。您的代码需要在JavaFX应用程序线程中运行:

Platform.runLater(new Runnable() {
        @Override
        public void run() {
            // your code goes here
        }
    });
如合同所述:

抛出

IllegalStateException
-如果在JavaFX应用程序线程以外的线程上调用此方法

因此,您只能在FX应用程序线程上执行此方法。这意味着您必须从已在FX应用程序线程上执行的方法调用
snapshot()
,或者必须在
平台中包装对
snapshot()
的调用。稍后运行(…)
。当然,后一个选项只有在FX应用程序线程正在运行时才起作用,这意味着必须初始化FX toolkit

因此,您无法在“headless”模式下完成此操作,您需要完整实现JavaFXToolkit,并使其初始化并仍在运行

启动FX应用程序工具包的方法是调用
应用程序。启动(…)
,可以从
应用程序的子类启动,也可以指定
应用程序的具体子类启动

请注意,在如何调用方面存在一些限制

首先,它将阻塞直到FX应用程序退出,因此您需要在后台线程中调用它。这将涉及在后台线程上调用
launch()
,然后阻塞直到FX Toolkit初始化完成,然后再继续

其次,在JVM的生命周期中,您只能调用
launch()
一次。这意味着,如果您的应用程序需要FX toolkit,您可能只需要在应用程序启动时启动它,并在应用程序关闭时退出它

最后,还要注意,根据上面链接的
快照(…)
文档

为了使CSS和布局能够正常工作,节点必须是场景的一部分(场景可以附加到舞台,但不需要)

针对“手动”启动FXToolkit的问题,发布了一个解决方案,我将在本文中重复

请注意,执行下面的应用程序会将几个
.png
文件写入驱动器(在工作目录中,即从何处运行)。您可以使用这些文件验证驱动器是否正常工作

import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.FutureTask;

import javax.imageio.ImageIO;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class StandaloneSnapshot {
    public static void main(String[] args) throws Exception {

        // start FX toolkit in background thread:
        new Thread(() -> Application.launch(FXStarter.class)).start();

        // block until FX toolkit initialization is complete:
        FXStarter.awaitFXToolkit();

        new  StandaloneSnapshot().manipulatePdf();
        
        // exit JavaFX toolkit:
        Platform.exit();
    }

    public static class FXStarter extends Application {

        private static final CountDownLatch latch = new CountDownLatch(1);
    
        public static void awaitFXToolkit() throws InterruptedException {
           latch.await();
        }
    
        @Override
        public void init() {
            latch.countDown();
        }
    
        @Override
        public void start(Stage primaryStage) {
            // no-op
        }
    }

    protected void manipulatePdf() throws Exception {

        WritableImage img1 = image1(); 
        // do something with the image:
        BufferedImage bImg1 = SwingFXUtils.fromFXImage(img1, new BufferedImage((int)img1.getWidth(), (int) img1.getHeight(), BufferedImage.TYPE_INT_ARGB));
        ImageIO.write(bImg1, "png", new File("rect.png"));

        WritableImage img2 = image2();
        // do something with the image:
        BufferedImage bImg2 = SwingFXUtils.fromFXImage(img2, new BufferedImage((int)img2.getWidth(), (int) img2.getHeight(), BufferedImage.TYPE_INT_ARGB));
        ImageIO.write(bImg2, "png", new File("chart.png"));
    }

    private WritableImage image1() throws Exception {

        Rectangle rectangle = new Rectangle();
        rectangle.setX(50);
        rectangle.setY(50);
        rectangle.setWidth(300);
        rectangle.setHeight(20);

        rectangle.setStroke(Color.WHITE);

        LinearGradient linearGrad = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE,           
                new Stop(0f, Color.rgb(255, 0, 0, 1)), new Stop(0.25f,
                        Color.rgb(255, 255, 0, 1)), new Stop(0.5f, Color.rgb(
                        255, 255, 255, 1)), new Stop(0.75f, Color.rgb(124, 252,
                        0, 1)), new Stop(1.0f, Color.rgb(0, 255, 0, 1)));
        rectangle.setFill(linearGrad);

        FutureTask<WritableImage> task = new FutureTask<>(() -> {
            new Scene(new Pane(rectangle));
            WritableImage img = new WritableImage((int) rectangle.getWidth(),
                (int) rectangle.getHeight());
            rectangle.snapshot(null, img);
            return img;
        });

        Platform.runLater(task);

        // wait for FX Application Thread to return image, and return the result:
        return task.get();

    }

    private WritableImage image2 () throws Exception {

        BarChart<String, Number> chart = new BarChart<>(new CategoryAxis(), new NumberAxis());
        Random rng = new Random();
        Series<String, Number> series = new Series<>();
        series.setName("Data");
        for (int i = 1; i <= 10; i++) {
            series.getData().add(new Data<>("Group " + i, rng.nextDouble()));
        }
        chart.getData().add(series);

        FutureTask<WritableImage> task = new FutureTask<>(() -> {
            new Scene(chart);
            WritableImage img = chart.snapshot(null, null);
            return img;
        });

        Platform.runLater(task);
        return task.get();
    }
}
导入java.awt.image.buffereImage;
导入java.io.File;
导入java.util.Random;
导入java.util.concurrent.CountDownLatch;
导入java.util.concurrent.FutureTask;
导入javax.imageio.imageio;
导入javafx.application.application;
导入javafx.application.Platform;
导入javafx.embed.swing.SwingFXUtils;
导入javafx.scene.scene;
导入javafx.scene.chart.BarChart;
导入javafx.scene.chart.CategoryAxis;
导入javafx.scene.chart.NumberAxis;
导入javafx.scene.chart.XYChart.Data;
导入javafx.scene.chart.XYChart.Series;
导入javafx.scene.image.WritableImage;
导入javafx.scene.layout.Pane;
导入javafx.scene.paint.Color;
导入javafx.scene.paint.CycleMethod;
导入javafx.scene.paint.LinearGradient;
导入javafx.scene.paint.Stop;
导入javafx.scene.shape.Rectangle;
导入javafx.stage.stage;
公共级StandaloneSnapshot{
公共静态void main(字符串[]args)引发异常{
//在后台线程中启动FX toolkit:
新线程(()->Application.launch(FXStarter.class)).start();
//阻止,直到FX toolkit初始化完成:
FXStarter.awaitFXToolkit();
新的StandaloneSnapshot();
//退出JavaFX工具包:
Platform.exit();
}
公共静态类FXStarter扩展应用程序{
专用静态最终倒计时闩锁=新倒计时闩锁(1);
public static void awaitFXToolkit()引发InterruptedException{
satch.wait();
}
@凌驾
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.FutureTask;

import javax.imageio.ImageIO;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.chart.XYChart.Series;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class StandaloneSnapshot {
    public static void main(String[] args) throws Exception {

        // start FX toolkit in background thread:
        new Thread(() -> Application.launch(FXStarter.class)).start();

        // block until FX toolkit initialization is complete:
        FXStarter.awaitFXToolkit();

        new  StandaloneSnapshot().manipulatePdf();
        
        // exit JavaFX toolkit:
        Platform.exit();
    }

    public static class FXStarter extends Application {

        private static final CountDownLatch latch = new CountDownLatch(1);
    
        public static void awaitFXToolkit() throws InterruptedException {
           latch.await();
        }
    
        @Override
        public void init() {
            latch.countDown();
        }
    
        @Override
        public void start(Stage primaryStage) {
            // no-op
        }
    }

    protected void manipulatePdf() throws Exception {

        WritableImage img1 = image1(); 
        // do something with the image:
        BufferedImage bImg1 = SwingFXUtils.fromFXImage(img1, new BufferedImage((int)img1.getWidth(), (int) img1.getHeight(), BufferedImage.TYPE_INT_ARGB));
        ImageIO.write(bImg1, "png", new File("rect.png"));

        WritableImage img2 = image2();
        // do something with the image:
        BufferedImage bImg2 = SwingFXUtils.fromFXImage(img2, new BufferedImage((int)img2.getWidth(), (int) img2.getHeight(), BufferedImage.TYPE_INT_ARGB));
        ImageIO.write(bImg2, "png", new File("chart.png"));
    }

    private WritableImage image1() throws Exception {

        Rectangle rectangle = new Rectangle();
        rectangle.setX(50);
        rectangle.setY(50);
        rectangle.setWidth(300);
        rectangle.setHeight(20);

        rectangle.setStroke(Color.WHITE);

        LinearGradient linearGrad = new LinearGradient(0, 0, 1, 0, true, CycleMethod.NO_CYCLE,           
                new Stop(0f, Color.rgb(255, 0, 0, 1)), new Stop(0.25f,
                        Color.rgb(255, 255, 0, 1)), new Stop(0.5f, Color.rgb(
                        255, 255, 255, 1)), new Stop(0.75f, Color.rgb(124, 252,
                        0, 1)), new Stop(1.0f, Color.rgb(0, 255, 0, 1)));
        rectangle.setFill(linearGrad);

        FutureTask<WritableImage> task = new FutureTask<>(() -> {
            new Scene(new Pane(rectangle));
            WritableImage img = new WritableImage((int) rectangle.getWidth(),
                (int) rectangle.getHeight());
            rectangle.snapshot(null, img);
            return img;
        });

        Platform.runLater(task);

        // wait for FX Application Thread to return image, and return the result:
        return task.get();

    }

    private WritableImage image2 () throws Exception {

        BarChart<String, Number> chart = new BarChart<>(new CategoryAxis(), new NumberAxis());
        Random rng = new Random();
        Series<String, Number> series = new Series<>();
        series.setName("Data");
        for (int i = 1; i <= 10; i++) {
            series.getData().add(new Data<>("Group " + i, rng.nextDouble()));
        }
        chart.getData().add(series);

        FutureTask<WritableImage> task = new FutureTask<>(() -> {
            new Scene(chart);
            WritableImage img = chart.snapshot(null, null);
            return img;
        });

        Platform.runLater(task);
        return task.get();
    }
}