# encoding: utf-8 """Chart data-label related oxml objects.""" from __future__ import absolute_import, division, print_function, unicode_literals 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): """ ```` 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( "\n" ' \n' " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ' \n' ' \n' ' \n' ' \n' ' \n' ' \n' "" % 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): """ ```` 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): """ ```` 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( "\n" ' \n' ' \n' ' \n' ' \n' ' \n' ' \n' ' \n' "" % 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('' % nsdecls("c")) def _new_showLegendKey(self): return parse_xml('' % nsdecls("c")) def _new_showPercent(self): return parse_xml('' % nsdecls("c")) def _new_showSerName(self): return parse_xml('' % nsdecls("c")) def _new_showVal(self): return parse_xml('' % nsdecls("c")) def _new_txPr(self): return CT_TextBody.new_txPr()