ps-python-pptx 1.1.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 (117) hide show
  1. pptx/__init__.py +84 -0
  2. pptx/action.py +270 -0
  3. pptx/api.py +49 -0
  4. pptx/chart/__init__.py +0 -0
  5. pptx/chart/axis.py +523 -0
  6. pptx/chart/category.py +200 -0
  7. pptx/chart/chart.py +280 -0
  8. pptx/chart/data.py +864 -0
  9. pptx/chart/datalabel.py +288 -0
  10. pptx/chart/legend.py +79 -0
  11. pptx/chart/marker.py +70 -0
  12. pptx/chart/plot.py +412 -0
  13. pptx/chart/point.py +101 -0
  14. pptx/chart/series.py +258 -0
  15. pptx/chart/xlsx.py +272 -0
  16. pptx/chart/xmlwriter.py +1840 -0
  17. pptx/dml/__init__.py +0 -0
  18. pptx/dml/chtfmt.py +40 -0
  19. pptx/dml/color.py +301 -0
  20. pptx/dml/effect.py +41 -0
  21. pptx/dml/fill.py +468 -0
  22. pptx/dml/line.py +100 -0
  23. pptx/enum/__init__.py +0 -0
  24. pptx/enum/action.py +71 -0
  25. pptx/enum/base.py +175 -0
  26. pptx/enum/chart.py +492 -0
  27. pptx/enum/dml.py +405 -0
  28. pptx/enum/lang.py +685 -0
  29. pptx/enum/shapes.py +1029 -0
  30. pptx/enum/text.py +371 -0
  31. pptx/exc.py +23 -0
  32. pptx/media.py +197 -0
  33. pptx/opc/__init__.py +0 -0
  34. pptx/opc/constants.py +331 -0
  35. pptx/opc/oxml.py +188 -0
  36. pptx/opc/package.py +762 -0
  37. pptx/opc/packuri.py +109 -0
  38. pptx/opc/serialized.py +296 -0
  39. pptx/opc/shared.py +20 -0
  40. pptx/opc/spec.py +44 -0
  41. pptx/oxml/__init__.py +492 -0
  42. pptx/oxml/action.py +53 -0
  43. pptx/oxml/chart/__init__.py +0 -0
  44. pptx/oxml/chart/axis.py +297 -0
  45. pptx/oxml/chart/chart.py +282 -0
  46. pptx/oxml/chart/datalabel.py +252 -0
  47. pptx/oxml/chart/legend.py +72 -0
  48. pptx/oxml/chart/marker.py +61 -0
  49. pptx/oxml/chart/plot.py +345 -0
  50. pptx/oxml/chart/series.py +254 -0
  51. pptx/oxml/chart/shared.py +219 -0
  52. pptx/oxml/coreprops.py +288 -0
  53. pptx/oxml/dml/__init__.py +0 -0
  54. pptx/oxml/dml/color.py +111 -0
  55. pptx/oxml/dml/fill.py +197 -0
  56. pptx/oxml/dml/line.py +12 -0
  57. pptx/oxml/ns.py +129 -0
  58. pptx/oxml/presentation.py +130 -0
  59. pptx/oxml/shapes/__init__.py +19 -0
  60. pptx/oxml/shapes/autoshape.py +455 -0
  61. pptx/oxml/shapes/connector.py +107 -0
  62. pptx/oxml/shapes/graphfrm.py +342 -0
  63. pptx/oxml/shapes/groupshape.py +280 -0
  64. pptx/oxml/shapes/picture.py +270 -0
  65. pptx/oxml/shapes/shared.py +523 -0
  66. pptx/oxml/simpletypes.py +740 -0
  67. pptx/oxml/slide.py +347 -0
  68. pptx/oxml/table.py +588 -0
  69. pptx/oxml/text.py +748 -0
  70. pptx/oxml/theme.py +29 -0
  71. pptx/oxml/xmlchemy.py +717 -0
  72. pptx/package.py +222 -0
  73. pptx/parts/__init__.py +0 -0
  74. pptx/parts/chart.py +95 -0
  75. pptx/parts/coreprops.py +167 -0
  76. pptx/parts/embeddedpackage.py +93 -0
  77. pptx/parts/image.py +275 -0
  78. pptx/parts/media.py +37 -0
  79. pptx/parts/presentation.py +126 -0
  80. pptx/parts/slide.py +297 -0
  81. pptx/parts/theme.py +13 -0
  82. pptx/presentation.py +113 -0
  83. pptx/py.typed +0 -0
  84. pptx/shapes/__init__.py +26 -0
  85. pptx/shapes/autoshape.py +355 -0
  86. pptx/shapes/base.py +293 -0
  87. pptx/shapes/connector.py +297 -0
  88. pptx/shapes/freeform.py +337 -0
  89. pptx/shapes/graphfrm.py +166 -0
  90. pptx/shapes/group.py +69 -0
  91. pptx/shapes/picture.py +203 -0
  92. pptx/shapes/placeholder.py +407 -0
  93. pptx/shapes/shapetree.py +1190 -0
  94. pptx/shared.py +82 -0
  95. pptx/slide.py +577 -0
  96. pptx/spec.py +632 -0
  97. pptx/table.py +520 -0
  98. pptx/templates/default.pptx +0 -0
  99. pptx/templates/docx-icon.emf +0 -0
  100. pptx/templates/generic-icon.emf +0 -0
  101. pptx/templates/notes.xml +23 -0
  102. pptx/templates/notesMaster.xml +352 -0
  103. pptx/templates/pptx-icon.emf +0 -0
  104. pptx/templates/theme.xml +321 -0
  105. pptx/templates/xlsx-icon.emf +0 -0
  106. pptx/text/__init__.py +0 -0
  107. pptx/text/fonts.py +399 -0
  108. pptx/text/layout.py +325 -0
  109. pptx/text/text.py +770 -0
  110. pptx/theme.py +135 -0
  111. pptx/types.py +36 -0
  112. pptx/util.py +284 -0
  113. ps_python_pptx-1.1.0.dist-info/METADATA +75 -0
  114. ps_python_pptx-1.1.0.dist-info/RECORD +117 -0
  115. ps_python_pptx-1.1.0.dist-info/WHEEL +5 -0
  116. ps_python_pptx-1.1.0.dist-info/licenses/LICENSE +20 -0
  117. ps_python_pptx-1.1.0.dist-info/top_level.txt +1 -0
pptx/chart/series.py ADDED
@@ -0,0 +1,258 @@
1
+ """Series-related objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+
7
+ from pptx.chart.datalabel import DataLabels
8
+ from pptx.chart.marker import Marker
9
+ from pptx.chart.point import BubblePoints, CategoryPoints, XyPoints
10
+ from pptx.dml.chtfmt import ChartFormat
11
+ from pptx.oxml.ns import qn
12
+ from pptx.util import lazyproperty
13
+
14
+
15
+ class _BaseSeries(object):
16
+ """
17
+ Base class for |BarSeries| and other series classes.
18
+ """
19
+
20
+ def __init__(self, ser):
21
+ super(_BaseSeries, self).__init__()
22
+ self._element = ser
23
+ self._ser = ser
24
+
25
+ @lazyproperty
26
+ def format(self):
27
+ """
28
+ The |ChartFormat| instance for this series, providing access to shape
29
+ properties such as fill and line.
30
+ """
31
+ return ChartFormat(self._ser)
32
+
33
+ @property
34
+ def index(self):
35
+ """
36
+ The zero-based integer index of this series as reported in its
37
+ `c:ser/c:idx` element.
38
+ """
39
+ return self._element.idx.val
40
+
41
+ @property
42
+ def name(self):
43
+ """
44
+ The string label given to this series, appears as the title of the
45
+ column for this series in the Excel worksheet. It also appears as the
46
+ label for this series in the legend.
47
+ """
48
+ names = self._element.xpath("./c:tx//c:pt/c:v/text()")
49
+ name = names[0] if names else ""
50
+ return name
51
+
52
+
53
+ class _BaseCategorySeries(_BaseSeries):
54
+ """Base class for |BarSeries| and other category chart series classes."""
55
+
56
+ @lazyproperty
57
+ def data_labels(self):
58
+ """|DataLabels| object controlling data labels for this series."""
59
+ return DataLabels(self._ser.get_or_add_dLbls())
60
+
61
+ @lazyproperty
62
+ def points(self):
63
+ """
64
+ The |CategoryPoints| object providing access to individual data
65
+ points in this series.
66
+ """
67
+ return CategoryPoints(self._ser)
68
+
69
+ @property
70
+ def values(self):
71
+ """
72
+ Read-only. A sequence containing the float values for this series, in
73
+ the order they appear on the chart.
74
+ """
75
+
76
+ def iter_values():
77
+ val = self._element.val
78
+ if val is None:
79
+ return
80
+ for idx in range(val.ptCount_val):
81
+ yield val.pt_v(idx)
82
+
83
+ return tuple(iter_values())
84
+
85
+
86
+ class _MarkerMixin(object):
87
+ """
88
+ Mixin class providing `.marker` property for line-type chart series. The
89
+ line-type charts are Line, XY, and Radar.
90
+ """
91
+
92
+ @lazyproperty
93
+ def marker(self):
94
+ """
95
+ The |Marker| instance for this series, providing access to data point
96
+ marker properties such as fill and line. Setting these properties
97
+ determines the appearance of markers for all points in this series
98
+ that are not overridden by settings at the point level.
99
+ """
100
+ return Marker(self._ser)
101
+
102
+
103
+ class AreaSeries(_BaseCategorySeries):
104
+ """
105
+ A data point series belonging to an area plot.
106
+ """
107
+
108
+
109
+ class BarSeries(_BaseCategorySeries):
110
+ """A data point series belonging to a bar plot."""
111
+
112
+ @property
113
+ def invert_if_negative(self):
114
+ """
115
+ |True| if a point having a value less than zero should appear with a
116
+ fill different than those with a positive value. |False| if the fill
117
+ should be the same regardless of the bar's value. When |True|, a bar
118
+ with a solid fill appears with white fill; in a bar with gradient
119
+ fill, the direction of the gradient is reversed, e.g. dark -> light
120
+ instead of light -> dark. The term "invert" here should be understood
121
+ to mean "invert the *direction* of the *fill gradient*".
122
+ """
123
+ invertIfNegative = self._element.invertIfNegative
124
+ if invertIfNegative is None:
125
+ return True
126
+ return invertIfNegative.val
127
+
128
+ @invert_if_negative.setter
129
+ def invert_if_negative(self, value):
130
+ invertIfNegative = self._element.get_or_add_invertIfNegative()
131
+ invertIfNegative.val = value
132
+
133
+
134
+ class LineSeries(_BaseCategorySeries, _MarkerMixin):
135
+ """
136
+ A data point series belonging to a line plot.
137
+ """
138
+
139
+ @property
140
+ def smooth(self):
141
+ """
142
+ Read/write boolean specifying whether to use curve smoothing to
143
+ form the line connecting the data points in this series into
144
+ a continuous curve. If |False|, a series of straight line segments
145
+ are used to connect the points.
146
+ """
147
+ smooth = self._element.smooth
148
+ if smooth is None:
149
+ return True
150
+ return smooth.val
151
+
152
+ @smooth.setter
153
+ def smooth(self, value):
154
+ self._element.get_or_add_smooth().val = value
155
+
156
+
157
+ class PieSeries(_BaseCategorySeries):
158
+ """
159
+ A data point series belonging to a pie plot.
160
+ """
161
+
162
+
163
+ class RadarSeries(_BaseCategorySeries, _MarkerMixin):
164
+ """
165
+ A data point series belonging to a radar plot.
166
+ """
167
+
168
+
169
+ class XySeries(_BaseSeries, _MarkerMixin):
170
+ """
171
+ A data point series belonging to an XY (scatter) plot.
172
+ """
173
+
174
+ def iter_values(self):
175
+ """
176
+ Generate each float Y value in this series, in the order they appear
177
+ on the chart. A value of `None` represents a missing Y value
178
+ (corresponding to a blank Excel cell).
179
+ """
180
+ yVal = self._element.yVal
181
+ if yVal is None:
182
+ return
183
+
184
+ for idx in range(yVal.ptCount_val):
185
+ yield yVal.pt_v(idx)
186
+
187
+ @lazyproperty
188
+ def points(self):
189
+ """
190
+ The |XyPoints| object providing access to individual data points in
191
+ this series.
192
+ """
193
+ return XyPoints(self._ser)
194
+
195
+ @property
196
+ def values(self):
197
+ """
198
+ Read-only. A sequence containing the float values for this series, in
199
+ the order they appear on the chart.
200
+ """
201
+ return tuple(self.iter_values())
202
+
203
+
204
+ class BubbleSeries(XySeries):
205
+ """
206
+ A data point series belonging to a bubble plot.
207
+ """
208
+
209
+ @lazyproperty
210
+ def points(self):
211
+ """
212
+ The |BubblePoints| object providing access to individual data point
213
+ objects used to discover and adjust the formatting and data labels of
214
+ a data point.
215
+ """
216
+ return BubblePoints(self._ser)
217
+
218
+
219
+ class SeriesCollection(Sequence):
220
+ """
221
+ A sequence of |Series| objects.
222
+ """
223
+
224
+ def __init__(self, parent_elm):
225
+ # *parent_elm* can be either a c:plotArea or xChart element
226
+ super(SeriesCollection, self).__init__()
227
+ self._element = parent_elm
228
+
229
+ def __getitem__(self, index):
230
+ ser = self._element.sers[index]
231
+ return _SeriesFactory(ser)
232
+
233
+ def __len__(self):
234
+ return len(self._element.sers)
235
+
236
+
237
+ def _SeriesFactory(ser):
238
+ """
239
+ Return an instance of the appropriate subclass of _BaseSeries based on the
240
+ xChart element *ser* appears in.
241
+ """
242
+ xChart_tag = ser.getparent().tag
243
+
244
+ try:
245
+ SeriesCls = {
246
+ qn("c:areaChart"): AreaSeries,
247
+ qn("c:barChart"): BarSeries,
248
+ qn("c:bubbleChart"): BubbleSeries,
249
+ qn("c:doughnutChart"): PieSeries,
250
+ qn("c:lineChart"): LineSeries,
251
+ qn("c:pieChart"): PieSeries,
252
+ qn("c:radarChart"): RadarSeries,
253
+ qn("c:scatterChart"): XySeries,
254
+ }[xChart_tag]
255
+ except KeyError:
256
+ raise NotImplementedError("series class for %s not yet implemented" % xChart_tag)
257
+
258
+ return SeriesCls(ser)
pptx/chart/xlsx.py ADDED
@@ -0,0 +1,272 @@
1
+ """Chart builder and related objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import io
6
+ from contextlib import contextmanager
7
+
8
+ from xlsxwriter import Workbook
9
+
10
+
11
+ class _BaseWorkbookWriter(object):
12
+ """Base class for workbook writers, providing shared members."""
13
+
14
+ def __init__(self, chart_data):
15
+ super(_BaseWorkbookWriter, self).__init__()
16
+ self._chart_data = chart_data
17
+
18
+ @property
19
+ def xlsx_blob(self):
20
+ """bytes for Excel file containing chart_data."""
21
+ xlsx_file = io.BytesIO()
22
+ with self._open_worksheet(xlsx_file) as (workbook, worksheet):
23
+ self._populate_worksheet(workbook, worksheet)
24
+ return xlsx_file.getvalue()
25
+
26
+ @contextmanager
27
+ def _open_worksheet(self, xlsx_file):
28
+ """
29
+ Enable XlsxWriter Worksheet object to be opened, operated on, and
30
+ then automatically closed within a `with` statement. A filename or
31
+ stream object (such as an `io.BytesIO` instance) is expected as
32
+ *xlsx_file*.
33
+ """
34
+ workbook = Workbook(xlsx_file, {"in_memory": True})
35
+ worksheet = workbook.add_worksheet()
36
+ yield workbook, worksheet
37
+ workbook.close()
38
+
39
+ def _populate_worksheet(self, workbook, worksheet):
40
+ """
41
+ Must be overridden by each subclass to provide the particulars of
42
+ writing the spreadsheet data.
43
+ """
44
+ raise NotImplementedError("must be provided by each subclass")
45
+
46
+
47
+ class CategoryWorkbookWriter(_BaseWorkbookWriter):
48
+ """
49
+ Determines Excel worksheet layout and can write an Excel workbook from
50
+ a CategoryChartData object. Serves as the authority for Excel worksheet
51
+ ranges.
52
+ """
53
+
54
+ @property
55
+ def categories_ref(self):
56
+ """
57
+ The Excel worksheet reference to the categories for this chart (not
58
+ including the column heading).
59
+ """
60
+ categories = self._chart_data.categories
61
+ if categories.depth == 0:
62
+ raise ValueError("chart data contains no categories")
63
+ right_col = chr(ord("A") + categories.depth - 1)
64
+ bottom_row = categories.leaf_count + 1
65
+ return "Sheet1!$A$2:$%s$%d" % (right_col, bottom_row)
66
+
67
+ def series_name_ref(self, series):
68
+ """
69
+ Return the Excel worksheet reference to the cell containing the name
70
+ for *series*. This also serves as the column heading for the series
71
+ values.
72
+ """
73
+ return "Sheet1!$%s$1" % self._series_col_letter(series)
74
+
75
+ def values_ref(self, series):
76
+ """
77
+ The Excel worksheet reference to the values for this series (not
78
+ including the column heading).
79
+ """
80
+ return "Sheet1!${col_letter}$2:${col_letter}${bottom_row}".format(
81
+ **{
82
+ "col_letter": self._series_col_letter(series),
83
+ "bottom_row": len(series) + 1,
84
+ }
85
+ )
86
+
87
+ @staticmethod
88
+ def _column_reference(column_number):
89
+ """Return str Excel column reference like 'BQ' for *column_number*.
90
+
91
+ *column_number* is an int in the range 1-16384 inclusive, where
92
+ 1 maps to column 'A'.
93
+ """
94
+ if column_number < 1 or column_number > 16384:
95
+ raise ValueError("column_number must be in range 1-16384")
96
+
97
+ # ---Work right-to-left, one order of magnitude at a time. Note there
98
+ # is no zero representation in Excel address scheme, so this is
99
+ # not just a conversion to base-26---
100
+
101
+ col_ref = ""
102
+ while column_number:
103
+ remainder = column_number % 26
104
+ if remainder == 0:
105
+ remainder = 26
106
+
107
+ col_letter = chr(ord("A") + remainder - 1)
108
+ col_ref = col_letter + col_ref
109
+
110
+ # ---Advance to next order of magnitude or terminate loop. The
111
+ # minus-one in this expression reflects the fact the next lower
112
+ # order of magnitude has a minumum value of 1 (not zero). This is
113
+ # essentially the complement to the "if it's 0 make it 26' step
114
+ # above.---
115
+ column_number = (column_number - 1) // 26
116
+
117
+ return col_ref
118
+
119
+ def _populate_worksheet(self, workbook, worksheet):
120
+ """
121
+ Write the chart data contents to *worksheet* in category chart
122
+ layout. Write categories starting in the first column starting in
123
+ the second row, and proceeding one column per category level (for
124
+ charts having multi-level categories). Write series as columns
125
+ starting in the next following column, placing the series title in
126
+ the first cell.
127
+ """
128
+ self._write_categories(workbook, worksheet)
129
+ self._write_series(workbook, worksheet)
130
+
131
+ def _series_col_letter(self, series):
132
+ """
133
+ The letter of the Excel worksheet column in which the data for a
134
+ series appears.
135
+ """
136
+ column_number = 1 + series.categories.depth + series.index
137
+ return self._column_reference(column_number)
138
+
139
+ def _write_categories(self, workbook, worksheet):
140
+ """
141
+ Write the categories column(s) to *worksheet*. Categories start in
142
+ the first column starting in the second row, and proceeding one
143
+ column per category level (for charts having multi-level categories).
144
+ A date category is formatted as a date. All others are formatted
145
+ `General`.
146
+ """
147
+ categories = self._chart_data.categories
148
+ num_format = workbook.add_format({"num_format": categories.number_format})
149
+ depth = categories.depth
150
+ for idx, level in enumerate(categories.levels):
151
+ col = depth - idx - 1
152
+ self._write_cat_column(worksheet, col, level, num_format)
153
+
154
+ def _write_cat_column(self, worksheet, col, level, num_format):
155
+ """
156
+ Write a category column defined by *level* to *worksheet* at offset
157
+ *col* and formatted with *num_format*.
158
+ """
159
+ worksheet.set_column(col, col, 10) # wide enough for a date
160
+ for off, name in level:
161
+ row = off + 1
162
+ worksheet.write(row, col, name, num_format)
163
+
164
+ def _write_series(self, workbook, worksheet):
165
+ """
166
+ Write the series column(s) to *worksheet*. Series start in the column
167
+ following the last categories column, placing the series title in the
168
+ first cell.
169
+ """
170
+ col_offset = self._chart_data.categories.depth
171
+ for idx, series in enumerate(self._chart_data):
172
+ num_format = workbook.add_format({"num_format": series.number_format})
173
+ series_col = idx + col_offset
174
+ worksheet.write(0, series_col, series.name)
175
+ worksheet.write_column(1, series_col, series.values, num_format)
176
+
177
+
178
+ class XyWorkbookWriter(_BaseWorkbookWriter):
179
+ """
180
+ Determines Excel worksheet layout and can write an Excel workbook from XY
181
+ chart data. Serves as the authority for Excel worksheet ranges.
182
+ """
183
+
184
+ def series_name_ref(self, series):
185
+ """
186
+ Return the Excel worksheet reference to the cell containing the name
187
+ for *series*. This also serves as the column heading for the series
188
+ Y values.
189
+ """
190
+ row = self.series_table_row_offset(series) + 1
191
+ return "Sheet1!$B$%d" % row
192
+
193
+ def series_table_row_offset(self, series):
194
+ """
195
+ Return the number of rows preceding the data table for *series* in
196
+ the Excel worksheet.
197
+ """
198
+ title_and_spacer_rows = series.index * 2
199
+ data_point_rows = series.data_point_offset
200
+ return title_and_spacer_rows + data_point_rows
201
+
202
+ def x_values_ref(self, series):
203
+ """
204
+ The Excel worksheet reference to the X values for this chart (not
205
+ including the column label).
206
+ """
207
+ top_row = self.series_table_row_offset(series) + 2
208
+ bottom_row = top_row + len(series) - 1
209
+ return "Sheet1!$A$%d:$A$%d" % (top_row, bottom_row)
210
+
211
+ def y_values_ref(self, series):
212
+ """
213
+ The Excel worksheet reference to the Y values for this chart (not
214
+ including the column label).
215
+ """
216
+ top_row = self.series_table_row_offset(series) + 2
217
+ bottom_row = top_row + len(series) - 1
218
+ return "Sheet1!$B$%d:$B$%d" % (top_row, bottom_row)
219
+
220
+ def _populate_worksheet(self, workbook, worksheet):
221
+ """
222
+ Write chart data contents to *worksheet* in the standard XY chart
223
+ layout. Write the data for each series to a separate two-column
224
+ table, X values in column A and Y values in column B. Place the
225
+ series label in the first (heading) cell of the column.
226
+ """
227
+ chart_num_format = workbook.add_format({"num_format": self._chart_data.number_format})
228
+ for series in self._chart_data:
229
+ series_num_format = workbook.add_format({"num_format": series.number_format})
230
+ offset = self.series_table_row_offset(series)
231
+ # write X values
232
+ worksheet.write_column(offset + 1, 0, series.x_values, chart_num_format)
233
+ # write Y values
234
+ worksheet.write(offset, 1, series.name)
235
+ worksheet.write_column(offset + 1, 1, series.y_values, series_num_format)
236
+
237
+
238
+ class BubbleWorkbookWriter(XyWorkbookWriter):
239
+ """
240
+ Service object that knows how to write an Excel workbook from bubble
241
+ chart data.
242
+ """
243
+
244
+ def bubble_sizes_ref(self, series):
245
+ """
246
+ The Excel worksheet reference to the range containing the bubble
247
+ sizes for *series* (not including the column heading cell).
248
+ """
249
+ top_row = self.series_table_row_offset(series) + 2
250
+ bottom_row = top_row + len(series) - 1
251
+ return "Sheet1!$C$%d:$C$%d" % (top_row, bottom_row)
252
+
253
+ def _populate_worksheet(self, workbook, worksheet):
254
+ """
255
+ Write chart data contents to *worksheet* in the bubble chart layout.
256
+ Write the data for each series to a separate three-column table with
257
+ X values in column A, Y values in column B, and bubble sizes in
258
+ column C. Place the series label in the first (heading) cell of the
259
+ values column.
260
+ """
261
+ chart_num_format = workbook.add_format({"num_format": self._chart_data.number_format})
262
+ for series in self._chart_data:
263
+ series_num_format = workbook.add_format({"num_format": series.number_format})
264
+ offset = self.series_table_row_offset(series)
265
+ # write X values
266
+ worksheet.write_column(offset + 1, 0, series.x_values, chart_num_format)
267
+ # write Y values
268
+ worksheet.write(offset, 1, series.name)
269
+ worksheet.write_column(offset + 1, 1, series.y_values, series_num_format)
270
+ # write bubble sizes
271
+ worksheet.write(offset, 2, "Size")
272
+ worksheet.write_column(offset + 1, 2, series.bubble_sizes, chart_num_format)