Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tbl: #86 add/remove row and column support #399

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

Ignisor
Copy link

@Ignisor Ignisor commented Jun 27, 2018

#86

Added methods for adding and deleting row/column in _RowCollection and _ColumnCollection classes.

@Ignisor
Copy link
Author

Ignisor commented Jun 27, 2018

@scanny take a look please)

@Ignisor
Copy link
Author

Ignisor commented Jul 12, 2018

@scanny 😞

@Ignisor
Copy link
Author

Ignisor commented Aug 2, 2018

ping @scanny

@scanny
Copy link
Owner

scanny commented Aug 7, 2018

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 :)

@scanny
Copy link
Owner

scanny commented Aug 19, 2018

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:trPrelements 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.

@Ignisor
Copy link
Author

Ignisor commented Aug 20, 2018

Hi @scanny. Thanks for your reply. I will try to do stuff that you mentioned when I will have some more free time :)

@OD1995
Copy link

OD1995 commented Apr 7, 2020

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 = ''

@willkim1
Copy link

willkim1 commented Jul 17, 2020

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

@mszbot
Copy link

mszbot commented Aug 12, 2021

@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:
image

pptx downloaded.pptx

@viseshrp
Copy link

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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants