【问题标题】:In openpyxl, how to move or copy a cell range with formatting, merged cells, formulas and hyperlinks在 openpyxl 中,如何移动或复制带有格式、合并单元格、公式和超链接的单元格范围
【发布时间】:2018-01-02 23:25:56
【问题描述】:

我正在尝试使用 Python 2.7 + openpyxl 在工作表中移动单元格区域。似乎是一项简单而基本的任务,结果却几乎是不可能的。这是我希望它在 Excel 中的外观:

为了使任务更容易,我们假设我只需要将范围从给定单元格移动到数据末尾(到最后一行和最后一列)。我的第一个想法很简单:

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)

但是,嘿,如何有效和充分地实现copy_cell? 通过反复试验,我找到了需要复制的 4 件事:

  1. 价值
  2. 风格
  3. 超链接
  4. 数字格式

它并没有像我预期的那样工作 - 大多数单元格都被正确复制,但超链接似乎不起作用,复制 .style 属性也不起作用(因此我尝试访问 _style 有效)和我最糟糕的出现问题 - 合并单元格范围。如何对付他们?我的copy_cell()现在是这样的:

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

难道没有更好的通用方法来复制整个单元格范围吗?或者至少是具有所有属性的整个细胞?如果没有,也许有一种有效的方法来移动或复制合并的单元格范围?

【问题讨论】:

    标签: python excel openpyxl


    【解决方案1】:

    这适用于我的版本3.0.9。虽然,这涵盖了复制/移动值和样式

    from copy import copy
    from openpyxl.cell.cell import Cell
    from openpyxl.worksheet.worksheet import Worksheet
    from openpyxl.utils import get_column_letter
    
    def move_cell(cell: Cell, rows: int, cols: int, preserve_original=False) -> None:
        """Move ``cell`` by ``rows`` and ``cols``. If ``preserve_original`` is True, do copy instead
        of a move.
    
        .. note:: Anything already present in the new destination gets overwritten.
        """
        new_column = get_column_letter(cell.column + cols)
        new_cell = cell.parent[f"{new_column}{cell.row + rows}"]
        new_cell.value = cell.value
        if cell.has_style:
            new_cell.font = copy(cell.font)
            new_cell.border = copy(cell.border)
            new_cell.fill = copy(cell.fill)
            new_cell.number_format = copy(cell.number_format)
            new_cell.protection = copy(cell.protection)
            new_cell.alignment = copy(cell.alignment)
        if not preserve_original:
            cell.value = ""
            cell.style = "Normal"
    
    def move_range(sheet: Worksheet, range_: str, rowscount: int, colscount: int,
                   preserve_original=False) -> None:
        """Move range of cells defined by ``range_`` within the ``sheet`` by ``rowscount`` and
        ``colscount``.
    
        If ``preserve_original`` is True, this function does copy instead of a move (any overlapping
        gets overwritten).
        """
        for row in sheet[range_]:
            for cell in row:
                move_cell(cell, rowscount, colscount, preserve_original)
    

    【讨论】:

      【解决方案2】:

      我已经建立了这个方法来复制单元格而不接触它们的内容:

      import copy
      
      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
      

      【讨论】:

      • 你可能需要使用deepcopy
      • 这是一个古老的问题,但我记得与deepcopy 斗争。它递归地执行复制并在单元结构内的一个数组上失败。
      • @CharlieClark 感谢您的指点。到目前为止,我还没有遇到任何问题,但如果出现任何奇怪的问题,我会记住deepcopy
      • 关于如何复制合并单元格的任何想法
      • 你能用一个例子解释一下吗? @KrystianCybulski 我只有错误:(函数'对象没有属性'复制'
      【解决方案3】:

      以下方法对我有用,您也可以指定不同的工作表

      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]
      

      您可以使用以下方式调用它:

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

      或者如果您需要移动一个单元格,您可以这样做:

      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]
      

      不过,请注意合并单元格必须分开完成。

      【讨论】:

      • 这是一个很老的问题,但感谢您的回答,它可能对将来的某人有所帮助。
      猜你喜欢
      • 1970-01-01
      • 2018-09-06
      • 2014-06-13
      • 1970-01-01
      • 2021-05-12
      • 1970-01-01
      • 2020-07-31
      • 1970-01-01
      • 2016-12-20
      相关资源
      最近更新 更多