在簡報中使用 Python 管理圖表資料標籤
簡介
資料標籤會顯示圖表系列與單獨資料點的資訊,協助讀者辨識數值並了解圖表。本文說明如何格式化數值、顯示百分比、讀取標籤文字、控制超出坐標軸最大值的標籤、調整類別坐標軸標籤間距,以及設定圓形圖標籤的位置。
設定圖表資料標籤的資料精度
使用 setNumberFormatOfValues 來格式化系列值。此範例建立一個使用預設資料的折線圖,顯示其資料表,並為第一個系列啟用數值標籤。格式 #,##0.00 會顯示千位分隔符與兩位小數,且不會更改底層的值。
import jpype
import asposeslides
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import ChartType, Presentation, SaveFormat
presentation = Presentation()
try:
slide = presentation.getSlides().get_Item(0)
chart = slide.getShapes().addChart(ChartType.Line, 50, 50, 450, 300)
chart.setDataTable(True)
series = chart.getChartData().getSeries().get_Item(0)
series.setNumberFormatOfValues("#,##0.00")
series.getLabels().getDefaultDataLabelFormat().setShowValue(True)
presentation.save("PrecisionOfDatalabels_out.pptx", SaveFormat.Pptx)
finally:
presentation.dispose()
若要格式化圖表內嵌工作簿的數值儲存格,請參閱 Set Custom Number Formats for Workbook Cells。該範例也示範了數值標籤遵循其來源儲存格格式,同時保留儲存的數值。
以百分比顯示為標籤
對於堆疊直條圖,將每個值計算為該類別總和的百分比,並將文字指派給由 getTextFrameForOverriding 回傳的文字框。本範例使用預設圖表資料,並以 8 點字型顯示保留兩位小數的百分比。總計為零的類別會被略過,以避免除以零的錯誤。若圖表資料變更,請重新計算自訂標籤文字。
import jpype
import asposeslides
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import ChartType, Portion, Presentation, SaveFormat
presentation = Presentation()
try:
slide = presentation.getSlides().get_Item(0)
chart = slide.getShapes().addChart(ChartType.StackedColumn, 20, 20, 400, 400)
chart_series = chart.getChartData().getSeries()
category_totals = [0.0] * chart.getChartData().getCategories().size()
for category_index in range(len(category_totals)):
for series_index in range(chart_series.size()):
data_point = chart_series.get_Item(series_index).getDataPoints().get_Item(category_index)
category_totals[category_index] += float(data_point.getValue().getData())
for series_index in range(chart_series.size()):
series = chart_series.get_Item(series_index)
series.getLabels().getDefaultDataLabelFormat().setShowLegendKey(False)
for point_index in range(series.getDataPoints().size()):
data_point = series.getDataPoints().get_Item(point_index)
label = data_point.getLabel()
if category_totals[point_index] == 0:
print(f"Cannot calculate a percentage for category {point_index}: the total is zero.")
continue
point_percentage = float(data_point.getValue().getData()) / category_totals[point_index] * 100
portion = Portion()
portion.setText(f"{point_percentage:.2f} %")
portion.getPortionFormat().setFontHeight(8)
label.getTextFrameForOverriding().setText("")
paragraph = label.getTextFrameForOverriding().getParagraphs().get_Item(0)
paragraph.getPortions().add(portion)
label_format = label.getDataLabelFormat()
label_format.setShowValue(True)
label_format.setShowSeriesName(False)
label_format.setShowPercentage(False)
label_format.setShowLegendKey(False)
label_format.setShowCategoryName(False)
label_format.setShowBubbleSize(False)
presentation.save("DisplayPercentageAsLabels_out.pptx", SaveFormat.Pptx)
finally:
presentation.dispose()
設定圖表資料標籤的百分比符號
當值以分數形式儲存時,使用 setNumberFormat 來顯示百分比。將 False 傳遞給 setNumberFormatLinkedToSource 可使標籤格式獨立於來源儲存格。
此範例建立一個 100% 堆疊直條圖,四個類別中含有紅色與藍色系列。每對數值的總和為 1。標籤格式 0.0% 會將 0.30 顯示為 30.0%,而垂直軸則使用兩位小數。兩個系列皆使用白色、10 點的標籤文字。
import jpype
import asposeslides
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import ChartType, FillType, Presentation, SaveFormat
Color = jpype.JClass("java.awt.Color")
presentation = Presentation()
try:
slide = presentation.getSlides().get_Item(0)
chart = slide.getShapes().addChart(ChartType.PercentsStackedColumn, 20, 20, 500, 400)
chart.getAxes().getVerticalAxis().setNumberFormatLinkedToSource(False)
chart.getAxes().getVerticalAxis().setNumberFormat("0.00%")
chart.getChartData().getSeries().clear()
chart.getChartData().getCategories().clear()
workbook = chart.getChartData().getChartDataWorkbook()
worksheet_index = 0
for i in range(4):
category_cell = workbook.getCell(worksheet_index, i + 1, 0, f"Category {i + 1}")
chart.getChartData().getCategories().add(category_cell)
series_names = ["Reds", "Blues"]
series_colors = [Color.RED, Color.BLUE]
values = [[0.30, 0.50, 0.80, 0.65], [0.70, 0.50, 0.20, 0.35]]
for i, series_name in enumerate(series_names):
series_cell = workbook.getCell(worksheet_index, 0, i + 1, series_name)
series = chart.getChartData().getSeries().add(series_cell, chart.getType())
for j, value in enumerate(values[i]):
value_cell = workbook.getCell(worksheet_index, j + 1, i + 1, jpype.JDouble(value))
series.getDataPoints().addDataPointForBarSeries(value_cell)
series.getFormat().getFill().setFillType(FillType.Solid)
series.getFormat().getFill().getSolidFillColor().setColor(series_colors[i])
label_format = series.getLabels().getDefaultDataLabelFormat()
label_format.setShowValue(True)
label_format.setNumberFormatLinkedToSource(False)
label_format.setNumberFormat("0.0%")
portion_format = label_format.getTextFormat().getPortionFormat()
portion_format.setFontHeight(10)
portion_format.getFillFormat().setFillType(FillType.Solid)
portion_format.getFillFormat().getSolidFillColor().setColor(Color.WHITE)
presentation.save("SetDataLabelsPercentageSign_out.pptx", SaveFormat.Pptx)
finally:
presentation.dispose()
讀取資料標籤的實際文字
使用 getActualLabelText 取得資料標籤設定所產生的文字。這在擷取報告用標籤、搜尋簡報內容或驗證產生的圖表時相當有用。在下列示例中,預設的 data label format 結合了每個類別名稱、系列名稱與數值。某一資料點將其值格式化為百分比,另一則使用從 getTextFrameForOverriding 取得的自訂文字。
import jpype
import asposeslides
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import ChartType, Presentation
presentation = Presentation()
try:
slide = presentation.getSlides().get_Item(0)
chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 300)
chart.getChartData().getSeries().clear()
chart.getChartData().getCategories().clear()
workbook = chart.getChartData().getChartDataWorkbook()
first_category_cell = workbook.getCell(0, 1, 0, "Q1")
chart.getChartData().getCategories().add(first_category_cell)
second_category_cell = workbook.getCell(0, 2, 0, "Q2")
chart.getChartData().getCategories().add(second_category_cell)
north_series_cell = workbook.getCell(0, 0, 1, "North")
north = chart.getChartData().getSeries().add(north_series_cell, chart.getType())
north_first_value_cell = workbook.getCell(0, 1, 1, jpype.JDouble(0.25))
north.getDataPoints().addDataPointForBarSeries(north_first_value_cell)
north_second_value_cell = workbook.getCell(0, 2, 1, jpype.JDouble(0.75))
north.getDataPoints().addDataPointForBarSeries(north_second_value_cell)
south_series_cell = workbook.getCell(0, 0, 2, "South")
south = chart.getChartData().getSeries().add(south_series_cell, chart.getType())
south_first_value_cell = workbook.getCell(0, 1, 2, jpype.JDouble(0.40))
south.getDataPoints().addDataPointForBarSeries(south_first_value_cell)
south_second_value_cell = workbook.getCell(0, 2, 2, jpype.JDouble(0.60))
south.getDataPoints().addDataPointForBarSeries(south_second_value_cell)
for series in chart.getChartData().getSeries():
label_format = series.getLabels().getDefaultDataLabelFormat()
label_format.setShowCategoryName(True)
label_format.setShowSeriesName(True)
label_format.setShowValue(True)
north.getLabels().get_Item(1).getDataLabelFormat().setNumberFormatLinkedToSource(False)
north.getLabels().get_Item(1).getDataLabelFormat().setNumberFormat("0%")
south.getLabels().get_Item(0).getTextFrameForOverriding().setText("Reviewed")
for series in chart.getChartData().getSeries():
for point in series.getDataPoints():
label = point.getLabel()
if not label.isVisible():
continue
print(f"Value: {point.getValue().getData()}; label: {label.getActualLabelText()}")
finally:
presentation.dispose()
資料點中儲存的數值仍為 0.75,即使其標籤顯示 75% 並附帶類別與系列名稱。自訂文字會取代產生的標籤文字。無論哪種情況,getActualLabelText 都會回傳最終的標籤字串。若只想擷取可見的標籤,請如上例分別檢查 isVisible。
控制超出坐標軸最大值的資料標籤
當手動限制坐標軸範圍時,部分資料點可能會超過其最大值。使用 setShowDataLabelsOverMaximum 來控制是否顯示其資料標籤。此設定會變更標籤的可見性,但不會改變坐標軸範圍或底層資料值。
下列範例建立一個包含 60 與 120 數值的 2D 群組直條圖。它將 False 傳遞給 setAutomaticMaxValue,並在垂直軸上使用 setMaxValue 設定最大值為 100。第一張投影片允許標籤超出最大值;該投影片的複本則停用此功能。兩張投影片皆儲存於 DataLabelsOverMaximum.pptx。
使用 setShowValue 來啟用數值標籤。圖表層級的設定本身不會啟用數值顯示,亦不會覆寫個別標籤已停用的數值顯示。此範例為整個系列啟用數值,並使用 setPosition 將標籤放置於每個直條的外側端點。
import jpype
import asposeslides
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import ChartType, LegendDataLabelPosition, Presentation, SaveFormat
presentation = Presentation()
try:
slide = presentation.getSlides().get_Item(0)
chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 50, 50, 600, 400)
chart.setLegend(False)
chart.getChartData().getSeries().clear()
chart.getChartData().getCategories().clear()
workbook = chart.getChartData().getChartDataWorkbook()
first_category = workbook.getCell(0, 1, 0, "Within range")
second_category = workbook.getCell(0, 2, 0, "Above maximum")
chart.getChartData().getCategories().add(first_category)
chart.getChartData().getCategories().add(second_category)
series_name = workbook.getCell(0, 0, 1, "Values")
series = chart.getChartData().getSeries().add(series_name, chart.getType())
first_value = workbook.getCell(0, 1, 1, jpype.JDouble(60))
second_value = workbook.getCell(0, 2, 1, jpype.JDouble(120))
series.getDataPoints().addDataPointForBarSeries(first_value)
series.getDataPoints().addDataPointForBarSeries(second_value)
series.getLabels().getDefaultDataLabelFormat().setShowValue(True)
series.getLabels().getDefaultDataLabelFormat().setPosition(LegendDataLabelPosition.OutsideEnd)
chart.getAxes().getVerticalAxis().setAutomaticMaxValue(False)
chart.getAxes().getVerticalAxis().setMaxValue(100)
chart.setShowDataLabelsOverMaximum(True)
second_slide = presentation.getSlides().addClone(slide)
second_chart = second_slide.getShapes().get_Item(0)
second_chart.setShowDataLabelsOverMaximum(False)
presentation.save("DataLabelsOverMaximum.pptx", SaveFormat.Pptx)
finally:
presentation.dispose()
以下圖片顯示 Microsoft PowerPoint 呈現的已儲存投影片。設定為 True 時,標籤 120 會在上邊界可見;設定為 False 時,則隱藏。標籤 60 仍保持可見,坐標軸最大值仍為 100,而第二個資料點在兩種情況下皆為 120。
| setShowDataLabelsOverMaximum(True) | setShowDataLabelsOverMaximum(False) |
|---|---|
![]() |
![]() |
Chart Type
此範例使用具有數值軸的 2D 直條圖。沒有數值軸的圖表,例如圓形圖與環形圖,無法以此方式限制軸最大值。設定標籤與坐標軸的距離
使用 setLabelOffset 來控制類別坐標軸標籤與坐標軸之間的距離。此數值是軸標籤最大字型大小的百分比。本範例建立一個群組直條圖,並將水平軸標籤偏移設定為 500。此設定影響類別坐標軸的標籤,而非附加於單一資料點的標籤。
import jpype
import asposeslides
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import ChartType, Presentation, SaveFormat
presentation = Presentation()
try:
slide = presentation.getSlides().get_Item(0)
chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 20, 20, 500, 300)
chart.getAxes().getHorizontalAxis().setLabelOffset(500)
presentation.save("SetCategoryAxisLabelDistance_out.pptx", SaveFormat.Pptx)
finally:
presentation.dispose()
調整標籤位置
在圓形圖上,調整資料標籤位置以改善間距並為引線留出空間。
此範例顯示第一筆資料點的數值,將其標籤放置在切片外部,並使用 setX 與 setY 調整水平與垂直偏移。這些偏移分別相對於圖表的寬度與高度。
import jpype
import asposeslides
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import ChartType, LegendDataLabelPosition, Presentation, SaveFormat
presentation = Presentation()
try:
slide = presentation.getSlides().get_Item(0)
chart = slide.getShapes().addChart(ChartType.Pie, 50, 50, 200, 200)
series = chart.getChartData().getSeries()
label = series.get_Item(0).getLabels().get_Item(0)
label.getDataLabelFormat().setShowValue(True)
label.getDataLabelFormat().setPosition(LegendDataLabelPosition.OutsideEnd)
label.setX(0.71)
label.setY(0.04)
presentation.save("presentation.pptx", SaveFormat.Pptx)
finally:
presentation.dispose()

格式化引線
當圓形圖的標籤位於切片外側時,引線可協助讀者將每個數值對應至正確的切片。使用對比色且稍粗的線條可讓這些連接更易於辨識,而加入透明度與虛線樣式則能使線條在視覺上不突兀。
使用 getLeaderLinesFormat 於系列的資料標籤集合層級格式化引線。series.getLabels().getLeaderLinesFormat().getLine() 中的設定會套用於整個集合,而非單一標籤。
此範例建立一個使用預設資料的圓形圖,將數值顯示於切片外側,並啟用 setShowLeaderLines。它使用 setX 與 setY 偏移(以圖表寬高的比例表示)將兩個標籤移得更遠。此範例不需要輸入簡報檔案。
線條的 getFillFormat 設定為實心藍色,alpha 值 191(255 為滿值),約 25% 透明度。setWidth 設定粗細為 2 點,setDashStyle 使線條呈虛線。
import jpype
import asposeslides
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import ChartType, FillType, LegendDataLabelPosition, LineDashStyle, Presentation, SaveFormat
Color = jpype.JClass("java.awt.Color")
presentation = Presentation()
try:
slide = presentation.getSlides().get_Item(0)
chart = slide.getShapes().addChart(ChartType.Pie, 80, 50, 560, 400)
chart.setLegend(False)
series = chart.getChartData().getSeries().get_Item(0)
label_format = series.getLabels().getDefaultDataLabelFormat()
label_format.setShowValue(True)
label_format.setPosition(LegendDataLabelPosition.OutsideEnd)
label_format.setShowLeaderLines(True)
# 將兩個標籤移得更遠,遠離其切片。
series.getLabels().get_Item(0).setX(0.10)
series.getLabels().get_Item(0).setY(-0.05)
series.getLabels().get_Item(1).setX(-0.10)
series.getLabels().get_Item(1).setY(0.05)
line = series.getLabels().getLeaderLinesFormat().getLine()
line.getFillFormat().setFillType(FillType.Solid)
color = Color(30, 90, 160, 191)
line.getFillFormat().getSolidFillColor().setColor(color)
line.setWidth(2)
line.setDashStyle(LineDashStyle.Dash)
presentation.save("FormattedLeaderLines.pptx", SaveFormat.Pptx)
finally:
presentation.dispose()
啟用引線並不保證每個標籤都會產生線條。是否可見取決於標籤的放置位置與圖表的版面配置行為。在本範例中,兩個被移離的標籤會有可見的引線;而靠近切片的標籤則不需要引線。
以下圖片顯示使用 Aspose.Slides 呈現的已儲存 FormattedLeaderLines.pptx:

在直條圖上方新增多列資料標籤
此範例建立一個直條圖,於繪圖區上方放置兩列資料標籤。Series A 顯示可見的直條,而 Series B 與 Series C 提供額外的標籤。透過移除填充與輪廓,使它們的直條隱藏。使用 ChartSeriesGroup.setOverlap 方法讓三個系列在相同的類別中心對齊。
ChartPlotArea 設定為標籤列保留空間。於 Chart.validateChartLayout 計算預設位置後,DataLabel.setX 與 DataLabel.setY 保持水平對齊,並套用垂直偏移以在兩列中排列標籤。這些數字仍是與系列值連結的資料標籤;只有列標題是獨立的文字圖形。
import jpype
import asposeslides
if not jpype.isJVMStarted():
jpype.startJVM()
from asposeslides.api import ChartType, FillType, LayoutTargetType, LegendDataLabelPosition, Presentation, SaveFormat, ShapeType
Color = jpype.JClass("java.awt.Color")
presentation = Presentation()
try:
slide = presentation.getSlides().get_Item(0)
chart = slide.getShapes().addChart(ChartType.ClusteredColumn, 40, 40, 640, 200)
chart.setTitle(False)
chart.setLegend(False)
chart.getTextFormat().getPortionFormat().setFontHeight(12)
chart.getChartData().getSeries().clear()
chart.getChartData().getCategories().clear()
workbook = chart.getChartData().getChartDataWorkbook()
workbook.clear(0)
categories = ["North", "South", "East", "West"]
series_names = ["Series A", "Series B", "Series C"]
series_values = [[35, 42, 28, 47], [22, 31, 19, 26], [12, 16, 14, 18]]
for category_index, category in enumerate(categories):
category_cell = workbook.getCell(0, category_index + 1, 0, category)
chart.getChartData().getCategories().add(category_cell)
for series_index, series_name in enumerate(series_names):
series_cell = workbook.getCell(0, 0, series_index + 1, series_name)
series = chart.getChartData().getSeries().add(series_cell, ChartType.ClusteredColumn)
for category_index, value in enumerate(series_values[series_index]):
value_cell = workbook.getCell(0, category_index + 1, series_index + 1, jpype.JDouble(value))
series.getDataPoints().addDataPointForBarSeries(value_cell)
if series_index > 0:
# 隱藏 B 和 C 的欄位,但保留它們的資料標籤。
series.getFormat().getFill().setFillType(FillType.NoFill)
series.getFormat().getLine().getFillFormat().setFillType(FillType.NoFill)
label_format = series.getLabels().getDefaultDataLabelFormat()
label_format.setShowValue(True)
label_format.getTextFormat().getPortionFormat().setFontHeight(12)
label_fill = label_format.getTextFormat().getPortionFormat().getFillFormat()
label_fill.setFillType(FillType.Solid)
label_fill.getSolidFillColor().setColor(Color.BLACK)
label_format.setPosition(LegendDataLabelPosition.InsideBase)
# 使所有三個系列與相同的類別中心對齊。
chart.getChartData().getSeries().get_Item(0).getParentSeriesGroup().setOverlap(jpype.JByte(100))
# 在此簡潔範例中使用較少的格線。
chart.getAxes().getVerticalAxis().setAutomaticMajorUnit(False)
chart.getAxes().getVerticalAxis().setMajorUnit(10)
# 為兩行資料標籤在繪圖區上方保留空間。
chart.getPlotArea().setLayoutTargetType(LayoutTargetType.Inner)
chart.getPlotArea().setX(0.15)
chart.getPlotArea().setY(0.32)
chart.getPlotArea().setWidth(0.80)
chart.getPlotArea().setHeight(0.48)
chart.validateChartLayout()
for series_index in range(1, len(series_names)):
series = chart.getChartData().getSeries().get_Item(series_index)
row_top = 0.15 if series_index == 1 else 0.03
for category_index in range(len(categories)):
data_label = series.getDataPoints().get_Item(category_index).getLabel()
# 保持預設的水平位置。Y 是相對於
# 預設標籤位置的偏移,以圖表高度的比例表示。
data_label.setX(0)
data_label.setY(row_top - data_label.getActualY() / chart.getHeight())
# 只有列標題是獨立的文字圖形。
heading_y = chart.getY() + row_top * chart.getHeight()
row_heading = slide.getShapes().addAutoShape(ShapeType.Rectangle, chart.getX(), heading_y, 85, 18)
row_heading.getFillFormat().setFillType(FillType.NoFill)
row_heading.getLineFormat().getFillFormat().setFillType(FillType.NoFill)
row_heading.addTextFrame(series_names[series_index])
row_heading.getTextFrame().getTextFrameFormat().setMarginTop(0)
row_heading.getTextFrame().getTextFrameFormat().setMarginBottom(0)
heading_format = row_heading.getTextFrame().getParagraphs().get_Item(0).getPortions().get_Item(0).getPortionFormat()
heading_format.setFontHeight(12)
heading_format.getFillFormat().setFillType(FillType.Solid)
heading_format.getFillFormat().getSolidFillColor().setColor(Color.BLACK)
presentation.save("multiple-rows-of-labels.pptx", SaveFormat.Pptx)
finally:
presentation.dispose()

常見問題
如何防止在密集圖表上資料標籤重疊?
結合自動標籤定位、引線與縮小字型大小;必要時,可隱藏部分欄位(例如類別),或僅對極端數值或關鍵點顯示標籤。
如何僅對零、負值或空值停用標籤?
在啟用標籤之前先過濾資料點,並依據定義的規則對值為 0、負值或缺失值關閉顯示。
如何確保匯出為 PDF/影像時標籤樣式一致?
明確設定字型系列與大小,並確認渲染環境中已安裝該字型,以避免使用備用字型。

