简体   繁体   中英

Python-pptx: “Show legend overlapping the chart”

I am using Office 2007. I found if I would like to show the legend overlapping the chart in office2007. The XML should be as the following.

`-<c:legend>
   <c:overlay val="1"/>` 

But no matter I use the API from python-pptx 'chart.legend.include_in_layout = True' or I leave it as the default. The generated XML would always be as the following.

`-<c:legend>
   <c:overlay/>` 

Without the val=1, then office2007 won't show the format properly. What can I do to force the python-pptx to write the val=1? thanks.

Explanation

In short, the True value is not explicitly set (in contrast to False ) because True corresponds to the default value of overlay 's val attribute.

To explain it in more detail - you can follow the python-pptx hierarchy as follows: overlay is mapped to CT_Boolean (all overlay oxml elements are instantiated from CT_Boolean). The actual val parameter is then mapped via OptionalAttribute and is defined with the default value of True :

class CT_Boolean(BaseOxmlElement):
    """
    Common complex type used for elements having a True/False value.
    """
    val = OptionalAttribute('val', XsdBoolean, default=True)

Now, when setting the optional attribute to its default value, it is actually skipped/deleted, as you can see here if value == self._default :

class OptionalAttribute(BaseAttribute):
    """
    Defines an optional attribute on a custom element class. An optional
    attribute returns a default value when not present for reading. When
    assigned |None|, the attribute is removed.
    """

    @property
    def _setter(self):
        def set_attr_value(obj, value):
            if value == self._default:
                if self._clark_name in obj.attrib:
                    del obj.attrib[self._clark_name]
                return
            str_value = self._simple_type.to_xml(value)
            obj.set(self._clark_name, str_value)
        return set_attr_value

Fix - provide custom CT_Boolean class

Add these lines somewhere before you need to use the overlay. It will overwrite python-pptx overlay mapping with the custom CT_Boolean_NoDefault class:

from pptx.oxml import register_element_cls
from pptx.oxml.xmlchemy import BaseOxmlElement, OptionalAttribute
from pptx.oxml.simpletypes import XsdBoolean


class CT_Boolean_NoDefault(BaseOxmlElement):
    """
    Common complex type used for elements having a True/False value with no
    default value.
    """
    val = OptionalAttribute('val', XsdBoolean)

register_element_cls('c:overlay', CT_Boolean_NoDefault)

This worked for me and finally I got:

<c:legend>
    <c:overlay val="1"/>
</c:legend>

Fix - modify python-pptx permanently

This is not recommended but you might want to modify python-pptx instead of adding the solution from above for each script you run.

First, add the following to pptx/oxml/chart/shared.py which defines a new bool class without a default value:

class CT_Boolean_NoDefault(BaseOxmlElement):
    """
    Common complex type used for elements having a True/False value.
    """
    val = OptionalAttribute('val', XsdBoolean)

Second, modify pptx/oxml/__init__.py to add the new bool class:

from .chart.shared import (
    CT_Boolean, CT_Double, CT_Layout, CT_LayoutMode, CT_ManualLayout,
    CT_NumFmt, CT_Tx, CT_UnsignedInt, CT_Boolean_NoDefault
)

Third, modify pptx/oxml/__init__.py to change the mapping of the overlay element to the new bool class:

register_element_cls('c:overlay', CT_Boolean_NoDefault)

Better solution

In case you have time, please submit a ticket here so it might become a permanent fix. In case @scanny finds some time, he will read this. Perhaps there is some better solution for this, too, and I've completely missed something.

@pansen 's analysis is spot-on. Here's an alternative way to get this working in your case that might be a little lighter weight:

def include_in_layout(legend):
    legend_element = legend._element
    overlay = legend_element.get_or_add_overlay()
    overlay.set('val', '1')

This appears to be a localized non-conformance of that version of PowerPoint with the ISO/IEC 29500 spec. As pansen rightly points out, a missing val attribute is to be interpreted the same as val=1 ( True ). I'd be interested to discover how extensive this non-conformance goes, ie what other elements exhibit this same behavior. The CT_Boolean type is used quite frequently in PowerPoint, for things like bold, italic, varyColors, smooth, and on and on. So a "compensating" fix would need to be applied carefully to avoid reporting incorrect results for other elements.

I think I'll take pansen's cue and use a specialized element class for this element only. It will still report True for an element without the val attribute, which will be inconsistent with the observed behavior on this version of PowerPoint; but assuming other versions behave correctly (according to the spec), the inconsistency will be localized and at least assigning True to that property will make the legend show up the way you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM