1863 lines
66 KiB
Python
1863 lines
66 KiB
Python
# encoding: utf-8
|
|
|
|
"""
|
|
Composers for default chart XML for various chart types.
|
|
"""
|
|
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
from copy import deepcopy
|
|
from xml.sax.saxutils import escape
|
|
|
|
from ..compat import to_unicode
|
|
from ..enum.chart import XL_CHART_TYPE
|
|
from ..oxml import parse_xml
|
|
from ..oxml.ns import nsdecls
|
|
|
|
|
|
def ChartXmlWriter(chart_type, chart_data):
|
|
"""
|
|
Factory function returning appropriate XML writer object for
|
|
*chart_type*, loaded with *chart_type* and *chart_data*.
|
|
"""
|
|
XL_CT = XL_CHART_TYPE
|
|
try:
|
|
BuilderCls = {
|
|
XL_CT.AREA: _AreaChartXmlWriter,
|
|
XL_CT.AREA_STACKED: _AreaChartXmlWriter,
|
|
XL_CT.AREA_STACKED_100: _AreaChartXmlWriter,
|
|
XL_CT.BAR_CLUSTERED: _BarChartXmlWriter,
|
|
XL_CT.BAR_STACKED: _BarChartXmlWriter,
|
|
XL_CT.BAR_STACKED_100: _BarChartXmlWriter,
|
|
XL_CT.BUBBLE: _BubbleChartXmlWriter,
|
|
XL_CT.BUBBLE_THREE_D_EFFECT: _BubbleChartXmlWriter,
|
|
XL_CT.COLUMN_CLUSTERED: _BarChartXmlWriter,
|
|
XL_CT.COLUMN_STACKED: _BarChartXmlWriter,
|
|
XL_CT.COLUMN_STACKED_100: _BarChartXmlWriter,
|
|
XL_CT.DOUGHNUT: _DoughnutChartXmlWriter,
|
|
XL_CT.DOUGHNUT_EXPLODED: _DoughnutChartXmlWriter,
|
|
XL_CT.LINE: _LineChartXmlWriter,
|
|
XL_CT.LINE_MARKERS: _LineChartXmlWriter,
|
|
XL_CT.LINE_MARKERS_STACKED: _LineChartXmlWriter,
|
|
XL_CT.LINE_MARKERS_STACKED_100: _LineChartXmlWriter,
|
|
XL_CT.LINE_STACKED: _LineChartXmlWriter,
|
|
XL_CT.LINE_STACKED_100: _LineChartXmlWriter,
|
|
XL_CT.PIE: _PieChartXmlWriter,
|
|
XL_CT.PIE_EXPLODED: _PieChartXmlWriter,
|
|
XL_CT.RADAR: _RadarChartXmlWriter,
|
|
XL_CT.RADAR_FILLED: _RadarChartXmlWriter,
|
|
XL_CT.RADAR_MARKERS: _RadarChartXmlWriter,
|
|
XL_CT.XY_SCATTER: _XyChartXmlWriter,
|
|
XL_CT.XY_SCATTER_LINES: _XyChartXmlWriter,
|
|
XL_CT.XY_SCATTER_LINES_NO_MARKERS: _XyChartXmlWriter,
|
|
XL_CT.XY_SCATTER_SMOOTH: _XyChartXmlWriter,
|
|
XL_CT.XY_SCATTER_SMOOTH_NO_MARKERS: _XyChartXmlWriter,
|
|
}[chart_type]
|
|
except KeyError:
|
|
raise NotImplementedError(
|
|
"XML writer for chart type %s not yet implemented" % chart_type
|
|
)
|
|
return BuilderCls(chart_type, chart_data)
|
|
|
|
|
|
def SeriesXmlRewriterFactory(chart_type, chart_data):
|
|
"""
|
|
Return a |_BaseSeriesXmlRewriter| subclass appropriate to *chart_type*.
|
|
"""
|
|
XL_CT = XL_CHART_TYPE
|
|
|
|
RewriterCls = {
|
|
# There are 73 distinct chart types, only specify non-category
|
|
# types, others default to _CategorySeriesXmlRewriter. Stock-type
|
|
# charts are multi-plot charts, so no guaratees on how they turn
|
|
# out.
|
|
XL_CT.BUBBLE: _BubbleSeriesXmlRewriter,
|
|
XL_CT.BUBBLE_THREE_D_EFFECT: _BubbleSeriesXmlRewriter,
|
|
XL_CT.XY_SCATTER: _XySeriesXmlRewriter,
|
|
XL_CT.XY_SCATTER_LINES: _XySeriesXmlRewriter,
|
|
XL_CT.XY_SCATTER_LINES_NO_MARKERS: _XySeriesXmlRewriter,
|
|
XL_CT.XY_SCATTER_SMOOTH: _XySeriesXmlRewriter,
|
|
XL_CT.XY_SCATTER_SMOOTH_NO_MARKERS: _XySeriesXmlRewriter,
|
|
}.get(chart_type, _CategorySeriesXmlRewriter)
|
|
|
|
return RewriterCls(chart_data)
|
|
|
|
|
|
class _BaseChartXmlWriter(object):
|
|
"""
|
|
Generates XML text (unicode) for a default chart, like the one added by
|
|
PowerPoint when you click the *Add Column Chart* button on the ribbon.
|
|
Differentiated XML for different chart types is provided by subclasses.
|
|
"""
|
|
|
|
def __init__(self, chart_type, series_seq):
|
|
super(_BaseChartXmlWriter, self).__init__()
|
|
self._chart_type = chart_type
|
|
self._chart_data = series_seq
|
|
self._series_seq = list(series_seq)
|
|
|
|
@property
|
|
def xml(self):
|
|
"""
|
|
The full XML stream for the chart specified by this chart builder, as
|
|
unicode text. This method must be overridden by each subclass.
|
|
"""
|
|
raise NotImplementedError("must be implemented by all subclasses")
|
|
|
|
|
|
class _BaseSeriesXmlWriter(object):
|
|
"""
|
|
Provides shared members for series XML writers.
|
|
"""
|
|
|
|
def __init__(self, series, date_1904=False):
|
|
super(_BaseSeriesXmlWriter, self).__init__()
|
|
self._series = series
|
|
self._date_1904 = date_1904
|
|
|
|
@property
|
|
def name(self):
|
|
"""
|
|
The XML-escaped name for this series.
|
|
"""
|
|
return escape(self._series.name)
|
|
|
|
def numRef_xml(self, wksht_ref, number_format, values):
|
|
"""
|
|
Return the ``<c:numRef>`` element specified by the parameters as
|
|
unicode text.
|
|
"""
|
|
pt_xml = self.pt_xml(values)
|
|
return (
|
|
" <c:numRef>\n"
|
|
" <c:f>{wksht_ref}</c:f>\n"
|
|
" <c:numCache>\n"
|
|
" <c:formatCode>{number_format}</c:formatCode>\n"
|
|
"{pt_xml}"
|
|
" </c:numCache>\n"
|
|
" </c:numRef>\n"
|
|
).format(
|
|
**{"wksht_ref": wksht_ref, "number_format": number_format, "pt_xml": pt_xml}
|
|
)
|
|
|
|
def pt_xml(self, values):
|
|
"""
|
|
Return the ``<c:ptCount>`` and sequence of ``<c:pt>`` elements
|
|
corresponding to *values* as a single unicode text string.
|
|
`c:ptCount` refers to the number of `c:pt` elements in this sequence.
|
|
The `idx` attribute value for `c:pt` elements locates the data point
|
|
in the overall data point sequence of the chart and is started at
|
|
*offset*.
|
|
"""
|
|
xml = (' <c:ptCount val="{pt_count}"/>\n').format(
|
|
pt_count=len(values)
|
|
)
|
|
|
|
pt_tmpl = (
|
|
' <c:pt idx="{idx}">\n'
|
|
" <c:v>{value}</c:v>\n"
|
|
" </c:pt>\n"
|
|
)
|
|
for idx, value in enumerate(values):
|
|
if value is None:
|
|
continue
|
|
xml += pt_tmpl.format(idx=idx, value=value)
|
|
|
|
return xml
|
|
|
|
@property
|
|
def tx(self):
|
|
"""
|
|
Return a ``<c:tx>`` oxml element for this series, containing the
|
|
series name.
|
|
"""
|
|
xml = self._tx_tmpl.format(
|
|
**{
|
|
"wksht_ref": self._series.name_ref,
|
|
"series_name": self.name,
|
|
"nsdecls": " %s" % nsdecls("c"),
|
|
}
|
|
)
|
|
return parse_xml(xml)
|
|
|
|
@property
|
|
def tx_xml(self):
|
|
"""
|
|
Return the ``<c:tx>`` (tx is short for 'text') element for this
|
|
series as unicode text. This element contains the series name.
|
|
"""
|
|
return self._tx_tmpl.format(
|
|
**{
|
|
"wksht_ref": self._series.name_ref,
|
|
"series_name": self.name,
|
|
"nsdecls": "",
|
|
}
|
|
)
|
|
|
|
@property
|
|
def _tx_tmpl(self):
|
|
"""
|
|
The string formatting template for the ``<c:tx>`` element for this
|
|
series, containing the series title and spreadsheet range reference.
|
|
"""
|
|
return (
|
|
" <c:tx{nsdecls}>\n"
|
|
" <c:strRef>\n"
|
|
" <c:f>{wksht_ref}</c:f>\n"
|
|
" <c:strCache>\n"
|
|
' <c:ptCount val="1"/>\n'
|
|
' <c:pt idx="0">\n'
|
|
" <c:v>{series_name}</c:v>\n"
|
|
" </c:pt>\n"
|
|
" </c:strCache>\n"
|
|
" </c:strRef>\n"
|
|
" </c:tx>\n"
|
|
)
|
|
|
|
|
|
class _BaseSeriesXmlRewriter(object):
|
|
"""
|
|
Base class for series XML rewriters.
|
|
"""
|
|
|
|
def __init__(self, chart_data):
|
|
super(_BaseSeriesXmlRewriter, self).__init__()
|
|
self._chart_data = chart_data
|
|
|
|
def replace_series_data(self, chartSpace):
|
|
"""
|
|
Rewrite the series data under *chartSpace* using the chart data
|
|
contents. All series-level formatting is left undisturbed. If
|
|
the chart data contains fewer series than *chartSpace*, the extra
|
|
series in *chartSpace* are deleted. If *chart_data* contains more
|
|
series than the *chartSpace* element, new series are added to the
|
|
last plot in the chart and series formatting is "cloned" from the
|
|
last series in that plot.
|
|
"""
|
|
plotArea, date_1904 = chartSpace.plotArea, chartSpace.date_1904
|
|
chart_data = self._chart_data
|
|
self._adjust_ser_count(plotArea, len(chart_data))
|
|
for ser, series_data in zip(plotArea.sers, chart_data):
|
|
self._rewrite_ser_data(ser, series_data, date_1904)
|
|
|
|
def _add_cloned_sers(self, plotArea, count):
|
|
"""
|
|
Add `c:ser` elements to the last xChart element in *plotArea*, cloned
|
|
from the last `c:ser` child of that last xChart.
|
|
"""
|
|
|
|
def clone_ser(ser):
|
|
new_ser = deepcopy(ser)
|
|
new_ser.idx.val = plotArea.next_idx
|
|
new_ser.order.val = plotArea.next_order
|
|
ser.addnext(new_ser)
|
|
return new_ser
|
|
|
|
last_ser = plotArea.last_ser
|
|
for _ in range(count):
|
|
last_ser = clone_ser(last_ser)
|
|
|
|
def _adjust_ser_count(self, plotArea, new_ser_count):
|
|
"""
|
|
Adjust the number of c:ser elements in *plotArea* to *new_ser_count*.
|
|
Excess c:ser elements are deleted from the end, along with any xChart
|
|
elements that are left empty as a result. Series elements are
|
|
considered in xChart + series order. Any new c:ser elements required
|
|
are added to the last xChart element and cloned from the last c:ser
|
|
element in that xChart.
|
|
"""
|
|
ser_count_diff = new_ser_count - len(plotArea.sers)
|
|
if ser_count_diff > 0:
|
|
self._add_cloned_sers(plotArea, ser_count_diff)
|
|
elif ser_count_diff < 0:
|
|
self._trim_ser_count_by(plotArea, abs(ser_count_diff))
|
|
|
|
def _rewrite_ser_data(self, ser, series_data, date_1904):
|
|
"""
|
|
Rewrite selected child elements of *ser* based on the values in
|
|
*series_data*.
|
|
"""
|
|
raise NotImplementedError("must be implemented by each subclass")
|
|
|
|
def _trim_ser_count_by(self, plotArea, count):
|
|
"""
|
|
Remove the last *count* ser elements from *plotArea*. Any xChart
|
|
elements having no ser child elements after trimming are also
|
|
removed.
|
|
"""
|
|
extra_sers = plotArea.sers[-count:]
|
|
for ser in extra_sers:
|
|
parent = ser.getparent()
|
|
parent.remove(ser)
|
|
extra_xCharts = [
|
|
xChart for xChart in plotArea.iter_xCharts() if len(xChart.sers) == 0
|
|
]
|
|
for xChart in extra_xCharts:
|
|
parent = xChart.getparent()
|
|
parent.remove(xChart)
|
|
|
|
|
|
class _AreaChartXmlWriter(_BaseChartXmlWriter):
|
|
"""
|
|
Provides specialized methods particular to the ``<c:areaChart>`` element.
|
|
"""
|
|
|
|
@property
|
|
def xml(self):
|
|
return (
|
|
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
|
|
'<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
|
|
'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
|
|
'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
|
|
'iceDocument/2006/relationships">\n'
|
|
' <c:date1904 val="0"/>\n'
|
|
' <c:roundedCorners val="0"/>\n'
|
|
" <c:chart>\n"
|
|
' <c:autoTitleDeleted val="0"/>\n'
|
|
" <c:plotArea>\n"
|
|
" <c:layout/>\n"
|
|
" <c:areaChart>\n"
|
|
"{grouping_xml}"
|
|
' <c:varyColors val="0"/>\n'
|
|
"{ser_xml}"
|
|
" <c:dLbls>\n"
|
|
' <c:showLegendKey val="0"/>\n'
|
|
' <c:showVal val="0"/>\n'
|
|
' <c:showCatName val="0"/>\n'
|
|
' <c:showSerName val="0"/>\n'
|
|
' <c:showPercent val="0"/>\n'
|
|
' <c:showBubbleSize val="0"/>\n'
|
|
" </c:dLbls>\n"
|
|
' <c:axId val="-2101159928"/>\n'
|
|
' <c:axId val="-2100718248"/>\n'
|
|
" </c:areaChart>\n"
|
|
"{cat_ax_xml}"
|
|
" <c:valAx>\n"
|
|
' <c:axId val="-2100718248"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="l"/>\n'
|
|
" <c:majorGridlines/>\n"
|
|
' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2101159928"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:crossBetween val="midCat"/>\n'
|
|
" </c:valAx>\n"
|
|
" </c:plotArea>\n"
|
|
" <c:legend>\n"
|
|
' <c:legendPos val="r"/>\n'
|
|
" <c:layout/>\n"
|
|
' <c:overlay val="0"/>\n'
|
|
" </c:legend>\n"
|
|
' <c:plotVisOnly val="1"/>\n'
|
|
' <c:dispBlanksAs val="zero"/>\n'
|
|
' <c:showDLblsOverMax val="0"/>\n'
|
|
" </c:chart>\n"
|
|
" <c:txPr>\n"
|
|
" <a:bodyPr/>\n"
|
|
" <a:lstStyle/>\n"
|
|
" <a:p>\n"
|
|
" <a:pPr>\n"
|
|
' <a:defRPr sz="1800"/>\n'
|
|
" </a:pPr>\n"
|
|
" <a:endParaRPr/>\n"
|
|
" </a:p>\n"
|
|
" </c:txPr>\n"
|
|
"</c:chartSpace>\n"
|
|
).format(
|
|
**{
|
|
"grouping_xml": self._grouping_xml,
|
|
"ser_xml": self._ser_xml,
|
|
"cat_ax_xml": self._cat_ax_xml,
|
|
}
|
|
)
|
|
|
|
@property
|
|
def _cat_ax_xml(self):
|
|
categories = self._chart_data.categories
|
|
|
|
if categories.are_dates:
|
|
return (
|
|
" <c:dateAx>\n"
|
|
' <c:axId val="-2101159928"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="b"/>\n'
|
|
' <c:numFmt formatCode="{nf}" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2100718248"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:auto val="1"/>\n'
|
|
' <c:lblOffset val="100"/>\n'
|
|
' <c:baseTimeUnit val="days"/>\n'
|
|
" </c:dateAx>\n"
|
|
).format(**{"nf": categories.number_format})
|
|
|
|
return (
|
|
" <c:catAx>\n"
|
|
' <c:axId val="-2101159928"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="b"/>\n'
|
|
' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2100718248"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:auto val="1"/>\n'
|
|
' <c:lblAlgn val="ctr"/>\n'
|
|
' <c:lblOffset val="100"/>\n'
|
|
' <c:noMultiLvlLbl val="0"/>\n'
|
|
" </c:catAx>\n"
|
|
)
|
|
|
|
@property
|
|
def _grouping_xml(self):
|
|
val = {
|
|
XL_CHART_TYPE.AREA: "standard",
|
|
XL_CHART_TYPE.AREA_STACKED: "stacked",
|
|
XL_CHART_TYPE.AREA_STACKED_100: "percentStacked",
|
|
}[self._chart_type]
|
|
return ' <c:grouping val="%s"/>\n' % val
|
|
|
|
@property
|
|
def _ser_xml(self):
|
|
xml = ""
|
|
for series in self._chart_data:
|
|
xml_writer = _CategorySeriesXmlWriter(series)
|
|
xml += (
|
|
" <c:ser>\n"
|
|
' <c:idx val="{ser_idx}"/>\n'
|
|
' <c:order val="{ser_order}"/>\n'
|
|
"{tx_xml}"
|
|
"{cat_xml}"
|
|
"{val_xml}"
|
|
" </c:ser>\n"
|
|
).format(
|
|
**{
|
|
"ser_idx": series.index,
|
|
"ser_order": series.index,
|
|
"tx_xml": xml_writer.tx_xml,
|
|
"cat_xml": xml_writer.cat_xml,
|
|
"val_xml": xml_writer.val_xml,
|
|
}
|
|
)
|
|
return xml
|
|
|
|
|
|
class _BarChartXmlWriter(_BaseChartXmlWriter):
|
|
"""
|
|
Provides specialized methods particular to the ``<c:barChart>`` element.
|
|
"""
|
|
|
|
@property
|
|
def xml(self):
|
|
return (
|
|
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
|
|
'<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
|
|
'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
|
|
'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
|
|
'iceDocument/2006/relationships">\n'
|
|
' <c:date1904 val="0"/>\n'
|
|
" <c:chart>\n"
|
|
" <c:plotArea>\n"
|
|
" <c:barChart>\n"
|
|
"{barDir_xml}"
|
|
"{grouping_xml}"
|
|
"{ser_xml}"
|
|
"{overlap_xml}"
|
|
' <c:axId val="-2068027336"/>\n'
|
|
' <c:axId val="-2113994440"/>\n'
|
|
" </c:barChart>\n"
|
|
"{cat_ax_xml}"
|
|
" <c:valAx>\n"
|
|
' <c:axId val="-2113994440"/>\n'
|
|
" <c:scaling/>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="{val_ax_pos}"/>\n'
|
|
" <c:majorGridlines/>\n"
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2068027336"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
" </c:valAx>\n"
|
|
" </c:plotArea>\n"
|
|
' <c:dispBlanksAs val="gap"/>\n'
|
|
" </c:chart>\n"
|
|
" <c:txPr>\n"
|
|
" <a:bodyPr/>\n"
|
|
" <a:lstStyle/>\n"
|
|
" <a:p>\n"
|
|
" <a:pPr>\n"
|
|
' <a:defRPr sz="1800"/>\n'
|
|
" </a:pPr>\n"
|
|
' <a:endParaRPr lang="en-US"/>\n'
|
|
" </a:p>\n"
|
|
" </c:txPr>\n"
|
|
"</c:chartSpace>\n"
|
|
).format(
|
|
**{
|
|
"barDir_xml": self._barDir_xml,
|
|
"grouping_xml": self._grouping_xml,
|
|
"ser_xml": self._ser_xml,
|
|
"overlap_xml": self._overlap_xml,
|
|
"cat_ax_xml": self._cat_ax_xml,
|
|
"val_ax_pos": self._val_ax_pos,
|
|
}
|
|
)
|
|
|
|
@property
|
|
def _barDir_xml(self):
|
|
XL = XL_CHART_TYPE
|
|
bar_types = (XL.BAR_CLUSTERED, XL.BAR_STACKED, XL.BAR_STACKED_100)
|
|
col_types = (XL.COLUMN_CLUSTERED, XL.COLUMN_STACKED, XL.COLUMN_STACKED_100)
|
|
if self._chart_type in bar_types:
|
|
return ' <c:barDir val="bar"/>\n'
|
|
elif self._chart_type in col_types:
|
|
return ' <c:barDir val="col"/>\n'
|
|
raise NotImplementedError(
|
|
"no _barDir_xml() for chart type %s" % self._chart_type
|
|
)
|
|
|
|
@property
|
|
def _cat_ax_pos(self):
|
|
return {
|
|
XL_CHART_TYPE.BAR_CLUSTERED: "l",
|
|
XL_CHART_TYPE.BAR_STACKED: "l",
|
|
XL_CHART_TYPE.BAR_STACKED_100: "l",
|
|
XL_CHART_TYPE.COLUMN_CLUSTERED: "b",
|
|
XL_CHART_TYPE.COLUMN_STACKED: "b",
|
|
XL_CHART_TYPE.COLUMN_STACKED_100: "b",
|
|
}[self._chart_type]
|
|
|
|
@property
|
|
def _cat_ax_xml(self):
|
|
categories = self._chart_data.categories
|
|
|
|
if categories.are_dates:
|
|
return (
|
|
" <c:dateAx>\n"
|
|
' <c:axId val="-2068027336"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="{cat_ax_pos}"/>\n'
|
|
' <c:numFmt formatCode="{nf}" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2113994440"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:auto val="1"/>\n'
|
|
' <c:lblOffset val="100"/>\n'
|
|
' <c:baseTimeUnit val="days"/>\n'
|
|
" </c:dateAx>\n"
|
|
).format(**{"cat_ax_pos": self._cat_ax_pos, "nf": categories.number_format})
|
|
|
|
return (
|
|
" <c:catAx>\n"
|
|
' <c:axId val="-2068027336"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="{cat_ax_pos}"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2113994440"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:auto val="1"/>\n'
|
|
' <c:lblAlgn val="ctr"/>\n'
|
|
' <c:lblOffset val="100"/>\n'
|
|
' <c:noMultiLvlLbl val="0"/>\n'
|
|
" </c:catAx>\n"
|
|
).format(**{"cat_ax_pos": self._cat_ax_pos})
|
|
|
|
@property
|
|
def _grouping_xml(self):
|
|
XL = XL_CHART_TYPE
|
|
clustered_types = (XL.BAR_CLUSTERED, XL.COLUMN_CLUSTERED)
|
|
stacked_types = (XL.BAR_STACKED, XL.COLUMN_STACKED)
|
|
percentStacked_types = (XL.BAR_STACKED_100, XL.COLUMN_STACKED_100)
|
|
if self._chart_type in clustered_types:
|
|
return ' <c:grouping val="clustered"/>\n'
|
|
elif self._chart_type in stacked_types:
|
|
return ' <c:grouping val="stacked"/>\n'
|
|
elif self._chart_type in percentStacked_types:
|
|
return ' <c:grouping val="percentStacked"/>\n'
|
|
raise NotImplementedError(
|
|
"no _grouping_xml() for chart type %s" % self._chart_type
|
|
)
|
|
|
|
@property
|
|
def _overlap_xml(self):
|
|
XL = XL_CHART_TYPE
|
|
percentStacked_types = (
|
|
XL.BAR_STACKED,
|
|
XL.BAR_STACKED_100,
|
|
XL.COLUMN_STACKED,
|
|
XL.COLUMN_STACKED_100,
|
|
)
|
|
if self._chart_type in percentStacked_types:
|
|
return ' <c:overlap val="100"/>\n'
|
|
return ""
|
|
|
|
@property
|
|
def _ser_xml(self):
|
|
xml = ""
|
|
for series in self._chart_data:
|
|
xml_writer = _CategorySeriesXmlWriter(series)
|
|
xml += (
|
|
" <c:ser>\n"
|
|
' <c:idx val="{ser_idx}"/>\n'
|
|
' <c:order val="{ser_order}"/>\n'
|
|
"{tx_xml}"
|
|
"{cat_xml}"
|
|
"{val_xml}"
|
|
" </c:ser>\n"
|
|
).format(
|
|
**{
|
|
"ser_idx": series.index,
|
|
"ser_order": series.index,
|
|
"tx_xml": xml_writer.tx_xml,
|
|
"cat_xml": xml_writer.cat_xml,
|
|
"val_xml": xml_writer.val_xml,
|
|
}
|
|
)
|
|
return xml
|
|
|
|
@property
|
|
def _val_ax_pos(self):
|
|
return {
|
|
XL_CHART_TYPE.BAR_CLUSTERED: "b",
|
|
XL_CHART_TYPE.BAR_STACKED: "b",
|
|
XL_CHART_TYPE.BAR_STACKED_100: "b",
|
|
XL_CHART_TYPE.COLUMN_CLUSTERED: "l",
|
|
XL_CHART_TYPE.COLUMN_STACKED: "l",
|
|
XL_CHART_TYPE.COLUMN_STACKED_100: "l",
|
|
}[self._chart_type]
|
|
|
|
|
|
class _DoughnutChartXmlWriter(_BaseChartXmlWriter):
|
|
"""
|
|
Provides specialized methods particular to the ``<c:doughnutChart>``
|
|
element.
|
|
"""
|
|
|
|
@property
|
|
def xml(self):
|
|
return (
|
|
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
|
|
'<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
|
|
'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
|
|
'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
|
|
'iceDocument/2006/relationships">\n'
|
|
' <c:date1904 val="0"/>\n'
|
|
' <c:roundedCorners val="0"/>\n'
|
|
" <c:chart>\n"
|
|
' <c:autoTitleDeleted val="0"/>\n'
|
|
" <c:plotArea>\n"
|
|
" <c:layout/>\n"
|
|
" <c:doughnutChart>\n"
|
|
' <c:varyColors val="1"/>\n'
|
|
"{ser_xml}"
|
|
" <c:dLbls>\n"
|
|
' <c:showLegendKey val="0"/>\n'
|
|
' <c:showVal val="0"/>\n'
|
|
' <c:showCatName val="0"/>\n'
|
|
' <c:showSerName val="0"/>\n'
|
|
' <c:showPercent val="0"/>\n'
|
|
' <c:showBubbleSize val="0"/>\n'
|
|
' <c:showLeaderLines val="1"/>\n'
|
|
" </c:dLbls>\n"
|
|
' <c:firstSliceAng val="0"/>\n'
|
|
' <c:holeSize val="50"/>\n'
|
|
" </c:doughnutChart>\n"
|
|
" </c:plotArea>\n"
|
|
" <c:legend>\n"
|
|
' <c:legendPos val="r"/>\n'
|
|
" <c:layout/>\n"
|
|
' <c:overlay val="0"/>\n'
|
|
" </c:legend>\n"
|
|
' <c:plotVisOnly val="1"/>\n'
|
|
' <c:dispBlanksAs val="gap"/>\n'
|
|
' <c:showDLblsOverMax val="0"/>\n'
|
|
" </c:chart>\n"
|
|
" <c:txPr>\n"
|
|
" <a:bodyPr/>\n"
|
|
" <a:lstStyle/>\n"
|
|
" <a:p>\n"
|
|
" <a:pPr>\n"
|
|
' <a:defRPr sz="1800"/>\n'
|
|
" </a:pPr>\n"
|
|
" <a:endParaRPr/>\n"
|
|
" </a:p>\n"
|
|
" </c:txPr>\n"
|
|
"</c:chartSpace>\n"
|
|
).format(**{"ser_xml": self._ser_xml})
|
|
|
|
@property
|
|
def _explosion_xml(self):
|
|
if self._chart_type == XL_CHART_TYPE.DOUGHNUT_EXPLODED:
|
|
return ' <c:explosion val="25"/>\n'
|
|
return ""
|
|
|
|
@property
|
|
def _ser_xml(self):
|
|
xml = ""
|
|
for series in self._chart_data:
|
|
xml_writer = _CategorySeriesXmlWriter(series)
|
|
xml += (
|
|
" <c:ser>\n"
|
|
' <c:idx val="{ser_idx}"/>\n'
|
|
' <c:order val="{ser_order}"/>\n'
|
|
"{tx_xml}"
|
|
"{explosion_xml}"
|
|
"{cat_xml}"
|
|
"{val_xml}"
|
|
" </c:ser>\n"
|
|
).format(
|
|
**{
|
|
"ser_idx": series.index,
|
|
"ser_order": series.index,
|
|
"tx_xml": xml_writer.tx_xml,
|
|
"explosion_xml": self._explosion_xml,
|
|
"cat_xml": xml_writer.cat_xml,
|
|
"val_xml": xml_writer.val_xml,
|
|
}
|
|
)
|
|
return xml
|
|
|
|
|
|
class _LineChartXmlWriter(_BaseChartXmlWriter):
|
|
"""
|
|
Provides specialized methods particular to the ``<c:lineChart>`` element.
|
|
"""
|
|
|
|
@property
|
|
def xml(self):
|
|
return (
|
|
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
|
|
'<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
|
|
'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
|
|
'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
|
|
'iceDocument/2006/relationships">\n'
|
|
' <c:date1904 val="0"/>\n'
|
|
" <c:chart>\n"
|
|
' <c:autoTitleDeleted val="0"/>\n'
|
|
" <c:plotArea>\n"
|
|
" <c:lineChart>\n"
|
|
"{grouping_xml}"
|
|
' <c:varyColors val="0"/>\n'
|
|
"{ser_xml}"
|
|
' <c:marker val="1"/>\n'
|
|
' <c:smooth val="0"/>\n'
|
|
' <c:axId val="2118791784"/>\n'
|
|
' <c:axId val="2140495176"/>\n'
|
|
" </c:lineChart>\n"
|
|
"{cat_ax_xml}"
|
|
" <c:valAx>\n"
|
|
' <c:axId val="2140495176"/>\n'
|
|
" <c:scaling/>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="l"/>\n'
|
|
" <c:majorGridlines/>\n"
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="2118791784"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
" </c:valAx>\n"
|
|
" </c:plotArea>\n"
|
|
" <c:legend>\n"
|
|
' <c:legendPos val="r"/>\n'
|
|
" <c:layout/>\n"
|
|
' <c:overlay val="0"/>\n'
|
|
" </c:legend>\n"
|
|
' <c:plotVisOnly val="1"/>\n'
|
|
' <c:dispBlanksAs val="gap"/>\n'
|
|
' <c:showDLblsOverMax val="0"/>\n'
|
|
" </c:chart>\n"
|
|
" <c:txPr>\n"
|
|
" <a:bodyPr/>\n"
|
|
" <a:lstStyle/>\n"
|
|
" <a:p>\n"
|
|
" <a:pPr>\n"
|
|
' <a:defRPr sz="1800"/>\n'
|
|
" </a:pPr>\n"
|
|
' <a:endParaRPr lang="en-US"/>\n'
|
|
" </a:p>\n"
|
|
" </c:txPr>\n"
|
|
"</c:chartSpace>\n"
|
|
).format(
|
|
**{
|
|
"grouping_xml": self._grouping_xml,
|
|
"ser_xml": self._ser_xml,
|
|
"cat_ax_xml": self._cat_ax_xml,
|
|
}
|
|
)
|
|
|
|
@property
|
|
def _cat_ax_xml(self):
|
|
categories = self._chart_data.categories
|
|
|
|
if categories.are_dates:
|
|
return (
|
|
" <c:dateAx>\n"
|
|
' <c:axId val="2118791784"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="b"/>\n'
|
|
' <c:numFmt formatCode="{nf}" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="2140495176"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:auto val="1"/>\n'
|
|
' <c:lblOffset val="100"/>\n'
|
|
' <c:baseTimeUnit val="days"/>\n'
|
|
" </c:dateAx>\n"
|
|
).format(**{"nf": categories.number_format})
|
|
|
|
return (
|
|
" <c:catAx>\n"
|
|
' <c:axId val="2118791784"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="b"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="2140495176"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:auto val="1"/>\n'
|
|
' <c:lblAlgn val="ctr"/>\n'
|
|
' <c:lblOffset val="100"/>\n'
|
|
' <c:noMultiLvlLbl val="0"/>\n'
|
|
" </c:catAx>\n"
|
|
)
|
|
|
|
@property
|
|
def _grouping_xml(self):
|
|
XL = XL_CHART_TYPE
|
|
standard_types = (XL.LINE, XL.LINE_MARKERS)
|
|
stacked_types = (XL.LINE_STACKED, XL.LINE_MARKERS_STACKED)
|
|
percentStacked_types = (XL.LINE_STACKED_100, XL.LINE_MARKERS_STACKED_100)
|
|
if self._chart_type in standard_types:
|
|
return ' <c:grouping val="standard"/>\n'
|
|
elif self._chart_type in stacked_types:
|
|
return ' <c:grouping val="stacked"/>\n'
|
|
elif self._chart_type in percentStacked_types:
|
|
return ' <c:grouping val="percentStacked"/>\n'
|
|
raise NotImplementedError(
|
|
"no _grouping_xml() for chart type %s" % self._chart_type
|
|
)
|
|
|
|
@property
|
|
def _marker_xml(self):
|
|
XL = XL_CHART_TYPE
|
|
no_marker_types = (XL.LINE, XL.LINE_STACKED, XL.LINE_STACKED_100)
|
|
if self._chart_type in no_marker_types:
|
|
return (
|
|
" <c:marker>\n"
|
|
' <c:symbol val="none"/>\n'
|
|
" </c:marker>\n"
|
|
)
|
|
return ""
|
|
|
|
@property
|
|
def _ser_xml(self):
|
|
xml = ""
|
|
for series in self._chart_data:
|
|
xml_writer = _CategorySeriesXmlWriter(series)
|
|
xml += (
|
|
" <c:ser>\n"
|
|
' <c:idx val="{ser_idx}"/>\n'
|
|
' <c:order val="{ser_order}"/>\n'
|
|
"{tx_xml}"
|
|
"{marker_xml}"
|
|
"{cat_xml}"
|
|
"{val_xml}"
|
|
' <c:smooth val="0"/>\n'
|
|
" </c:ser>\n"
|
|
).format(
|
|
**{
|
|
"ser_idx": series.index,
|
|
"ser_order": series.index,
|
|
"tx_xml": xml_writer.tx_xml,
|
|
"marker_xml": self._marker_xml,
|
|
"cat_xml": xml_writer.cat_xml,
|
|
"val_xml": xml_writer.val_xml,
|
|
}
|
|
)
|
|
return xml
|
|
|
|
|
|
class _PieChartXmlWriter(_BaseChartXmlWriter):
|
|
"""
|
|
Provides specialized methods particular to the ``<c:pieChart>`` element.
|
|
"""
|
|
|
|
@property
|
|
def xml(self):
|
|
return (
|
|
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
|
|
'<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
|
|
'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
|
|
'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
|
|
'iceDocument/2006/relationships">\n'
|
|
" <c:chart>\n"
|
|
" <c:plotArea>\n"
|
|
" <c:pieChart>\n"
|
|
' <c:varyColors val="1"/>\n'
|
|
"{ser_xml}"
|
|
" </c:pieChart>\n"
|
|
" </c:plotArea>\n"
|
|
' <c:dispBlanksAs val="gap"/>\n'
|
|
" </c:chart>\n"
|
|
" <c:txPr>\n"
|
|
" <a:bodyPr/>\n"
|
|
" <a:lstStyle/>\n"
|
|
" <a:p>\n"
|
|
" <a:pPr>\n"
|
|
' <a:defRPr sz="1800"/>\n'
|
|
" </a:pPr>\n"
|
|
' <a:endParaRPr lang="en-US"/>\n'
|
|
" </a:p>\n"
|
|
" </c:txPr>\n"
|
|
"</c:chartSpace>\n"
|
|
).format(**{"ser_xml": self._ser_xml})
|
|
|
|
@property
|
|
def _explosion_xml(self):
|
|
if self._chart_type == XL_CHART_TYPE.PIE_EXPLODED:
|
|
return ' <c:explosion val="25"/>\n'
|
|
return ""
|
|
|
|
@property
|
|
def _ser_xml(self):
|
|
xml_writer = _CategorySeriesXmlWriter(self._chart_data[0])
|
|
xml = (
|
|
" <c:ser>\n"
|
|
' <c:idx val="0"/>\n'
|
|
' <c:order val="0"/>\n'
|
|
"{tx_xml}"
|
|
"{explosion_xml}"
|
|
"{cat_xml}"
|
|
"{val_xml}"
|
|
" </c:ser>\n"
|
|
).format(
|
|
**{
|
|
"tx_xml": xml_writer.tx_xml,
|
|
"explosion_xml": self._explosion_xml,
|
|
"cat_xml": xml_writer.cat_xml,
|
|
"val_xml": xml_writer.val_xml,
|
|
}
|
|
)
|
|
return xml
|
|
|
|
|
|
class _RadarChartXmlWriter(_BaseChartXmlWriter):
|
|
"""
|
|
Generates XML for the ``<c:radarChart>`` element.
|
|
"""
|
|
|
|
@property
|
|
def xml(self):
|
|
return (
|
|
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
|
|
'<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
|
|
'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
|
|
'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
|
|
'iceDocument/2006/relationships">\n'
|
|
' <c:date1904 val="0"/>\n'
|
|
' <c:roundedCorners val="0"/>\n'
|
|
' <mc:AlternateContent xmlns:mc="http://schemas.openxmlformats.'
|
|
'org/markup-compatibility/2006">\n'
|
|
' <mc:Choice xmlns:c14="http://schemas.microsoft.com/office/d'
|
|
'rawing/2007/8/2/chart" Requires="c14">\n'
|
|
' <c14:style val="118"/>\n'
|
|
" </mc:Choice>\n"
|
|
" <mc:Fallback>\n"
|
|
' <c:style val="18"/>\n'
|
|
" </mc:Fallback>\n"
|
|
" </mc:AlternateContent>\n"
|
|
" <c:chart>\n"
|
|
" <c:plotArea>\n"
|
|
" <c:layout/>\n"
|
|
" <c:radarChart>\n"
|
|
' <c:radarStyle val="{radar_style}"/>\n'
|
|
' <c:varyColors val="0"/>\n'
|
|
"{ser_xml}"
|
|
' <c:axId val="2073612648"/>\n'
|
|
' <c:axId val="-2112772216"/>\n'
|
|
" </c:radarChart>\n"
|
|
" <c:catAx>\n"
|
|
' <c:axId val="2073612648"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="b"/>\n'
|
|
" <c:majorGridlines/>\n"
|
|
' <c:numFmt formatCode="m/d/yy" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2112772216"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:auto val="1"/>\n'
|
|
' <c:lblAlgn val="ctr"/>\n'
|
|
' <c:lblOffset val="100"/>\n'
|
|
' <c:noMultiLvlLbl val="0"/>\n'
|
|
" </c:catAx>\n"
|
|
" <c:valAx>\n"
|
|
' <c:axId val="-2112772216"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="l"/>\n'
|
|
" <c:majorGridlines/>\n"
|
|
' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="cross"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="2073612648"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:crossBetween val="between"/>\n'
|
|
" </c:valAx>\n"
|
|
" </c:plotArea>\n"
|
|
' <c:plotVisOnly val="1"/>\n'
|
|
' <c:dispBlanksAs val="gap"/>\n'
|
|
' <c:showDLblsOverMax val="0"/>\n'
|
|
" </c:chart>\n"
|
|
" <c:txPr>\n"
|
|
" <a:bodyPr/>\n"
|
|
" <a:lstStyle/>\n"
|
|
" <a:p>\n"
|
|
" <a:pPr>\n"
|
|
' <a:defRPr sz="1800"/>\n'
|
|
" </a:pPr>\n"
|
|
' <a:endParaRPr lang="en-US"/>\n'
|
|
" </a:p>\n"
|
|
" </c:txPr>\n"
|
|
"</c:chartSpace>\n"
|
|
).format(**{"radar_style": self._radar_style, "ser_xml": self._ser_xml})
|
|
|
|
@property
|
|
def _marker_xml(self):
|
|
if self._chart_type == XL_CHART_TYPE.RADAR:
|
|
return (
|
|
" <c:marker>\n"
|
|
' <c:symbol val="none"/>\n'
|
|
" </c:marker>\n"
|
|
)
|
|
return ""
|
|
|
|
@property
|
|
def _radar_style(self):
|
|
if self._chart_type == XL_CHART_TYPE.RADAR_FILLED:
|
|
return "filled"
|
|
return "marker"
|
|
|
|
@property
|
|
def _ser_xml(self):
|
|
xml = ""
|
|
for series in self._chart_data:
|
|
xml_writer = _CategorySeriesXmlWriter(series)
|
|
xml += (
|
|
" <c:ser>\n"
|
|
' <c:idx val="{ser_idx}"/>\n'
|
|
' <c:order val="{ser_order}"/>\n'
|
|
"{tx_xml}"
|
|
"{marker_xml}"
|
|
"{cat_xml}"
|
|
"{val_xml}"
|
|
' <c:smooth val="0"/>\n'
|
|
" </c:ser>\n"
|
|
).format(
|
|
**{
|
|
"ser_idx": series.index,
|
|
"ser_order": series.index,
|
|
"tx_xml": xml_writer.tx_xml,
|
|
"marker_xml": self._marker_xml,
|
|
"cat_xml": xml_writer.cat_xml,
|
|
"val_xml": xml_writer.val_xml,
|
|
}
|
|
)
|
|
return xml
|
|
|
|
|
|
class _XyChartXmlWriter(_BaseChartXmlWriter):
|
|
"""
|
|
Generates XML for the ``<c:scatterChart>`` element.
|
|
"""
|
|
|
|
@property
|
|
def xml(self):
|
|
xml = (
|
|
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
|
|
'<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
|
|
'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
|
|
'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
|
|
'iceDocument/2006/relationships">\n'
|
|
" <c:chart>\n"
|
|
" <c:plotArea>\n"
|
|
" <c:scatterChart>\n"
|
|
' <c:scatterStyle val="%s"/>\n'
|
|
' <c:varyColors val="0"/>\n'
|
|
"%s"
|
|
' <c:axId val="-2128940872"/>\n'
|
|
' <c:axId val="-2129643912"/>\n'
|
|
" </c:scatterChart>\n"
|
|
" <c:valAx>\n"
|
|
' <c:axId val="-2128940872"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="b"/>\n'
|
|
' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2129643912"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:crossBetween val="midCat"/>\n'
|
|
" </c:valAx>\n"
|
|
" <c:valAx>\n"
|
|
' <c:axId val="-2129643912"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="l"/>\n'
|
|
" <c:majorGridlines/>\n"
|
|
' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2128940872"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:crossBetween val="midCat"/>\n'
|
|
" </c:valAx>\n"
|
|
" </c:plotArea>\n"
|
|
" <c:legend>\n"
|
|
' <c:legendPos val="r"/>\n'
|
|
" <c:layout/>\n"
|
|
' <c:overlay val="0"/>\n'
|
|
" </c:legend>\n"
|
|
' <c:plotVisOnly val="1"/>\n'
|
|
' <c:dispBlanksAs val="gap"/>\n'
|
|
' <c:showDLblsOverMax val="0"/>\n'
|
|
" </c:chart>\n"
|
|
" <c:txPr>\n"
|
|
" <a:bodyPr/>\n"
|
|
" <a:lstStyle/>\n"
|
|
" <a:p>\n"
|
|
" <a:pPr>\n"
|
|
' <a:defRPr sz="1800"/>\n'
|
|
" </a:pPr>\n"
|
|
' <a:endParaRPr lang="en-US"/>\n'
|
|
" </a:p>\n"
|
|
" </c:txPr>\n"
|
|
"</c:chartSpace>\n"
|
|
) % (self._scatterStyle_val, self._ser_xml)
|
|
return xml
|
|
|
|
@property
|
|
def _marker_xml(self):
|
|
no_marker_types = (
|
|
XL_CHART_TYPE.XY_SCATTER_LINES_NO_MARKERS,
|
|
XL_CHART_TYPE.XY_SCATTER_SMOOTH_NO_MARKERS,
|
|
)
|
|
if self._chart_type in no_marker_types:
|
|
return (
|
|
" <c:marker>\n"
|
|
' <c:symbol val="none"/>\n'
|
|
" </c:marker>\n"
|
|
)
|
|
return ""
|
|
|
|
@property
|
|
def _scatterStyle_val(self):
|
|
smooth_types = (
|
|
XL_CHART_TYPE.XY_SCATTER_SMOOTH,
|
|
XL_CHART_TYPE.XY_SCATTER_SMOOTH_NO_MARKERS,
|
|
)
|
|
if self._chart_type in smooth_types:
|
|
return "smoothMarker"
|
|
return "lineMarker"
|
|
|
|
@property
|
|
def _ser_xml(self):
|
|
xml = ""
|
|
for series in self._chart_data:
|
|
xml_writer = _XySeriesXmlWriter(series)
|
|
xml += (
|
|
" <c:ser>\n"
|
|
' <c:idx val="{ser_idx}"/>\n'
|
|
' <c:order val="{ser_order}"/>\n'
|
|
"{tx_xml}"
|
|
"{spPr_xml}"
|
|
"{marker_xml}"
|
|
"{xVal_xml}"
|
|
"{yVal_xml}"
|
|
' <c:smooth val="0"/>\n'
|
|
" </c:ser>\n"
|
|
).format(
|
|
**{
|
|
"ser_idx": series.index,
|
|
"ser_order": series.index,
|
|
"tx_xml": xml_writer.tx_xml,
|
|
"spPr_xml": self._spPr_xml,
|
|
"marker_xml": self._marker_xml,
|
|
"xVal_xml": xml_writer.xVal_xml,
|
|
"yVal_xml": xml_writer.yVal_xml,
|
|
}
|
|
)
|
|
return xml
|
|
|
|
@property
|
|
def _spPr_xml(self):
|
|
if self._chart_type == XL_CHART_TYPE.XY_SCATTER:
|
|
return (
|
|
" <c:spPr>\n"
|
|
' <a:ln w="47625">\n'
|
|
" <a:noFill/>\n"
|
|
" </a:ln>\n"
|
|
" </c:spPr>\n"
|
|
)
|
|
return ""
|
|
|
|
|
|
class _BubbleChartXmlWriter(_XyChartXmlWriter):
|
|
"""
|
|
Provides specialized methods particular to the ``<c:bubbleChart>``
|
|
element.
|
|
"""
|
|
|
|
@property
|
|
def xml(self):
|
|
xml = (
|
|
"<?xml version='1.0' encoding='UTF-8' standalone='yes'?>\n"
|
|
'<c:chartSpace xmlns:c="http://schemas.openxmlformats.org/drawin'
|
|
'gml/2006/chart" xmlns:a="http://schemas.openxmlformats.org/draw'
|
|
'ingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/off'
|
|
'iceDocument/2006/relationships">\n'
|
|
" <c:chart>\n"
|
|
' <c:autoTitleDeleted val="0"/>\n'
|
|
" <c:plotArea>\n"
|
|
" <c:layout/>\n"
|
|
" <c:bubbleChart>\n"
|
|
' <c:varyColors val="0"/>\n'
|
|
"%s"
|
|
" <c:dLbls>\n"
|
|
' <c:showLegendKey val="0"/>\n'
|
|
' <c:showVal val="0"/>\n'
|
|
' <c:showCatName val="0"/>\n'
|
|
' <c:showSerName val="0"/>\n'
|
|
' <c:showPercent val="0"/>\n'
|
|
' <c:showBubbleSize val="0"/>\n'
|
|
" </c:dLbls>\n"
|
|
' <c:bubbleScale val="100"/>\n'
|
|
' <c:showNegBubbles val="0"/>\n'
|
|
' <c:axId val="-2115720072"/>\n'
|
|
' <c:axId val="-2115723560"/>\n'
|
|
" </c:bubbleChart>\n"
|
|
" <c:valAx>\n"
|
|
' <c:axId val="-2115720072"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="b"/>\n'
|
|
' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2115723560"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:crossBetween val="midCat"/>\n'
|
|
" </c:valAx>\n"
|
|
" <c:valAx>\n"
|
|
' <c:axId val="-2115723560"/>\n'
|
|
" <c:scaling>\n"
|
|
' <c:orientation val="minMax"/>\n'
|
|
" </c:scaling>\n"
|
|
' <c:delete val="0"/>\n'
|
|
' <c:axPos val="l"/>\n'
|
|
" <c:majorGridlines/>\n"
|
|
' <c:numFmt formatCode="General" sourceLinked="1"/>\n'
|
|
' <c:majorTickMark val="out"/>\n'
|
|
' <c:minorTickMark val="none"/>\n'
|
|
' <c:tickLblPos val="nextTo"/>\n'
|
|
' <c:crossAx val="-2115720072"/>\n'
|
|
' <c:crosses val="autoZero"/>\n'
|
|
' <c:crossBetween val="midCat"/>\n'
|
|
" </c:valAx>\n"
|
|
" </c:plotArea>\n"
|
|
" <c:legend>\n"
|
|
' <c:legendPos val="r"/>\n'
|
|
" <c:layout/>\n"
|
|
' <c:overlay val="0"/>\n'
|
|
" </c:legend>\n"
|
|
' <c:plotVisOnly val="1"/>\n'
|
|
' <c:dispBlanksAs val="gap"/>\n'
|
|
' <c:showDLblsOverMax val="0"/>\n'
|
|
" </c:chart>\n"
|
|
" <c:txPr>\n"
|
|
" <a:bodyPr/>\n"
|
|
" <a:lstStyle/>\n"
|
|
" <a:p>\n"
|
|
" <a:pPr>\n"
|
|
' <a:defRPr sz="1800"/>\n'
|
|
" </a:pPr>\n"
|
|
' <a:endParaRPr lang="en-US"/>\n'
|
|
" </a:p>\n"
|
|
" </c:txPr>\n"
|
|
"</c:chartSpace>\n"
|
|
) % self._ser_xml
|
|
return xml
|
|
|
|
@property
|
|
def _bubble3D_val(self):
|
|
if self._chart_type == XL_CHART_TYPE.BUBBLE_THREE_D_EFFECT:
|
|
return "1"
|
|
return "0"
|
|
|
|
@property
|
|
def _ser_xml(self):
|
|
xml = ""
|
|
for series in self._chart_data:
|
|
xml_writer = _BubbleSeriesXmlWriter(series)
|
|
xml += (
|
|
" <c:ser>\n"
|
|
' <c:idx val="{ser_idx}"/>\n'
|
|
' <c:order val="{ser_order}"/>\n'
|
|
"{tx_xml}"
|
|
' <c:invertIfNegative val="0"/>\n'
|
|
"{xVal_xml}"
|
|
"{yVal_xml}"
|
|
"{bubbleSize_xml}"
|
|
' <c:bubble3D val="{bubble3D_val}"/>\n'
|
|
" </c:ser>\n"
|
|
).format(
|
|
**{
|
|
"ser_idx": series.index,
|
|
"ser_order": series.index,
|
|
"tx_xml": xml_writer.tx_xml,
|
|
"xVal_xml": xml_writer.xVal_xml,
|
|
"yVal_xml": xml_writer.yVal_xml,
|
|
"bubbleSize_xml": xml_writer.bubbleSize_xml,
|
|
"bubble3D_val": self._bubble3D_val,
|
|
}
|
|
)
|
|
return xml
|
|
|
|
|
|
class _CategorySeriesXmlWriter(_BaseSeriesXmlWriter):
|
|
"""
|
|
Generates XML snippets particular to a category chart series.
|
|
"""
|
|
|
|
@property
|
|
def cat(self):
|
|
"""
|
|
Return the ``<c:cat>`` element XML for this series, as an oxml
|
|
element.
|
|
"""
|
|
categories = self._series.categories
|
|
|
|
if categories.are_numeric:
|
|
return parse_xml(
|
|
self._numRef_cat_tmpl.format(
|
|
**{
|
|
"wksht_ref": self._series.categories_ref,
|
|
"number_format": categories.number_format,
|
|
"cat_count": categories.leaf_count,
|
|
"cat_pt_xml": self._cat_num_pt_xml,
|
|
"nsdecls": " %s" % nsdecls("c"),
|
|
}
|
|
)
|
|
)
|
|
|
|
if categories.depth == 1:
|
|
return parse_xml(
|
|
self._cat_tmpl.format(
|
|
**{
|
|
"wksht_ref": self._series.categories_ref,
|
|
"cat_count": categories.leaf_count,
|
|
"cat_pt_xml": self._cat_pt_xml,
|
|
"nsdecls": " %s" % nsdecls("c"),
|
|
}
|
|
)
|
|
)
|
|
|
|
return parse_xml(
|
|
self._multiLvl_cat_tmpl.format(
|
|
**{
|
|
"wksht_ref": self._series.categories_ref,
|
|
"cat_count": categories.leaf_count,
|
|
"lvl_xml": self._lvl_xml(categories),
|
|
"nsdecls": " %s" % nsdecls("c"),
|
|
}
|
|
)
|
|
)
|
|
|
|
@property
|
|
def cat_xml(self):
|
|
"""
|
|
The unicode XML snippet for the ``<c:cat>`` element for this series,
|
|
containing the category labels and spreadsheet reference.
|
|
"""
|
|
categories = self._series.categories
|
|
|
|
if categories.are_numeric:
|
|
return self._numRef_cat_tmpl.format(
|
|
**{
|
|
"wksht_ref": self._series.categories_ref,
|
|
"number_format": categories.number_format,
|
|
"cat_count": categories.leaf_count,
|
|
"cat_pt_xml": self._cat_num_pt_xml,
|
|
"nsdecls": "",
|
|
}
|
|
)
|
|
|
|
if categories.depth == 1:
|
|
return self._cat_tmpl.format(
|
|
**{
|
|
"wksht_ref": self._series.categories_ref,
|
|
"cat_count": categories.leaf_count,
|
|
"cat_pt_xml": self._cat_pt_xml,
|
|
"nsdecls": "",
|
|
}
|
|
)
|
|
|
|
return self._multiLvl_cat_tmpl.format(
|
|
**{
|
|
"wksht_ref": self._series.categories_ref,
|
|
"cat_count": categories.leaf_count,
|
|
"lvl_xml": self._lvl_xml(categories),
|
|
"nsdecls": "",
|
|
}
|
|
)
|
|
|
|
@property
|
|
def val(self):
|
|
"""
|
|
The ``<c:val>`` XML for this series, as an oxml element.
|
|
"""
|
|
xml = self._val_tmpl.format(
|
|
**{
|
|
"nsdecls": " %s" % nsdecls("c"),
|
|
"values_ref": self._series.values_ref,
|
|
"number_format": self._series.number_format,
|
|
"val_count": len(self._series),
|
|
"val_pt_xml": self._val_pt_xml,
|
|
}
|
|
)
|
|
return parse_xml(xml)
|
|
|
|
@property
|
|
def val_xml(self):
|
|
"""
|
|
Return the unicode XML snippet for the ``<c:val>`` element describing
|
|
this series, containing the series values and their spreadsheet range
|
|
reference.
|
|
"""
|
|
return self._val_tmpl.format(
|
|
**{
|
|
"nsdecls": "",
|
|
"values_ref": self._series.values_ref,
|
|
"number_format": self._series.number_format,
|
|
"val_count": len(self._series),
|
|
"val_pt_xml": self._val_pt_xml,
|
|
}
|
|
)
|
|
|
|
@property
|
|
def _cat_num_pt_xml(self):
|
|
"""
|
|
The unicode XML snippet for the ``<c:pt>`` elements when category
|
|
labels are numeric (including date type).
|
|
"""
|
|
xml = ""
|
|
for idx, category in enumerate(self._series.categories):
|
|
xml += (
|
|
' <c:pt idx="{cat_idx}">\n'
|
|
" <c:v>{cat_lbl_str}</c:v>\n"
|
|
" </c:pt>\n"
|
|
).format(
|
|
**{
|
|
"cat_idx": idx,
|
|
"cat_lbl_str": category.numeric_str_val(self._date_1904),
|
|
}
|
|
)
|
|
return xml
|
|
|
|
@property
|
|
def _cat_pt_xml(self):
|
|
"""
|
|
The unicode XML snippet for the ``<c:pt>`` elements containing the
|
|
category names for this series.
|
|
"""
|
|
xml = ""
|
|
for idx, category in enumerate(self._series.categories):
|
|
xml += (
|
|
' <c:pt idx="{cat_idx}">\n'
|
|
" <c:v>{cat_label}</c:v>\n"
|
|
" </c:pt>\n"
|
|
).format(
|
|
**{"cat_idx": idx, "cat_label": escape(to_unicode(category.label))}
|
|
)
|
|
return xml
|
|
|
|
@property
|
|
def _cat_tmpl(self):
|
|
"""
|
|
The template for the ``<c:cat>`` element for this series, containing
|
|
the category labels and spreadsheet reference.
|
|
"""
|
|
return (
|
|
" <c:cat{nsdecls}>\n"
|
|
" <c:strRef>\n"
|
|
" <c:f>{wksht_ref}</c:f>\n"
|
|
" <c:strCache>\n"
|
|
' <c:ptCount val="{cat_count}"/>\n'
|
|
"{cat_pt_xml}"
|
|
" </c:strCache>\n"
|
|
" </c:strRef>\n"
|
|
" </c:cat>\n"
|
|
)
|
|
|
|
def _lvl_xml(self, categories):
|
|
"""
|
|
The unicode XML snippet for the ``<c:lvl>`` elements containing
|
|
multi-level category names.
|
|
"""
|
|
|
|
def lvl_pt_xml(level):
|
|
xml = ""
|
|
for idx, name in level:
|
|
xml += (
|
|
' <c:pt idx="%d">\n'
|
|
" <c:v>%s</c:v>\n"
|
|
" </c:pt>\n"
|
|
) % (idx, escape("%s" % name))
|
|
return xml
|
|
|
|
xml = ""
|
|
for level in categories.levels:
|
|
xml += (
|
|
" <c:lvl>\n" "{lvl_pt_xml}" " </c:lvl>\n"
|
|
).format(**{"lvl_pt_xml": lvl_pt_xml(level)})
|
|
return xml
|
|
|
|
@property
|
|
def _multiLvl_cat_tmpl(self):
|
|
"""
|
|
The template for the ``<c:cat>`` element for this series when there
|
|
are multi-level (nested) categories.
|
|
"""
|
|
return (
|
|
" <c:cat{nsdecls}>\n"
|
|
" <c:multiLvlStrRef>\n"
|
|
" <c:f>{wksht_ref}</c:f>\n"
|
|
" <c:multiLvlStrCache>\n"
|
|
' <c:ptCount val="{cat_count}"/>\n'
|
|
"{lvl_xml}"
|
|
" </c:multiLvlStrCache>\n"
|
|
" </c:multiLvlStrRef>\n"
|
|
" </c:cat>\n"
|
|
)
|
|
|
|
@property
|
|
def _numRef_cat_tmpl(self):
|
|
"""
|
|
The template for the ``<c:cat>`` element for this series when the
|
|
labels are numeric (or date) values.
|
|
"""
|
|
return (
|
|
" <c:cat{nsdecls}>\n"
|
|
" <c:numRef>\n"
|
|
" <c:f>{wksht_ref}</c:f>\n"
|
|
" <c:numCache>\n"
|
|
" <c:formatCode>{number_format}</c:formatCode>\n"
|
|
' <c:ptCount val="{cat_count}"/>\n'
|
|
"{cat_pt_xml}"
|
|
" </c:numCache>\n"
|
|
" </c:numRef>\n"
|
|
" </c:cat>\n"
|
|
)
|
|
|
|
@property
|
|
def _val_pt_xml(self):
|
|
"""
|
|
The unicode XML snippet containing the ``<c:pt>`` elements containing
|
|
the values for this series.
|
|
"""
|
|
xml = ""
|
|
for idx, value in enumerate(self._series.values):
|
|
if value is None:
|
|
continue
|
|
xml += (
|
|
' <c:pt idx="{val_idx:d}">\n'
|
|
" <c:v>{value}</c:v>\n"
|
|
" </c:pt>\n"
|
|
).format(**{"val_idx": idx, "value": value})
|
|
return xml
|
|
|
|
@property
|
|
def _val_tmpl(self):
|
|
"""
|
|
The template for the ``<c:val>`` element for this series, containing
|
|
the series values and their spreadsheet range reference.
|
|
"""
|
|
return (
|
|
" <c:val{nsdecls}>\n"
|
|
" <c:numRef>\n"
|
|
" <c:f>{values_ref}</c:f>\n"
|
|
" <c:numCache>\n"
|
|
" <c:formatCode>{number_format}</c:formatCode>\n"
|
|
' <c:ptCount val="{val_count}"/>\n'
|
|
"{val_pt_xml}"
|
|
" </c:numCache>\n"
|
|
" </c:numRef>\n"
|
|
" </c:val>\n"
|
|
)
|
|
|
|
|
|
class _XySeriesXmlWriter(_BaseSeriesXmlWriter):
|
|
"""
|
|
Generates XML snippets particular to an XY series.
|
|
"""
|
|
|
|
@property
|
|
def xVal(self):
|
|
"""
|
|
Return the ``<c:xVal>`` element for this series as an oxml element.
|
|
This element contains the X values for this series.
|
|
"""
|
|
xml = self._xVal_tmpl.format(
|
|
**{
|
|
"nsdecls": " %s" % nsdecls("c"),
|
|
"numRef_xml": self.numRef_xml(
|
|
self._series.x_values_ref,
|
|
self._series.number_format,
|
|
self._series.x_values,
|
|
),
|
|
}
|
|
)
|
|
return parse_xml(xml)
|
|
|
|
@property
|
|
def xVal_xml(self):
|
|
"""
|
|
Return the ``<c:xVal>`` element for this series as unicode text. This
|
|
element contains the X values for this series.
|
|
"""
|
|
return self._xVal_tmpl.format(
|
|
**{
|
|
"nsdecls": "",
|
|
"numRef_xml": self.numRef_xml(
|
|
self._series.x_values_ref,
|
|
self._series.number_format,
|
|
self._series.x_values,
|
|
),
|
|
}
|
|
)
|
|
|
|
@property
|
|
def yVal(self):
|
|
"""
|
|
Return the ``<c:yVal>`` element for this series as an oxml element.
|
|
This element contains the Y values for this series.
|
|
"""
|
|
xml = self._yVal_tmpl.format(
|
|
**{
|
|
"nsdecls": " %s" % nsdecls("c"),
|
|
"numRef_xml": self.numRef_xml(
|
|
self._series.y_values_ref,
|
|
self._series.number_format,
|
|
self._series.y_values,
|
|
),
|
|
}
|
|
)
|
|
return parse_xml(xml)
|
|
|
|
@property
|
|
def yVal_xml(self):
|
|
"""
|
|
Return the ``<c:yVal>`` element for this series as unicode text. This
|
|
element contains the Y values for this series.
|
|
"""
|
|
return self._yVal_tmpl.format(
|
|
**{
|
|
"nsdecls": "",
|
|
"numRef_xml": self.numRef_xml(
|
|
self._series.y_values_ref,
|
|
self._series.number_format,
|
|
self._series.y_values,
|
|
),
|
|
}
|
|
)
|
|
|
|
@property
|
|
def _xVal_tmpl(self):
|
|
"""
|
|
The template for the ``<c:xVal>`` element for this series, containing
|
|
the X values and their spreadsheet range reference.
|
|
"""
|
|
return " <c:xVal{nsdecls}>\n" "{numRef_xml}" " </c:xVal>\n"
|
|
|
|
@property
|
|
def _yVal_tmpl(self):
|
|
"""
|
|
The template for the ``<c:yVal>`` element for this series, containing
|
|
the Y values and their spreadsheet range reference.
|
|
"""
|
|
return " <c:yVal{nsdecls}>\n" "{numRef_xml}" " </c:yVal>\n"
|
|
|
|
|
|
class _BubbleSeriesXmlWriter(_XySeriesXmlWriter):
|
|
"""
|
|
Generates XML snippets particular to a bubble chart series.
|
|
"""
|
|
|
|
@property
|
|
def bubbleSize(self):
|
|
"""
|
|
Return the ``<c:bubbleSize>`` element for this series as an oxml
|
|
element. This element contains the bubble size values for this
|
|
series.
|
|
"""
|
|
xml = self._bubbleSize_tmpl.format(
|
|
**{
|
|
"nsdecls": " %s" % nsdecls("c"),
|
|
"numRef_xml": self.numRef_xml(
|
|
self._series.bubble_sizes_ref,
|
|
self._series.number_format,
|
|
self._series.bubble_sizes,
|
|
),
|
|
}
|
|
)
|
|
return parse_xml(xml)
|
|
|
|
@property
|
|
def bubbleSize_xml(self):
|
|
"""
|
|
Return the ``<c:bubbleSize>`` element for this series as unicode
|
|
text. This element contains the bubble size values for all the
|
|
data points in the chart.
|
|
"""
|
|
return self._bubbleSize_tmpl.format(
|
|
**{
|
|
"nsdecls": "",
|
|
"numRef_xml": self.numRef_xml(
|
|
self._series.bubble_sizes_ref,
|
|
self._series.number_format,
|
|
self._series.bubble_sizes,
|
|
),
|
|
}
|
|
)
|
|
|
|
@property
|
|
def _bubbleSize_tmpl(self):
|
|
"""
|
|
The template for the ``<c:bubbleSize>`` element for this series,
|
|
containing the bubble size values and their spreadsheet range
|
|
reference.
|
|
"""
|
|
return (
|
|
" <c:bubbleSize{nsdecls}>\n"
|
|
"{numRef_xml}"
|
|
" </c:bubbleSize>\n"
|
|
)
|
|
|
|
|
|
class _BubbleSeriesXmlRewriter(_BaseSeriesXmlRewriter):
|
|
"""
|
|
A series rewriter suitable for bubble charts.
|
|
"""
|
|
|
|
def _rewrite_ser_data(self, ser, series_data, date_1904):
|
|
"""
|
|
Rewrite the ``<c:tx>``, ``<c:cat>`` and ``<c:val>`` child elements
|
|
of *ser* based on the values in *series_data*.
|
|
"""
|
|
ser._remove_tx()
|
|
ser._remove_xVal()
|
|
ser._remove_yVal()
|
|
ser._remove_bubbleSize()
|
|
|
|
xml_writer = _BubbleSeriesXmlWriter(series_data)
|
|
|
|
ser._insert_tx(xml_writer.tx)
|
|
ser._insert_xVal(xml_writer.xVal)
|
|
ser._insert_yVal(xml_writer.yVal)
|
|
ser._insert_bubbleSize(xml_writer.bubbleSize)
|
|
|
|
|
|
class _CategorySeriesXmlRewriter(_BaseSeriesXmlRewriter):
|
|
"""
|
|
A series rewriter suitable for category charts.
|
|
"""
|
|
|
|
def _rewrite_ser_data(self, ser, series_data, date_1904):
|
|
"""
|
|
Rewrite the ``<c:tx>``, ``<c:cat>`` and ``<c:val>`` child elements
|
|
of *ser* based on the values in *series_data*.
|
|
"""
|
|
ser._remove_tx()
|
|
ser._remove_cat()
|
|
ser._remove_val()
|
|
|
|
xml_writer = _CategorySeriesXmlWriter(series_data, date_1904)
|
|
|
|
ser._insert_tx(xml_writer.tx)
|
|
ser._insert_cat(xml_writer.cat)
|
|
ser._insert_val(xml_writer.val)
|
|
|
|
|
|
class _XySeriesXmlRewriter(_BaseSeriesXmlRewriter):
|
|
"""
|
|
A series rewriter suitable for XY (aka. scatter) charts.
|
|
"""
|
|
|
|
def _rewrite_ser_data(self, ser, series_data, date_1904):
|
|
"""
|
|
Rewrite the ``<c:tx>``, ``<c:xVal>`` and ``<c:yVal>`` child elements
|
|
of *ser* based on the values in *series_data*.
|
|
"""
|
|
ser._remove_tx()
|
|
ser._remove_xVal()
|
|
ser._remove_yVal()
|
|
|
|
xml_writer = _XySeriesXmlWriter(series_data)
|
|
|
|
ser._insert_tx(xml_writer.tx)
|
|
ser._insert_xVal(xml_writer.xVal)
|
|
ser._insert_yVal(xml_writer.yVal)
|