Source code for pptx.chart.chart

"""Chart-related objects such as Chart and ChartTitle."""

from __future__ import annotations

from collections.abc import Sequence

from pptx.chart.axis import CategoryAxis, DateAxis, ValueAxis
from pptx.chart.legend import Legend
from pptx.chart.plot import PlotFactory, PlotTypeInspector
from pptx.chart.series import SeriesCollection
from pptx.chart.xmlwriter import SeriesXmlRewriterFactory
from pptx.dml.chtfmt import ChartFormat
from pptx.shared import ElementProxy, PartElementProxy
from pptx.text.text import Font, TextFrame
from pptx.util import lazyproperty


[docs]class Chart(PartElementProxy): """A chart object.""" def __init__(self, chartSpace, chart_part): super(Chart, self).__init__(chartSpace, chart_part) self._chartSpace = chartSpace @property def category_axis(self): """ The category axis of this chart. In the case of an XY or Bubble chart, this is the X axis. Raises |ValueError| if no category axis is defined (as is the case for a pie chart, for example). """ catAx_lst = self._chartSpace.catAx_lst if catAx_lst: return CategoryAxis(catAx_lst[0]) dateAx_lst = self._chartSpace.dateAx_lst if dateAx_lst: return DateAxis(dateAx_lst[0]) valAx_lst = self._chartSpace.valAx_lst if valAx_lst: return ValueAxis(valAx_lst[0]) raise ValueError("chart has no category axis") @property def chart_style(self): """ Read/write integer index of chart style used to format this chart. Range is from 1 to 48. Value is |None| if no explicit style has been assigned, in which case the default chart style is used. Assigning |None| causes any explicit setting to be removed. The integer index corresponds to the style's position in the chart style gallery in the PowerPoint UI. """ style = self._chartSpace.style if style is None: return None return style.val @chart_style.setter def chart_style(self, value): self._chartSpace._remove_style() if value is None: return self._chartSpace._add_style(val=value) @property def chart_title(self): """A |ChartTitle| object providing access to title properties. Calling this property is destructive in the sense it adds a chart title element (`c:title`) to the chart XML if one is not already present. Use :attr:`has_title` to test for presence of a chart title non-destructively. """ return ChartTitle(self._element.get_or_add_title()) @property def chart_type(self): """Member of :ref:`XlChartType` enumeration specifying type of this chart. If the chart has two plots, for example, a line plot overlayed on a bar plot, the type reported is for the first (back-most) plot. Read-only. """ first_plot = self.plots[0] return PlotTypeInspector.chart_type(first_plot)
[docs] @lazyproperty def font(self): """Font object controlling text format defaults for this chart.""" defRPr = self._chartSpace.get_or_add_txPr().p_lst[0].get_or_add_pPr().get_or_add_defRPr() return Font(defRPr)
@property def has_legend(self): """ Read/write boolean, |True| if the chart has a legend. Assigning |True| causes a legend to be added to the chart if it doesn't already have one. Assigning False removes any existing legend definition along with any existing legend settings. """ return self._chartSpace.chart.has_legend @has_legend.setter def has_legend(self, value): self._chartSpace.chart.has_legend = bool(value) @property def has_title(self): """Read/write boolean, specifying whether this chart has a title. Assigning |True| causes a title to be added if not already present. Assigning |False| removes any existing title along with its text and settings. """ title = self._chartSpace.chart.title if title is None: return False return True @has_title.setter def has_title(self, value): chart = self._chartSpace.chart if bool(value) is False: chart._remove_title() autoTitleDeleted = chart.get_or_add_autoTitleDeleted() autoTitleDeleted.val = True return chart.get_or_add_title() @property def legend(self): """ A |Legend| object providing access to the properties of the legend for this chart. """ legend_elm = self._chartSpace.chart.legend if legend_elm is None: return None return Legend(legend_elm)
[docs] @lazyproperty def plots(self): """ The sequence of plots in this chart. A plot, called a *chart group* in the Microsoft API, is a distinct sequence of one or more series depicted in a particular charting type. For example, a chart having a series plotted as a line overlaid on three series plotted as columns would have two plots; the first corresponding to the three column series and the second to the line series. Plots are sequenced in the order drawn, i.e. back-most to front-most. Supports *len()*, membership (e.g. ``p in plots``), iteration, slicing, and indexed access (e.g. ``plot = plots[i]``). """ plotArea = self._chartSpace.chart.plotArea return _Plots(plotArea, self)
[docs] def replace_data(self, chart_data): """ Use the categories and series values in the |ChartData| object *chart_data* to replace those in the XML and Excel worksheet for this chart. """ rewriter = SeriesXmlRewriterFactory(self.chart_type, chart_data) rewriter.replace_series_data(self._chartSpace) self._workbook.update_from_xlsx_blob(chart_data.xlsx_blob)
[docs] @lazyproperty def series(self): """ A |SeriesCollection| object containing all the series in this chart. When the chart has multiple plots, all the series for the first plot appear before all those for the second, and so on. Series within a plot have an explicit ordering and appear in that sequence. """ return SeriesCollection(self._chartSpace.plotArea)
@property def value_axis(self): """ The |ValueAxis| object providing access to properties of the value axis of this chart. Raises |ValueError| if the chart has no value axis. """ valAx_lst = self._chartSpace.valAx_lst if not valAx_lst: raise ValueError("chart has no value axis") idx = 1 if len(valAx_lst) > 1 else 0 return ValueAxis(valAx_lst[idx]) @property def _workbook(self): """ The |ChartWorkbook| object providing access to the Excel source data for this chart. """ return self.part.chart_workbook
[docs]class ChartTitle(ElementProxy): """Provides properties for manipulating a chart title.""" # This shares functionality with AxisTitle, which could be factored out # into a base class, perhaps pptx.chart.shared.BaseTitle. I suspect they # actually differ in certain fuller behaviors, but at present they're # essentially identical. def __init__(self, title): super(ChartTitle, self).__init__(title) self._title = title
[docs] @lazyproperty def format(self): """|ChartFormat| object providing access to line and fill formatting. Return the |ChartFormat| object providing shape formatting properties for this chart title, such as its line color and fill. """ return ChartFormat(self._title)
@property def has_text_frame(self): """Read/write Boolean specifying whether this title has a text frame. Return |True| if this chart title has a text frame, and |False| otherwise. Assigning |True| causes a text frame to be added if not already present. Assigning |False| causes any existing text frame to be removed along with its text and formatting. """ if self._title.tx_rich is None: return False return True @has_text_frame.setter def has_text_frame(self, value): if bool(value) is False: self._title._remove_tx() return self._title.get_or_add_tx_rich() @property def text_frame(self): """|TextFrame| instance for this chart title. Return a |TextFrame| instance allowing read/write access to the text of this chart title and its text formatting properties. Accessing this property is destructive in the sense it adds a text frame if one is not present. Use :attr:`has_text_frame` to test for the presence of a text frame non-destructively. """ rich = self._title.get_or_add_tx_rich() return TextFrame(rich, self)
class _Plots(Sequence): """ The sequence of plots in a chart, such as a bar plot or a line plot. Most charts have only a single plot. The concept is necessary when two chart types are displayed in a single set of axes, like a bar plot with a superimposed line plot. """ def __init__(self, plotArea, chart): super(_Plots, self).__init__() self._plotArea = plotArea self._chart = chart def __getitem__(self, index): xCharts = self._plotArea.xCharts if isinstance(index, slice): plots = [PlotFactory(xChart, self._chart) for xChart in xCharts] return plots[index] else: xChart = xCharts[index] return PlotFactory(xChart, self._chart) def __len__(self): return len(self._plotArea.xCharts)