首页 文章

使用JavaFX图表API绘制图表图像

提问于
浏览
8

我只想从JavaFX图表API生成图表图像 . 我不想显示应用程序窗口,也不想启动应用程序(如果没有必要) .

public class LineChartSample extends Application {
    private List<Integer> data;

    @Override public void start(Stage stage) {
        stage.setTitle("Line Chart Sample");
        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("Month");       

        final LineChart<String,Number> lineChart = 
                new LineChart<String,Number>(xAxis,yAxis);

        lineChart.setTitle("Stock Monitoring, 2010");

        XYChart.Series series = new XYChart.Series();
        series.setName("My portfolio");

        series.getData().add(new XYChart.Data("Jan", 23));
        series.getData().add(new XYChart.Data("Feb", 14));        

        Scene scene  = new Scene(lineChart,800,600);
        lineChart.getData().add(series);

        WritableImage image = scene.snapshot(null);
        ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", chartFile);

        //stage.setScene(scene);
        //stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

   public setData(List<Integer> data) {this.data = data;}
}

在start方法里面,我实际上需要访问外部数据才能 Build 系列数据,但是似乎无法从start方法访问外部数据,如果我将数据存储在成员变量 data 中,那么's null when the start is called. I actually don' t关心阶段和场景对象,只要图表图像可以渲染,我该如何解决问题?我想构建一个可以使用输入数据调用的API,并使用数据绘制图表,然后返回该文件 .

public File toLineChart(List<Integer> data) {
...
}

2 回答

  • 1

    您不需要显示 Stage ,但 Node 必须附加到 Scene . 来自doc of snapshot

    注意:为了使CSS和布局正常工作,节点必须是场景的一部分(场景可以附加到舞台,但不一定是) .

    修改 Scene 的一个限制是它必须发生在JavaFX应用程序线程上,该线程具有必须初始化JavaFX工具包的先决条件 .

    初始化可以通过扩展 Application 类( launch 方法将为您执行此操作)来完成,或者作为一种解决方法,您可以在Swing Event Dispatcher Thread上创建新的 JFXPanel 实例 .

    如果要扩展 Application 并在 start 方法中执行某些代码,则确保此代码将在JavaFX应用程序线程上执行,否则您可以使用从不同线程调用的 Platform.runLater(...) 块来确保相同 .

    Here is a possible example:

    该类提供了一种静态方法,用于将图表绘制到文件中,如果创建成功与否,则返回 Filenull .

    在此方法中,通过在Swing EDT上创建 JFXPanel 来初始化JavaFX Toolkit,然后创建图表,完成JavaFX Application Thread . 在该方法中使用两个布尔值来存储操作完成并成功 .

    在completed标志切换为true之前,该方法不会返回 .

    Note: 这个实际上只是一个可以改进的(工作)例子 .

    public class JavaFXPlotter {
    
        public static File toLineChart(String title, String seriesName, List<Integer> times, List<Integer> data) {
    
            File chartFile = new File("D:\\charttest.png");
    
            // results: {completed, successful}
            Boolean[] results = new Boolean[] { false, false };
    
            SwingUtilities.invokeLater(() -> {
    
                // Initialize FX Toolkit
                new JFXPanel();
    
                Platform.runLater(() -> {
                    final NumberAxis xAxis = new NumberAxis();
                    final NumberAxis yAxis = new NumberAxis();
    
                    final LineChart<Number, Number> lineChart = new LineChart<Number, Number>(xAxis, yAxis);
    
                    lineChart.setTitle(title);
    
                    XYChart.Series<Number, Number> series = new XYChart.Series<>();
                    series.setName(seriesName);
    
                    for (int i = 0; i < times.size(); i++)
                        series.getData().add(new XYChart.Data<Number, Number>(times.get(i), data.get(i)));
    
                    lineChart.getData().add(series);
    
                    Scene scene = new Scene(lineChart, 800, 600);
    
                    WritableImage image = scene.snapshot(null);
    
                    try {
                        ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", chartFile);
                        results[1] = true;
                    } catch (Exception e) {
                        results[0] = true;
                    } finally {
                        results[0] = true;
                    }
                });
            });
    
            while (!results[0]) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return (results[1]) ? chartFile : null;
        }
    
    
    }
    

    以及可能的用法

    List<Integer> times = Arrays.asList(new Integer[] { 0, 1, 2, 3, 4, 5 });
    List<Integer> data = Arrays.asList(new Integer[] { 4, 1, 5, 3, 0, 7 });
    
    File lineChart = JavaFXPlotter.toLineChart("Sample", "Some sample data", times, data);
    
    if (lineChart != null)
        System.out.println("Image generation is done! Path: " + lineChart.getAbsolutePath());
    else
        System.out.println("File creation failed!");
    
    System.exit(0);
    

    和生成的图片(charttest.png)

    enter image description here

  • 6

    从命令行:

    import java.io.File;
    import java.io.IOException;
    import java.util.logging.Level;
    import java.util.logging.Logger;
    import javafx.application.Application;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.embed.swing.SwingFXUtils;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    import javafx.scene.chart.*;
    import javafx.scene.Group;
    import javafx.scene.image.WritableImage;
    import javax.imageio.ImageIO;
    
    public class PieChartSample extends Application {
    
        private static String[] arguments; 
    
        @Override public void start(Stage stage) {
            Scene scene = new Scene(new Group());
            stage.setTitle("Imported Fruits");
            stage.setWidth(500);
            stage.setHeight(500);
    
            ObservableList<PieChart.Data> pieChartData = FXCollections.observableArrayList();
    
    //               
            final PieChart chart = new PieChart(pieChartData);
            chart.setTitle("Imported Fruits");
            for(int i = 0; i < arguments.length; i+=2)
            {
                System.out.println(arguments[i] + " " + arguments[i+1]);
                chart.getData().add(new PieChart.Data(arguments[i], Double.parseDouble(arguments[i+1])));
            }
    
            ((Group) scene.getRoot()).getChildren().add(chart);
    
            saveAsPng(scene);
            System.out.println("Done!");
            System.exit(0);
            //stage.setScene(scene);
            //stage.show();
        }
    
        public static void main(String[] args) {
            arguments = args;
            launch(args);
        }
    
        static void saveAsPng(Scene scene){
            try 
            {
                WritableImage image = scene.snapshot(null);
    
                File file = new File("tempPieChart.png");
    
                ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", file);
            } 
            catch (IOException ex) 
            {
                Logger.getLogger(PieChartSample.class.getName()).log(Level.SEVERE, null, ex);
            }
    
        }
    }
    

    下一步:清理并构建您的程序 . 然后:在dist文件夹中找到jar文件之后:将命令提示符导航到jar所在的dist文件夹中 . 然后运行:java -jar PieChartSample.jar Banana 14 Orange 20 Grape 15

    enter image description here

    结果:与PieChartSample.jar文件位于同一文件夹中

    enter image description here

相关问题