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.
Files changed (175) hide show
  1. pptx2/__init__.py +152 -0
  2. pptx2/_color.py +75 -0
  3. pptx2/_slide_importer.py +597 -0
  4. pptx2/_svg.py +155 -0
  5. pptx2/_template_applier.py +292 -0
  6. pptx2/_textstyle.py +187 -0
  7. pptx2/accessibility.py +365 -0
  8. pptx2/action.py +270 -0
  9. pptx2/animation.py +2237 -0
  10. pptx2/api.py +49 -0
  11. pptx2/audit.py +258 -0
  12. pptx2/chart/__init__.py +0 -0
  13. pptx2/chart/analytics.py +381 -0
  14. pptx2/chart/axis.py +543 -0
  15. pptx2/chart/category.py +200 -0
  16. pptx2/chart/chart.py +670 -0
  17. pptx2/chart/data.py +864 -0
  18. pptx2/chart/datalabel.py +406 -0
  19. pptx2/chart/legend.py +86 -0
  20. pptx2/chart/marker.py +70 -0
  21. pptx2/chart/palettes.py +129 -0
  22. pptx2/chart/plot.py +462 -0
  23. pptx2/chart/point.py +101 -0
  24. pptx2/chart/quick_layouts.py +325 -0
  25. pptx2/chart/series.py +334 -0
  26. pptx2/chart/xlsx.py +272 -0
  27. pptx2/chart/xmlwriter.py +1845 -0
  28. pptx2/compose/__init__.py +28 -0
  29. pptx2/compose/from_spec.py +1094 -0
  30. pptx2/design/__init__.py +8 -0
  31. pptx2/design/components.py +607 -0
  32. pptx2/design/figures.py +389 -0
  33. pptx2/design/layout.py +370 -0
  34. pptx2/design/recipes.py +1967 -0
  35. pptx2/design/style.py +209 -0
  36. pptx2/design/tokens.py +915 -0
  37. pptx2/diagrams.py +754 -0
  38. pptx2/dml/__init__.py +0 -0
  39. pptx2/dml/chtfmt.py +40 -0
  40. pptx2/dml/color.py +496 -0
  41. pptx2/dml/effect.py +909 -0
  42. pptx2/dml/fill.py +691 -0
  43. pptx2/dml/line.py +287 -0
  44. pptx2/dml/picture.py +212 -0
  45. pptx2/dml/three_d.py +381 -0
  46. pptx2/enum/__init__.py +0 -0
  47. pptx2/enum/action.py +71 -0
  48. pptx2/enum/animation.py +31 -0
  49. pptx2/enum/base.py +218 -0
  50. pptx2/enum/chart.py +574 -0
  51. pptx2/enum/dml.py +740 -0
  52. pptx2/enum/lang.py +685 -0
  53. pptx2/enum/presentation.py +133 -0
  54. pptx2/enum/shapes.py +1029 -0
  55. pptx2/enum/text.py +230 -0
  56. pptx2/exc.py +42 -0
  57. pptx2/formats.py +139 -0
  58. pptx2/geometry.py +420 -0
  59. pptx2/inherit.py +109 -0
  60. pptx2/lint.py +2256 -0
  61. pptx2/math.py +177 -0
  62. pptx2/media.py +197 -0
  63. pptx2/opc/__init__.py +0 -0
  64. pptx2/opc/constants.py +332 -0
  65. pptx2/opc/oxml.py +188 -0
  66. pptx2/opc/package.py +762 -0
  67. pptx2/opc/packuri.py +109 -0
  68. pptx2/opc/serialized.py +296 -0
  69. pptx2/opc/shared.py +20 -0
  70. pptx2/opc/spec.py +45 -0
  71. pptx2/oxml/__init__.py +555 -0
  72. pptx2/oxml/action.py +53 -0
  73. pptx2/oxml/chart/__init__.py +0 -0
  74. pptx2/oxml/chart/axis.py +337 -0
  75. pptx2/oxml/chart/chart.py +481 -0
  76. pptx2/oxml/chart/datalabel.py +253 -0
  77. pptx2/oxml/chart/legend.py +72 -0
  78. pptx2/oxml/chart/marker.py +61 -0
  79. pptx2/oxml/chart/plot.py +365 -0
  80. pptx2/oxml/chart/series.py +425 -0
  81. pptx2/oxml/chart/shared.py +220 -0
  82. pptx2/oxml/coreprops.py +288 -0
  83. pptx2/oxml/dml/__init__.py +0 -0
  84. pptx2/oxml/dml/color.py +135 -0
  85. pptx2/oxml/dml/effect.py +213 -0
  86. pptx2/oxml/dml/fill.py +316 -0
  87. pptx2/oxml/dml/line.py +12 -0
  88. pptx2/oxml/dml/three_d.py +110 -0
  89. pptx2/oxml/ns.py +135 -0
  90. pptx2/oxml/presentation.py +313 -0
  91. pptx2/oxml/shapes/__init__.py +19 -0
  92. pptx2/oxml/shapes/autoshape.py +467 -0
  93. pptx2/oxml/shapes/connector.py +107 -0
  94. pptx2/oxml/shapes/graphfrm.py +347 -0
  95. pptx2/oxml/shapes/groupshape.py +329 -0
  96. pptx2/oxml/shapes/picture.py +270 -0
  97. pptx2/oxml/shapes/shared.py +577 -0
  98. pptx2/oxml/simpletypes.py +1027 -0
  99. pptx2/oxml/slide.py +563 -0
  100. pptx2/oxml/table.py +650 -0
  101. pptx2/oxml/text.py +815 -0
  102. pptx2/oxml/theme.py +36 -0
  103. pptx2/oxml/xmlchemy.py +717 -0
  104. pptx2/package.py +222 -0
  105. pptx2/parts/__init__.py +0 -0
  106. pptx2/parts/chart.py +95 -0
  107. pptx2/parts/coreprops.py +167 -0
  108. pptx2/parts/diagram.py +37 -0
  109. pptx2/parts/embeddedpackage.py +93 -0
  110. pptx2/parts/image.py +275 -0
  111. pptx2/parts/media.py +37 -0
  112. pptx2/parts/presentation.py +136 -0
  113. pptx2/parts/slide.py +371 -0
  114. pptx2/presentation.py +408 -0
  115. pptx2/py.typed +0 -0
  116. pptx2/render.py +586 -0
  117. pptx2/section.py +272 -0
  118. pptx2/shapes/__init__.py +26 -0
  119. pptx2/shapes/autoshape.py +442 -0
  120. pptx2/shapes/base.py +1078 -0
  121. pptx2/shapes/connector.py +297 -0
  122. pptx2/shapes/freeform.py +337 -0
  123. pptx2/shapes/graphfrm.py +316 -0
  124. pptx2/shapes/group.py +264 -0
  125. pptx2/shapes/picture.py +422 -0
  126. pptx2/shapes/placeholder.py +468 -0
  127. pptx2/shapes/shapetree.py +2027 -0
  128. pptx2/shared.py +82 -0
  129. pptx2/skill/SKILL.md +450 -0
  130. pptx2/skill/__init__.py +78 -0
  131. pptx2/skill/__main__.py +64 -0
  132. pptx2/skill/references/animations.md +189 -0
  133. pptx2/skill/references/basics.md +421 -0
  134. pptx2/skill/references/charts.md +254 -0
  135. pptx2/skill/references/compose.md +234 -0
  136. pptx2/skill/references/design.md +366 -0
  137. pptx2/skill/references/effects.md +249 -0
  138. pptx2/skill/references/end-to-end-deck.md +231 -0
  139. pptx2/skill/references/geometry-and-arrows.md +334 -0
  140. pptx2/skill/references/lint.md +275 -0
  141. pptx2/skill/references/math.md +86 -0
  142. pptx2/skill/references/picture-effects.md +129 -0
  143. pptx2/skill/references/render.md +151 -0
  144. pptx2/skill/references/smart-art.md +75 -0
  145. pptx2/skill/references/space-aware-authoring.md +249 -0
  146. pptx2/skill/references/tables.md +244 -0
  147. pptx2/skill/references/theme.md +127 -0
  148. pptx2/skill/references/three-d.md +109 -0
  149. pptx2/skill/references/transitions.md +100 -0
  150. pptx2/slide.py +1244 -0
  151. pptx2/smart_art.py +220 -0
  152. pptx2/spec.py +633 -0
  153. pptx2/table.py +1181 -0
  154. pptx2/table_styles.py +184 -0
  155. pptx2/templates/default.pptx +0 -0
  156. pptx2/templates/docx-icon.emf +0 -0
  157. pptx2/templates/generic-icon.emf +0 -0
  158. pptx2/templates/notes.xml +23 -0
  159. pptx2/templates/notesMaster.xml +352 -0
  160. pptx2/templates/pptx-icon.emf +0 -0
  161. pptx2/templates/theme.xml +321 -0
  162. pptx2/templates/xlsx-icon.emf +0 -0
  163. pptx2/text/__init__.py +0 -0
  164. pptx2/text/fonts.py +482 -0
  165. pptx2/text/layout.py +374 -0
  166. pptx2/text/text.py +1272 -0
  167. pptx2/theme.py +721 -0
  168. pptx2/types.py +36 -0
  169. pptx2/util.py +263 -0
  170. python_pptx2-2.13.0.dist-info/METADATA +351 -0
  171. python_pptx2-2.13.0.dist-info/RECORD +175 -0
  172. python_pptx2-2.13.0.dist-info/WHEEL +5 -0
  173. python_pptx2-2.13.0.dist-info/entry_points.txt +3 -0
  174. python_pptx2-2.13.0.dist-info/licenses/LICENSE +22 -0
  175. 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)