Skip to content

feature: Point.invertIfNegative #776

@gergness

Description

@gergness

It is currently possible to set invertIfNegative property on a BarSeries, but when the fill colors for the data points within that bar series have been overridden, PowerPoint does not respect the series level property, it instead expects each data point to have its own invertIfNegative.

I don't think it is currently possible to set this property on a data point without digging into python-pptx's internal XML representation. Would it be possible to add a property on Point (or possibly make a subclass of Point for only points belonging to a BarSeries) that allows setting invertIfNegative?

Here's code to reproduce, an ugly work around, and below are the pptx files and images of them rendering in powerpoint.

from copy import deepcopy

from pptx import Presentation

from pptx.chart.data import CategoryChartData
from pptx.chart.series import BarSeries
from pptx.dml.color import RGBColor
from pptx.enum.chart import XL_CHART_TYPE
from pptx.util import Inches

# create presentation with 1 slide ------
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[5])

# define chart data ---------------------
chart_data = CategoryChartData()
chart_data.categories = ['apple', 'banana', 'carrot']
chart_data.add_series('Series 1', (5.2, -2.1, -25))

# add chart to slide --------------------
x, y, cx, cy = Inches(2), Inches(2), Inches(6), Inches(4.5)
graphic_frame = slide.shapes.add_chart(
    XL_CHART_TYPE.COLUMN_CLUSTERED, x, y, cx, cy, chart_data
)
chart = graphic_frame.chart

# ---Set invert_if_negative=False for bar charts---
for ser in chart.series:
    ser.invert_if_negative = False

# ---But also set the colors for each bar
points = chart.series[0].points
for idx, rgb in enumerate(("FF0000", "00FF00", "0000FF")):
    fill = points[idx].format.fill
    fill.solid()
    fill.fore_color.rgb = RGBColor.from_string(rgb)

prs.save('ignores-invertIfNegative.pptx')


# --- A fix is to dig into the pptx internals and copy the invertIfNegative 
# --- to each data point
for idx in range(3):
    invertIfNegative = deepcopy(points._element.get_or_add_invertIfNegative())
    dPt = points[idx]._ser.get_or_add_dPt_for_point(idx)
    dPt.append(invertIfNegative)

prs.save('fixed.pptx')

ignores-invertIfNegative.pptx
Screen Shot 2021-12-17 at 11 09 05 AM

fixed.pptx
Screen Shot 2021-12-17 at 11 08 13 AM

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions