在 Java 中管理演示文稿的图表数据系列

概述

图表将其绘制的数据存储在图表数据工作簿中。一个IChartSeries 表示一组相关值,系列中的每个IChartDataPoint 引用一个或多个工作簿单元格。IChartCategory 对象提供系列共享的标签或分组值。因此,系列名称、类别和数据点值连接到IChartDataCell 对象,而不仅仅存储为显示文本。

对于典型的类别图,默认工作簿使用第 0 行存放系列名称,第 0 列存放类别名称,其余单元格存放系列数值。传递给IChartDataWorkbook.getCell 的工作表、行和列索引为零基。这种布局在创建带有默认数据的图表时很有用,但不要假设每个已有图表都采用此布局。对于已加载的演示文稿,请在更改工作簿数值之前检查系列、类别和数据点引用的单元格。

图表设置有三种不同的作用域:

当未显式设置点或系列填充时,图表样式和主题决定自动外观。 当系列和点的格式均存在时,点的格式优先于该点。

chart-series-powerpoint

设置图表系列重叠

IChartSeries.getOverlap 报告 2D 图表中条形或柱形的重叠程度,范围为 -100 到 100%。它是父系列组设置的只读投影。使用IChartSeriesGroup.setOverlap 可更新该组中所有兼容系列。此选项适用于显示分组条形或柱形的图表类型;对组合图中不相关的系列组没有影响。

以下示例为包含第一系列的组设置重叠:

import com.aspose.slides.*;

final int firstSlideIndex = 0;
final int firstSeriesIndex = 0;
final byte overlapPercent = 30;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(firstSlideIndex);

    // 新图表包含示例系列、类别和数值。
    IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

    IChartSeries series = chart.getChartData().getSeries().get_Item(firstSeriesIndex);
    series.getParentSeriesGroup().setOverlap(overlapPercent);

    presentation.save("series_overlap.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

结果:

The series overlap

更改系列填充颜色

使用IChartSeries.getFormat 为整个系列设置默认填充。如果某个点已经有显式填充,其IChartDataPoint.getFormat 设置会覆盖该点的系列填充。

以下示例为第一系列应用实心蓝色填充:

import com.aspose.slides.*;
import java.awt.Color;

final int firstSlideIndex = 0;
final int firstSeriesIndex = 0;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(firstSlideIndex);

    IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

    IChartSeries series = chart.getChartData().getSeries().get_Item(firstSeriesIndex);
    series.getFormat().getFill().setFillType(FillType.Solid);
    series.getFormat().getFill().getSolidFillColor().setColor(Color.BLUE);

    presentation.save("series_color.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

结果:

The color of the series

更改系列名称

系列名称存储在图表数据工作簿中,通常显示在图例中。在为聚类柱形图创建的默认工作簿中,单元格 B1 位于第 0 行第 1 列,包含第一系列的名称。下面示例中的命名常量明确了该结构:

import com.aspose.slides.*;

final int firstSlideIndex = 0;
final int worksheetIndex = 0;
final int seriesNameRowIndex = 0;
final int firstSeriesColumnIndex = 1;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(firstSlideIndex);

    IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

    IChartDataWorkbook workbook = chart.getChartData().getChartDataWorkbook();
    IChartDataCell seriesNameCell = workbook.getCell(worksheetIndex, seriesNameRowIndex, firstSeriesColumnIndex);
    seriesNameCell.setValue("Revenue");

    presentation.save("series_name.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

您还可以更新IChartSeries.getName 已引用的单元格。此方法避免对已有图表假设特定的行列位置:

import com.aspose.slides.*;

final int firstSlideIndex = 0;
final int firstSeriesIndex = 0;
final int firstNameCellIndex = 0;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(firstSlideIndex);

    IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

    IChartSeries series = chart.getChartData().getSeries().get_Item(firstSeriesIndex);
    IChartDataCell seriesNameCell = series.getName().getAsCells().get_Item(firstNameCellIndex);
    seriesNameCell.setValue("Revenue");

    presentation.save("series_name.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

结果:

The series name

使用多个单元格创建具有名称的系列

当产品名称和报告期间分别存放在不同工作簿单元格时,复合系列名称会很有用。例如,您可以将 B1 中的 Product A 与 C1 中的 2026 合并为单一系列名称,同时保持两部分均链接到其源单元格。

使用IChartDataWorkbook.getCellCollection 检索名称范围,然后将该集合传递给IChartSeriesCollection.add。skipHiddenCells 参数控制是否包含隐藏单元格:true 排除,false 包含。此示例使用 false 包含名称范围内的所有单元格。

以下示例创建一个包含一个系列和两个数据点的演示文稿。B1:C1 只提供系列名称;A2:A3 提供类别标签,B2:B3 提供数值。

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 50, 50, 620, 180);

    chart.getChartData().getSeries().clear();
    chart.getChartData().getCategories().clear();
    chart.setLegend(true);

    IChartDataWorkbook workbook = chart.getChartData().getChartDataWorkbook();
    workbook.clear(0);

    // 这两个单元格提供系列名称。
    workbook.getCell(0, 0, 1, "Product A");
    workbook.getCell(0, 0, 2, "2026");
    IChartCellCollection nameCells = workbook.getCellCollection("Sheet1!$B$1:$C$1", false);
    IChartSeries series = chart.getChartData().getSeries().add(nameCells, ChartType.ClusteredColumn);

    // 单独的单元格提供类别和数值数据点。
    IChartDataCell northCategory = workbook.getCell(0, 1, 0, "North");
    IChartDataCell southCategory = workbook.getCell(0, 2, 0, "South");
    chart.getChartData().getCategories().add(northCategory);
    chart.getChartData().getCategories().add(southCategory);
    IChartDataCell northValue = workbook.getCell(0, 1, 1, 120);
    IChartDataCell southValue = workbook.getCell(0, 2, 1, 150);
    series.getDataPoints().addDataPointForBarSeries(northValue);
    series.getDataPoints().addDataPointForBarSeries(southValue);

    presentation.save("composite_series_name.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

生成的系列名称为 Product A 2026,两个单元格值之间有空格。图例将其显示为两列的单一条目。下图展示了结果:

Column chart with North and South values and the composite series name Product A 2026 in the legend

获取自动系列填充颜色

IChartSeries.getAutomaticSeriesColor 返回根据系列索引和图表样式计算的颜色。这是系列填充未显式定义时使用的颜色。调用该方法仅读取计算后的颜色,不会分配新的填充。

以下示例打印每个默认系列的自动颜色:

import com.aspose.slides.*;
import java.awt.Color;

final int firstSlideIndex = 0;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(firstSlideIndex);

    IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

    int seriesCount = chart.getChartData().getSeries().size();
    for (int seriesIndex = 0; seriesIndex < seriesCount; seriesIndex++) {
        IChartSeries series = chart.getChartData().getSeries().get_Item(seriesIndex);
        Color automaticColor = series.getAutomaticSeriesColor();
        System.out.println("Series " + seriesIndex + ": " + automaticColor);
    }
} finally {
    presentation.dispose();
}

默认图表样式的示例输出:

Series 0: java.awt.Color[r=79,g=129,b=189]
Series 1: java.awt.Color[r=192,g=80,b=77]
Series 2: java.awt.Color[r=155,g=187,b=89]

具体颜色取决于图表样式和主题。

为图表系列设置负值填充颜色翻转

对于条形、柱形和气泡系列,使用IChartSeries.setInvertIfNegative 可以在负值时显示不同的填充。将常规系列填充设为实心,启用翻转,并通过IChartSeries.getInvertedSolidFillColor 设置负值颜色。负数在工作簿中保持不变,仅改变显示颜色。

以下示例用一个系列替换默认图表数据。工作表第 0 行存放系列名称,第 0 列存放类别名称,第 1 列存放数值:

import com.aspose.slides.*;
import java.awt.Color;

final int firstSlideIndex = 0;
final int worksheetIndex = 0;
final int headerRowIndex = 0;
final int categoryColumnIndex = 0;
final int firstSeriesColumnIndex = 1;
final int firstDataRowIndex = 1;

String[] categoryNames = { "Category 1", "Category 2", "Category 3" };
int[] seriesValues = { -20, 50, -30 };

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(firstSlideIndex);

    IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 200);
    IChartData chartData = chart.getChartData();
    IChartDataWorkbook workbook = chartData.getChartDataWorkbook();

    chartData.getSeries().clear();
    chartData.getCategories().clear();

    IChartDataCell seriesNameCell = workbook.getCell(worksheetIndex, headerRowIndex, firstSeriesColumnIndex, "Series 1");
    int chartType = chart.getType();
    IChartSeries series = chartData.getSeries().add(seriesNameCell, chartType);

    for (int categoryIndex = 0; categoryIndex < categoryNames.length; categoryIndex++) {
        int dataRowIndex = firstDataRowIndex + categoryIndex;
        String categoryName = categoryNames[categoryIndex];
        int seriesValue = seriesValues[categoryIndex];

        IChartDataCell categoryCell = workbook.getCell(worksheetIndex, dataRowIndex, categoryColumnIndex, categoryName);
        chartData.getCategories().add(categoryCell);

        IChartDataCell valueCell = workbook.getCell(worksheetIndex, dataRowIndex, firstSeriesColumnIndex, seriesValue);
        series.getDataPoints().addDataPointForBarSeries(valueCell);
    }

    Color automaticSeriesColor = series.getAutomaticSeriesColor();
    series.getFormat().getFill().setFillType(FillType.Solid);
    series.getFormat().getFill().getSolidFillColor().setColor(automaticSeriesColor);
    series.setInvertIfNegative(true);
    series.getInvertedSolidFillColor().setColor(Color.RED);

    presentation.save("inverted_solid_fill_color.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

结果:

The inverted solid fill color

您可以通过IChartDataPoint.setInvertIfNegative 为单个点启用翻转。下面示例在系列上禁用翻转,仅在选中的点上启用,并为该点赋予负值以便看到效果:

import com.aspose.slides.*;
import java.awt.Color;

final int firstSlideIndex = 0;
final int firstSeriesIndex = 0;
final int targetDataPointIndex = 2;
final int negativeValue = -30;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(firstSlideIndex);

    IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

    IChartSeries series = chart.getChartData().getSeries().get_Item(firstSeriesIndex);
    Color automaticSeriesColor = series.getAutomaticSeriesColor();
    series.getFormat().getFill().setFillType(FillType.Solid);
    series.getFormat().getFill().getSolidFillColor().setColor(automaticSeriesColor);
    series.getInvertedSolidFillColor().setColor(Color.RED);
    series.setInvertIfNegative(false);

    IChartDataPoint dataPoint = series.getDataPoints().get_Item(targetDataPointIndex);
    dataPoint.getValue().getAsCell().setValue(negativeValue);
    dataPoint.setInvertIfNegative(true);

    presentation.save("data_point_invert_color_if_negative.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

清除特定数据点的值

要使某一点为空而不删除其他点,可将其对应的工作簿单元格设为 null。对于柱形图,可通过IChartDataPoint.getValue 获取绘制的数值。数据点仍保持在相同的类别位置,但图表会根据空值设置将其视为空白。

以下示例仅清除第一系列中的第二个点:

import com.aspose.slides.*;

final int firstSlideIndex = 0;
final int firstSeriesIndex = 0;
final int targetDataPointIndex = 1;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(firstSlideIndex);

    IChart chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 200);

    IChartSeries series = chart.getChartData().getSeries().get_Item(firstSeriesIndex);
    IChartDataPoint dataPoint = series.getDataPoints().get_Item(targetDataPointIndex);
    dataPoint.getValue().getAsCell().setValue(null);

    presentation.save("clear_data_point_value.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

散点图使用独立的 X 和 Y 单元格,气泡图还使用大小单元格。仅清除表示您想删除的数值的单元格。不要在希望保留其他点时调用IChartDataPointCollection.clear,因为该方法会删除该系列的所有数据点。

控制空单元格的显示方式

包含值的隐藏单元格与空单元格是不同的情况。要包含或排除隐藏工作表行列中的数据,请参阅Include Data from Hidden Rows and Columns。

空工作簿单元格表示缺失数据;包含 0 的单元格表示已知的数值。调用IChartDataCell.setValue 并传入 null 可使单元格为空。数值零始终保持为零,无论空单元格设置为何。

使用IChart.setDisplayBlanksAs 选择图表如何显示空单元格。此设置适用于整个图表,改变空白的绘制方式,而不会将空工作簿单元格填充为零或插值。

以下自包含示例创建一条折线图,包含一个系列,清除第 3 天的数值,并分别以每种模式保存相同图表。无需输入文件。IChartDataWorkbook 使用工作表 0,第 0 列存放类别标签,第 1 列存放数值;第 0 行存放系列名称。最终数据为 10, 20, empty, 30, 40。

import com.aspose.slides.*;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(0);

    IChart chart = slide.getShapes().addChart(ChartType.LineWithMarkers, 40, 40, 640, 400);
    IChartData chartData = chart.getChartData();
    IChartDataWorkbook workbook = chartData.getChartDataWorkbook();

    chartData.getSeries().clear();
    chartData.getCategories().clear();

    IChartDataCell seriesNameCell = workbook.getCell(0, 0, 1, "Measurements");
    IChartSeries series = chartData.getSeries().add(seriesNameCell, chart.getType());
    int[] values = { 10, 20, 25, 30, 40 };

    for (int i = 0; i < values.length; i++) {
        IChartDataCell categoryCell = workbook.getCell(0, i + 1, 0, "Day " + (i + 1));
        chartData.getCategories().add(categoryCell);
        IChartDataCell valueCell = workbook.getCell(0, i + 1, 1, values[i]);
        series.getDataPoints().addDataPointForLineSeries(valueCell);
    }

    // 让第3天真正为空,同时保留其类别和数据点。
    workbook.getCell(0, 3, 1).setValue(null);

    int[] modes = { DisplayBlanksAsType.Gap, DisplayBlanksAsType.Zero, DisplayBlanksAsType.Span };
    String[] modeNames = { "Gap", "Zero", "Span" };
    for (int i = 0; i < modes.length; i++) {
        chart.setDisplayBlanksAs(modes[i]);
        presentation.save("empty_cells_" + modeNames[i] + ".pptx", SaveFormat.Pptx);
    }
} finally {
    presentation.dispose();
}

每个输出文件在保存前存储相应模式:empty_cells_Gap.pptx、empty_cells_Zero.pptx、empty_cells_Span.pptx。如果只需一种版本,可在保存演示文稿前设置所需模式,而不是遍历所有模式。

下图比较了三个文件中的相同数据。第 3 天在工作簿中始终为空:

Line charts with identical data: Gap breaks the line at Day 3, Zero drops the line to zero, and Span connects Day 2 to Day 4.

可见效果取决于图表类型。折线图能够直观比较所有三种模式。条形和柱形图没有线段可跨越缺失的类别,因此 Span 无法产生上图所示的连接段;缺失的柱形和零高度柱形也可能看起来相同。类似地,仅有标记的散点图没有连接线。不要期望每种图表类型都产生三种不同结果;请检查您使用的图表类型的输出。

设置系列间距宽度

间距宽度是相邻条形或柱形簇之间的空间,以条形或柱形宽度的百分比表示。与重叠类似,它属于父系列组,而不是单个系列。对该组调用一次IChartSeriesGroup.setGapWidth 即可。数值越大,簇之间的间距越宽;数值越小,簇越密集。

以下示例更改间距宽度并仅保存最终演示文稿:

import com.aspose.slides.*;

final int firstSlideIndex = 0;
final int firstSeriesIndex = 0;
final int gapWidthPercent = 30;

Presentation presentation = new Presentation();
try {
    ISlide slide = presentation.getSlides().get_Item(firstSlideIndex);

    IChart chart = slide.getShapes().addChart(ChartType.StackedColumn, 20, 20, 500, 200);

    IChartSeries series = chart.getChartData().getSeries().get_Item(firstSeriesIndex);
    series.getParentSeriesGroup().setGapWidth(gapWidthPercent);

    presentation.save("gap_width_30.pptx", SaveFormat.Pptx);
} finally {
    presentation.dispose();
}

结果:

The gap width

常见问题

哪些图表类型支持数据系列?

所有由ChartType 枚举表示的图表类型均使用图表数据,但其系列的数值结构或设置并不完全相同。例如,类别图使用类别和数值,散点图使用 X 与 Y 值,气泡图还增加气泡大小。请使用与系列类型匹配的数据点创建方法。重叠和间距等选项仅适用于兼容的条形或柱形组。

什么是图表系列组?

IChartSeriesGroup 包含共享组级绘制设置的兼容系列。组合图可以包含多个组,因此通过某个系列访问的组的更改不一定会影响图表中所有系列。

新创建的图表是否包含默认数据?

是的。默认情况下,IShapeCollection.addChart 会创建示例系列、类别和数值。您可以编辑这些单元格,或在添加完全自定义的数据集之前清除系列和类别集合。也可以使用重载方法创建不带默认数据的图表。

图表对象如何与工作簿单元格关联?

系列名称、类别标签和数据点数值引用IChartDataWorkbook 中的单元格。更改被引用的单元格会更新相应的图表元素。构建自定义数据时,请保持类别行和系列值行对齐,以便每个点绘制在预期的类别下。

如何只清除一个点而不是整个系列?

将相应的数值单元格设为 null,以保留该点的类别位置为空点。仅在希望删除该系列所有点时才使用IChartDataPointCollection.clear,因为该方法会删除该系列的全部数据点。如果同时删除类别,请更新所有系列,使其数值仍与类别集合保持对齐。

空点如何显示?

显示方式取决于图表类型以及通过IChart.setDisplayBlanksAs 配置的值。受支持的图表可以将空白显示为间隙、零值或连接相邻点。请选择与演示文稿中缺失数据意义相符的设置。完整示例和视觉对比请参阅Control the Display of Empty Cells。

负值如何格式化?

对于受支持的条形、柱形和气泡系列,调用IChartSeries.setInvertIfNegative 并设置IChartSeries.getInvertedSolidFillColor 返回的颜色。您也可以使用IChartDataPoint.setInvertIfNegative 为单个点覆盖此行为。这些方法影响格式,而不改变存储的数值。

当系列和点都被格式化时,哪个格式生效?

显式的数据点格式对该点具有最高优先级。其他点继续使用显式的系列格式,或在系列格式未定义时使用自动的图表样式和主题。组设置(如重叠和间距)控制布局,不属于点级别的格式覆盖。

图表可以包含的系列数量是否有限制?

Aspose.Slides 没有单独的固定系列计数限制。实际上,演示文稿文件的约束、可用内存、渲染时间以及图表可读性决定了实际可接受的上限。

当柱形之间过近或过远时该怎么办?

在相应的父系列组上调用IChartSeriesGroup.setGapWidth。增大数值可扩大簇之间的间距,减小数值则使簇更紧凑。