使用 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
我正在尝试使用 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