Python Docx установить фон ячейки таблицы и цвет текста

Я использую Python 2.7 с DOCX, и я хотел бы изменить фон и цвет текста ячеек в моей таблице в зависимости от условий.

Я не смог найти ни одного полезного ресурса о форматировании одной ячейки

Какие-либо предложения?

Редактировать 1

мой код

style_footer = "DarkList"
style_red = "ColorfulList"
style_yellow = "LightShading"
style_green = "MediumShading2-Accent6"
style_transperent = "TableNormal"
for a,rec in enumerate(data):
    #V headinh se piše prvo polje iz table heada
    document.add_heading(rec['tableHead'][0][0], level=1)
    image_path = imageFolder + "\\" + slike[a]
    document.add_picture(image_path, height=Inches(3.5))

    #y += 28
    #worksheet.insert_image( y, 1,imageFolder + "/" + slike[a])


    for i, head in enumerate(rec['tableHead']):
        table = document.add_table(rows=1, cols = len(head))
        hdr_cells = table.rows[0].cells
        for a in range(0,len(head)):
            hdr_cells[a].text = head[a] 


    for a,body in enumerate(rec['tableData']):
        row_cells = table.add_row().cells

        for a in range(0,len(body)):
            if body[a]['style'] == 'footer':
                stil = style_footer
            elif body[a]['style'] == 'red':
                stil = style_red

            elif body[a]['style'] == 'yellow':
                stil = style_yellow
            elif body[a]['style'] == 'green':
                stil = style_green

            else:
                stil = style_transperent

            row_cells[a].add_paragraph(body[a]['value'], stil)

document.save(wordDoc)

Все клетки все те же.

10 ответов

Если вы хотите покрасить конкретную ячейку в таблице, вы можете использовать код ниже. Например, допустим, вам нужно заполнить первую ячейку в первой строке таблицы цветом RGB 1F5C8B:

from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml

shading_elm_1 = parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w')))
table.rows[0].cells[0]._tc.get_or_add_tcPr().append(shading_elm_1)

Теперь, если вы хотите также заполнить вторую ячейку в первой строке тем же цветом, вы должны создать новый элемент, иначе, если вы используете тот же элемент, что и выше, заливка будет двигаться дальше и исчезнет из первой ячейки...

shading_elm_2 = parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w')))
table.rows[0].cells[1]._tc.get_or_add_tcPr().append(shading_elm_2)

... и так далее для других клеток.

Источник: https://groups.google.com/forum/

С помощью решения Никоса Тавулариса мы должны создать новый элемент для каждой ячейки. Я создал функцию, которая этого добивается. Работает в версии Python 3.5.6 и версии python-docx 0.8.10

from docx.oxml import OxmlElement
from docx.oxml.ns import qn

def set_table_header_bg_color(table.rows[row_ix].cell):
    """
    set background shading for Header Rows
    """
    tblCell = cell._tc
    tblCellProperties = tc.get_or_add_tcPr()
    clShading = OxmlElement('w:shd')
    clShading.set(qn('w:fill'), "00519E") #Hex of Dark Blue Shade {R:0x00, G:0x51, B:0x9E}
    trPr.append(clShading)
    return cell
"""
End of set_table_header_bg_color Function
"""
# main function
"""
..
..
..
1. Load Document
..
2. Access the required section
..
3. Load the required Table
..
4. Traverse to the cell by accessing the rows object
..
"""
for each_row in table.rows :
    for each_cell in each_row.cells:
        if each_cell.value satisfies a condition:
            set_table_header_bg_color(table.rows[row_ix].cell)
"""
5. Continue execution
"""

Похоже, вместо использования cell.text = "Something" метод, который вам нужно использовать cell.add_paragraph("SomeText", a_style) с определенным стилем - вероятно, один из:

  • ColorfulGrid
  • ColorfulGrid-Accent1
  • ColorfulGrid-Accent2
  • ColorfulGrid-Accent3
  • ColorfulGrid-Accent4
  • ColorfulGrid-Accent5
  • ColorfulGrid-Accent6

Полный список здесь.

Если вы используете шаблон документа "по умолчанию" - в противном случае вам придется создать свой собственный.

Мы обнаружили, что если вы сделаете cell.add_paragraph('sometext', style_object), он сохранит существующий пустой абзац и добавит дополнительный абзац со стилем, что не идеально.

То, что вы хотите сделать, это что-то вроде:

# replace the entire content of cell with new text paragraph
cell.text = 'some text'
# assign new style to the first paragraph
cell.paragraphs[0].style = style_object 

Обратите внимание, что стиль применяется к абзацу, а не к ячейке, что не идеально для фоновых цветов (поскольку он не заполняет ячейку ввода, если у вас есть отступы. Я не нашел способа обойти это (кроме Если вы хотите, чтобы КАЖДАЯ ячейка имела цвет фона, вы можете применить стиль к table.style).

Также убедитесь, что ваши стили определены. Ты можешь проверить

styles = documents.styles
for s in styles:
  print s.name

чтобы увидеть все стили, которые у вас есть. Вы можете определить новые стили, а также загрузить шаблонный документ уже с заранее определенными стилями.

Я сделал видео, демонстрирующее способ сделать это здесь. Я черпал вдохновение у людей выше, но у меня все еще были проблемы, поэтому я сделал это тоже, чтобы помочь другим.

https://www.youtube.com/watch?v=1Mgb95yigkk&amp;amp;list=PL_W7lgC2xeJfWBUllp7ALKOM5GUBMCVoP

          from docx import Document
    from docx.oxml import OxmlElement
    from docx.oxml.ns import qn
    document = Document("youfile.docx")
    Table = document.tables[0]

    #GET CELLS XML ELEMENT
    cell_xml_element = Table.rows[1].cells[0]._tc
    #RETRIEVE THE TABLE CELL PROPERTIES
    table_cell_properties = cell_xml_element.get_or_add_tcPr()
    #CREATE SHADING OBJECT
    shade_obj = OxmlElement('w:shd')
    #SET THE SHADING OBJECT
    shade_obj.set(qn('w:fill'), "ff00ff")
    #APPEND THE PROPERTIES TO THE TABLE CELL PROPERTIES
    table_cell_properties.append(shade_obj)

    document.save("yoursavefile.docx")

Приведенный выше код изменит первую ячейку второй строки первой таблицы в документе. Пример вывода.

Взяв ответ Никоса Тавулариса, я бы просто изменил объявление shading_elm_1, как если бы вы включили цвет ячейки в цикл, например, все может стать беспорядочным.

Таким образом, мое предложение было бы следующим:

from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml

table.rows[0].cells[0]._tc.get_or_add_tcPr().append(parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w'))))

Если вы также хотите изменить цвет текста, вы можете установить его на прогонах внутри ячейки. Я написал эту функцию для одновременной обработки фона ячейки и цвета текста (используя метод Nikos для заливки):

      def shade_cell(cell, fill=None, color=None):

    if fill:
        shading_elm = parse_xml(r'<w:shd {} w:fill="{}"/>'.format(nsdecls('w'), fill))
        cell._tc.get_or_add_tcPr().append(shading_elm)

    if color:
        for p in cell.paragraphs:
            for r in p.runs:
                r.font.color.rgb = RGBColor.from_string(color)

Первоначально я пытался расширить решение Nikos, добавив w:color="XXXXXX"к w:shdтег, но это не сработало для меня. Однако настройка цвета шрифта при каждом запуске дала желаемый результат.

Я собрал предыдущие ответы и добавил некоторые функции. Не стесняйтесь тестировать: создайте новый файл, запустите «основную» часть внизу.

      """ adder for python-docx in order to change text style in tables:
font color, italic, bold
cell background color
based on answers on
https://stackoverflow.com/questions/26752856/python-docx-set-table-cell-background-and-text-color
"""
import docx # import python-docx (in order to create .docx report file)
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml

def change_table_cell(cell, background_color=None, font_color=None, font_size=None, bold=None, italic=None):
    """ changes the background_color or font_color or font style (bold, italic) of this cell.
    Leave the params as 'None' if you do not want to change them.
    params:
        cell: the cell to manipulate
        background_color: name for the color, e.g. "red" or "ff0000"
        font_color:
        font_size: size in pt (e.g. 10)
        bold:   requested font style. True or False, or None if it shall remain unchanged
        italic: requested font style. True or False, or None if it shall remain unchanged
    background_color: the color of cells background"""
    if background_color:
        shading_elm = parse_xml(r'<w:shd {} w:fill="{}"/>'.format(nsdecls('w'), background_color))
        cell._tc.get_or_add_tcPr().append(shading_elm)

    if font_color:
        for p in cell.paragraphs:
            for r in p.runs:
                r.font.color.rgb = docx.shared.RGBColor.from_string(font_color)

    if font_size:
        for p in cell.paragraphs:
            for r in p.runs:
                r.font.size = docx.shared.Pt(font_size)

    if bold is not None:
        for p in cell.paragraphs:
            for r in p.runs:
                r.bold = bold

    if italic is not None:
        for p in cell.paragraphs:
            for r in p.runs:
                r.italic = italic

def change_table_row(table_row, background_color=None, font_color=None, font_size=None, bold=None, italic=None):
    for cell in table_row.cells:
        change_table_cell(cell, background_color=background_color, font_color=font_color, font_size=font_size,
                          bold=bold,
                          italic=italic)

if __name__ == "__main__":  # do the following  code only if we run the file itself
    #document = docx.Document('template.docx') # create an instance of a word document, use the style that we have defined in 'template.docx'
    document = docx.Document()
    num_rows = 4
    num_cols = 3
    table = document.add_table(rows=num_rows, cols=num_cols)  # create empty table
    #table.style = document.styles['MyTableStyleBlue']  # test overwriting the predefined style
    # fill table
    for row in range(num_rows):
        for col in range(num_cols):
            table.rows[row].cells[col].text = f'row/col=({row},{col})'

    """ change color (see https://stackoverflow.com/questions/26752856/python-docx-set-table-cell-background-and-text-color) """
    # Nikos Tavoularis answered Apr 18, 2017 at 8:38
    shading_elm_1 = parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w')))
    table.rows[0].cells[0]._tc.get_or_add_tcPr().append(shading_elm_1)

    # test new function derived from dyoung's answere of May 25 at 7:34, 2022
    change_table_cell(table.rows[0].cells[0], background_color=None, font_color="ff0000", bold=False)
    change_table_cell(table.rows[1].cells[2], background_color="00ff00", font_color="ff0000", font_size=20, bold=True)
    change_table_row(table.rows[3], background_color="lightgreen", font_color="0000ff", italic=True)  # https://www.delftstack.com/howto/python/colors-in-python/

    document.save('table_shading_test.docx')

Если вы хотите изменить фон всего столбца, вот функция для этого. Передайте количество строк и столбца таблицы в эту функцию в качестве аргументов. Переберите количество строк и измените цвет фона для каждой ячейки.

      # to change background colour of the column cells
def change_columns_background(row_count, *columns):
    for i in range(1, row_count + 1):
        shading_elm_1 = parse_xml(
            r'<w:shd {} w:fill="BDD6EE"/>'.format(nsdecls('w')))
        columns[0].cells[i]._tc.get_or_add_tcPr().append(shading_elm_1)

Пример вызова функции:

      # change the background color of first column for a table
        change_columns_background(row_count, my_table.columns[0])

Если вы хотите перебирать ячейки подряд, используйте:

def color_row(row=0):
    'make row of cells background colored, defaults to column header row'
    row = t.rows[row]
    for cell in row.cells:
        shading_elm_2 = parse_xml(r'<w:shd {} w:fill="1F5C8B"/>'.format(nsdecls('w')))
        cell._tc.get_or_add_tcPr().append(shading_elm_2)

запустить функцию, чтобы раскрасить ячейки во второй строке

color_row(2)

Другие вопросы по тегам