python-pptx2 2.13.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pptx2/__init__.py +152 -0
- pptx2/_color.py +75 -0
- pptx2/_slide_importer.py +597 -0
- pptx2/_svg.py +155 -0
- pptx2/_template_applier.py +292 -0
- pptx2/_textstyle.py +187 -0
- pptx2/accessibility.py +365 -0
- pptx2/action.py +270 -0
- pptx2/animation.py +2237 -0
- pptx2/api.py +49 -0
- pptx2/audit.py +258 -0
- pptx2/chart/__init__.py +0 -0
- pptx2/chart/analytics.py +381 -0
- pptx2/chart/axis.py +543 -0
- pptx2/chart/category.py +200 -0
- pptx2/chart/chart.py +670 -0
- pptx2/chart/data.py +864 -0
- pptx2/chart/datalabel.py +406 -0
- pptx2/chart/legend.py +86 -0
- pptx2/chart/marker.py +70 -0
- pptx2/chart/palettes.py +129 -0
- pptx2/chart/plot.py +462 -0
- pptx2/chart/point.py +101 -0
- pptx2/chart/quick_layouts.py +325 -0
- pptx2/chart/series.py +334 -0
- pptx2/chart/xlsx.py +272 -0
- pptx2/chart/xmlwriter.py +1845 -0
- pptx2/compose/__init__.py +28 -0
- pptx2/compose/from_spec.py +1094 -0
- pptx2/design/__init__.py +8 -0
- pptx2/design/components.py +607 -0
- pptx2/design/figures.py +389 -0
- pptx2/design/layout.py +370 -0
- pptx2/design/recipes.py +1967 -0
- pptx2/design/style.py +209 -0
- pptx2/design/tokens.py +915 -0
- pptx2/diagrams.py +754 -0
- pptx2/dml/__init__.py +0 -0
- pptx2/dml/chtfmt.py +40 -0
- pptx2/dml/color.py +496 -0
- pptx2/dml/effect.py +909 -0
- pptx2/dml/fill.py +691 -0
- pptx2/dml/line.py +287 -0
- pptx2/dml/picture.py +212 -0
- pptx2/dml/three_d.py +381 -0
- pptx2/enum/__init__.py +0 -0
- pptx2/enum/action.py +71 -0
- pptx2/enum/animation.py +31 -0
- pptx2/enum/base.py +218 -0
- pptx2/enum/chart.py +574 -0
- pptx2/enum/dml.py +740 -0
- pptx2/enum/lang.py +685 -0
- pptx2/enum/presentation.py +133 -0
- pptx2/enum/shapes.py +1029 -0
- pptx2/enum/text.py +230 -0
- pptx2/exc.py +42 -0
- pptx2/formats.py +139 -0
- pptx2/geometry.py +420 -0
- pptx2/inherit.py +109 -0
- pptx2/lint.py +2256 -0
- pptx2/math.py +177 -0
- pptx2/media.py +197 -0
- pptx2/opc/__init__.py +0 -0
- pptx2/opc/constants.py +332 -0
- pptx2/opc/oxml.py +188 -0
- pptx2/opc/package.py +762 -0
- pptx2/opc/packuri.py +109 -0
- pptx2/opc/serialized.py +296 -0
- pptx2/opc/shared.py +20 -0
- pptx2/opc/spec.py +45 -0
- pptx2/oxml/__init__.py +555 -0
- pptx2/oxml/action.py +53 -0
- pptx2/oxml/chart/__init__.py +0 -0
- pptx2/oxml/chart/axis.py +337 -0
- pptx2/oxml/chart/chart.py +481 -0
- pptx2/oxml/chart/datalabel.py +253 -0
- pptx2/oxml/chart/legend.py +72 -0
- pptx2/oxml/chart/marker.py +61 -0
- pptx2/oxml/chart/plot.py +365 -0
- pptx2/oxml/chart/series.py +425 -0
- pptx2/oxml/chart/shared.py +220 -0
- pptx2/oxml/coreprops.py +288 -0
- pptx2/oxml/dml/__init__.py +0 -0
- pptx2/oxml/dml/color.py +135 -0
- pptx2/oxml/dml/effect.py +213 -0
- pptx2/oxml/dml/fill.py +316 -0
- pptx2/oxml/dml/line.py +12 -0
- pptx2/oxml/dml/three_d.py +110 -0
- pptx2/oxml/ns.py +135 -0
- pptx2/oxml/presentation.py +313 -0
- pptx2/oxml/shapes/__init__.py +19 -0
- pptx2/oxml/shapes/autoshape.py +467 -0
- pptx2/oxml/shapes/connector.py +107 -0
- pptx2/oxml/shapes/graphfrm.py +347 -0
- pptx2/oxml/shapes/groupshape.py +329 -0
- pptx2/oxml/shapes/picture.py +270 -0
- pptx2/oxml/shapes/shared.py +577 -0
- pptx2/oxml/simpletypes.py +1027 -0
- pptx2/oxml/slide.py +563 -0
- pptx2/oxml/table.py +650 -0
- pptx2/oxml/text.py +815 -0
- pptx2/oxml/theme.py +36 -0
- pptx2/oxml/xmlchemy.py +717 -0
- pptx2/package.py +222 -0
- pptx2/parts/__init__.py +0 -0
- pptx2/parts/chart.py +95 -0
- pptx2/parts/coreprops.py +167 -0
- pptx2/parts/diagram.py +37 -0
- pptx2/parts/embeddedpackage.py +93 -0
- pptx2/parts/image.py +275 -0
- pptx2/parts/media.py +37 -0
- pptx2/parts/presentation.py +136 -0
- pptx2/parts/slide.py +371 -0
- pptx2/presentation.py +408 -0
- pptx2/py.typed +0 -0
- pptx2/render.py +586 -0
- pptx2/section.py +272 -0
- pptx2/shapes/__init__.py +26 -0
- pptx2/shapes/autoshape.py +442 -0
- pptx2/shapes/base.py +1078 -0
- pptx2/shapes/connector.py +297 -0
- pptx2/shapes/freeform.py +337 -0
- pptx2/shapes/graphfrm.py +316 -0
- pptx2/shapes/group.py +264 -0
- pptx2/shapes/picture.py +422 -0
- pptx2/shapes/placeholder.py +468 -0
- pptx2/shapes/shapetree.py +2027 -0
- pptx2/shared.py +82 -0
- pptx2/skill/SKILL.md +450 -0
- pptx2/skill/__init__.py +78 -0
- pptx2/skill/__main__.py +64 -0
- pptx2/skill/references/animations.md +189 -0
- pptx2/skill/references/basics.md +421 -0
- pptx2/skill/references/charts.md +254 -0
- pptx2/skill/references/compose.md +234 -0
- pptx2/skill/references/design.md +366 -0
- pptx2/skill/references/effects.md +249 -0
- pptx2/skill/references/end-to-end-deck.md +231 -0
- pptx2/skill/references/geometry-and-arrows.md +334 -0
- pptx2/skill/references/lint.md +275 -0
- pptx2/skill/references/math.md +86 -0
- pptx2/skill/references/picture-effects.md +129 -0
- pptx2/skill/references/render.md +151 -0
- pptx2/skill/references/smart-art.md +75 -0
- pptx2/skill/references/space-aware-authoring.md +249 -0
- pptx2/skill/references/tables.md +244 -0
- pptx2/skill/references/theme.md +127 -0
- pptx2/skill/references/three-d.md +109 -0
- pptx2/skill/references/transitions.md +100 -0
- pptx2/slide.py +1244 -0
- pptx2/smart_art.py +220 -0
- pptx2/spec.py +633 -0
- pptx2/table.py +1181 -0
- pptx2/table_styles.py +184 -0
- pptx2/templates/default.pptx +0 -0
- pptx2/templates/docx-icon.emf +0 -0
- pptx2/templates/generic-icon.emf +0 -0
- pptx2/templates/notes.xml +23 -0
- pptx2/templates/notesMaster.xml +352 -0
- pptx2/templates/pptx-icon.emf +0 -0
- pptx2/templates/theme.xml +321 -0
- pptx2/templates/xlsx-icon.emf +0 -0
- pptx2/text/__init__.py +0 -0
- pptx2/text/fonts.py +482 -0
- pptx2/text/layout.py +374 -0
- pptx2/text/text.py +1272 -0
- pptx2/theme.py +721 -0
- pptx2/types.py +36 -0
- pptx2/util.py +263 -0
- python_pptx2-2.13.0.dist-info/METADATA +351 -0
- python_pptx2-2.13.0.dist-info/RECORD +175 -0
- python_pptx2-2.13.0.dist-info/WHEEL +5 -0
- python_pptx2-2.13.0.dist-info/entry_points.txt +3 -0
- python_pptx2-2.13.0.dist-info/licenses/LICENSE +22 -0
- python_pptx2-2.13.0.dist-info/top_level.txt +1 -0
pptx2/chart/plot.py
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
"""Plot-related objects.
|
|
2
|
+
|
|
3
|
+
A plot is known as a chart group in the MS API. A chart can have more than one plot overlayed on
|
|
4
|
+
each other, such as a line plot layered over a bar plot.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from pptx2.chart.category import Categories
|
|
10
|
+
from pptx2.chart.datalabel import DataLabels
|
|
11
|
+
from pptx2.chart.series import SeriesCollection
|
|
12
|
+
from pptx2.enum.chart import XL_CHART_TYPE as XL
|
|
13
|
+
from pptx2.oxml.ns import qn
|
|
14
|
+
from pptx2.oxml.simpletypes import ST_BarDir, ST_Grouping
|
|
15
|
+
from pptx2.util import lazyproperty
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class _BasePlot(object):
|
|
19
|
+
"""
|
|
20
|
+
A distinct plot that appears in the plot area of a chart. A chart may
|
|
21
|
+
have more than one plot, in which case they appear as superimposed
|
|
22
|
+
layers, such as a line plot appearing on top of a bar chart.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, xChart, chart):
|
|
26
|
+
super(_BasePlot, self).__init__()
|
|
27
|
+
self._element = xChart
|
|
28
|
+
self._chart = chart
|
|
29
|
+
|
|
30
|
+
@lazyproperty
|
|
31
|
+
def categories(self):
|
|
32
|
+
"""
|
|
33
|
+
Returns a |category.Categories| sequence object containing
|
|
34
|
+
a |category.Category| object for each of the category labels
|
|
35
|
+
associated with this plot. The |category.Category| class derives from
|
|
36
|
+
``str``, so the returned value can be treated as a simple sequence of
|
|
37
|
+
strings for the common case where all you need is the labels in the
|
|
38
|
+
order they appear on the chart. |category.Categories| provides
|
|
39
|
+
additional properties for dealing with hierarchical categories when
|
|
40
|
+
required.
|
|
41
|
+
"""
|
|
42
|
+
return Categories(self._element)
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def chart(self):
|
|
46
|
+
"""
|
|
47
|
+
The |Chart| object containing this plot.
|
|
48
|
+
"""
|
|
49
|
+
return self._chart
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def data_labels(self):
|
|
53
|
+
"""
|
|
54
|
+
|DataLabels| instance providing properties and methods on the
|
|
55
|
+
collection of data labels associated with this plot.
|
|
56
|
+
"""
|
|
57
|
+
dLbls = self._element.dLbls
|
|
58
|
+
if dLbls is None:
|
|
59
|
+
raise ValueError("plot has no data labels, set has_data_labels = True first")
|
|
60
|
+
return DataLabels(dLbls)
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def has_data_labels(self):
|
|
64
|
+
"""
|
|
65
|
+
Read/write boolean, |True| if the series has data labels. Assigning
|
|
66
|
+
|True| causes data labels to be added to the plot. Assigning False
|
|
67
|
+
removes any existing data labels.
|
|
68
|
+
"""
|
|
69
|
+
return self._element.dLbls is not None
|
|
70
|
+
|
|
71
|
+
@has_data_labels.setter
|
|
72
|
+
def has_data_labels(self, value):
|
|
73
|
+
"""
|
|
74
|
+
Add, remove, or leave alone the ``<c:dLbls>`` child element depending
|
|
75
|
+
on current state and assigned *value*. If *value* is |True| and no
|
|
76
|
+
``<c:dLbls>`` element is present, a new default element is added with
|
|
77
|
+
default child elements and settings. When |False|, any existing dLbls
|
|
78
|
+
element is removed.
|
|
79
|
+
"""
|
|
80
|
+
if bool(value) is False:
|
|
81
|
+
self._element._remove_dLbls()
|
|
82
|
+
else:
|
|
83
|
+
if self._element.dLbls is None:
|
|
84
|
+
dLbls = self._element._add_dLbls()
|
|
85
|
+
dLbls.showVal.val = True
|
|
86
|
+
|
|
87
|
+
@lazyproperty
|
|
88
|
+
def series(self):
|
|
89
|
+
"""
|
|
90
|
+
A sequence of |Series| objects representing the series in this plot,
|
|
91
|
+
in the order they appear in the plot.
|
|
92
|
+
"""
|
|
93
|
+
return SeriesCollection(self._element)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def vary_by_categories(self):
|
|
97
|
+
"""
|
|
98
|
+
Read/write boolean value specifying whether to use a different color
|
|
99
|
+
for each of the points in this plot. Only effective when there is
|
|
100
|
+
a single series; PowerPoint automatically varies color by series when
|
|
101
|
+
more than one series is present.
|
|
102
|
+
"""
|
|
103
|
+
varyColors = self._element.varyColors
|
|
104
|
+
if varyColors is None:
|
|
105
|
+
return True
|
|
106
|
+
return varyColors.val
|
|
107
|
+
|
|
108
|
+
@vary_by_categories.setter
|
|
109
|
+
def vary_by_categories(self, value):
|
|
110
|
+
self._element.get_or_add_varyColors().val = bool(value)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class AreaPlot(_BasePlot):
|
|
114
|
+
"""
|
|
115
|
+
An area plot.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class Area3DPlot(_BasePlot):
|
|
120
|
+
"""
|
|
121
|
+
A 3-dimensional area plot.
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class BarPlot(_BasePlot):
|
|
126
|
+
"""
|
|
127
|
+
A bar chart-style plot.
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def gap_width(self):
|
|
132
|
+
"""
|
|
133
|
+
Width of gap between bar(s) of each category, as an integer
|
|
134
|
+
percentage of the bar width. The default value for a new bar chart is
|
|
135
|
+
150, representing 150% or 1.5 times the width of a single bar.
|
|
136
|
+
"""
|
|
137
|
+
gapWidth = self._element.gapWidth
|
|
138
|
+
if gapWidth is None:
|
|
139
|
+
return 150
|
|
140
|
+
return gapWidth.val
|
|
141
|
+
|
|
142
|
+
@gap_width.setter
|
|
143
|
+
def gap_width(self, value):
|
|
144
|
+
gapWidth = self._element.get_or_add_gapWidth()
|
|
145
|
+
gapWidth.val = value
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def overlap(self):
|
|
149
|
+
"""
|
|
150
|
+
Read/write int value in range -100..100 specifying a percentage of
|
|
151
|
+
the bar width by which to overlap adjacent bars in a multi-series bar
|
|
152
|
+
chart. Default is 0. A setting of -100 creates a gap of a full bar
|
|
153
|
+
width and a setting of 100 causes all the bars in a category to be
|
|
154
|
+
superimposed. A stacked bar plot has overlap of 100 by default.
|
|
155
|
+
"""
|
|
156
|
+
overlap = self._element.overlap
|
|
157
|
+
if overlap is None:
|
|
158
|
+
return 0
|
|
159
|
+
return overlap.val
|
|
160
|
+
|
|
161
|
+
@overlap.setter
|
|
162
|
+
def overlap(self, value):
|
|
163
|
+
"""
|
|
164
|
+
Set the value of the ``<c:overlap>`` child element to *int_value*,
|
|
165
|
+
or remove the overlap element if *int_value* is 0.
|
|
166
|
+
"""
|
|
167
|
+
if value == 0:
|
|
168
|
+
self._element._remove_overlap()
|
|
169
|
+
return
|
|
170
|
+
self._element.get_or_add_overlap().val = value
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class BubblePlot(_BasePlot):
|
|
174
|
+
"""
|
|
175
|
+
A bubble chart plot.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def bubble_scale(self):
|
|
180
|
+
"""
|
|
181
|
+
An integer between 0 and 300 inclusive indicating the percentage of
|
|
182
|
+
the default size at which bubbles should be displayed. Assigning
|
|
183
|
+
|None| produces the same behavior as assigning `100`.
|
|
184
|
+
"""
|
|
185
|
+
bubbleScale = self._element.bubbleScale
|
|
186
|
+
if bubbleScale is None:
|
|
187
|
+
return 100
|
|
188
|
+
return bubbleScale.val
|
|
189
|
+
|
|
190
|
+
@bubble_scale.setter
|
|
191
|
+
def bubble_scale(self, value):
|
|
192
|
+
bubbleChart = self._element
|
|
193
|
+
bubbleChart._remove_bubbleScale()
|
|
194
|
+
if value is None:
|
|
195
|
+
return
|
|
196
|
+
bubbleScale = bubbleChart._add_bubbleScale()
|
|
197
|
+
bubbleScale.val = value
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class DoughnutPlot(_BasePlot):
|
|
201
|
+
"""
|
|
202
|
+
An doughnut plot.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def hole_size(self):
|
|
207
|
+
"""
|
|
208
|
+
Read/write int in range 10..90 specifying the size of the doughnut
|
|
209
|
+
hole as a percentage of the chart radius. The PowerPoint default for a
|
|
210
|
+
new doughnut chart is 50, which is what this property reports when no
|
|
211
|
+
explicit ``<c:holeSize>`` element is present.
|
|
212
|
+
"""
|
|
213
|
+
holeSize = self._element.holeSize
|
|
214
|
+
if holeSize is None:
|
|
215
|
+
return 50
|
|
216
|
+
return holeSize.val
|
|
217
|
+
|
|
218
|
+
@hole_size.setter
|
|
219
|
+
def hole_size(self, value):
|
|
220
|
+
holeSize = self._element.get_or_add_holeSize()
|
|
221
|
+
holeSize.val = value
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class LinePlot(_BasePlot):
|
|
225
|
+
"""
|
|
226
|
+
A line chart-style plot.
|
|
227
|
+
"""
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def smooth(self):
|
|
231
|
+
"""
|
|
232
|
+
Read/write bool specifying whether the lines in this plot are smoothed
|
|
233
|
+
(curve-fitted) rather than drawn as straight segments between points.
|
|
234
|
+
Reading returns |True| only when *every* series in the plot is
|
|
235
|
+
smoothed. Assigning a value sets the ``<c:smooth>`` element on each
|
|
236
|
+
line series in the plot.
|
|
237
|
+
"""
|
|
238
|
+
sers = self._element.sers
|
|
239
|
+
if not sers:
|
|
240
|
+
return False
|
|
241
|
+
for ser in sers:
|
|
242
|
+
smooth = ser.smooth
|
|
243
|
+
if smooth is None or not smooth.val:
|
|
244
|
+
return False
|
|
245
|
+
return True
|
|
246
|
+
|
|
247
|
+
@smooth.setter
|
|
248
|
+
def smooth(self, value):
|
|
249
|
+
value = bool(value)
|
|
250
|
+
for ser in self._element.sers:
|
|
251
|
+
ser.get_or_add_smooth().val = value
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
class PiePlot(_BasePlot):
|
|
255
|
+
"""
|
|
256
|
+
A pie chart-style plot.
|
|
257
|
+
"""
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class RadarPlot(_BasePlot):
|
|
261
|
+
"""
|
|
262
|
+
A radar-style plot.
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class XyPlot(_BasePlot):
|
|
267
|
+
"""
|
|
268
|
+
An XY (scatter) plot.
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def PlotFactory(xChart, chart):
|
|
273
|
+
"""
|
|
274
|
+
Return an instance of the appropriate subclass of _BasePlot based on the
|
|
275
|
+
tagname of *xChart*.
|
|
276
|
+
"""
|
|
277
|
+
try:
|
|
278
|
+
PlotCls = {
|
|
279
|
+
qn("c:areaChart"): AreaPlot,
|
|
280
|
+
qn("c:area3DChart"): Area3DPlot,
|
|
281
|
+
qn("c:barChart"): BarPlot,
|
|
282
|
+
qn("c:bubbleChart"): BubblePlot,
|
|
283
|
+
qn("c:doughnutChart"): DoughnutPlot,
|
|
284
|
+
qn("c:lineChart"): LinePlot,
|
|
285
|
+
qn("c:pieChart"): PiePlot,
|
|
286
|
+
qn("c:radarChart"): RadarPlot,
|
|
287
|
+
qn("c:scatterChart"): XyPlot,
|
|
288
|
+
}[xChart.tag]
|
|
289
|
+
except KeyError:
|
|
290
|
+
raise ValueError("unsupported plot type %s" % xChart.tag)
|
|
291
|
+
|
|
292
|
+
return PlotCls(xChart, chart)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
class PlotTypeInspector(object):
|
|
296
|
+
"""
|
|
297
|
+
"One-shot" service object that knows how to identify the type of a plot
|
|
298
|
+
as a member of the XL_CHART_TYPE enumeration.
|
|
299
|
+
"""
|
|
300
|
+
|
|
301
|
+
@classmethod
|
|
302
|
+
def chart_type(cls, plot):
|
|
303
|
+
"""
|
|
304
|
+
Return the member of :ref:`XlChartType` that corresponds to the chart
|
|
305
|
+
type of *plot*.
|
|
306
|
+
"""
|
|
307
|
+
try:
|
|
308
|
+
chart_type_method = {
|
|
309
|
+
"AreaPlot": cls._differentiate_area_chart_type,
|
|
310
|
+
"Area3DPlot": cls._differentiate_area_3d_chart_type,
|
|
311
|
+
"BarPlot": cls._differentiate_bar_chart_type,
|
|
312
|
+
"BubblePlot": cls._differentiate_bubble_chart_type,
|
|
313
|
+
"DoughnutPlot": cls._differentiate_doughnut_chart_type,
|
|
314
|
+
"LinePlot": cls._differentiate_line_chart_type,
|
|
315
|
+
"PiePlot": cls._differentiate_pie_chart_type,
|
|
316
|
+
"RadarPlot": cls._differentiate_radar_chart_type,
|
|
317
|
+
"XyPlot": cls._differentiate_xy_chart_type,
|
|
318
|
+
}[plot.__class__.__name__]
|
|
319
|
+
except KeyError:
|
|
320
|
+
raise NotImplementedError(
|
|
321
|
+
"chart_type() not implemented for %s" % plot.__class__.__name__
|
|
322
|
+
)
|
|
323
|
+
return chart_type_method(plot)
|
|
324
|
+
|
|
325
|
+
@classmethod
|
|
326
|
+
def _differentiate_area_3d_chart_type(cls, plot):
|
|
327
|
+
return {
|
|
328
|
+
ST_Grouping.STANDARD: XL.THREE_D_AREA,
|
|
329
|
+
ST_Grouping.STACKED: XL.THREE_D_AREA_STACKED,
|
|
330
|
+
ST_Grouping.PERCENT_STACKED: XL.THREE_D_AREA_STACKED_100,
|
|
331
|
+
}[plot._element.grouping_val]
|
|
332
|
+
|
|
333
|
+
@classmethod
|
|
334
|
+
def _differentiate_area_chart_type(cls, plot):
|
|
335
|
+
return {
|
|
336
|
+
ST_Grouping.STANDARD: XL.AREA,
|
|
337
|
+
ST_Grouping.STACKED: XL.AREA_STACKED,
|
|
338
|
+
ST_Grouping.PERCENT_STACKED: XL.AREA_STACKED_100,
|
|
339
|
+
}[plot._element.grouping_val]
|
|
340
|
+
|
|
341
|
+
@classmethod
|
|
342
|
+
def _differentiate_bar_chart_type(cls, plot):
|
|
343
|
+
barChart = plot._element
|
|
344
|
+
if barChart.barDir.val == ST_BarDir.BAR:
|
|
345
|
+
return {
|
|
346
|
+
ST_Grouping.CLUSTERED: XL.BAR_CLUSTERED,
|
|
347
|
+
ST_Grouping.STACKED: XL.BAR_STACKED,
|
|
348
|
+
ST_Grouping.PERCENT_STACKED: XL.BAR_STACKED_100,
|
|
349
|
+
}[barChart.grouping_val]
|
|
350
|
+
if barChart.barDir.val == ST_BarDir.COL:
|
|
351
|
+
return {
|
|
352
|
+
ST_Grouping.CLUSTERED: XL.COLUMN_CLUSTERED,
|
|
353
|
+
ST_Grouping.STACKED: XL.COLUMN_STACKED,
|
|
354
|
+
ST_Grouping.PERCENT_STACKED: XL.COLUMN_STACKED_100,
|
|
355
|
+
}[barChart.grouping_val]
|
|
356
|
+
raise ValueError("invalid barChart.barDir value '%s'" % barChart.barDir.val)
|
|
357
|
+
|
|
358
|
+
@classmethod
|
|
359
|
+
def _differentiate_bubble_chart_type(cls, plot):
|
|
360
|
+
def first_bubble3D(bubbleChart):
|
|
361
|
+
results = bubbleChart.xpath("c:ser/c:bubble3D")
|
|
362
|
+
return results[0] if results else None
|
|
363
|
+
|
|
364
|
+
bubbleChart = plot._element
|
|
365
|
+
bubble3D = first_bubble3D(bubbleChart)
|
|
366
|
+
|
|
367
|
+
if bubble3D is None:
|
|
368
|
+
return XL.BUBBLE
|
|
369
|
+
if bubble3D.val:
|
|
370
|
+
return XL.BUBBLE_THREE_D_EFFECT
|
|
371
|
+
return XL.BUBBLE
|
|
372
|
+
|
|
373
|
+
@classmethod
|
|
374
|
+
def _differentiate_doughnut_chart_type(cls, plot):
|
|
375
|
+
doughnutChart = plot._element
|
|
376
|
+
explosion = doughnutChart.xpath("./c:ser/c:explosion")
|
|
377
|
+
return XL.DOUGHNUT_EXPLODED if explosion else XL.DOUGHNUT
|
|
378
|
+
|
|
379
|
+
@classmethod
|
|
380
|
+
def _differentiate_line_chart_type(cls, plot):
|
|
381
|
+
lineChart = plot._element
|
|
382
|
+
|
|
383
|
+
def has_line_markers():
|
|
384
|
+
matches = lineChart.xpath('c:ser/c:marker/c:symbol[@val="none"]')
|
|
385
|
+
if matches:
|
|
386
|
+
return False
|
|
387
|
+
return True
|
|
388
|
+
|
|
389
|
+
if has_line_markers():
|
|
390
|
+
return {
|
|
391
|
+
ST_Grouping.STANDARD: XL.LINE_MARKERS,
|
|
392
|
+
ST_Grouping.STACKED: XL.LINE_MARKERS_STACKED,
|
|
393
|
+
ST_Grouping.PERCENT_STACKED: XL.LINE_MARKERS_STACKED_100,
|
|
394
|
+
}[plot._element.grouping_val]
|
|
395
|
+
else:
|
|
396
|
+
return {
|
|
397
|
+
ST_Grouping.STANDARD: XL.LINE,
|
|
398
|
+
ST_Grouping.STACKED: XL.LINE_STACKED,
|
|
399
|
+
ST_Grouping.PERCENT_STACKED: XL.LINE_STACKED_100,
|
|
400
|
+
}[plot._element.grouping_val]
|
|
401
|
+
|
|
402
|
+
@classmethod
|
|
403
|
+
def _differentiate_pie_chart_type(cls, plot):
|
|
404
|
+
pieChart = plot._element
|
|
405
|
+
explosion = pieChart.xpath("./c:ser/c:explosion")
|
|
406
|
+
return XL.PIE_EXPLODED if explosion else XL.PIE
|
|
407
|
+
|
|
408
|
+
@classmethod
|
|
409
|
+
def _differentiate_radar_chart_type(cls, plot):
|
|
410
|
+
radarChart = plot._element
|
|
411
|
+
radar_style = radarChart.xpath("c:radarStyle")[0].get("val")
|
|
412
|
+
|
|
413
|
+
def noMarkers():
|
|
414
|
+
matches = radarChart.xpath("c:ser/c:marker/c:symbol")
|
|
415
|
+
if matches and matches[0].get("val") == "none":
|
|
416
|
+
return True
|
|
417
|
+
return False
|
|
418
|
+
|
|
419
|
+
if radar_style is None:
|
|
420
|
+
return XL.RADAR
|
|
421
|
+
if radar_style == "filled":
|
|
422
|
+
return XL.RADAR_FILLED
|
|
423
|
+
if noMarkers():
|
|
424
|
+
return XL.RADAR
|
|
425
|
+
return XL.RADAR_MARKERS
|
|
426
|
+
|
|
427
|
+
@classmethod
|
|
428
|
+
def _differentiate_xy_chart_type(cls, plot):
|
|
429
|
+
scatterChart = plot._element
|
|
430
|
+
|
|
431
|
+
def noLine():
|
|
432
|
+
return bool(scatterChart.xpath("c:ser/c:spPr/a:ln/a:noFill"))
|
|
433
|
+
|
|
434
|
+
def noMarkers():
|
|
435
|
+
symbols = scatterChart.xpath("c:ser/c:marker/c:symbol")
|
|
436
|
+
if symbols and symbols[0].get("val") == "none":
|
|
437
|
+
return True
|
|
438
|
+
return False
|
|
439
|
+
|
|
440
|
+
scatter_style = scatterChart.xpath("c:scatterStyle")[0].get("val")
|
|
441
|
+
|
|
442
|
+
# ``val="marker"`` (markers-only) is the canonical OOXML way to
|
|
443
|
+
# encode XY_SCATTER and is what the writer now emits. The legacy
|
|
444
|
+
# ``lineMarker + noLine`` combination is still recognised so
|
|
445
|
+
# decks saved by earlier releases keep round-tripping. See
|
|
446
|
+
# IMPROVEMENTS item 5.
|
|
447
|
+
if scatter_style == "marker":
|
|
448
|
+
return XL.XY_SCATTER
|
|
449
|
+
|
|
450
|
+
if scatter_style == "lineMarker":
|
|
451
|
+
if noLine():
|
|
452
|
+
return XL.XY_SCATTER
|
|
453
|
+
if noMarkers():
|
|
454
|
+
return XL.XY_SCATTER_LINES_NO_MARKERS
|
|
455
|
+
return XL.XY_SCATTER_LINES
|
|
456
|
+
|
|
457
|
+
if scatter_style == "smoothMarker":
|
|
458
|
+
if noMarkers():
|
|
459
|
+
return XL.XY_SCATTER_SMOOTH_NO_MARKERS
|
|
460
|
+
return XL.XY_SCATTER_SMOOTH
|
|
461
|
+
|
|
462
|
+
return XL.XY_SCATTER
|
pptx2/chart/point.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Data point-related objects."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Sequence
|
|
6
|
+
|
|
7
|
+
from pptx2.chart.datalabel import DataLabel
|
|
8
|
+
from pptx2.chart.marker import Marker
|
|
9
|
+
from pptx2.dml.chtfmt import ChartFormat
|
|
10
|
+
from pptx2.util import lazyproperty
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class _BasePoints(Sequence):
|
|
14
|
+
"""
|
|
15
|
+
Sequence providing access to the individual data points in a series.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, ser):
|
|
19
|
+
super(_BasePoints, self).__init__()
|
|
20
|
+
self._element = ser
|
|
21
|
+
self._ser = ser
|
|
22
|
+
|
|
23
|
+
def __getitem__(self, idx):
|
|
24
|
+
if idx < 0 or idx >= self.__len__():
|
|
25
|
+
raise IndexError("point index out of range")
|
|
26
|
+
return Point(self._ser, idx)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class BubblePoints(_BasePoints):
|
|
30
|
+
"""
|
|
31
|
+
Sequence providing access to the individual data points in
|
|
32
|
+
a |BubbleSeries| object.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __len__(self):
|
|
36
|
+
return min(
|
|
37
|
+
self._ser.xVal_ptCount_val,
|
|
38
|
+
self._ser.yVal_ptCount_val,
|
|
39
|
+
self._ser.bubbleSize_ptCount_val,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CategoryPoints(_BasePoints):
|
|
44
|
+
"""
|
|
45
|
+
Sequence providing access to individual |Point| objects, each
|
|
46
|
+
representing the visual properties of a data point in the specified
|
|
47
|
+
category series.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __len__(self):
|
|
51
|
+
return self._ser.cat_ptCount_val
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class Point(object):
|
|
55
|
+
"""
|
|
56
|
+
Provides access to the properties of an individual data point in
|
|
57
|
+
a series, such as the visual properties of its marker and the text and
|
|
58
|
+
font of its data label.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def __init__(self, ser, idx):
|
|
62
|
+
super(Point, self).__init__()
|
|
63
|
+
self._element = ser
|
|
64
|
+
self._ser = ser
|
|
65
|
+
self._idx = idx
|
|
66
|
+
|
|
67
|
+
@lazyproperty
|
|
68
|
+
def data_label(self):
|
|
69
|
+
"""
|
|
70
|
+
The |DataLabel| object representing the label on this data point.
|
|
71
|
+
"""
|
|
72
|
+
return DataLabel(self._ser, self._idx)
|
|
73
|
+
|
|
74
|
+
@lazyproperty
|
|
75
|
+
def format(self):
|
|
76
|
+
"""
|
|
77
|
+
The |ChartFormat| object providing access to the shape formatting
|
|
78
|
+
properties of this data point, such as line and fill.
|
|
79
|
+
"""
|
|
80
|
+
dPt = self._ser.get_or_add_dPt_for_point(self._idx)
|
|
81
|
+
return ChartFormat(dPt)
|
|
82
|
+
|
|
83
|
+
@lazyproperty
|
|
84
|
+
def marker(self):
|
|
85
|
+
"""
|
|
86
|
+
The |Marker| instance for this point, providing access to the visual
|
|
87
|
+
properties of the data point marker, such as fill and line. Setting
|
|
88
|
+
these properties overrides any value set at the series level.
|
|
89
|
+
"""
|
|
90
|
+
dPt = self._ser.get_or_add_dPt_for_point(self._idx)
|
|
91
|
+
return Marker(dPt)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class XyPoints(_BasePoints):
|
|
95
|
+
"""
|
|
96
|
+
Sequence providing access to the individual data points in an |XySeries|
|
|
97
|
+
object.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
def __len__(self):
|
|
101
|
+
return min(self._ser.xVal_ptCount_val, self._ser.yVal_ptCount_val)
|