使用 Apache POI 更改 Excel 折线图中的数据范围
Change Data Range in Excel Line Chart using Apache POI
我正在尝试查看是否可以使用 Apache POI 更改折线图中系列的数据范围。
我能够从图表本身中提取系列,但找不到允许我更改数据范围的方法。
XSSFWorkbook workbook = new XSSFWorkbook("C:\Workbook.xlsx");
Sheet worksheet = workbook.getSheetAt(0);
XSSFDrawing drawing = (XSSFDrawing) worksheet.createDrawingPatriarch();
List<XSSFChart> charts = drawing.getCharts();
for (XSSFChart chart : charts) {
String title = chart.getTitleText().toString();
if (title.equals("Z-Acceleration")) {
CTChart cc = chart.getCTChart();
CTPlotArea plotArea = cc.getPlotArea();
CTLineSer[] ccc = plotArea.getLineChartArray()[0].getSerArray();
for (CTLineSer s : ccc) {
System.out.println(s.xmlText());
}
System.out.println(ccc.length);
}
}
我打印了 XML 文本,看看它是否确实能够正确地从图表中提取系列,并且能够找到它的标题和数据范围,但无法更改它。
好的,既然这是个好问题,让我们举一个具体的例子来说明如何使用 apache poi
.
更改 Excel 折线图中的数据范围
让我们从以下内容开始 sheet:
然后是下面的代码:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.SpreadsheetVersion;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;
class ExcelChangeChartDataSource {
static XSSFChart getChartWithTitle(XSSFSheet sheet, String wantedTitle) {
if (sheet == null || wantedTitle == null) return null;
XSSFDrawing drawing = sheet.createDrawingPatriarch();
List<XSSFChart> charts = drawing.getCharts();
for (XSSFChart chart : charts) {
String title = chart.getTitleText().toString();
if (wantedTitle.equals(title)) return chart;
}
return null;
}
static void addMonthDataToChart(XSSFSheet sheet, XSSFChart chart, String month, Double[] seriesData) {
CTChart ctChart = chart.getCTChart();
CTPlotArea ctPlotArea = ctChart.getPlotArea();
List<CTLineSer> ctLineSerList = ctPlotArea.getLineChartArray(0).getSerList();
Row row;
Cell cell;
int ser = 0;
for (CTLineSer ctLineSer : ctLineSerList) {
CTAxDataSource cttAxDataSource = ctLineSer.getCat();
CTStrRef ctStrRef = cttAxDataSource.getStrRef();
AreaReference catReference = new AreaReference(ctStrRef.getF(), SpreadsheetVersion.EXCEL2007);
CellReference firstCatCell = catReference.getFirstCell();
CellReference lastCatCell = catReference.getLastCell();
if (firstCatCell.getCol() == lastCatCell.getCol()) {
int col = firstCatCell.getCol();
int lastRow = lastCatCell.getRow();
row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
cell.setCellValue(month);
ctStrRef.setF(new AreaReference(
firstCatCell,
new CellReference(lastCatCell.getSheetName(), lastRow+1, col, true, true),
SpreadsheetVersion.EXCEL2007).formatAsString()
);
CTNumDataSource ctNumDataSource = ctLineSer.getVal();
CTNumRef ctNumRef = ctNumDataSource.getNumRef();
AreaReference numReference = new AreaReference(ctNumRef.getF(), SpreadsheetVersion.EXCEL2007);
CellReference firstNumCell = numReference.getFirstCell();
CellReference lastNumCell = numReference.getLastCell();
if (lastNumCell.getRow() == lastRow && firstNumCell.getCol() == lastNumCell.getCol()) {
col = firstNumCell.getCol();
row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
if (ser < seriesData.length) cell.setCellValue(seriesData[ser]);
ctNumRef.setF(new AreaReference(
firstNumCell,
new CellReference(lastNumCell.getSheetName(), lastRow+1, col, true, true),
SpreadsheetVersion.EXCEL2007).formatAsString()
);
}
}
ser++;
}
}
public static void main(String[] args) throws Exception {
XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("WorkbookWithChart.xlsx"));
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFChart chart = getChartWithTitle(sheet, "Z-Acceleration");
if (chart != null) {
addMonthDataToChart(sheet, chart, "Apr", new Double[]{7d,3d,5d});
addMonthDataToChart(sheet, chart, "Mai", new Double[]{2d,6d,8d});
addMonthDataToChart(sheet, chart, "Jun", new Double[]{1d,9d,4d});
addMonthDataToChart(sheet, chart, "Jul", new Double[]{5d,6d});
}
FileOutputStream out = new FileOutputStream("WorkbookWithChartNew.xlsx");
workbook.write(out);
out.close();
workbook.close();
}
}
产生以下结果:
此代码使用 org.openxmlformats.schemas.drawingml.x2006.chart.*
类 并且可以与 apache poi 3.17
以及 apache poi 4.1.0
.
一起使用
遗憾的是,org.openxmlformats.schemas.drawingml.x2006.chart.*
public 没有任何 API
文档可用。所以如果我们需要它,我们需要从central.maven.org/maven2/org/apache/poi/ooxml-schemas/1.3下载ooxml-schemas-1.3-sources.jar
。然后解压缩。然后转到目录 ooxml-schemas-1.3
并执行 javadoc -d javadoc -sourcepath ./ -subpackages org
。之后我们在 ooxml-schemas-1.3/javadoc
中找到 API
文档。从 overview-tree.html
开始阅读。
对于apache poi 4.1.0
,我们需要ooxml-schemas-1.4
。
我也尝试过使用 apache poi 4.1.0
中的新 XDDF
东西。但是起初代码并没有真正便宜多少,其次它的缺点是当 XDDFNumericalDataSource<Double> values
中的某些数据不存在时 XDDFChart.plot
失败。然后我们必须将这些数据点设置为 0。但这与不存在不同。所以在这种情况下使用新的 XDDF
stuff 并不是真正的进步。但是尽管如此,这是我试过的代码:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.xddf.usermodel.chart.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;
class ExcelChangeChartDataSource {
static XSSFChart getChartWithTitle(XSSFSheet sheet, String wantedTitle) {
if (sheet == null || wantedTitle == null) return null;
XSSFDrawing drawing = sheet.createDrawingPatriarch();
List<XSSFChart> charts = drawing.getCharts();
for (XSSFChart chart : charts) {
String title = chart.getTitleText().toString();
if (wantedTitle.equals(title)) return chart;
}
return null;
}
static void addMonthDataToChart(XSSFSheet sheet, XSSFChart chart, String month, Double[] seriesData) {
Row row;
Cell cell;
List<XDDFChartData> chartDataList = chart.getChartSeries();
XDDFChartData chartData = chartDataList.get(0);
List<XDDFChartData.Series> seriesList = chartData.getSeries();
int ser = 0;
for (XDDFChartData.Series series : seriesList) {
XDDFDataSource categoryData = series.getCategoryData();
AreaReference catReference = new AreaReference(categoryData.getDataRangeReference(), SpreadsheetVersion.EXCEL2007);
CellReference firstCatCell = catReference.getFirstCell();
CellReference lastCatCell = catReference.getLastCell();
if (firstCatCell.getCol() == lastCatCell.getCol()) {
int col = firstCatCell.getCol();
int lastRow = lastCatCell.getRow();
row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
cell.setCellValue(month);
XDDFDataSource<String> category = XDDFDataSourcesFactory.fromStringCellRange(
sheet,
new CellRangeAddress(firstCatCell.getRow(), lastRow+1, col, col));
XDDFNumericalDataSource valuesData = series.getValuesData();
AreaReference numReference = new AreaReference(valuesData.getDataRangeReference(), SpreadsheetVersion.EXCEL2007);
CellReference firstNumCell = numReference.getFirstCell();
CellReference lastNumCell = numReference.getLastCell();
if (lastNumCell.getRow() == lastRow && firstNumCell.getCol() == lastNumCell.getCol()) {
col = firstNumCell.getCol();
row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
if (ser < seriesData.length) cell.setCellValue(seriesData[ser]);
else cell.setCellValue(0); // Here we need set 0 where it not should be needed.
XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(
sheet,
new CellRangeAddress(firstNumCell.getRow(), lastRow+1, col, col));
series.replaceData(category, values);
}
}
ser++;
}
chart.plot(chartData);
}
public static void main(String[] args) throws Exception {
XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("WorkbookWithChart.xlsx"));
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFChart chart = getChartWithTitle(sheet, "Z-Acceleration");
if (chart != null) {
addMonthDataToChart(sheet, chart, "Apr", new Double[]{7d,3d,5d});
addMonthDataToChart(sheet, chart, "Mai", new Double[]{2d,6d,8d});
addMonthDataToChart(sheet, chart, "Jun", new Double[]{1d,9d,4d});
addMonthDataToChart(sheet, chart, "Jul", new Double[]{5d,6d});
}
FileOutputStream out = new FileOutputStream("WorkbookWithChartNew.xlsx");
workbook.write(out);
out.close();
workbook.close();
}
}
您可以通过将行高设置为零来从图表显示中删除数据,而不是添加新行。
然后,使用以下代码将您不想在图表中看到的行设置为零高度。
for(int i=8;i<14;i++) {
sheet.getRow(i).setZeroHeight(true);;
}
现在您将看到输出 Excel 更改为以下内容。
顺便说一句,您还可以通过将图表标题链接到单元格值来更新图表标题,如上例所示,图表标题链接到单元格 A1。
我正在尝试查看是否可以使用 Apache POI 更改折线图中系列的数据范围。
我能够从图表本身中提取系列,但找不到允许我更改数据范围的方法。
XSSFWorkbook workbook = new XSSFWorkbook("C:\Workbook.xlsx");
Sheet worksheet = workbook.getSheetAt(0);
XSSFDrawing drawing = (XSSFDrawing) worksheet.createDrawingPatriarch();
List<XSSFChart> charts = drawing.getCharts();
for (XSSFChart chart : charts) {
String title = chart.getTitleText().toString();
if (title.equals("Z-Acceleration")) {
CTChart cc = chart.getCTChart();
CTPlotArea plotArea = cc.getPlotArea();
CTLineSer[] ccc = plotArea.getLineChartArray()[0].getSerArray();
for (CTLineSer s : ccc) {
System.out.println(s.xmlText());
}
System.out.println(ccc.length);
}
}
我打印了 XML 文本,看看它是否确实能够正确地从图表中提取系列,并且能够找到它的标题和数据范围,但无法更改它。
好的,既然这是个好问题,让我们举一个具体的例子来说明如何使用 apache poi
.
让我们从以下内容开始 sheet:
然后是下面的代码:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.SpreadsheetVersion;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;
class ExcelChangeChartDataSource {
static XSSFChart getChartWithTitle(XSSFSheet sheet, String wantedTitle) {
if (sheet == null || wantedTitle == null) return null;
XSSFDrawing drawing = sheet.createDrawingPatriarch();
List<XSSFChart> charts = drawing.getCharts();
for (XSSFChart chart : charts) {
String title = chart.getTitleText().toString();
if (wantedTitle.equals(title)) return chart;
}
return null;
}
static void addMonthDataToChart(XSSFSheet sheet, XSSFChart chart, String month, Double[] seriesData) {
CTChart ctChart = chart.getCTChart();
CTPlotArea ctPlotArea = ctChart.getPlotArea();
List<CTLineSer> ctLineSerList = ctPlotArea.getLineChartArray(0).getSerList();
Row row;
Cell cell;
int ser = 0;
for (CTLineSer ctLineSer : ctLineSerList) {
CTAxDataSource cttAxDataSource = ctLineSer.getCat();
CTStrRef ctStrRef = cttAxDataSource.getStrRef();
AreaReference catReference = new AreaReference(ctStrRef.getF(), SpreadsheetVersion.EXCEL2007);
CellReference firstCatCell = catReference.getFirstCell();
CellReference lastCatCell = catReference.getLastCell();
if (firstCatCell.getCol() == lastCatCell.getCol()) {
int col = firstCatCell.getCol();
int lastRow = lastCatCell.getRow();
row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
cell.setCellValue(month);
ctStrRef.setF(new AreaReference(
firstCatCell,
new CellReference(lastCatCell.getSheetName(), lastRow+1, col, true, true),
SpreadsheetVersion.EXCEL2007).formatAsString()
);
CTNumDataSource ctNumDataSource = ctLineSer.getVal();
CTNumRef ctNumRef = ctNumDataSource.getNumRef();
AreaReference numReference = new AreaReference(ctNumRef.getF(), SpreadsheetVersion.EXCEL2007);
CellReference firstNumCell = numReference.getFirstCell();
CellReference lastNumCell = numReference.getLastCell();
if (lastNumCell.getRow() == lastRow && firstNumCell.getCol() == lastNumCell.getCol()) {
col = firstNumCell.getCol();
row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
if (ser < seriesData.length) cell.setCellValue(seriesData[ser]);
ctNumRef.setF(new AreaReference(
firstNumCell,
new CellReference(lastNumCell.getSheetName(), lastRow+1, col, true, true),
SpreadsheetVersion.EXCEL2007).formatAsString()
);
}
}
ser++;
}
}
public static void main(String[] args) throws Exception {
XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("WorkbookWithChart.xlsx"));
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFChart chart = getChartWithTitle(sheet, "Z-Acceleration");
if (chart != null) {
addMonthDataToChart(sheet, chart, "Apr", new Double[]{7d,3d,5d});
addMonthDataToChart(sheet, chart, "Mai", new Double[]{2d,6d,8d});
addMonthDataToChart(sheet, chart, "Jun", new Double[]{1d,9d,4d});
addMonthDataToChart(sheet, chart, "Jul", new Double[]{5d,6d});
}
FileOutputStream out = new FileOutputStream("WorkbookWithChartNew.xlsx");
workbook.write(out);
out.close();
workbook.close();
}
}
产生以下结果:
此代码使用 org.openxmlformats.schemas.drawingml.x2006.chart.*
类 并且可以与 apache poi 3.17
以及 apache poi 4.1.0
.
遗憾的是,org.openxmlformats.schemas.drawingml.x2006.chart.*
public 没有任何 API
文档可用。所以如果我们需要它,我们需要从central.maven.org/maven2/org/apache/poi/ooxml-schemas/1.3下载ooxml-schemas-1.3-sources.jar
。然后解压缩。然后转到目录 ooxml-schemas-1.3
并执行 javadoc -d javadoc -sourcepath ./ -subpackages org
。之后我们在 ooxml-schemas-1.3/javadoc
中找到 API
文档。从 overview-tree.html
开始阅读。
对于apache poi 4.1.0
,我们需要ooxml-schemas-1.4
。
我也尝试过使用 apache poi 4.1.0
中的新 XDDF
东西。但是起初代码并没有真正便宜多少,其次它的缺点是当 XDDFNumericalDataSource<Double> values
中的某些数据不存在时 XDDFChart.plot
失败。然后我们必须将这些数据点设置为 0。但这与不存在不同。所以在这种情况下使用新的 XDDF
stuff 并不是真正的进步。但是尽管如此,这是我试过的代码:
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.*;
import org.apache.poi.xssf.usermodel.*;
import org.apache.poi.ss.SpreadsheetVersion;
import org.apache.poi.xddf.usermodel.chart.*;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;
class ExcelChangeChartDataSource {
static XSSFChart getChartWithTitle(XSSFSheet sheet, String wantedTitle) {
if (sheet == null || wantedTitle == null) return null;
XSSFDrawing drawing = sheet.createDrawingPatriarch();
List<XSSFChart> charts = drawing.getCharts();
for (XSSFChart chart : charts) {
String title = chart.getTitleText().toString();
if (wantedTitle.equals(title)) return chart;
}
return null;
}
static void addMonthDataToChart(XSSFSheet sheet, XSSFChart chart, String month, Double[] seriesData) {
Row row;
Cell cell;
List<XDDFChartData> chartDataList = chart.getChartSeries();
XDDFChartData chartData = chartDataList.get(0);
List<XDDFChartData.Series> seriesList = chartData.getSeries();
int ser = 0;
for (XDDFChartData.Series series : seriesList) {
XDDFDataSource categoryData = series.getCategoryData();
AreaReference catReference = new AreaReference(categoryData.getDataRangeReference(), SpreadsheetVersion.EXCEL2007);
CellReference firstCatCell = catReference.getFirstCell();
CellReference lastCatCell = catReference.getLastCell();
if (firstCatCell.getCol() == lastCatCell.getCol()) {
int col = firstCatCell.getCol();
int lastRow = lastCatCell.getRow();
row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
cell.setCellValue(month);
XDDFDataSource<String> category = XDDFDataSourcesFactory.fromStringCellRange(
sheet,
new CellRangeAddress(firstCatCell.getRow(), lastRow+1, col, col));
XDDFNumericalDataSource valuesData = series.getValuesData();
AreaReference numReference = new AreaReference(valuesData.getDataRangeReference(), SpreadsheetVersion.EXCEL2007);
CellReference firstNumCell = numReference.getFirstCell();
CellReference lastNumCell = numReference.getLastCell();
if (lastNumCell.getRow() == lastRow && firstNumCell.getCol() == lastNumCell.getCol()) {
col = firstNumCell.getCol();
row = sheet.getRow(lastRow+1); if (row == null) row = sheet.createRow(lastRow+1);
cell = row.getCell(col); if (cell == null) cell = row.createCell(col);
if (ser < seriesData.length) cell.setCellValue(seriesData[ser]);
else cell.setCellValue(0); // Here we need set 0 where it not should be needed.
XDDFNumericalDataSource<Double> values = XDDFDataSourcesFactory.fromNumericCellRange(
sheet,
new CellRangeAddress(firstNumCell.getRow(), lastRow+1, col, col));
series.replaceData(category, values);
}
}
ser++;
}
chart.plot(chartData);
}
public static void main(String[] args) throws Exception {
XSSFWorkbook workbook = (XSSFWorkbook)WorkbookFactory.create(new FileInputStream("WorkbookWithChart.xlsx"));
XSSFSheet sheet = workbook.getSheetAt(0);
XSSFChart chart = getChartWithTitle(sheet, "Z-Acceleration");
if (chart != null) {
addMonthDataToChart(sheet, chart, "Apr", new Double[]{7d,3d,5d});
addMonthDataToChart(sheet, chart, "Mai", new Double[]{2d,6d,8d});
addMonthDataToChart(sheet, chart, "Jun", new Double[]{1d,9d,4d});
addMonthDataToChart(sheet, chart, "Jul", new Double[]{5d,6d});
}
FileOutputStream out = new FileOutputStream("WorkbookWithChartNew.xlsx");
workbook.write(out);
out.close();
workbook.close();
}
}
您可以通过将行高设置为零来从图表显示中删除数据,而不是添加新行。
然后,使用以下代码将您不想在图表中看到的行设置为零高度。
for(int i=8;i<14;i++) {
sheet.getRow(i).setZeroHeight(true);;
}
现在您将看到输出 Excel 更改为以下内容。
顺便说一句,您还可以通过将图表标题链接到单元格值来更新图表标题,如上例所示,图表标题链接到单元格 A1。