Python-PPTX: Changing table style or adding borders to cells

Question:

I’ve started putting together some code to take Pandas data and put it into a PowerPoint slide. The template I’m using defaults to Medium Style 2 – Accent 1 which would be fine as changing the font and background are fairly easy, but there doesn’t appear to be an implemented portion to python-pptx that allows for changing cell borders. Below is my code, open to any solution. (Altering the XML or changing the template default to populate a better style would be good options for me, but haven’t found good documentation on how to do either). Medium Style 4 would be ideal for me as it has exactly the borders I’m looking for.

import pandas
import numpy
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor

#Template Location
tmplLoc = 'C:/Desktop/'

#Read in Template
prs = Presentation(tmplLoc+'Template.pptx')

#Import data as Pandas Dataframe - dummy data for now
df = pandas.DataFrame(numpy.random.randn(10,10),columns=list('ABCDEFGHIJ'))

#Determine Table Header
header = list(df.columns.values)

#Determine rows and columns
in_rows = df.shape[0]
in_cols = df.shape[1]

#Insert table from C1 template
slide_layout = prs.slide_layouts[11]
slide = prs.slides.add_slide(slide_layout)

#Set slide title
title_placeholder = slide.shapes.title
title_placeholder.text = "Slide Title"

#Augment placeholder to be a table
placeholder = slide.placeholders[1]
graphic_frame = placeholder.insert_table(rows = in_rows+1, cols = in_cols)
table = graphic_frame.table
#table.apply_style = 'MediumStyle4'
#table.apply_style = 'D7AC3CCA-C797-4891-BE02-D94E43425B78'

#Set column widths
table.columns[0].width = Inches(2.23)
table.columns[1].width = Inches(0.9)
table.columns[2].width = Inches(0.6)
table.columns[3].width = Inches(2)
table.columns[4].width = Inches(0.6)
table.columns[5].width = Inches(0.6)
table.columns[6].width = Inches(0.6)
table.columns[7].width = Inches(0.6)
table.columns[8].width = Inches(0.6)
table.columns[9].width = Inches(0.6)
#total_width = 2.23+0.9+0.6+2+0.6*6

#Insert data into table
for rows in xrange(in_rows+1):
    for cols in xrange(in_cols):
        #Write column titles
        if rows == 0:
            table.cell(rows, cols).text = header[cols]
            table.cell(rows, cols).text_frame.paragraphs[0].font.size=Pt(14)
            table.cell(rows, cols).text_frame.paragraphs[0].font.color.rgb = RGBColor(255, 255, 255)
            table.cell(rows, cols).fill.solid()
            table.cell(rows, cols).fill.fore_color.rgb=RGBColor(0, 58, 111) 
        #Write rest of table entries
        else:
            table.cell(rows, cols).text = str("{0:.2f}".format(df.iloc[rows-1,cols]))
            table.cell(rows, cols).text_frame.paragraphs[0].font.size=Pt(10)
            table.cell(rows, cols).text_frame.paragraphs[0].font.color.rgb = RGBColor(0, 0, 0)
            table.cell(rows, cols).fill.solid()
            table.cell(rows, cols).fill.fore_color.rgb=RGBColor(255, 255, 255)

#Write Table to File
prs.save('C:/Desktop/test.pptx')
Asked By: JJFord3

||

Answers:

Maybe not really clean code but allowed me to adjust all borders of all cells in a table:

from pptx.oxml.xmlchemy import OxmlElement

def SubElement(parent, tagname, **kwargs):
        element = OxmlElement(tagname)
        element.attrib.update(kwargs)
        parent.append(element)
        return element

def _set_cell_border(cell, border_color="000000", border_width='12700'):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    for lines in ['a:lnL','a:lnR','a:lnT','a:lnB']:
        ln = SubElement(tcPr, lines, w=border_width, cap='flat', cmpd='sng', algn='ctr')
        solidFill = SubElement(ln, 'a:solidFill')
        srgbClr = SubElement(solidFill, 'a:srgbClr', val=border_color)
        prstDash = SubElement(ln, 'a:prstDash', val='solid')
        round_ = SubElement(ln, 'a:round')
        headEnd = SubElement(ln, 'a:headEnd', type='none', w='med', len='med')
        tailEnd = SubElement(ln, 'a:tailEnd', type='none', w='med', len='med')

Based on this post: https://groups.google.com/forum/#!topic/python-pptx/UTkdemIZICw

Answered By: JuuLes87

In case someone else comes across this issue again, some changes should be made to the solution posted by JuuLes87 to avoid that Microsoft Office PowerPoint requires to repair the generated presentation.

After carefully inspecting the xml string of the table generated by pptx, I found that the requirement to repair the presentation seemed to be due to the duplicated nodes of ‘a:lnL’ or ‘a:lnR’ or ‘a:lnT’ or ‘a:lnB’ in the children elements of ‘a:tcPr’. So we only need to remove nodes of [‘a:lnL’,’a:lnR’,’a:lnT’,’a:lnB’] before these nodes are inserted as below.

from pptx.oxml.xmlchemy import OxmlElement

def SubElement(parent, tagname, **kwargs):
    element = OxmlElement(tagname)
    element.attrib.update(kwargs)
    parent.append(element)
    return element

def _set_cell_border(cell, border_color="000000", border_width='12700'):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    for lines in ['a:lnL','a:lnR','a:lnT','a:lnB']:
        
        # Every time before a node is inserted, the nodes with the same tag should be removed.
        tag = lines.split(":")[-1]
        for e in tcPr.getchildren():
            if tag in str(e.tag):
                tcPr.remove(e)
        # end

        ln = SubElement(tcPr, lines, w=border_width, cap='flat', cmpd='sng', algn='ctr')
        solidFill = SubElement(ln, 'a:solidFill')
        srgbClr = SubElement(solidFill, 'a:srgbClr', val=border_color)
        prstDash = SubElement(ln, 'a:prstDash', val='solid')
        round_ = SubElement(ln, 'a:round')
        headEnd = SubElement(ln, 'a:headEnd', type='none', w='med', len='med')
        tailEnd = SubElement(ln, 'a:tailEnd', type='none', w='med', len='med')
Answered By: NeverMore

I had a hard time figuring out why this wasn’t working. For anyone else struggling with this, I had to add the following to the end of the function:

return cell

When using, you want to use the function as such:

cell = _set_cell_border(cell)
Answered By: JonB