使用 copy.deepcopy 和 python-pptx 将列添加到 table 会导致单元格属性损坏

Using copy.deepcopy with python-pptx to add a column to a table leads to cell attributes being corrupted

我正在尝试使用 python-pptx 将一列附加到 PowerPoint 中的 table。许多线程提到了解决方案:

def append_col(prs_obj, sl_i, sh_i):
    # prs_obj is a pptx.Presentation('path') object. 
    # sli_i and sh_i are int indexs to locate a particular table object.

    tab = prs_obj.slides[sl_i].shapes[sh_i].table
    new_col = copy.deepcopy(tab._tbl.tblGrid.gridCol_lst[-1])
    tab._tbl.tblGrid.append(new_col)  # copies last grid element

    for tr in tab._tbl.tr_lst:
        # duplicate last cell of each row
        new_tc = copy.deepcopy(tr.tc_lst[-1])
        tr.append(new_tc)
        cell = _Cell(new_tc, tr.tc_lst)
        cell.text = '--'
    return tab

在 运行 这之后,当您打开 PowerPoint 时,新的列将会出现,但它不会包含 cell.text。如果您在单元格中单击并键入,字母将出现在前一列的单元格中。保存 powerpoint 使您可以正常编辑该列,但显然您已经丢失了 cell.text(和格式)。

问题更新 1 - 来自 @scanny

的评论

对于最简单的情况,a (1x3) table,像这样:|xx|--|xx|附加列前后的 tab._tbl.xml 打印是:

xml diff 1

xml diff 2

xml diff 3

xml diff 4

问题更新 2 - 来自 @scanny 的评论 我修改了上面的 append_col 函数,强制从复制的 gridCol 中删除 extLst 元素。这解决了在一个单元格中键入而文本出现在另一个单元格中的问题。

def append_col(prs_obj, sl_i, sh_i):
    # existing lines removed for brevity

    # New Code

    tblchildren = tab._tbl.getchildren()
        for child in tblchildren:
            if isinstance(child, oxml.table.CT_TableGrid):
                ws = set()
                for j in child:
                    if j.w not in ws:
                        ws.add(j.w)
                    else:
                        for elem in j:
                            j.remove(elem)
    return tab

但是 cell.text(和格式)仍然缺失。此外,手动保存演示文稿会更改 tab.xml 对象。手动打开PowerPoint演示文稿前后的截图为:

AFTER removing extLst, before manual save - xml diff 1

AFTER removing extLst, AFTER manual save - xml diff 2

如果你真的想解决这类问题,你需要 reverse-engineer 这个词 XML tables 的这个方面。

开始的地方是 table 之前和之后(添加一列)XML 转储,识别 Word 所做的更改,然后复制重要的内容(例如 revision-numbers 可能 没关系

这个过程可以通过一个小例子来简化,比如 2 x 2 table 到 2 x 3 table。

您可以使用 .xml 属性为 python-docx XML 元素获取 XML,例如:

print(tab._tbl.xml)

您可以比较 deepcopy 结果,然后根据具体差异开始解释结果无效。我希望您会发现 table 项目具有唯一的 ID,当您复制它们时,会发生奇怪的事情。

在 Scanny 的帮助下,我想出了以下可行的解决方法:

def append_col(prs_obj, sl_i, sh_i):
    tab = prs_obj.slides[sl_i].shapes[sh_i].table
    new_col = copy.deepcopy(tab._tbl.tblGrid.gridCol_lst[-1])
    tab._tbl.tblGrid.append(new_col)  # copies last grid element
    for tr in tab._tbl.tr_lst:
        new_tc = copy.deepcopy(tr.tc_lst[-1])
        tr.tc_lst[-1].addnext(new_tc)
        cell = _Cell(new_tc, tr.tc_lst)
        for paragraph in cell.text_frame.paragraphs:
            for run in paragraph.runs:
                run.text = '--'
    tblchildren = tab._tbl.getchildren()
    for child in tblchildren:
        if isinstance(child, oxml.table.CT_TableGrid):
            ws = set()
            for j in child:
                if j.w not in ws:
                    ws.add(j.w)
                else:
                    # print('j:\n', j.xml)
                    for elem in j:
                            j.remove(elem)
    return tab