In openpyxl, how to move or copy a cell range with formatting, merged cells, formulas and hyperlinks

ぐ巨炮叔叔 提交于 2019-12-22 16:40:54

问题


I am trying to move a cell range inside a worksheet using Python 2.7 + openpyxl. What seemed to be an easy and basic task turned out to be close to impossible. Here is how I'd like it to look in Excel:

To make the task easier let's assume I only need to move range from given cell to the end of data (to last row and column). My first idea was easy:

for i, column in enumerate(columns):
    if i >= starting_col:
        for j, cell in enumerate(column):
            if j >= starting_row:
                copy_cell(ws, j+1, i+1, j+1, i+movement)

But hey, what how to efficiently and fully realize copy_cell? By trial and error I managed to come to 4 things I need to copy:

  1. Value
  2. Style
  3. Hyperlinks
  4. Number format

It didn't work quite as I expected - most of cells are copied properly but hyperlinks don't seem to work, copying .style attribute didn't work either (hence my try to access _style which worked) and my worst problem appeared - merged cell ranged. How to deal with them? My copy_cell() looks like this at the moment:

def copy_cell(ws, from_row, from_col, to_row, to_col):
    # first value
    ws.cell(row=to_row, column=to_col).value = ws.cell(row=from_row, column=from_col).value
    # second formatting
    from_style = ws.cell(row=from_row, column=from_col)._style
    ws.cell(row=to_row, column=to_col)._style = from_style
    ws.cell(row=to_row, column=to_col).hyperlink = ws.cell(row=from_row, column=from_col).hyperlink
    ws.cell(row=to_row, column=to_col).number_format = ws.cell(row=from_row, column=from_col).number_format

Isn't there a better, generic way to copy whole cell range? Or at least whole cell with all of its attributes? If not, maybe there is an efficient way to move or copy merged cell ranges?


回答1:


The following methods work for me, you can also specify a different worksheet:

from copy import copy

def copy_cell(source_cell, coord, tgt):
    tgt[coord].value = source_cell.value
    if source_cell.has_style:
        tgt[coord]._style = copy(source_cell._style)
    return tgt[coord]

You can call it with the following:

copy_cell(worksheet['E6'], 'D11', worksheet)

Or if you instead need to move a cell, you can do this:

def move_cell(source_cell, coord, tgt):
    tgt[coord].value = source_cell.value
    if source_cell.has_style:
        tgt[coord]._style = copy(source_cell._style)

    del source_cell.parent._cells[(source_cell.row, source_cell.col_idx)]

    return tgt[coord]

Nevertheless, notice that the merge cells have to be done separatedly.




回答2:


I have built this method to copy cells without touching their contents:

def move_cell(source_cell, dest_row, dest_col, preserve_original=False):
    """
    :param source_cell: cell to be moved to new coordinates
    :param dest_row: 1-indexed destination row
    :param dest_col: 1-indexed destination column
    :param preserve_original: if True, does a copy instead of a move
    """
    if preserve_original:
        cell_to_move = copy.copy(source_cell)
    else:
        cell_to_move = source_cell

    worksheet = cell_to_move.parent
    source_address = (cell_to_move.row, cell_to_move.col_idx)
    dest_address = (dest_row, dest_col)

    cell_to_move.row = dest_row
    cell_to_move.col_idx = dest_col
    worksheet._cells[dest_address] = cell_to_move
    if not preserve_original:
        del worksheet._cells[source_address]

    return cell_to_move


来源:https://stackoverflow.com/questions/45347284/in-openpyxl-how-to-move-or-copy-a-cell-range-with-formatting-merged-cells-for

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!