power-pptx

API: Charts

Member reference for power_pptx.chart. For the narrative version — how the pieces fit together and which one to reach for — see Charts.

add_chart

ShapeTree.add_chart(chart_type, x, y, cx, cy, chart_data) returns theGraphicFrame shape, not the chart; reach the chart via .chart. The data is copied into an embedded XLSX workbook so PowerPoint's "Edit Data" works.

from power_pptx import Presentation
from power_pptx.chart.data import CategoryChartData, XyChartData, BubbleChartData
from power_pptx.enum.chart import XL_CHART_TYPE
from power_pptx.util import Inches

prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[6])

chart_data = CategoryChartData()
chart_data.categories = ["Q1", "Q2", "Q3", "Q4"]
chart_data.add_series("Revenue", (1.2, 1.8, 2.4, 3.1))

# add_chart(chart_type, x, y, cx, cy, chart_data) -> GraphicFrame
x, y, cx, cy = Inches(1), Inches(1), Inches(8), Inches(4.5)
chart = slide.shapes.add_chart(
    XL_CHART_TYPE.COLUMN_CLUSTERED, x, y, cx, cy, chart_data
).chart

Chart data

CategoryChartData serves every category-based chart type,XyChartData serves XY_SCATTER* and BubbleChartDataserves BUBBLE*. ChartData is a deprecated alias ofCategoryChartData.

data = CategoryChartData(number_format="General")

data.categories = ["Q1", "Q2", "Q3"]     # replaces the whole list
data.add_category("Q4")                  # append one; returns Category
data.categories.are_dates                # date labels -> time-scale axis
data.categories.are_numeric
data.categories.depth                    # 1 flat, 2 with sub-categories
data.categories.leaf_count               # values expected per series
data.categories.number_format = "General"

series = data.add_series("Revenue", (1.2, 1.8, 2.4), number_format=None)
series.add_data_point(3.1)
series.name, series.values, series.index

# Two-level categories: every leaf takes one value
nested = CategoryChartData()
emea = nested.add_category("EMEA")       # Category
emea.add_sub_category("UK")
emea.add_sub_category("DE")
nested.add_series("Bookings", (4.1, 3.2))
from power_pptx.chart.data import XyChartData, BubbleChartData

xy = XyChartData(number_format="General")
s = xy.add_series("Trial")          # XySeriesData
s.add_data_point(1.0, 2.5)          # (x, y)
s.x_values, s.y_values

bubbles = BubbleChartData()
b = bubbles.add_series("Accounts")  # BubbleSeriesData
b.add_data_point(1.0, 2.0, 10)      # (x, y, size)
b.bubble_sizes

Chart

chart.chart_type                   # XL_CHART_TYPE of the back-most plot
chart.shape                        # the containing GraphicFrame
chart.font                         # Font: chart-wide text defaults

chart.has_title                    # bool, read/write (non-destructive test)
chart.chart_title                  # ChartTitle; CREATES the title element
chart.chart_title.text_frame.text = "Revenue"
chart.chart_title.has_text_frame
chart.chart_title.format           # ChartFormat (fill / line)

chart.plots                        # Sequence[_BasePlot], back-most first
chart.series                       # SeriesCollection across all plots

chart.value_axis                   # ValueAxis
chart.category_axis                # CategoryAxis | DateAxis (ValueAxis on XY)
chart.secondary_value_axis         # ValueAxis; CREATES it on first access

chart.has_legend = True            # False removes the element and its settings
chart.legend                       # Legend | None
chart.chart_style = 10             # built-in style index 1..48

chart.replace_data(chart_data)     # swap data, keep formatting

Plot

A plot (a "chart group" in the Microsoft object model) is one run of series drawn in a single charting type. add_chart always produces exactly one; combo charts with two plots come from template decks. Concrete classes: AreaPlot, Area3DPlot,BarPlot, BubblePlot, DoughnutPlot,LinePlot, PiePlot, RadarPlot, XyPlot.

plot = chart.plots[0]

plot.chart                         # back-reference to the owning Chart
plot.categories                    # Categories (read back from the XML)
plot.series                        # SeriesCollection for this plot only
plot.has_data_labels = True
plot.data_labels                   # DataLabels
plot.vary_by_categories            # colour each point differently

# This chart is COLUMN_CLUSTERED, so its plot is a BarPlot
plot.gap_width = 60                # % of bar width between category groups
plot.overlap = -10                 # -100..100, series overlap within a group

Members that exist only on particular plot classes:

MemberClassRange / meaning
gap_widthBarPlotSpace between category groups, as a % of bar width (default 150)
overlapBarPlot-100..100; how far series overlap within a group (default 0)
hole_sizeDoughnutPlot10..90, default 50 fork
smoothLinePlotCurve-fit every series; reads True only when all are smoothed fork
bubble_scaleBubblePlot0..300, % of default bubble size

Series

Concrete classes: AreaSeries, BarSeries, LineSeries,PieSeries, RadarSeries, XySeries,BubbleSeries. chart.series lists every plot's series in draw order;plot.series narrows to one plot.

series = chart.series[0]

series.name, series.index
series.values                      # tuple of floats
series.format                      # ChartFormat -> .fill / .line
series.data_labels                 # DataLabels for this series
series.points                      # CategoryPoints / XyPoints / BubblePoints

# bar, line, scatter, area and bubble series only (fork)
series.trendlines                  # Trendlines
series.error_bars                  # ErrorBars
series.axis_group                  # "primary" | "secondary"

# BarSeries
series.invert_if_negative = True
MemberClassMeaning
invert_if_negativeBarSeriesFill negative bars with the inverse colour
smoothLineSeriesCurve-fit this one series
markerLineSeries, RadarSeries, XySeries, BubbleSeriesMarker: style, size, format
trendlines, error_bars, axis_groupArea, Bar, Line, Xy, BubbleNot permitted on PieSeries / RadarSeries fork

Points and markers

point = chart.series[0].points[0]
point.format                       # ChartFormat -> per-point fill / line
point.marker                       # Marker
point.data_label                   # DataLabel

from power_pptx.enum.chart import XL_MARKER_STYLE
marker = point.marker
marker.style = XL_MARKER_STYLE.CIRCLE
marker.size = 7                    # 2..72 points
marker.format.fill.solid()

Axes

chart.category_axis returns a DateAxis when the categories are dates, and the X ValueAxis on scatter/bubble charts. It raises ValueError on chart types with no axes (pie, doughnut), as does secondary_value_axis.

axis = chart.value_axis            # or chart.category_axis

axis.visible = True
axis.has_major_gridlines = True
axis.has_minor_gridlines = False
axis.major_gridlines.format.line.color.rgb = "#EEEEF2"
axis.major_tick_mark                # XL_TICK_MARK
axis.minor_tick_mark
axis.tick_label_position            # XL_TICK_LABEL_POSITION
axis.tick_labels                    # TickLabels
axis.format                         # ChartFormat -> the axis line itself
axis.has_title = True
axis.axis_title.text_frame.text = "$M"   # CREATES the title element

# ValueAxis only
axis.minimum_scale, axis.maximum_scale   # None = auto
axis.major_unit, axis.minor_unit
axis.log_base = 10                  # 2..1000; None restores linear (fork)
axis.crosses                        # XL_AXIS_CROSSES
axis.crosses_at

# CategoryAxis / DateAxis only
chart.category_axis.category_type   # XL_CATEGORY_TYPE
chart.category_axis.reverse_order   # BAR_* defaults True (fork)

TickLabels

ticks = chart.value_axis.tick_labels
ticks.font                          # Font
ticks.number_format = "#,##0"
ticks.number_format_is_linked = False

# offset lives on the category axis only (0..1000, label distance)
chart.category_axis.tick_labels.offset = 100

DataLabels and DataLabel

from power_pptx.enum.chart import XL_LABEL_POSITION

plot = chart.plots[0]
plot.has_data_labels = True                  # required: reading data_labels
                                             # on a plot without them raises
labels = plot.data_labels                    # or chart.series[0].data_labels
labels.font
labels.number_format = "0.0%"
labels.number_format_is_linked = False
labels.position = XL_LABEL_POSITION.OUTSIDE_END
labels.show_value
labels.show_category_name
labels.show_series_name
labels.show_legend_key
labels.show_percentage
labels.collision_strategy = "auto"           # fork; write-only

label = chart.series[0].points[0].data_label
label.font
label.position
label.has_text_frame = True
label.text_frame.text = "best quarter"

Legend

from power_pptx.enum.chart import XL_LEGEND_POSITION

chart.has_legend = True
legend = chart.legend
legend.position = XL_LEGEND_POSITION.BOTTOM
legend.include_in_layout = False
legend.horz_offset = 0.0            # -1.0..1.0, only when position is CORNER
legend.font

Trendlines and error bars fork

Available on bar, line, scatter, area and bubble series only — the schema forbids them on pie and radar. Trendline kinds: "linear", "poly", "exp","log", "power", "movingAvg".

s = chart.series[0]

t = s.trendlines.add("linear", show_equation=True, show_r_squared=True)
s.trendlines.add("poly", order=3)            # 2..6
s.trendlines.add("movingAvg", period=2)
t.trendline_type                             # XL_TRENDLINE_TYPE
t.name, t.forward, t.backward

s.error_bars.fixed(0.5)
s.error_bars.percentage(5)
s.error_bars.standard_deviation(1)
s.error_bars.standard_error()
s.error_bars.custom(plus=[0.1, 0.2], minus=[0.1, 0.3])
s.error_bars.exists, s.error_bars.include_type, s.error_bars.value
s.error_bars.remove()

power_pptx.formats fork

Number-format string builders for anything that takes a number_format — axis tick labels, data labels, CategoryChartData(number_format=...), per-series formats and table cells. Each returns a plain Excel format str. decimals must be>= 0; a negative value raises ValueError.

CallReturnsRenders 1234.5 / 0.275 as
currency()"$"#,##0$1,235
currency("£", decimals=2)"£"#,##0.00£1,234.50
currency("USD ")"USD "#,##0USD 1,235
percent()0%28%
percent(decimals=1)0.0%27.5%
decimal()#,##0.001,234.50
decimal(decimals=0, thousands_sep=False)01235
thousands()#,##01,235
scientific()0.00E+001.23E+03
date()yyyy-MM-dd2026-08-24
date("MMM YYYY")MMM yyyyAug 2026

date(pattern) lowercases the tokens Excel wants lowercase (YYYY, YY, DD, DDD, DDDD,HH, H, SS, S) and deliberately leavesM/MM/MMM/MMMM capitalised, because lowercase m means minutes in Excel's grammar.

from power_pptx.formats import (
    currency, percent, decimal, thousands, scientific, date
)

chart.value_axis.tick_labels.number_format = currency("$", decimals=0)
chart.value_axis.tick_labels.number_format_is_linked = False

chart.plots[0].has_data_labels = True
chart.plots[0].data_labels.number_format = percent(decimals=1)
chart.plots[0].data_labels.number_format_is_linked = False

Assigning a format while number_format_is_linked is True has no visible effect — PowerPoint re-reads the format from the embedded workbook cell. Set the flag to False alongside every assignment.

Palettes & quick layouts fork

from power_pptx.chart import palettes, quick_layouts

chart.recolour("vibrant")          # fork: one call, dispatches per type
chart.recolour("modern", by="series")    # or by="category" / "auto"
chart.recolor("modern")            # US-spelling alias
chart.apply_palette("modern")      # warns + reroutes on pie/doughnut
chart.color_by_category("modern")  # per-point recolour
chart.apply_dark_theme(text="#E5E7EB", line="#374151")   # fork
chart.text_color = "#FFFFFF"       # fork; write-only
chart.line_color = "#9CA3AF"       # fork; write-only

chart.apply_quick_layout("minimal")          # 10 presets, or a dict spec
palettes.palette_names()           # modern, classic, editorial, vibrant, ...
palettes.resolve_palette("editorial")        # list[RGBColor]
palettes.CHART_PALETTES            # the mapping itself
quick_layouts.layout_names()       # title_legend_right, minimal, dense, ...

Chart types

XL_CHART_TYPE covers the full OOXML set in 73 members: COLUMN_*,BAR_*, LINE*, PIE*, DOUGHNUT*,AREA*, XY_SCATTER*, BUBBLE*, RADAR*,STOCK_*, SURFACE*, the THREE_D_* variants and theCONE_* / CYLINDER_* / PYRAMID_* shapes. The complete member list is on API: Enumerations, along with every other chart enum (XL_LEGEND_POSITION, XL_LABEL_POSITION,XL_TICK_MARK, XL_MARKER_STYLE, XL_TRENDLINE_TYPE, …).