init: 폴더구조 설계 및 인프라 설계

This commit is contained in:
DongHyeonka
2026-07-24 14:31:53 +09:00
parent 34ad612281
commit f9c463f87a
1839 changed files with 323096 additions and 1 deletions
@@ -0,0 +1,297 @@
"""Axis-related oxml objects."""
from __future__ import annotations
from pptx.enum.chart import XL_AXIS_CROSSES, XL_TICK_LABEL_POSITION, XL_TICK_MARK
from pptx.oxml.chart.shared import CT_Title
from pptx.oxml.simpletypes import ST_AxisUnit, ST_LblOffset, ST_Orientation
from pptx.oxml.text import CT_TextBody
from pptx.oxml.xmlchemy import (
BaseOxmlElement,
OneAndOnlyOne,
OptionalAttribute,
RequiredAttribute,
ZeroOrOne,
)
class BaseAxisElement(BaseOxmlElement):
"""Base class for catAx, dateAx, valAx, and perhaps other axis elements."""
@property
def defRPr(self):
"""
``<a:defRPr>`` great-great-grandchild element, added with its
ancestors if not present.
"""
txPr = self.get_or_add_txPr()
defRPr = txPr.defRPr
return defRPr
@property
def orientation(self):
"""Value of `val` attribute of `c:scaling/c:orientation` grandchild element.
Defaults to `ST_Orientation.MIN_MAX` if attribute or any ancestors are not
present.
"""
orientation = self.scaling.orientation
if orientation is None:
return ST_Orientation.MIN_MAX
return orientation.val
@orientation.setter
def orientation(self, value):
"""`value` is a member of `ST_Orientation`."""
self.scaling._remove_orientation()
if value == ST_Orientation.MAX_MIN:
self.scaling.get_or_add_orientation().val = value
def _new_title(self):
return CT_Title.new_title()
def _new_txPr(self):
return CT_TextBody.new_txPr()
class CT_AxisUnit(BaseOxmlElement):
"""Used for `c:majorUnit` and `c:minorUnit` elements, and others."""
val = RequiredAttribute("val", ST_AxisUnit)
class CT_CatAx(BaseAxisElement):
"""`c:catAx` element, defining a category axis."""
_tag_seq = (
"c:axId",
"c:scaling",
"c:delete",
"c:axPos",
"c:majorGridlines",
"c:minorGridlines",
"c:title",
"c:numFmt",
"c:majorTickMark",
"c:minorTickMark",
"c:tickLblPos",
"c:spPr",
"c:txPr",
"c:crossAx",
"c:crosses",
"c:crossesAt",
"c:auto",
"c:lblAlgn",
"c:lblOffset",
"c:tickLblSkip",
"c:tickMarkSkip",
"c:noMultiLvlLbl",
"c:extLst",
)
scaling = OneAndOnlyOne("c:scaling")
delete_ = ZeroOrOne("c:delete", successors=_tag_seq[3:])
majorGridlines = ZeroOrOne("c:majorGridlines", successors=_tag_seq[5:])
minorGridlines = ZeroOrOne("c:minorGridlines", successors=_tag_seq[6:])
title = ZeroOrOne("c:title", successors=_tag_seq[7:])
numFmt = ZeroOrOne("c:numFmt", successors=_tag_seq[8:])
majorTickMark = ZeroOrOne("c:majorTickMark", successors=_tag_seq[9:])
minorTickMark = ZeroOrOne("c:minorTickMark", successors=_tag_seq[10:])
tickLblPos = ZeroOrOne("c:tickLblPos", successors=_tag_seq[11:])
spPr = ZeroOrOne("c:spPr", successors=_tag_seq[12:])
txPr = ZeroOrOne("c:txPr", successors=_tag_seq[13:])
crosses = ZeroOrOne("c:crosses", successors=_tag_seq[15:])
crossesAt = ZeroOrOne("c:crossesAt", successors=_tag_seq[16:])
lblOffset = ZeroOrOne("c:lblOffset", successors=_tag_seq[19:])
del _tag_seq
class CT_ChartLines(BaseOxmlElement):
"""Used for `c:majorGridlines` and `c:minorGridlines`.
Specifies gridlines visual properties such as color and width.
"""
spPr = ZeroOrOne("c:spPr", successors=())
class CT_Crosses(BaseOxmlElement):
"""`c:crosses` element, specifying where the other axis crosses this one."""
val = RequiredAttribute("val", XL_AXIS_CROSSES)
class CT_DateAx(BaseAxisElement):
"""`c:dateAx` element, defining a date (category) axis."""
_tag_seq = (
"c:axId",
"c:scaling",
"c:delete",
"c:axPos",
"c:majorGridlines",
"c:minorGridlines",
"c:title",
"c:numFmt",
"c:majorTickMark",
"c:minorTickMark",
"c:tickLblPos",
"c:spPr",
"c:txPr",
"c:crossAx",
"c:crosses",
"c:crossesAt",
"c:auto",
"c:lblOffset",
"c:baseTimeUnit",
"c:majorUnit",
"c:majorTimeUnit",
"c:minorUnit",
"c:minorTimeUnit",
"c:extLst",
)
scaling = OneAndOnlyOne("c:scaling")
delete_ = ZeroOrOne("c:delete", successors=_tag_seq[3:])
majorGridlines = ZeroOrOne("c:majorGridlines", successors=_tag_seq[5:])
minorGridlines = ZeroOrOne("c:minorGridlines", successors=_tag_seq[6:])
title = ZeroOrOne("c:title", successors=_tag_seq[7:])
numFmt = ZeroOrOne("c:numFmt", successors=_tag_seq[8:])
majorTickMark = ZeroOrOne("c:majorTickMark", successors=_tag_seq[9:])
minorTickMark = ZeroOrOne("c:minorTickMark", successors=_tag_seq[10:])
tickLblPos = ZeroOrOne("c:tickLblPos", successors=_tag_seq[11:])
spPr = ZeroOrOne("c:spPr", successors=_tag_seq[12:])
txPr = ZeroOrOne("c:txPr", successors=_tag_seq[13:])
crosses = ZeroOrOne("c:crosses", successors=_tag_seq[15:])
crossesAt = ZeroOrOne("c:crossesAt", successors=_tag_seq[16:])
lblOffset = ZeroOrOne("c:lblOffset", successors=_tag_seq[18:])
del _tag_seq
class CT_LblOffset(BaseOxmlElement):
"""`c:lblOffset` custom element class."""
val = OptionalAttribute("val", ST_LblOffset, default=100)
class CT_Orientation(BaseOxmlElement):
"""`c:xAx/c:scaling/c:orientation` element, defining category order.
Used to reverse the order categories appear in on a bar chart so they start at the
top rather than the bottom. Because we read top-to-bottom, the default way looks odd
to many and perhaps most folks. Also applicable to value and date axes.
"""
val = OptionalAttribute("val", ST_Orientation, default=ST_Orientation.MIN_MAX)
class CT_Scaling(BaseOxmlElement):
"""`c:scaling` element.
Defines axis scale characteristics such as maximum value, log vs. linear, etc.
"""
_tag_seq = ("c:logBase", "c:orientation", "c:max", "c:min", "c:extLst")
orientation = ZeroOrOne("c:orientation", successors=_tag_seq[2:])
max = ZeroOrOne("c:max", successors=_tag_seq[3:])
min = ZeroOrOne("c:min", successors=_tag_seq[4:])
del _tag_seq
@property
def maximum(self):
"""
The float value of the ``<c:max>`` child element, or |None| if no max
element is present.
"""
max = self.max
if max is None:
return None
return max.val
@maximum.setter
def maximum(self, value):
"""
Set the value of the ``<c:max>`` child element to the float *value*,
or remove the max element if *value* is |None|.
"""
self._remove_max()
if value is None:
return
self._add_max(val=value)
@property
def minimum(self):
"""
The float value of the ``<c:min>`` child element, or |None| if no min
element is present.
"""
min = self.min
if min is None:
return None
return min.val
@minimum.setter
def minimum(self, value):
"""
Set the value of the ``<c:min>`` child element to the float *value*,
or remove the min element if *value* is |None|.
"""
self._remove_min()
if value is None:
return
self._add_min(val=value)
class CT_TickLblPos(BaseOxmlElement):
"""`c:tickLblPos` element."""
val = OptionalAttribute("val", XL_TICK_LABEL_POSITION)
class CT_TickMark(BaseOxmlElement):
"""Used for `c:minorTickMark` and `c:majorTickMark`."""
val = OptionalAttribute("val", XL_TICK_MARK, default=XL_TICK_MARK.CROSS)
class CT_ValAx(BaseAxisElement):
"""`c:valAx` element, defining a value axis."""
_tag_seq = (
"c:axId",
"c:scaling",
"c:delete",
"c:axPos",
"c:majorGridlines",
"c:minorGridlines",
"c:title",
"c:numFmt",
"c:majorTickMark",
"c:minorTickMark",
"c:tickLblPos",
"c:spPr",
"c:txPr",
"c:crossAx",
"c:crosses",
"c:crossesAt",
"c:crossBetween",
"c:majorUnit",
"c:minorUnit",
"c:dispUnits",
"c:extLst",
)
scaling = OneAndOnlyOne("c:scaling")
delete_ = ZeroOrOne("c:delete", successors=_tag_seq[3:])
majorGridlines = ZeroOrOne("c:majorGridlines", successors=_tag_seq[5:])
minorGridlines = ZeroOrOne("c:minorGridlines", successors=_tag_seq[6:])
title = ZeroOrOne("c:title", successors=_tag_seq[7:])
numFmt = ZeroOrOne("c:numFmt", successors=_tag_seq[8:])
majorTickMark = ZeroOrOne("c:majorTickMark", successors=_tag_seq[9:])
minorTickMark = ZeroOrOne("c:minorTickMark", successors=_tag_seq[10:])
tickLblPos = ZeroOrOne("c:tickLblPos", successors=_tag_seq[11:])
spPr = ZeroOrOne("c:spPr", successors=_tag_seq[12:])
txPr = ZeroOrOne("c:txPr", successors=_tag_seq[13:])
crossAx = ZeroOrOne("c:crossAx", successors=_tag_seq[14:])
crosses = ZeroOrOne("c:crosses", successors=_tag_seq[15:])
crossesAt = ZeroOrOne("c:crossesAt", successors=_tag_seq[16:])
majorUnit = ZeroOrOne("c:majorUnit", successors=_tag_seq[18:])
minorUnit = ZeroOrOne("c:minorUnit", successors=_tag_seq[19:])
del _tag_seq
@@ -0,0 +1,282 @@
"""Custom element classes for top-level chart-related XML elements."""
from __future__ import annotations
from typing import cast
from pptx.oxml import parse_xml
from pptx.oxml.chart.shared import CT_Title
from pptx.oxml.ns import nsdecls, qn
from pptx.oxml.simpletypes import ST_Style, XsdString
from pptx.oxml.text import CT_TextBody
from pptx.oxml.xmlchemy import (
BaseOxmlElement,
OneAndOnlyOne,
RequiredAttribute,
ZeroOrMore,
ZeroOrOne,
)
class CT_Chart(BaseOxmlElement):
"""`c:chart` custom element class."""
_tag_seq = (
"c:title",
"c:autoTitleDeleted",
"c:pivotFmts",
"c:view3D",
"c:floor",
"c:sideWall",
"c:backWall",
"c:plotArea",
"c:legend",
"c:plotVisOnly",
"c:dispBlanksAs",
"c:showDLblsOverMax",
"c:extLst",
)
title = ZeroOrOne("c:title", successors=_tag_seq[1:])
autoTitleDeleted = ZeroOrOne("c:autoTitleDeleted", successors=_tag_seq[2:])
plotArea = OneAndOnlyOne("c:plotArea")
legend = ZeroOrOne("c:legend", successors=_tag_seq[9:])
rId: str = RequiredAttribute("r:id", XsdString) # pyright: ignore[reportAssignmentType]
@property
def has_legend(self):
"""
True if this chart has a legend defined, False otherwise.
"""
legend = self.legend
if legend is None:
return False
return True
@has_legend.setter
def has_legend(self, bool_value):
"""
Add, remove, or leave alone the ``<c:legend>`` child element depending
on current state and *bool_value*. If *bool_value* is |True| and no
``<c:legend>`` element is present, a new default element is added.
When |False|, any existing legend element is removed.
"""
if bool(bool_value) is False:
self._remove_legend()
else:
if self.legend is None:
self._add_legend()
@staticmethod
def new_chart(rId: str) -> CT_Chart:
"""Return a new `c:chart` element."""
return cast(CT_Chart, parse_xml(f'<c:chart {nsdecls("c")} {nsdecls("r")} r:id="{rId}"/>'))
def _new_title(self):
return CT_Title.new_title()
class CT_ChartSpace(BaseOxmlElement):
"""`c:chartSpace` root element of a chart part."""
_tag_seq = (
"c:date1904",
"c:lang",
"c:roundedCorners",
"c:style",
"c:clrMapOvr",
"c:pivotSource",
"c:protection",
"c:chart",
"c:spPr",
"c:txPr",
"c:externalData",
"c:printSettings",
"c:userShapes",
"c:extLst",
)
date1904 = ZeroOrOne("c:date1904", successors=_tag_seq[1:])
style = ZeroOrOne("c:style", successors=_tag_seq[4:])
chart = OneAndOnlyOne("c:chart")
txPr = ZeroOrOne("c:txPr", successors=_tag_seq[10:])
externalData = ZeroOrOne("c:externalData", successors=_tag_seq[11:])
del _tag_seq
@property
def catAx_lst(self):
return self.chart.plotArea.catAx_lst
@property
def date_1904(self):
"""
Return |True| if the `c:date1904` child element resolves truthy,
|False| otherwise. This value indicates whether date number values
are based on the 1900 or 1904 epoch.
"""
date1904 = self.date1904
if date1904 is None:
return False
return date1904.val
@property
def dateAx_lst(self):
return self.xpath("c:chart/c:plotArea/c:dateAx")
def get_or_add_title(self):
"""Return the `c:title` grandchild, newly created if not present."""
return self.chart.get_or_add_title()
@property
def plotArea(self):
"""
Return the required `c:chartSpace/c:chart/c:plotArea` grandchild
element.
"""
return self.chart.plotArea
@property
def valAx_lst(self):
return self.chart.plotArea.valAx_lst
@property
def xlsx_part_rId(self):
"""
The string in the required ``r:id`` attribute of the
`<c:externalData>` child, or |None| if no externalData element is
present.
"""
externalData = self.externalData
if externalData is None:
return None
return externalData.rId
def _add_externalData(self):
"""
Always add a ``<c:autoUpdate val="0"/>`` child so auto-updating
behavior is off by default.
"""
externalData = self._new_externalData()
externalData._add_autoUpdate(val=False)
self._insert_externalData(externalData)
return externalData
def _new_txPr(self):
return CT_TextBody.new_txPr()
class CT_ExternalData(BaseOxmlElement):
"""
`<c:externalData>` element, defining link to embedded Excel package part
containing the chart data.
"""
autoUpdate = ZeroOrOne("c:autoUpdate")
rId = RequiredAttribute("r:id", XsdString)
class CT_PlotArea(BaseOxmlElement):
"""
``<c:plotArea>`` element.
"""
catAx = ZeroOrMore("c:catAx")
valAx = ZeroOrMore("c:valAx")
def iter_sers(self):
"""
Generate each of the `c:ser` elements in this chart, ordered first by
the document order of the containing xChart element, then by their
ordering within the xChart element (not necessarily document order).
"""
for xChart in self.iter_xCharts():
for ser in xChart.iter_sers():
yield ser
def iter_xCharts(self):
"""
Generate each xChart child element in document.
"""
plot_tags = (
qn("c:area3DChart"),
qn("c:areaChart"),
qn("c:bar3DChart"),
qn("c:barChart"),
qn("c:bubbleChart"),
qn("c:doughnutChart"),
qn("c:line3DChart"),
qn("c:lineChart"),
qn("c:ofPieChart"),
qn("c:pie3DChart"),
qn("c:pieChart"),
qn("c:radarChart"),
qn("c:scatterChart"),
qn("c:stockChart"),
qn("c:surface3DChart"),
qn("c:surfaceChart"),
)
for child in self.iterchildren():
if child.tag not in plot_tags:
continue
yield child
@property
def last_ser(self):
"""
Return the last `<c:ser>` element in the last xChart element, based
on series order (not necessarily the same element as document order).
"""
last_xChart = self.xCharts[-1]
sers = last_xChart.sers
if not sers:
return None
return sers[-1]
@property
def next_idx(self):
"""
Return the next available `c:ser/c:idx` value within the scope of
this chart, the maximum idx value found on existing series,
incremented by one.
"""
idx_vals = [s.idx.val for s in self.sers]
if not idx_vals:
return 0
return max(idx_vals) + 1
@property
def next_order(self):
"""
Return the next available `c:ser/c:order` value within the scope of
this chart, the maximum order value found on existing series,
incremented by one.
"""
order_vals = [s.order.val for s in self.sers]
if not order_vals:
return 0
return max(order_vals) + 1
@property
def sers(self):
"""
Return a sequence containing all the `c:ser` elements in this chart,
ordered first by the document order of the containing xChart element,
then by their ordering within the xChart element (not necessarily
document order).
"""
return tuple(self.iter_sers())
@property
def xCharts(self):
"""
Return a sequence containing all the `c:{x}Chart` elements in this
chart, in document order.
"""
return tuple(self.iter_xCharts())
class CT_Style(BaseOxmlElement):
"""
``<c:style>`` element; defines the chart style.
"""
val = RequiredAttribute("val", ST_Style)
@@ -0,0 +1,252 @@
"""Chart data-label related oxml objects."""
from __future__ import annotations
from pptx.enum.chart import XL_DATA_LABEL_POSITION
from pptx.oxml import parse_xml
from pptx.oxml.ns import nsdecls
from pptx.oxml.text import CT_TextBody
from pptx.oxml.xmlchemy import (
BaseOxmlElement,
OneAndOnlyOne,
RequiredAttribute,
ZeroOrMore,
ZeroOrOne,
)
class CT_DLbl(BaseOxmlElement):
"""
``<c:dLbl>`` element specifying the properties of the data label for an
individual data point.
"""
_tag_seq = (
"c:idx",
"c:layout",
"c:tx",
"c:numFmt",
"c:spPr",
"c:txPr",
"c:dLblPos",
"c:showLegendKey",
"c:showVal",
"c:showCatName",
"c:showSerName",
"c:showPercent",
"c:showBubbleSize",
"c:separator",
"c:extLst",
)
idx = OneAndOnlyOne("c:idx")
tx = ZeroOrOne("c:tx", successors=_tag_seq[3:])
spPr = ZeroOrOne("c:spPr", successors=_tag_seq[5:])
txPr = ZeroOrOne("c:txPr", successors=_tag_seq[6:])
dLblPos = ZeroOrOne("c:dLblPos", successors=_tag_seq[7:])
del _tag_seq
def get_or_add_rich(self):
"""
Return the `c:rich` descendant representing the text frame of the
data label, newly created if not present. Any existing `c:strRef`
element is removed along with its contents.
"""
tx = self.get_or_add_tx()
tx._remove_strRef()
return tx.get_or_add_rich()
def get_or_add_tx_rich(self):
"""
Return the `c:tx[c:rich]` subtree, newly created if not present.
"""
tx = self.get_or_add_tx()
tx._remove_strRef()
tx.get_or_add_rich()
return tx
@property
def idx_val(self):
"""
The integer value of the `val` attribute on the required `c:idx`
child.
"""
return self.idx.val
@classmethod
def new_dLbl(cls):
"""Return a newly created "loose" `c:dLbl` element.
The `c:dLbl` element contains the same (fairly extensive) default
subtree added by PowerPoint when an individual data label is
customized in the UI. Note that the idx value must be set by the
client. Failure to set the idx value will likely result in any
changes not being visible and may result in a repair error on open.
"""
return parse_xml(
"<c:dLbl %s>\n"
' <c:idx val="666"/>\n'
" <c:spPr/>\n"
" <c:txPr>\n"
" <a:bodyPr/>\n"
" <a:lstStyle/>\n"
" <a:p>\n"
" <a:pPr>\n"
" <a:defRPr/>\n"
" </a:pPr>\n"
" </a:p>\n"
" </c:txPr>\n"
' <c:showLegendKey val="0"/>\n'
' <c:showVal val="1"/>\n'
' <c:showCatName val="0"/>\n'
' <c:showSerName val="0"/>\n'
' <c:showPercent val="0"/>\n'
' <c:showBubbleSize val="0"/>\n'
"</c:dLbl>" % nsdecls("c", "a")
)
def remove_tx_rich(self):
"""
Remove any `c:tx[c:rich]` child, or do nothing if not present.
"""
matches = self.xpath("c:tx[c:rich]")
if not matches:
return
tx = matches[0]
self.remove(tx)
def _new_txPr(self):
return CT_TextBody.new_txPr()
class CT_DLblPos(BaseOxmlElement):
"""
``<c:dLblPos>`` element specifying the positioning of a data label with
respect to its data point.
"""
val = RequiredAttribute("val", XL_DATA_LABEL_POSITION)
class CT_DLbls(BaseOxmlElement):
"""`c:dLbls` element specifying properties for a set of data labels."""
_tag_seq = (
"c:dLbl",
"c:numFmt",
"c:spPr",
"c:txPr",
"c:dLblPos",
"c:showLegendKey",
"c:showVal",
"c:showCatName",
"c:showSerName",
"c:showPercent",
"c:showBubbleSize",
"c:separator",
"c:showLeaderLines",
"c:leaderLines",
"c:extLst",
)
dLbl = ZeroOrMore("c:dLbl", successors=_tag_seq[1:])
numFmt = ZeroOrOne("c:numFmt", successors=_tag_seq[2:])
txPr = ZeroOrOne("c:txPr", successors=_tag_seq[4:])
dLblPos = ZeroOrOne("c:dLblPos", successors=_tag_seq[5:])
showLegendKey = ZeroOrOne("c:showLegendKey", successors=_tag_seq[6:])
showVal = ZeroOrOne("c:showVal", successors=_tag_seq[7:])
showCatName = ZeroOrOne("c:showCatName", successors=_tag_seq[8:])
showSerName = ZeroOrOne("c:showSerName", successors=_tag_seq[9:])
showPercent = ZeroOrOne("c:showPercent", successors=_tag_seq[10:])
del _tag_seq
@property
def defRPr(self):
"""
``<a:defRPr>`` great-great-grandchild element, added with its
ancestors if not present.
"""
txPr = self.get_or_add_txPr()
defRPr = txPr.defRPr
return defRPr
def get_dLbl_for_point(self, idx):
"""
Return the `c:dLbl` child representing the label for the data point
at index *idx*.
"""
matches = self.xpath('c:dLbl[c:idx[@val="%d"]]' % idx)
if matches:
return matches[0]
return None
def get_or_add_dLbl_for_point(self, idx):
"""
Return the `c:dLbl` element representing the label of the point at
index *idx*.
"""
matches = self.xpath('c:dLbl[c:idx[@val="%d"]]' % idx)
if matches:
return matches[0]
return self._insert_dLbl_in_sequence(idx)
@classmethod
def new_dLbls(cls):
"""Return a newly created "loose" `c:dLbls` element."""
return parse_xml(
"<c:dLbls %s>\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>" % nsdecls("c")
)
def _insert_dLbl_in_sequence(self, idx):
"""
Return a newly created `c:dLbl` element having `c:idx` child of *idx*
and inserted in numeric sequence among the `c:dLbl` children of this
element.
"""
new_dLbl = self._new_dLbl()
new_dLbl.idx.val = idx
dLbl = None
for dLbl in self.dLbl_lst:
if dLbl.idx_val > idx:
dLbl.addprevious(new_dLbl)
return new_dLbl
if dLbl is not None:
dLbl.addnext(new_dLbl)
else:
self.insert(0, new_dLbl)
return new_dLbl
def _new_dLbl(self):
return CT_DLbl.new_dLbl()
def _new_showCatName(self):
"""Return a new `c:showCatName` with value initialized.
This method is called by the metaclass-generated code whenever a new
`c:showCatName` element is required. In this case, it defaults to
`val=true`, which is not what we need so we override to make val
explicitly False.
"""
return parse_xml('<c:showCatName %s val="0"/>' % nsdecls("c"))
def _new_showLegendKey(self):
return parse_xml('<c:showLegendKey %s val="0"/>' % nsdecls("c"))
def _new_showPercent(self):
return parse_xml('<c:showPercent %s val="0"/>' % nsdecls("c"))
def _new_showSerName(self):
return parse_xml('<c:showSerName %s val="0"/>' % nsdecls("c"))
def _new_showVal(self):
return parse_xml('<c:showVal %s val="0"/>' % nsdecls("c"))
def _new_txPr(self):
return CT_TextBody.new_txPr()
@@ -0,0 +1,72 @@
"""lxml custom element classes for legend-related XML elements."""
from __future__ import annotations
from pptx.enum.chart import XL_LEGEND_POSITION
from pptx.oxml.text import CT_TextBody
from pptx.oxml.xmlchemy import BaseOxmlElement, OptionalAttribute, ZeroOrOne
class CT_Legend(BaseOxmlElement):
"""
``<c:legend>`` custom element class
"""
_tag_seq = (
"c:legendPos",
"c:legendEntry",
"c:layout",
"c:overlay",
"c:spPr",
"c:txPr",
"c:extLst",
)
legendPos = ZeroOrOne("c:legendPos", successors=_tag_seq[1:])
layout = ZeroOrOne("c:layout", successors=_tag_seq[3:])
overlay = ZeroOrOne("c:overlay", successors=_tag_seq[4:])
txPr = ZeroOrOne("c:txPr", successors=_tag_seq[6:])
del _tag_seq
@property
def defRPr(self):
"""
`./c:txPr/a:p/a:pPr/a:defRPr` great-great-grandchild element, added
with its ancestors if not present.
"""
txPr = self.get_or_add_txPr()
defRPr = txPr.defRPr
return defRPr
@property
def horz_offset(self):
"""
The float value in ./c:layout/c:manualLayout/c:x when
./c:layout/c:manualLayout/c:xMode@val == "factor". 0.0 if that
XPath expression has no match.
"""
layout = self.layout
if layout is None:
return 0.0
return layout.horz_offset
@horz_offset.setter
def horz_offset(self, offset):
"""
Set the value of ./c:layout/c:manualLayout/c:x@val to *offset* and
./c:layout/c:manualLayout/c:xMode@val to "factor". Remove
./c:layout/c:manualLayout if *offset* == 0.
"""
layout = self.get_or_add_layout()
layout.horz_offset = offset
def _new_txPr(self):
return CT_TextBody.new_txPr()
class CT_LegendPos(BaseOxmlElement):
"""
``<c:legendPos>`` element specifying position of legend with respect to
chart as a member of ST_LegendPos.
"""
val = OptionalAttribute("val", XL_LEGEND_POSITION, default=XL_LEGEND_POSITION.RIGHT)
@@ -0,0 +1,61 @@
"""Series-related oxml objects."""
from __future__ import annotations
from pptx.enum.chart import XL_MARKER_STYLE
from pptx.oxml.simpletypes import ST_MarkerSize
from pptx.oxml.xmlchemy import BaseOxmlElement, RequiredAttribute, ZeroOrOne
class CT_Marker(BaseOxmlElement):
"""
`c:marker` custom element class, containing visual properties for a data
point marker on line-type charts.
"""
_tag_seq = ("c:symbol", "c:size", "c:spPr", "c:extLst")
symbol = ZeroOrOne("c:symbol", successors=_tag_seq[1:])
size = ZeroOrOne("c:size", successors=_tag_seq[2:])
spPr = ZeroOrOne("c:spPr", successors=_tag_seq[3:])
del _tag_seq
@property
def size_val(self):
"""
Return the value of `./c:size/@val`, specifying the size of this
marker in points. Returns |None| if no `c:size` element is present or
its val attribute is not present.
"""
size = self.size
if size is None:
return None
return size.val
@property
def symbol_val(self):
"""
Return the value of `./c:symbol/@val`, specifying the shape of this
marker. Returns |None| if no `c:symbol` element is present.
"""
symbol = self.symbol
if symbol is None:
return None
return symbol.val
class CT_MarkerSize(BaseOxmlElement):
"""
`c:size` custom element class, specifying the size (in points) of a data
point marker for a line, XY, or radar chart.
"""
val = RequiredAttribute("val", ST_MarkerSize)
class CT_MarkerStyle(BaseOxmlElement):
"""
`c:symbol` custom element class, specifying the shape of a data point
marker for a line, XY, or radar chart.
"""
val = RequiredAttribute("val", XL_MARKER_STYLE)
@@ -0,0 +1,345 @@
"""Plot-related oxml objects."""
from __future__ import annotations
from pptx.oxml.chart.datalabel import CT_DLbls
from pptx.oxml.simpletypes import (
ST_BarDir,
ST_BubbleScale,
ST_GapAmount,
ST_Grouping,
ST_Overlap,
)
from pptx.oxml.xmlchemy import (
BaseOxmlElement,
OneAndOnlyOne,
OptionalAttribute,
ZeroOrMore,
ZeroOrOne,
)
class BaseChartElement(BaseOxmlElement):
"""
Base class for barChart, lineChart, and other plot elements.
"""
@property
def cat(self):
"""
Return the `c:cat` element of the first series in this xChart, or
|None| if not present.
"""
cats = self.xpath("./c:ser[1]/c:cat")
return cats[0] if cats else None
@property
def cat_pt_count(self):
"""
Return the value of the `c:ptCount` descendent of this xChart
element. Its parent can be one of three element types. This value
represents the true number of (leaf) categories, although they might
not all have a corresponding `c:pt` sibling; a category with no label
does not get a `c:pt` element. Returns 0 if there is no `c:ptCount`
descendent.
"""
cat_ptCounts = self.xpath("./c:ser//c:cat//c:ptCount")
if not cat_ptCounts:
return 0
return cat_ptCounts[0].val
@property
def cat_pts(self):
"""
Return a sequence representing the `c:pt` elements under the `c:cat`
element of the first series in this xChart element. A category having
no value will have no corresponding `c:pt` element; |None| will
appear in that position in such cases. Items appear in `idx` order.
Only those in the first ``<c:lvl>`` element are included in the case
of multi-level categories.
"""
cat_pts = self.xpath("./c:ser[1]/c:cat//c:lvl[1]/c:pt")
if not cat_pts:
cat_pts = self.xpath("./c:ser[1]/c:cat//c:pt")
cat_pt_dict = dict((pt.idx, pt) for pt in cat_pts)
return [cat_pt_dict.get(idx, None) for idx in range(self.cat_pt_count)]
@property
def grouping_val(self):
"""
Return the value of the ``./c:grouping{val=?}`` attribute, taking
defaults into account when items are not present.
"""
grouping = self.grouping
if grouping is None:
return ST_Grouping.STANDARD
val = grouping.val
if val is None:
return ST_Grouping.STANDARD
return val
def iter_sers(self):
"""
Generate each ``<c:ser>`` child element in this xChart in
c:order/@val sequence (not document or c:idx order).
"""
def ser_order(ser):
return ser.order.val
return (ser for ser in sorted(self.xpath("./c:ser"), key=ser_order))
@property
def sers(self):
"""
Sequence of ``<c:ser>`` child elements in this xChart in c:order/@val
sequence (not document or c:idx order).
"""
return tuple(self.iter_sers())
def _new_dLbls(self):
return CT_DLbls.new_dLbls()
class CT_Area3DChart(BaseChartElement):
"""
``<c:area3DChart>`` element.
"""
grouping = ZeroOrOne(
"c:grouping",
successors=(
"c:varyColors",
"c:ser",
"c:dLbls",
"c:dropLines",
"c:gapDepth",
"c:axId",
),
)
class CT_AreaChart(BaseChartElement):
"""
``<c:areaChart>`` element.
"""
_tag_seq = (
"c:grouping",
"c:varyColors",
"c:ser",
"c:dLbls",
"c:dropLines",
"c:axId",
"c:extLst",
)
grouping = ZeroOrOne("c:grouping", successors=_tag_seq[1:])
varyColors = ZeroOrOne("c:varyColors", successors=_tag_seq[2:])
ser = ZeroOrMore("c:ser", successors=_tag_seq[3:])
dLbls = ZeroOrOne("c:dLbls", successors=_tag_seq[4:])
del _tag_seq
class CT_BarChart(BaseChartElement):
"""
``<c:barChart>`` element.
"""
_tag_seq = (
"c:barDir",
"c:grouping",
"c:varyColors",
"c:ser",
"c:dLbls",
"c:gapWidth",
"c:overlap",
"c:serLines",
"c:axId",
"c:extLst",
)
barDir = OneAndOnlyOne("c:barDir")
grouping = ZeroOrOne("c:grouping", successors=_tag_seq[2:])
varyColors = ZeroOrOne("c:varyColors", successors=_tag_seq[3:])
ser = ZeroOrMore("c:ser", successors=_tag_seq[4:])
dLbls = ZeroOrOne("c:dLbls", successors=_tag_seq[5:])
gapWidth = ZeroOrOne("c:gapWidth", successors=_tag_seq[6:])
overlap = ZeroOrOne("c:overlap", successors=_tag_seq[7:])
del _tag_seq
@property
def grouping_val(self):
"""
Return the value of the ``./c:grouping{val=?}`` attribute, taking
defaults into account when items are not present.
"""
grouping = self.grouping
if grouping is None:
return ST_Grouping.CLUSTERED
val = grouping.val
if val is None:
return ST_Grouping.CLUSTERED
return val
class CT_BarDir(BaseOxmlElement):
"""
``<c:barDir>`` child of a barChart element, specifying the orientation of
the bars, 'bar' if they are horizontal and 'col' if they are vertical.
"""
val = OptionalAttribute("val", ST_BarDir, default=ST_BarDir.COL)
class CT_BubbleChart(BaseChartElement):
"""
``<c:bubbleChart>`` custom element class
"""
_tag_seq = (
"c:varyColors",
"c:ser",
"c:dLbls",
"c:axId",
"c:bubble3D",
"c:bubbleScale",
"c:showNegBubbles",
"c:sizeRepresents",
"c:axId",
"c:extLst",
)
ser = ZeroOrMore("c:ser", successors=_tag_seq[2:])
dLbls = ZeroOrOne("c:dLbls", successors=_tag_seq[3:])
bubble3D = ZeroOrOne("c:bubble3D", successors=_tag_seq[5:])
bubbleScale = ZeroOrOne("c:bubbleScale", successors=_tag_seq[6:])
del _tag_seq
class CT_BubbleScale(BaseChartElement):
"""
``<c:bubbleScale>`` custom element class
"""
val = OptionalAttribute("val", ST_BubbleScale, default=100)
class CT_DoughnutChart(BaseChartElement):
"""
``<c:doughnutChart>`` element.
"""
_tag_seq = (
"c:varyColors",
"c:ser",
"c:dLbls",
"c:firstSliceAng",
"c:holeSize",
"c:extLst",
)
varyColors = ZeroOrOne("c:varyColors", successors=_tag_seq[1:])
ser = ZeroOrMore("c:ser", successors=_tag_seq[2:])
dLbls = ZeroOrOne("c:dLbls", successors=_tag_seq[3:])
del _tag_seq
class CT_GapAmount(BaseOxmlElement):
"""
``<c:gapWidth>`` child of ``<c:barChart>`` element, also used for other
purposes like error bars.
"""
val = OptionalAttribute("val", ST_GapAmount, default=150)
class CT_Grouping(BaseOxmlElement):
"""
``<c:grouping>`` child of an xChart element, specifying a value like
'clustered' or 'stacked'. Also used for variants with the same tag name
like CT_BarGrouping.
"""
val = OptionalAttribute("val", ST_Grouping)
class CT_LineChart(BaseChartElement):
"""
``<c:lineChart>`` custom element class
"""
_tag_seq = (
"c:grouping",
"c:varyColors",
"c:ser",
"c:dLbls",
"c:dropLines",
"c:hiLowLines",
"c:upDownBars",
"c:marker",
"c:smooth",
"c:axId",
"c:extLst",
)
grouping = ZeroOrOne("c:grouping", successors=(_tag_seq[1:]))
varyColors = ZeroOrOne("c:varyColors", successors=_tag_seq[2:])
ser = ZeroOrMore("c:ser", successors=_tag_seq[3:])
dLbls = ZeroOrOne("c:dLbls", successors=(_tag_seq[4:]))
del _tag_seq
class CT_Overlap(BaseOxmlElement):
"""
``<c:overlap>`` element specifying bar overlap as an integer percentage
of bar width, in range -100 to 100.
"""
val = OptionalAttribute("val", ST_Overlap, default=0)
class CT_PieChart(BaseChartElement):
"""
``<c:pieChart>`` custom element class
"""
_tag_seq = ("c:varyColors", "c:ser", "c:dLbls", "c:firstSliceAng", "c:extLst")
varyColors = ZeroOrOne("c:varyColors", successors=_tag_seq[1:])
ser = ZeroOrMore("c:ser", successors=_tag_seq[2:])
dLbls = ZeroOrOne("c:dLbls", successors=_tag_seq[3:])
del _tag_seq
class CT_RadarChart(BaseChartElement):
"""
``<c:radarChart>`` custom element class
"""
_tag_seq = (
"c:radarStyle",
"c:varyColors",
"c:ser",
"c:dLbls",
"c:axId",
"c:extLst",
)
varyColors = ZeroOrOne("c:varyColors", successors=_tag_seq[2:])
ser = ZeroOrMore("c:ser", successors=_tag_seq[3:])
dLbls = ZeroOrOne("c:dLbls", successors=(_tag_seq[4:]))
del _tag_seq
class CT_ScatterChart(BaseChartElement):
"""
``<c:scatterChart>`` custom element class
"""
_tag_seq = (
"c:scatterStyle",
"c:varyColors",
"c:ser",
"c:dLbls",
"c:axId",
"c:extLst",
)
varyColors = ZeroOrOne("c:varyColors", successors=_tag_seq[2:])
ser = ZeroOrMore("c:ser", successors=_tag_seq[3:])
del _tag_seq
@@ -0,0 +1,254 @@
"""Series-related oxml objects."""
from __future__ import annotations
from pptx.oxml.chart.datalabel import CT_DLbls
from pptx.oxml.simpletypes import XsdUnsignedInt
from pptx.oxml.xmlchemy import (
BaseOxmlElement,
OneAndOnlyOne,
OxmlElement,
RequiredAttribute,
ZeroOrMore,
ZeroOrOne,
)
class CT_AxDataSource(BaseOxmlElement):
"""
``<c:cat>`` custom element class used in category charts to specify
category labels and hierarchy.
"""
multiLvlStrRef = ZeroOrOne("c:multiLvlStrRef", successors=())
@property
def lvls(self):
"""
Return a list containing the `c:lvl` descendent elements in document
order. These will only be present when the required single child
is a `c:multiLvlStrRef` element. Returns an empty list when no
`c:lvl` descendent elements are present.
"""
return self.xpath(".//c:lvl")
class CT_DPt(BaseOxmlElement):
"""
``<c:dPt>`` custom element class, containing visual properties for a data
point.
"""
_tag_seq = (
"c:idx",
"c:invertIfNegative",
"c:marker",
"c:bubble3D",
"c:explosion",
"c:spPr",
"c:pictureOptions",
"c:extLst",
)
idx = OneAndOnlyOne("c:idx")
marker = ZeroOrOne("c:marker", successors=_tag_seq[3:])
spPr = ZeroOrOne("c:spPr", successors=_tag_seq[6:])
del _tag_seq
@classmethod
def new_dPt(cls):
"""
Return a newly created "loose" `c:dPt` element containing its default
subtree.
"""
dPt = OxmlElement("c:dPt")
dPt.append(OxmlElement("c:idx"))
return dPt
class CT_Lvl(BaseOxmlElement):
"""
``<c:lvl>`` custom element class used in multi-level categories to
specify a level of hierarchy.
"""
pt = ZeroOrMore("c:pt", successors=())
class CT_NumDataSource(BaseOxmlElement):
"""
``<c:yVal>`` custom element class used in XY and bubble charts, and
perhaps others.
"""
numRef = OneAndOnlyOne("c:numRef")
@property
def ptCount_val(self):
"""
Return the value of `./c:numRef/c:numCache/c:ptCount/@val`,
specifying how many `c:pt` elements are in this numeric data cache.
Returns 0 if no `c:ptCount` element is present, as this is the least
disruptive way to degrade when no cached point data is available.
This situation is not expected, but is valid according to the schema.
"""
results = self.xpath(".//c:ptCount/@val")
return int(results[0]) if results else 0
def pt_v(self, idx):
"""
Return the Y value for data point *idx* in this cache, or None if no
value is present for that data point.
"""
results = self.xpath(".//c:pt[@idx=%d]" % idx)
return results[0].value if results else None
class CT_SeriesComposite(BaseOxmlElement):
"""
``<c:ser>`` custom element class. Note there are several different series
element types in the schema, such as ``CT_LineSer`` and ``CT_BarSer``,
but they all share the same tag name. This class acts as a composite and
depends on the caller not to do anything invalid for a series belonging
to a particular plot type.
"""
_tag_seq = (
"c:idx",
"c:order",
"c:tx",
"c:spPr",
"c:invertIfNegative",
"c:pictureOptions",
"c:marker",
"c:explosion",
"c:dPt",
"c:dLbls",
"c:trendline",
"c:errBars",
"c:cat",
"c:val",
"c:xVal",
"c:yVal",
"c:shape",
"c:smooth",
"c:bubbleSize",
"c:bubble3D",
"c:extLst",
)
idx = OneAndOnlyOne("c:idx")
order = OneAndOnlyOne("c:order")
tx = ZeroOrOne("c:tx", successors=_tag_seq[3:])
spPr = ZeroOrOne("c:spPr", successors=_tag_seq[4:])
invertIfNegative = ZeroOrOne("c:invertIfNegative", successors=_tag_seq[5:])
marker = ZeroOrOne("c:marker", successors=_tag_seq[7:])
dPt = ZeroOrMore("c:dPt", successors=_tag_seq[9:])
dLbls = ZeroOrOne("c:dLbls", successors=_tag_seq[10:])
cat = ZeroOrOne("c:cat", successors=_tag_seq[13:])
val = ZeroOrOne("c:val", successors=_tag_seq[14:])
xVal = ZeroOrOne("c:xVal", successors=_tag_seq[15:])
yVal = ZeroOrOne("c:yVal", successors=_tag_seq[16:])
smooth = ZeroOrOne("c:smooth", successors=_tag_seq[18:])
bubbleSize = ZeroOrOne("c:bubbleSize", successors=_tag_seq[19:])
del _tag_seq
@property
def bubbleSize_ptCount_val(self):
"""
Return the number of bubble size values as reflected in the `val`
attribute of `./c:bubbleSize//c:ptCount`, or 0 if not present.
"""
vals = self.xpath("./c:bubbleSize//c:ptCount/@val")
if not vals:
return 0
return int(vals[0])
@property
def cat_ptCount_val(self):
"""
Return the number of categories as reflected in the `val` attribute
of `./c:cat//c:ptCount`, or 0 if not present.
"""
vals = self.xpath("./c:cat//c:ptCount/@val")
if not vals:
return 0
return int(vals[0])
def get_dLbl(self, idx):
"""
Return the `c:dLbl` element representing the label for the data point
at offset *idx* in this series, or |None| if not present.
"""
dLbls = self.dLbls
if dLbls is None:
return None
return dLbls.get_dLbl_for_point(idx)
def get_or_add_dLbl(self, idx):
"""
Return the `c:dLbl` element representing the label of the point at
offset *idx* in this series, newly created if not yet present.
"""
dLbls = self.get_or_add_dLbls()
return dLbls.get_or_add_dLbl_for_point(idx)
def get_or_add_dPt_for_point(self, idx):
"""
Return the `c:dPt` child representing the visual properties of the
data point at index *idx*.
"""
matches = self.xpath('c:dPt[c:idx[@val="%d"]]' % idx)
if matches:
return matches[0]
dPt = self._add_dPt()
dPt.idx.val = idx
return dPt
@property
def xVal_ptCount_val(self):
"""
Return the number of X values as reflected in the `val` attribute of
`./c:xVal//c:ptCount`, or 0 if not present.
"""
vals = self.xpath("./c:xVal//c:ptCount/@val")
if not vals:
return 0
return int(vals[0])
@property
def yVal_ptCount_val(self):
"""
Return the number of Y values as reflected in the `val` attribute of
`./c:yVal//c:ptCount`, or 0 if not present.
"""
vals = self.xpath("./c:yVal//c:ptCount/@val")
if not vals:
return 0
return int(vals[0])
def _new_dLbls(self):
"""Override metaclass method that creates `c:dLbls` element."""
return CT_DLbls.new_dLbls()
def _new_dPt(self):
"""
Overrides the metaclass generated method to get `c:dPt` with minimal
subtree.
"""
return CT_DPt.new_dPt()
class CT_StrVal_NumVal_Composite(BaseOxmlElement):
"""
``<c:pt>`` element, can be either CT_StrVal or CT_NumVal complex type.
Using this class for both, differentiating as needed.
"""
v = OneAndOnlyOne("c:v")
idx = RequiredAttribute("idx", XsdUnsignedInt)
@property
def value(self):
"""
The float value of the text in the required ``<c:v>`` child.
"""
return float(self.v.text)
@@ -0,0 +1,219 @@
"""Shared oxml objects for charts."""
from __future__ import annotations
from pptx.oxml import parse_xml
from pptx.oxml.ns import nsdecls
from pptx.oxml.simpletypes import (
ST_LayoutMode,
XsdBoolean,
XsdDouble,
XsdString,
XsdUnsignedInt,
)
from pptx.oxml.xmlchemy import (
BaseOxmlElement,
OptionalAttribute,
RequiredAttribute,
ZeroOrOne,
)
class CT_Boolean(BaseOxmlElement):
"""
Common complex type used for elements having a True/False value.
"""
val = OptionalAttribute("val", XsdBoolean, default=True)
class CT_Boolean_Explicit(BaseOxmlElement):
"""Always spells out the `val` attribute, e.g. `val=1`.
At least one boolean element is improperly interpreted by one or more
versions of PowerPoint. The `c:overlay` element is interpreted as |False|
when no `val` attribute is present, contrary to the behavior described in
the schema. A remedy for this is to interpret a missing `val` attribute
as |True| (consistent with the spec), but always write the attribute
whenever there is occasion for changing the element.
"""
_val = OptionalAttribute("val", XsdBoolean, default=True)
@property
def val(self):
return self._val
@val.setter
def val(self, value):
val_str = "1" if bool(value) is True else "0"
self.set("val", val_str)
class CT_Double(BaseOxmlElement):
"""
Used for floating point values.
"""
val = RequiredAttribute("val", XsdDouble)
class CT_Layout(BaseOxmlElement):
"""
``<c:layout>`` custom element class
"""
manualLayout = ZeroOrOne("c:manualLayout", successors=("c:extLst",))
@property
def horz_offset(self):
"""
The float value in ./c:manualLayout/c:x when
c:layout/c:manualLayout/c:xMode@val == "factor". 0.0 if that XPath
expression finds no match.
"""
manualLayout = self.manualLayout
if manualLayout is None:
return 0.0
return manualLayout.horz_offset
@horz_offset.setter
def horz_offset(self, offset):
"""
Set the value of ./c:manualLayout/c:x@val to *offset* and
./c:manualLayout/c:xMode@val to "factor". Remove ./c:manualLayout if
*offset* == 0.
"""
if offset == 0.0:
self._remove_manualLayout()
return
manualLayout = self.get_or_add_manualLayout()
manualLayout.horz_offset = offset
class CT_LayoutMode(BaseOxmlElement):
"""
Used for ``<c:xMode>``, ``<c:yMode>``, ``<c:wMode>``, and ``<c:hMode>``
child elements of CT_ManualLayout.
"""
val = OptionalAttribute("val", ST_LayoutMode, default=ST_LayoutMode.FACTOR)
class CT_ManualLayout(BaseOxmlElement):
"""
``<c:manualLayout>`` custom element class
"""
_tag_seq = (
"c:layoutTarget",
"c:xMode",
"c:yMode",
"c:wMode",
"c:hMode",
"c:x",
"c:y",
"c:w",
"c:h",
"c:extLst",
)
xMode = ZeroOrOne("c:xMode", successors=_tag_seq[2:])
x = ZeroOrOne("c:x", successors=_tag_seq[6:])
del _tag_seq
@property
def horz_offset(self):
"""
The float value in ./c:x@val when ./c:xMode@val == "factor". 0.0 when
./c:x is not present or ./c:xMode@val != "factor".
"""
x, xMode = self.x, self.xMode
if x is None or xMode is None or xMode.val != ST_LayoutMode.FACTOR:
return 0.0
return x.val
@horz_offset.setter
def horz_offset(self, offset):
"""
Set the value of ./c:x@val to *offset* and ./c:xMode@val to "factor".
"""
self.get_or_add_xMode().val = ST_LayoutMode.FACTOR
self.get_or_add_x().val = offset
class CT_NumFmt(BaseOxmlElement):
"""
``<c:numFmt>`` element specifying the formatting for number labels on a
tick mark or data point.
"""
formatCode = RequiredAttribute("formatCode", XsdString)
sourceLinked = OptionalAttribute("sourceLinked", XsdBoolean)
class CT_Title(BaseOxmlElement):
"""`c:title` custom element class."""
_tag_seq = ("c:tx", "c:layout", "c:overlay", "c:spPr", "c:txPr", "c:extLst")
tx = ZeroOrOne("c:tx", successors=_tag_seq[1:])
spPr = ZeroOrOne("c:spPr", successors=_tag_seq[4:])
del _tag_seq
def get_or_add_tx_rich(self):
"""Return `c:tx/c:rich`, newly created if not present.
Return the `c:rich` grandchild at `c:tx/c:rich`. Both the `c:tx` and
`c:rich` elements are created if not already present. Any
`c:tx/c:strRef` element is removed. (Such an element would contain
a cell reference for the axis title text in the chart's Excel
worksheet.)
"""
tx = self.get_or_add_tx()
tx._remove_strRef()
return tx.get_or_add_rich()
@property
def tx_rich(self):
"""Return `c:tx/c:rich` or |None| if not present."""
richs = self.xpath("c:tx/c:rich")
if not richs:
return None
return richs[0]
@staticmethod
def new_title():
"""Return "loose" `c:title` element containing default children."""
return parse_xml(
"<c:title %s>" " <c:layout/>" ' <c:overlay val="0"/>' "</c:title>" % nsdecls("c")
)
class CT_Tx(BaseOxmlElement):
"""
``<c:tx>`` element containing the text for a label on a data point or
other chart item.
"""
strRef = ZeroOrOne("c:strRef")
rich = ZeroOrOne("c:rich")
def _new_rich(self):
return parse_xml(
"<c:rich %s>"
" <a:bodyPr/>"
" <a:lstStyle/>"
" <a:p>"
" <a:pPr>"
" <a:defRPr/>"
" </a:pPr>"
" </a:p>"
"</c:rich>" % nsdecls("c", "a")
)
class CT_UnsignedInt(BaseOxmlElement):
"""
``<c:idx>`` element and others.
"""
val = RequiredAttribute("val", XsdUnsignedInt)