about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/pptx/chart/plot.py
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/pptx/chart/plot.py
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are here HEAD master
Diffstat (limited to '.venv/lib/python3.12/site-packages/pptx/chart/plot.py')
-rw-r--r--.venv/lib/python3.12/site-packages/pptx/chart/plot.py412
1 files changed, 412 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/pptx/chart/plot.py b/.venv/lib/python3.12/site-packages/pptx/chart/plot.py
new file mode 100644
index 00000000..6e723585
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/pptx/chart/plot.py
@@ -0,0 +1,412 @@
+"""Plot-related objects.
+
+A plot is known as a chart group in the MS API. A chart can have more than one plot overlayed on
+each other, such as a line plot layered over a bar plot.
+"""
+
+from __future__ import annotations
+
+from pptx.chart.category import Categories
+from pptx.chart.datalabel import DataLabels
+from pptx.chart.series import SeriesCollection
+from pptx.enum.chart import XL_CHART_TYPE as XL
+from pptx.oxml.ns import qn
+from pptx.oxml.simpletypes import ST_BarDir, ST_Grouping
+from pptx.util import lazyproperty
+
+
+class _BasePlot(object):
+    """
+    A distinct plot that appears in the plot area of a chart. A chart may
+    have more than one plot, in which case they appear as superimposed
+    layers, such as a line plot appearing on top of a bar chart.
+    """
+
+    def __init__(self, xChart, chart):
+        super(_BasePlot, self).__init__()
+        self._element = xChart
+        self._chart = chart
+
+    @lazyproperty
+    def categories(self):
+        """
+        Returns a |category.Categories| sequence object containing
+        a |category.Category| object for each of the category labels
+        associated with this plot. The |category.Category| class derives from
+        ``str``, so the returned value can be treated as a simple sequence of
+        strings for the common case where all you need is the labels in the
+        order they appear on the chart. |category.Categories| provides
+        additional properties for dealing with hierarchical categories when
+        required.
+        """
+        return Categories(self._element)
+
+    @property
+    def chart(self):
+        """
+        The |Chart| object containing this plot.
+        """
+        return self._chart
+
+    @property
+    def data_labels(self):
+        """
+        |DataLabels| instance providing properties and methods on the
+        collection of data labels associated with this plot.
+        """
+        dLbls = self._element.dLbls
+        if dLbls is None:
+            raise ValueError("plot has no data labels, set has_data_labels = True first")
+        return DataLabels(dLbls)
+
+    @property
+    def has_data_labels(self):
+        """
+        Read/write boolean, |True| if the series has data labels. Assigning
+        |True| causes data labels to be added to the plot. Assigning False
+        removes any existing data labels.
+        """
+        return self._element.dLbls is not None
+
+    @has_data_labels.setter
+    def has_data_labels(self, value):
+        """
+        Add, remove, or leave alone the ``<c:dLbls>`` child element depending
+        on current state and assigned *value*. If *value* is |True| and no
+        ``<c:dLbls>`` element is present, a new default element is added with
+        default child elements and settings. When |False|, any existing dLbls
+        element is removed.
+        """
+        if bool(value) is False:
+            self._element._remove_dLbls()
+        else:
+            if self._element.dLbls is None:
+                dLbls = self._element._add_dLbls()
+                dLbls.showVal.val = True
+
+    @lazyproperty
+    def series(self):
+        """
+        A sequence of |Series| objects representing the series in this plot,
+        in the order they appear in the plot.
+        """
+        return SeriesCollection(self._element)
+
+    @property
+    def vary_by_categories(self):
+        """
+        Read/write boolean value specifying whether to use a different color
+        for each of the points in this plot. Only effective when there is
+        a single series; PowerPoint automatically varies color by series when
+        more than one series is present.
+        """
+        varyColors = self._element.varyColors
+        if varyColors is None:
+            return True
+        return varyColors.val
+
+    @vary_by_categories.setter
+    def vary_by_categories(self, value):
+        self._element.get_or_add_varyColors().val = bool(value)
+
+
+class AreaPlot(_BasePlot):
+    """
+    An area plot.
+    """
+
+
+class Area3DPlot(_BasePlot):
+    """
+    A 3-dimensional area plot.
+    """
+
+
+class BarPlot(_BasePlot):
+    """
+    A bar chart-style plot.
+    """
+
+    @property
+    def gap_width(self):
+        """
+        Width of gap between bar(s) of each category, as an integer
+        percentage of the bar width. The default value for a new bar chart is
+        150, representing 150% or 1.5 times the width of a single bar.
+        """
+        gapWidth = self._element.gapWidth
+        if gapWidth is None:
+            return 150
+        return gapWidth.val
+
+    @gap_width.setter
+    def gap_width(self, value):
+        gapWidth = self._element.get_or_add_gapWidth()
+        gapWidth.val = value
+
+    @property
+    def overlap(self):
+        """
+        Read/write int value in range -100..100 specifying a percentage of
+        the bar width by which to overlap adjacent bars in a multi-series bar
+        chart. Default is 0. A setting of -100 creates a gap of a full bar
+        width and a setting of 100 causes all the bars in a category to be
+        superimposed. A stacked bar plot has overlap of 100 by default.
+        """
+        overlap = self._element.overlap
+        if overlap is None:
+            return 0
+        return overlap.val
+
+    @overlap.setter
+    def overlap(self, value):
+        """
+        Set the value of the ``<c:overlap>`` child element to *int_value*,
+        or remove the overlap element if *int_value* is 0.
+        """
+        if value == 0:
+            self._element._remove_overlap()
+            return
+        self._element.get_or_add_overlap().val = value
+
+
+class BubblePlot(_BasePlot):
+    """
+    A bubble chart plot.
+    """
+
+    @property
+    def bubble_scale(self):
+        """
+        An integer between 0 and 300 inclusive indicating the percentage of
+        the default size at which bubbles should be displayed. Assigning
+        |None| produces the same behavior as assigning `100`.
+        """
+        bubbleScale = self._element.bubbleScale
+        if bubbleScale is None:
+            return 100
+        return bubbleScale.val
+
+    @bubble_scale.setter
+    def bubble_scale(self, value):
+        bubbleChart = self._element
+        bubbleChart._remove_bubbleScale()
+        if value is None:
+            return
+        bubbleScale = bubbleChart._add_bubbleScale()
+        bubbleScale.val = value
+
+
+class DoughnutPlot(_BasePlot):
+    """
+    An doughnut plot.
+    """
+
+
+class LinePlot(_BasePlot):
+    """
+    A line chart-style plot.
+    """
+
+
+class PiePlot(_BasePlot):
+    """
+    A pie chart-style plot.
+    """
+
+
+class RadarPlot(_BasePlot):
+    """
+    A radar-style plot.
+    """
+
+
+class XyPlot(_BasePlot):
+    """
+    An XY (scatter) plot.
+    """
+
+
+def PlotFactory(xChart, chart):
+    """
+    Return an instance of the appropriate subclass of _BasePlot based on the
+    tagname of *xChart*.
+    """
+    try:
+        PlotCls = {
+            qn("c:areaChart"): AreaPlot,
+            qn("c:area3DChart"): Area3DPlot,
+            qn("c:barChart"): BarPlot,
+            qn("c:bubbleChart"): BubblePlot,
+            qn("c:doughnutChart"): DoughnutPlot,
+            qn("c:lineChart"): LinePlot,
+            qn("c:pieChart"): PiePlot,
+            qn("c:radarChart"): RadarPlot,
+            qn("c:scatterChart"): XyPlot,
+        }[xChart.tag]
+    except KeyError:
+        raise ValueError("unsupported plot type %s" % xChart.tag)
+
+    return PlotCls(xChart, chart)
+
+
+class PlotTypeInspector(object):
+    """
+    "One-shot" service object that knows how to identify the type of a plot
+    as a member of the XL_CHART_TYPE enumeration.
+    """
+
+    @classmethod
+    def chart_type(cls, plot):
+        """
+        Return the member of :ref:`XlChartType` that corresponds to the chart
+        type of *plot*.
+        """
+        try:
+            chart_type_method = {
+                "AreaPlot": cls._differentiate_area_chart_type,
+                "Area3DPlot": cls._differentiate_area_3d_chart_type,
+                "BarPlot": cls._differentiate_bar_chart_type,
+                "BubblePlot": cls._differentiate_bubble_chart_type,
+                "DoughnutPlot": cls._differentiate_doughnut_chart_type,
+                "LinePlot": cls._differentiate_line_chart_type,
+                "PiePlot": cls._differentiate_pie_chart_type,
+                "RadarPlot": cls._differentiate_radar_chart_type,
+                "XyPlot": cls._differentiate_xy_chart_type,
+            }[plot.__class__.__name__]
+        except KeyError:
+            raise NotImplementedError(
+                "chart_type() not implemented for %s" % plot.__class__.__name__
+            )
+        return chart_type_method(plot)
+
+    @classmethod
+    def _differentiate_area_3d_chart_type(cls, plot):
+        return {
+            ST_Grouping.STANDARD: XL.THREE_D_AREA,
+            ST_Grouping.STACKED: XL.THREE_D_AREA_STACKED,
+            ST_Grouping.PERCENT_STACKED: XL.THREE_D_AREA_STACKED_100,
+        }[plot._element.grouping_val]
+
+    @classmethod
+    def _differentiate_area_chart_type(cls, plot):
+        return {
+            ST_Grouping.STANDARD: XL.AREA,
+            ST_Grouping.STACKED: XL.AREA_STACKED,
+            ST_Grouping.PERCENT_STACKED: XL.AREA_STACKED_100,
+        }[plot._element.grouping_val]
+
+    @classmethod
+    def _differentiate_bar_chart_type(cls, plot):
+        barChart = plot._element
+        if barChart.barDir.val == ST_BarDir.BAR:
+            return {
+                ST_Grouping.CLUSTERED: XL.BAR_CLUSTERED,
+                ST_Grouping.STACKED: XL.BAR_STACKED,
+                ST_Grouping.PERCENT_STACKED: XL.BAR_STACKED_100,
+            }[barChart.grouping_val]
+        if barChart.barDir.val == ST_BarDir.COL:
+            return {
+                ST_Grouping.CLUSTERED: XL.COLUMN_CLUSTERED,
+                ST_Grouping.STACKED: XL.COLUMN_STACKED,
+                ST_Grouping.PERCENT_STACKED: XL.COLUMN_STACKED_100,
+            }[barChart.grouping_val]
+        raise ValueError("invalid barChart.barDir value '%s'" % barChart.barDir.val)
+
+    @classmethod
+    def _differentiate_bubble_chart_type(cls, plot):
+        def first_bubble3D(bubbleChart):
+            results = bubbleChart.xpath("c:ser/c:bubble3D")
+            return results[0] if results else None
+
+        bubbleChart = plot._element
+        bubble3D = first_bubble3D(bubbleChart)
+
+        if bubble3D is None:
+            return XL.BUBBLE
+        if bubble3D.val:
+            return XL.BUBBLE_THREE_D_EFFECT
+        return XL.BUBBLE
+
+    @classmethod
+    def _differentiate_doughnut_chart_type(cls, plot):
+        doughnutChart = plot._element
+        explosion = doughnutChart.xpath("./c:ser/c:explosion")
+        return XL.DOUGHNUT_EXPLODED if explosion else XL.DOUGHNUT
+
+    @classmethod
+    def _differentiate_line_chart_type(cls, plot):
+        lineChart = plot._element
+
+        def has_line_markers():
+            matches = lineChart.xpath('c:ser/c:marker/c:symbol[@val="none"]')
+            if matches:
+                return False
+            return True
+
+        if has_line_markers():
+            return {
+                ST_Grouping.STANDARD: XL.LINE_MARKERS,
+                ST_Grouping.STACKED: XL.LINE_MARKERS_STACKED,
+                ST_Grouping.PERCENT_STACKED: XL.LINE_MARKERS_STACKED_100,
+            }[plot._element.grouping_val]
+        else:
+            return {
+                ST_Grouping.STANDARD: XL.LINE,
+                ST_Grouping.STACKED: XL.LINE_STACKED,
+                ST_Grouping.PERCENT_STACKED: XL.LINE_STACKED_100,
+            }[plot._element.grouping_val]
+
+    @classmethod
+    def _differentiate_pie_chart_type(cls, plot):
+        pieChart = plot._element
+        explosion = pieChart.xpath("./c:ser/c:explosion")
+        return XL.PIE_EXPLODED if explosion else XL.PIE
+
+    @classmethod
+    def _differentiate_radar_chart_type(cls, plot):
+        radarChart = plot._element
+        radar_style = radarChart.xpath("c:radarStyle")[0].get("val")
+
+        def noMarkers():
+            matches = radarChart.xpath("c:ser/c:marker/c:symbol")
+            if matches and matches[0].get("val") == "none":
+                return True
+            return False
+
+        if radar_style is None:
+            return XL.RADAR
+        if radar_style == "filled":
+            return XL.RADAR_FILLED
+        if noMarkers():
+            return XL.RADAR
+        return XL.RADAR_MARKERS
+
+    @classmethod
+    def _differentiate_xy_chart_type(cls, plot):
+        scatterChart = plot._element
+
+        def noLine():
+            return bool(scatterChart.xpath("c:ser/c:spPr/a:ln/a:noFill"))
+
+        def noMarkers():
+            symbols = scatterChart.xpath("c:ser/c:marker/c:symbol")
+            if symbols and symbols[0].get("val") == "none":
+                return True
+            return False
+
+        scatter_style = scatterChart.xpath("c:scatterStyle")[0].get("val")
+
+        if scatter_style == "lineMarker":
+            if noLine():
+                return XL.XY_SCATTER
+            if noMarkers():
+                return XL.XY_SCATTER_LINES_NO_MARKERS
+            return XL.XY_SCATTER_LINES
+
+        if scatter_style == "smoothMarker":
+            if noMarkers():
+                return XL.XY_SCATTER_SMOOTH_NO_MARKERS
+            return XL.XY_SCATTER_SMOOTH
+
+        return XL.XY_SCATTER