【问题标题】:Show SUM Value above Stacked bar with Apache poi使用 Apache poi 在堆积条上方显示 SUM 值
【发布时间】:2020-07-02 12:14:00
【问题描述】:

我目前正在研究功能,它应该在 .pptx 文件中生成堆叠图表。

为此,我正在使用此处的代码: java create a chart in a powerpoint using APACHE POI

我做了一些修改。主要是我将分组设置为 Stacked 并将重叠设置为 100(因此 subBars 看起来像一个更大的条)。

现在我需要在每个 Bar 上方显示 subBars 值的总和。我的问题来了。如何使用 Apache Poi 和 openxmlformats.schemas.drawingml.x2006.chart 实现这一点(下面的第二个 pohoto)?

一个想法是在顶部创建另一个 SubBar,使其透明并将其标签设置为我想要的 SUM,但我找不到设置标签的方法(只有 Value,在这种情况下,我的透明 SubBar 占用了太多空间它看起来很糟糕 - 如下图所示)。

这是我需要的样子:

除了 StackOverflow 上的一些线程之外,我找不到任何文档或任何内容。 你们有什么想法,如何实现这一目标?

编辑 1

在 Alex 的帮助下,我能够打印 BAR 上方的 SUM 值。现在我只需要摆脱这些 0 值:

编辑 2

Axel 提到了一个问题,导致显示零(来自 EDIT 1)。 Axel 在他的答案中编辑了代码,所以现在零已经消失了。

【问题讨论】:

    标签: java charts apache-poi powerpoint openxml


    【解决方案1】:

    你会如何使用PowerPoint 来做到这一点?我看到的唯一方法是使用堆叠条形图与折线图的组合,其中折线图显示总和值并设置为不可见。所以只有折线图的数据标签是可见的。 How to add total labels to stacked column chart in Excel?中描述的方式。

    使用当前的apache poi 4.1.2 可以使用新的XDDF 东西来实现。对于Excel 图表,我已经在How to generate editable Stacked-bar-chart using apache poi 4.0.1 and java? 中展示了这一点。

    我还将展示一个PowerPoint 图表的完整示例:

    import java.io.*;
    
    import org.apache.poi.xslf.usermodel.*;
    
    import org.apache.poi.ss.util.*;
    import org.apache.poi.util.Units;
    
    import org.apache.poi.xddf.usermodel.*;
    import org.apache.poi.xddf.usermodel.chart.*;
    
    import java.util.*;
    
    public class CreatePowerPointStackedBarChartXDDFChart {
    
     public static void main(String[] args) throws Exception {
      try (XMLSlideShow slideShow = new XMLSlideShow()) {
    
       XSLFSlide slide = slideShow.createSlide();
    
       // create the data
       String[] categories = new String[]{"KW1", "KW2", "KW3", "KW4", "KW5", "KW6"};
       int numOfPoints = categories.length;
    
       Double[][] values = new Double [][] {
        new Double[]{10d, 0d, 20d, 5d, 30d, 10d},
        new Double[]{15d, 35d, 25d, 15d, 10d, 8d},
        new Double[]{5d, 15d, 0d, 25d, 15d, 0d},
        new Double[]{10d, 5d, 30d, 30d, 20d, 12d}
       };
       Double[] sums = new Double[numOfPoints];
       for (int i = 0; i < sums.length; i++) {
        double sum = 0;
        for (Double[] valueRow : values) {
         sum += valueRow[i];
        }
        sums[i] = sum;
       }
    
       // create the chart
       XSLFChart chart = slideShow.createChart();
    
       // add chart to slide
       slide.addChart(chart, new java.awt.geom.Rectangle2D.Double(1d*Units.EMU_PER_CENTIMETER, 1d*Units.EMU_PER_CENTIMETER, 20d*Units.EMU_PER_CENTIMETER, 15d*Units.EMU_PER_CENTIMETER));
    
       // bar chart
    
       // create data sources
       String categoryDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, 0, 0));
       XDDFDataSource<String> categoriesData = XDDFDataSourcesFactory.fromArray(categories, categoryDataRange, 0);
    
       List<XDDFNumericalDataSource<Double>> valuesData = new ArrayList<XDDFNumericalDataSource<Double>>();
       int c = 1;
       for (Double[] valueRow : values) {
        String valuesDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, c, c));
        valuesData.add(XDDFDataSourcesFactory.fromArray(valueRow, valuesDataRange, c));
        c++;
       }
    
       // create axis
       XDDFCategoryAxis bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
       XDDFValueAxis leftAxis = chart.createValueAxis(AxisPosition.LEFT);
       leftAxis.setCrosses(AxisCrosses.AUTO_ZERO);
       // Set AxisCrossBetween, so the left axis crosses the category axis between the categories.
       // Else first and last category is exactly on cross points and the bars are only half visible.
       leftAxis.setCrossBetween(AxisCrossBetween.BETWEEN);
    
       // create chart data
       XDDFChartData data = chart.createData(ChartTypes.BAR, bottomAxis, leftAxis);
       ((XDDFBarChartData)data).setBarDirection(BarDirection.COL);
       // stacked bar chart
       ((XDDFBarChartData)data).setBarGrouping(BarGrouping.STACKED);
       ((XDDFBarChartData)data).setOverlap((byte)100);
    
       // create series
       if (valuesData.size() == 1) {
        // if only one series do not vary colors for each bar
        ((XDDFBarChartData)data).setVaryColors(false);
       } else {
        // if more than one series do vary colors of the series
        ((XDDFBarChartData)data).setVaryColors(true);
       }
    
       for (int s = 0; s < valuesData.size(); s++) {
        XDDFChartData.Series series = data.addSeries(categoriesData, valuesData.get(s));
        series.setTitle("Series"+(s+1), chart.setSheetTitle("Series"+(s+1), s+1));
       }
    
       // plot chart data
       chart.plot(data);
    
       // add data labels
       for (int s = 0 ; s < valuesData.size(); s++) {
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).addNewDLbls();
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls()
         .addNewDLblPos().setVal(org.openxmlformats.schemas.drawingml.x2006.chart.STDLblPos.CTR);
    
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls().addNewNumFmt();
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls().getNumFmt()
         .setSourceLinked(false);
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls().getNumFmt()
         .setFormatCode("0;-0;");
    
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls().addNewShowVal().setVal(true);
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls().addNewShowLegendKey().setVal(false);
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls().addNewShowCatName().setVal(false);
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls().addNewShowSerName().setVal(false);
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls().addNewShowPercent().setVal(false);
        chart.getCTChart().getPlotArea().getBarChartArray(0).getSerArray(s).getDLbls().addNewShowBubbleSize().setVal(false);
       }
    
    
       // line chart
       c = values.length + 1;
       // create data source
       String sumDataRange = chart.formatRange(new CellRangeAddress(1, numOfPoints, c, c));
       XDDFNumericalDataSource<Double> sumData = XDDFDataSourcesFactory.fromArray(sums, sumDataRange, c);
    
       // axis must be there but must not be visible
       bottomAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);
       bottomAxis.setVisible(false);
       leftAxis = chart.createValueAxis(AxisPosition.LEFT);
       leftAxis.setVisible(false);
    
       // set correct cross axis
       bottomAxis.crossAxis(leftAxis);
       leftAxis.crossAxis(bottomAxis);
    
       data = chart.createData(ChartTypes.LINE, bottomAxis, leftAxis);
       XDDFChartData.Series series = data.addSeries(categoriesData, sumData);
       series.setTitle("sum", chart.setSheetTitle("sum", c));
       ((XDDFLineChartData.Series)series).setSmooth(false);
       ((XDDFLineChartData.Series)series).setMarkerStyle(MarkerStyle.NONE);
       // don't show the line
       XDDFShapeProperties shapeProperties = new XDDFShapeProperties();
       shapeProperties.setLineProperties(new XDDFLineProperties(new XDDFNoFillProperties()));
       series.setShapeProperties(shapeProperties);
    
       // plot chart data
       chart.plot(data);
    
       // correct the id and order, must not start 0 again because there are bar series already
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getIdx().setVal(c);
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getOrder().setVal(c);
                
       // add data labels
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).addNewDLbls();
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls()
        .addNewDLblPos().setVal(org.openxmlformats.schemas.drawingml.x2006.chart.STDLblPos.T);
    
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls().addNewNumFmt();
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls().getNumFmt()
        .setSourceLinked(false);
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls().getNumFmt()
        .setFormatCode("0;-0;");
    
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls().addNewShowVal().setVal(true);
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls().addNewShowLegendKey().setVal(false);
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls().addNewShowCatName().setVal(false);
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls().addNewShowSerName().setVal(false);
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls().addNewShowPercent().setVal(false);
       chart.getCTChart().getPlotArea().getLineChartArray(0).getSerArray(0).getDLbls().addNewShowBubbleSize().setVal(false);
     
       // Write the output to a file
       try (FileOutputStream fileOut = new FileOutputStream("CreatePowerPointStackedBarChartXDDFChart.pptx")) {
        slideShow.write(fileOut);
       }
      }
     }
    
    }
    

    【讨论】:

    • 谢谢!!这正是我一直在寻找的。最初我使用的是 CT.. 对象,但这段代码似乎对用户更加友好,所以我将使用这些。
    • 还有一个问题。当它的值为0时,如何不显示条形标签?使用 CT 对象,我可以跳过它(理论上),但在这里它一次从数组中设置整个系列。我可以在之后删除它还是以某种方式将每个 subBar 单独设置为系列?
    • 我试过你的例子,它在那里显示 0。你确定它适用于你的例子吗?也许我们有一些不同版本的 lib 或其他东西,但我得到了 Apache POI 4.1.2。
    • @AxelRitcher:我已经更新了帖子,请查看最后一张图片。
    • @AxelRitcher:我正在使用 Microsoft 365 商业版 2005(内部版本 12827.20336)。能否请您上传 .pptx 文件,以便我尝试打开它?看看它是否真的是 PowerPoint 的问题。
    猜你喜欢
    • 1970-01-01
    • 2014-03-08
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    相关资源
    最近更新 更多