0
votes

Similar questions have been asked, but I still can't get it to work despite multiple tries. Python PPTX Bar Chart negative values , Color Specific Bar Chart Differently in Python PPTX

Here is a simple example:

from pptx import Presentation
from pptx.chart.data import ChartData
from pptx.enum.chart import XL_CHART_TYPE
from pptx.util import Cm
from pptx.dml.color import RGBColor

_red = RGBColor(255, 0, 0)
_green = RGBColor(0, 128, 0)

# create presentation with 1 slide ------
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[5])
x = ['one','two','three', 'four']
y = [-257.0, 1920.0, 2809.0, 500.0]
specs = {
    'height': Cm(7.82),
    'width': Cm(14.8),
    'left': Cm(2.53),
    'top': Cm(5.72)}

data = ChartData()
data.categories = x
label_values = tuple(y)
data.add_series('Series 1', label_values)
frame = slide.shapes.add_chart(XL_CHART_TYPE.BAR_CLUSTERED,                 
specs['left'],specs['top'], specs['width'],specs['height'], data)
chart = frame.chart
series = chart.series[0]

for i in range(0,len(y)):
    point = series.points[i]
    fill = point.format.fill
    fill.solid()
    fill.fore_color.rgb = _red if y[i]<0 else _green
series.invert_if_negative = False

prs.save('chart-01.pptx')

What I want

What I'm getting

I have tried many different configurations. Python-pptx is working, because by setting the boolean to true or false in my code with series.invert_if_negative the checkbox acts accordingly when I open powerpoint presentation as shown in the second image. It only displays correctly in powerpoint if I manually toggle it a couple times after the presentation has already been generated. I tried changing the location of the statement, setting it to true or false and even having it alternate multiple times before settling on false.

Interestingly this only happens with negative bar chart i.e. with positive values I can change the color as I please. Even if all the values are negative. I can't set them all to a certain color. The only time they have color and don't display as white boxes is when I don't try to change their color at all and they default to the presentation theme colors.

I suspect this is a powerpoint issue rather than pptx-python problem, but any help in resolving this will be much appreciated!

Powerpoint version: 15.36 on Mac Python-pptx version: 0.6.7

Thanks for the amazing library Scanny and your responses to all the other questions. It has been loads of help.

=======UPDATE=======

There is a workaround to this problem which is to use the patterned fill and set both back_color and fore_color to the same color to achieve the same effect.

2

2 Answers

1
votes

This could be a discrepancy between the default XML produced by python-pptx and that produced (and then later interpreted) by PowerPoint.

You should start by checking your version to make sure you're on the latest. There were a couple cases a while back where PowerPoint wasn't respecting the defaults stated in the spec, like perhaps c:invertIfNegative defaults to True. We added some code to fix those up.

Here are a couple tweaks to your code:

chart = slide.shapes.add_chart(
    XL_CHART_TYPE.BAR_CLUSTERED, specs['left'], specs['top'], 
    specs['width'], specs['height'], data
).chart
series = chart.series[0]
series.invert_if_negative = False

for idx, point in enumerate(series.points):
    fill = point.format.fill
    fill.solid()
    fill.fore_color.rgb = _red if y[idx]<0 else _green

print(series._element.xml)

prs.save('chart-01.pptx')

I don't think this fixes anything, but perhaps makes intent a little clearer and mostly prints out the XML for the series. You can move that line around before and after setting the invert option for example.

What you're looking for in that XML is an element like <c:invertIfNegative val=0>. If it's not present or has some other value, that would explain some things.

The "white" color on negative values is more precisely "no color", which now that I think of it could be because there is no background color set. It's reasonable to assume PowerPoint swaps foreground and background when "inverting". So one thing to try is setting the background color for negative items rather than foreground and see if that does anything interesting.

If you're unable to work this out, I recommend you open an issue on the GitHub issues list for python-pptx where we can dig into it a little better with the conversational mode there rather than with the Q&A format here.

0
votes

Haven't got the pptx module installed so I couldn't test my answer. But I can see your if-statement is formatted incorrect.

Try this:

from pptx import Presentation
from pptx.chart.data import ChartData
from pptx.enum.chart import XL_CHART_TYPE
from pptx.util import Cm
from pptx.dml.color import RGBColor

_red = RGBColor(255, 0, 0)
_green = RGBColor(0, 128, 0)

# create presentation with 1 slide ------
prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[5])
x = ['one','two','three', 'four']
y = [-257.0, 1920.0, 2809.0, 500.0]
specs = {
    'height': Cm(7.82),
    'width': Cm(14.8),
    'left': Cm(2.53),
    'top': Cm(5.72)}

data = ChartData()
data.categories = x
label_values = tuple(y)
data.add_series('Series 1', label_values)
frame = slide.shapes.add_chart(XL_CHART_TYPE.BAR_CLUSTERED,                 
specs['left'],specs['top'], specs['width'],specs['height'], data)
chart = frame.chart
series = chart.series[0]

for i in range(0,len(y)):
    point = series.points[i]
    fill = point.format.fill
    fill.solid()
    if y[i]<0:
        fill.fore_color.rgb = _red  
    else:
        fill.fore_color.rgb = _green
series.invert_if_negative = False