python-pptx
python-pptx copied to clipboard
tbl: #86 add/remove row and column support
https://github.com/scanny/python-pptx/issues/86
Added methods for adding and deleting row/column in _RowCollection and _ColumnCollection classes.
@scanny take a look please)
@scanny :disappointed:
ping @scanny
Apologies @Ignisor, I'll take a look at this later this week. Generally, best practice is open a conversation before starting work so I expect your submission. I don't always have time to look at unsolicited PRs because so few of them meet the basic standards. But I see you've included tests so I'll take a closer look :)
Hi @Ignisor, I've had a chance to take a look.
I think the best next step is for you to draft an enhancement proposal page (aka. analysis page) on this we can use to hash out the design details.
There's an existing Table
page here: https://github.com/scanny/python-pptx/blob/master/docs/dev/analysis/shp-table.rst, but that one came from early in the project and probably best for us to have a separate page devoted to Table Row
so we're not distracted by all the other details. This other page is recent and probably will be a better model: https://github.com/scanny/python-pptx/blob/master/docs/dev/analysis/dml-gradient.rst, in particular as far as the sections we'll need. You can cut and paste from the Table page as needed, in particular I think that will be a good source for the XML Schema excerpt, no need to dig that out element by element again.
There are a couple of general concerns I have. We can take those up as we work through the analysis document together, but here's a preview.
-
While adding a row should be straightforward, deleting a row is a potentially tricky business. The reason is that a cell can contain relationships to items outside the table (outside the slide in fact), and simply deleting a row (along with its cells) will leave those relationships dangling, possibly corrupting the file (leading to a repair error on load).
Personally, I'd be fully satisfied with adding a row, leaving deleting a row to another effort. But if you were committed to that functionality, we'd need the analysis to include an exhaustive account of all relationships that could appear in a table cell and how those would be resolved before deletion.
I'm sure hyperlinks can appear in a cell, and perhaps as the whole cell itself (rather than on a run of text inside the cell), so that's one potential relationship. I don't believe pictures can appear, so that's probably one we don't have to worry about. In any case, I think you get the idea.
-
Adding content by deep-copying entails the same risks. It works fine for "leaf" content like text, but breaks for similar reasons as delete for relationship content and any other content that has unique identifiers.
I'd be inclined to add a row by the same mechanism we use when we add a new table. The code is already in place and I believe structured such that it's suitable for reuse.
I understand there's some desire for format matching, and perhaps we can do that by copying only the
a:trPr
elements or whatever, but I'd want to see that thought through. I can easily imagine situations where that would be undesirable. I think the best bet here is to follow PowerPoint UI behavior, which is one of the reasons we have that section in the analysis document. -
One other thing I'm thinking is that as long as we're working on adding a row, we ought to at least look at how we might easily extend that functionality to insert a row at an arbitrary position. So the call signature for
Table.add_row()
might be:table.add_row(idx=None)
Where idx can be set if desired to determine the actual insert location (including using negative offsets like -1 to insert second to last), but defaults to len(rows).
Let me know if you need help getting started or locating the bits you need for the document.
Hi @scanny. Thanks for your reply. I will try to do stuff that you mentioned when I will have some more free time :)
I've attempted to use the code you added (below) to add a new column with the same formatting to a table. It has created a new column, but the formatting is not the same. The column is transparent with black borders. Do you know why this hasn't worked?
import copy
from pptx.table import _Cell
TAB = slide.shapes[0].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 = ''
It's been a while since this has been asked, but here is my hacky extension to ignisor's table.py pull request.
Allows me to copy a certain row and insert it into the table any number of times. Not up to scanny standards, but may be helpful to reference.
def copy_row_insert_after(self, copy_idx, insert_idx, init_cell_func: Optional[Callable[[_Cell], None]] = None):
'''
Duplicates target row to keep formatting and resets it's cells text_frames
(e.g. ``row = table.rows.copy_row_insert_after(0,1)``, which copies the first row and inserts after the second row as new third row).
Returns new |_Row| instance.
'''
new_row = copy.deepcopy(self._tbl.tr_lst[copy_idx]) # copies idx row element. Note: tr_lst idx is != _tbl idx.
for tc in new_row.tc_lst:
cell = _Cell(tc, new_row.tc_lst)
if init_cell_func:
init_cell_func(cell)
#_tbl[0] xml sets up the table and relationships <a:tblPr>: try table.rows.debug_tbl_idx(0)
#https://python-pptx.readthedocs.io/en/latest/dev/analysis/tbl-table.html?highlight=a%3AtblPr#xml-semantics
#_tbl[1] xml sets up the columns <a:tblGrid>: try table.rows.debug_tbl_idx(1)
#_tbl[2] xml is the first row <a:tr>: try table.rows.debug_tbl_idx(2)
self._tbl.insert(insert_idx + 2 + 1,new_row) #rows begin starting idx 2. Need to read _tbl[0], _tbl[1] xml.
return _Row(new_row, self)
def debug_tbl_idx(self, idx):
'''
Debugs _tbl items
(e.g. debugText = table.rows.debug_tbl_idx(0)).
Returns _tbl[0] xml.
'''
debugXml = self._tbl[idx].xml
return debugXml
@Ignisor I used this code to add a column and add text however the text doesnt show in powerpoint. Oddly enough, if you open the pptx in google slides or WPS it shows but not in powerpoint. Any idea if adding text to the newly added columns work for you using this code?
Example output:
Made something for my needs thanks to this PR:
def resize_table(table, num_rows, num_cols):
# CAVEAT: This hack to remove rows/cols can only be used for empty tables
# or tables without links to other content.
# https://github.com/scanny/python-pptx/pull/399#issuecomment-414149996
while len(table.rows) > num_rows:
row = table.rows[len(table.rows) - 1]
table._tbl.remove(row._tr)
while len(table.columns) > num_cols:
column = table.columns[len(table.columns) - 1]
col_idx = table._tbl.tblGrid.index(column._gridCol)
for tr in table._tbl.tr_lst:
tr.remove(tr.tc_lst[col_idx])
table._tbl.tblGrid.remove(column._gridCol)