Python-docx: Changing Table Cell Borders and Inserting XML Elements

Created on 13 Sep 2017  路  2Comments  路  Source: python-openxml/python-docx

Hello,

I am trying to modify a table to remove the borders around the first and last rows of a table. I realize that there is not a method for adjusting tcBorders in python-docx at this point. How do I insert some XML into the XML section? I need to insert this...

        <w:tcBorders>
            <w:top w:val="nil"/>
            <w:left w:val="nil"/>
            <w:bottom w:val="single" w:color="auto" w:space="0" w:sz="4"/>
            <w:right w:val="nil"/>
        </w:tcBorders>

To accomplish my goal.

I spent a good deal of time playing with some of the XML type functionality available. I am at a loss.

This is what is generated...

<w:tc>
    <w:tcPr>
        <w:tcW w:w="2160" w:type="dxa"/>
    </w:tcPr>
    <w:p>
        <w:r>
            <w:t>Upper Left</w:t>
        </w:r>
    </w:p>
</w:tc>

This is what I need...

<w:tc>
    <w:tcPr>
        <w:tcW w:w="2160" w:type="dxa"/>
        <w:tcBorders>
            <w:top w:val="nil"/>
            <w:left w:val="nil"/>
            <w:bottom w:val="single" w:color="auto" w:space="0" w:sz="4"/>
            <w:right w:val="nil"/>
        </w:tcBorders>
    </w:tcPr>
    <w:p w:rsidRDefault="00777F07" w:rsidR="00D60748">
        <w:r>
            <w:t>Upper Left</w:t>
        </w:r>
    </w:p>
</w:tc>

This is what I have come up with so far...

# -*- coding: utf-8 -*-
"""
table.py

Generate a Word Document and manipulate the XML.
"""

from docx import Document
from docx.oxml.table import CT_Row, CT_Tc

d = Document()

d.add_heading('Test Table', level=3)

table = d.add_table(rows=4, cols=4, style='TableGrid')

table.rows[0].cells[0].text = "Upper Left"
table.rows[3].cells[3].text = "Lower Right"

lastRow = len(table.rows) - 1

tbl = table._tbl

rows = tbl.getchildren()

for row in rows:

    if isinstance(row, CT_Row):

        cells = row.getchildren()

        if row.tr_idx == 0:    # Top Row
            print "Top Row"       

            for cell in cells:

                if isinstance(cell, CT_Tc):

                    tcPr = cell.tcPr

                    print type(tcPr)

        elif row.tr_idx == lastRow:    # Bottom Row
            print "Bottom Row"       

            for cell in cells:

                if isinstance(cell, CT_Tc):

                    tcPr = cell.tcPr

                    print type(tcPr)
                    print cell.xml

d.save('originalTable.docx')

All help and suggestions are appreciated.

Thank you,

Willie

Most helpful comment

Hi. I tried to define a table style myself recently and met the same problem as yours. I found a solution and hope it is still helpful to you.

def modifyBorder(table):
    tbl = table._tbl # get xml element in table
    for cell in tbl.iter_tcs():
        tcPr = cell.tcPr # get tcPr element, in which we can define style of borders
        tcBorders = OxmlElement('w:tcBorders')
        top = OxmlElement('w:top')
        top.set(qn('w:val'), 'nil')

        left = OxmlElement('w:left')
        left.set(qn('w:val'), 'nil')

        bottom = OxmlElement('w:bottom')
        bottom.set(qn('w:val'), 'nil')
        bottom.set(qn('w:sz'), '4')
        bottom.set(qn('w:space'), '0')
        bottom.set(qn('w:color'), 'auto')

        right = OxmlElement('w:right')
        right.set(qn('w:val'), 'nil')

        tcBorders.append(top)
        tcBorders.append(left)
        tcBorders.append(bottom)
        tcBorders.append(right)
        tcPr.append(tcBorders)

After this modification, the table should be what you need. Actually I was inspired by https://github.com/python-openxml/python-docx/issues/105. Hope this will help.

All 2 comments

Hi. I tried to define a table style myself recently and met the same problem as yours. I found a solution and hope it is still helpful to you.

def modifyBorder(table):
    tbl = table._tbl # get xml element in table
    for cell in tbl.iter_tcs():
        tcPr = cell.tcPr # get tcPr element, in which we can define style of borders
        tcBorders = OxmlElement('w:tcBorders')
        top = OxmlElement('w:top')
        top.set(qn('w:val'), 'nil')

        left = OxmlElement('w:left')
        left.set(qn('w:val'), 'nil')

        bottom = OxmlElement('w:bottom')
        bottom.set(qn('w:val'), 'nil')
        bottom.set(qn('w:sz'), '4')
        bottom.set(qn('w:space'), '0')
        bottom.set(qn('w:color'), 'auto')

        right = OxmlElement('w:right')
        right.set(qn('w:val'), 'nil')

        tcBorders.append(top)
        tcBorders.append(left)
        tcBorders.append(bottom)
        tcBorders.append(right)
        tcPr.append(tcBorders)

After this modification, the table should be what you need. Actually I was inspired by https://github.com/python-openxml/python-docx/issues/105. Hope this will help.

Hi @KailiDing ,

I tried following your suggestion but for some reason, it does not add any border. I would quite like to circumvent defining a style in a template document as the borders required for my tables need to be added dynamically. Is there something glaringly obvious that I am missing?

Minimally reproducible example:

from docx import Document
from docx.oxml.table import CT_Row, CT_Tc
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
import pandas as pd

#setup
df = pd.DataFramedf = pd.DataFrame({"Variable1": {0:"var1", 1:"var1", 2:"var1", 3:"var1", 4:"var1", 5:"var1", 6:"var1", 7:"var2", 8:"var2",9:"var2",10: "var2",11: "var2",12: "var2",13: "var3",14: "var3",15: "var3",16: "var3",17: "var3",18: "var4",19: "var4",20: "var4",21: "var4",22: "var5",23: "var5",24: "var5",25: "var6",26: "var6",27: "var7"},
 "Variable2": {0: "var2",1: "var3",2: "var4",3: "var5",4: "var6",5: "var7",6: "var8",7: "var3",8: "var4",9: "var5",10: "var6",11: "var7",12: "var8",13: "var4",14: "var5",15: "var6",16: "var7",17: "var8",18: "var5",19: "var6",20: "var7",21: "var8",22: "var6",23: "var7",24: "var8",25: "var7",26: "var8",27: "var8"},
 "Correlation_Coefficient": {0: "0.44",1: "0.26",2: "0.22",3: "0.33",4: "-0.07",5: "0.08",6: "0.07",7: "0.26",8: "0.21",9: "0.31",10: "-0.06",11: "0.06",12: "0.15",13: "0.03",14: "0.70",15: "-0.75",16: "0.04",17: "0.01",18: "0.69",19: "0.60",20: "0.09",21: "0.05",22: "-0.13",23: "0.06",24: "0.02",25: "0.02",26: "0.02",27: "0.76"},
 "CI_low": {0: "0.37",1: "0.17",2: "0.13",3: "0.25",4: "-0.15",5: "-0.01",6: "-0.02",7: "0.18",8: "0.12",9: "0.23",10: "-0.14",11: "-0.03",12: "0.06",13: "-0.06",14: "0.66",15: "-0.78",16: "-0.05",17: "-0.08",18: "0.64",19: "0.54",20: "-0.00",21: "-0.04",22: "-0.22",23: "-0.03",24: "-0.07",25: "-0.07",26: "-0.07",27: "0.72"},
 "CI_high": {0: "0.51",1: "0.34",2: "0.30",3: "0.40",4: "0.02",5: "0.17",6: "0.16",7: "0.34",8: "0.29",9: "0.39",10: "0.03",11: "0.15",12: "0.23",13: "0.12",14: "0.75",15: "-0.71",16: "0.13",17: "0.09",18: "0.73",19: "0.65",20: "0.17",21: "0.14",22: "-0.04",23: "0.15",24: "0.10",25: "0.10",26: "0.10",27: "0.79"},
 "pvalues": {0: "0.00",1: "0.00",2: "0.00",3: "0.00",4: "0.14",5: "0.07",6: "0.12",7: "0.00",8: "0.00",9: "0.00",10: "0.20",11: "0.18",12: "0.00",13: "0.47",14: "0.00",15: "0.00",16: "0.35",17: "0.88",18: "0.00",19: "0.00",20: "0.06",21: "0.28",22: "0.00",23: "0.18",24: "0.72",25: "0.72",26: "0.72",27: "0.00"}})
doc = Document()
table = doc.add_table(rows=len(df), cols=len(df.columns))

#writing some data
for i in range(0, len(df.columns)):
    headings = table.rows[0].cells
    headings[i].text = df.columns[i]

for i in range(1, len(df)):
    for j in range(0, len(df.columns)):
        row = table.rows[i].cells
        row[j].text = str(df.iloc[i, j])

#border formatting attempt
tbl = table._tbl
for cell in tbl.iter_tcs():
    tcPr = cell.tcPr
    tcBorders = OxmlElement("w:tcBorders")
    top = OxmlElement("w:top")
    top.set(qn("w:val"), "nil")

    left = OxmlElement("w:left")
    left.set(qn("w:val"), "nil")

    bottom = OxmlElement("w:bottom")
    bottom.set(qn("w:val"), "nil")
    bottom.set(qn("w:sz"), "4")
    bottom.set(qn("w:space"), "0")
    bottom.set(qn("w:color"), "auto")

    right = OxmlElement("w:right")
    right.set(qn("w:val"), "nil")

    tcBorders.append(top)
    tcBorders.append(left)
    tcBorders.append(bottom)
    tcBorders.append(right)
    tcPr.append(tcBorders)

doc.save("test.docx")
Was this page helpful?
0 / 5 - 0 ratings

Related issues

carlosmtz2106 picture carlosmtz2106  路  7Comments

sskadit picture sskadit  路  4Comments

ssuzen picture ssuzen  路  6Comments

frenet picture frenet  路  7Comments

raphaelvalentin picture raphaelvalentin  路  7Comments