简体   繁体   English

使用openpyxl编辑Excel文件时丢失合并的单元格边框

[英]Losing merged cells border while editing Excel file with openpyxl

I have two sheets in an Excel file and the first one is a cover sheet which I don't need to edit.我在 Excel 文件中有两张工作表,第一张是我不需要编辑的封面。 There are a few merged cells in the cover sheet, and when I edit the file using openpyxl, without even touching the cover sheet, I lose borders from the merged cells.封面中有几个合并的单元格,当我使用 openpyxl 编辑文件时,甚至没有触及封面,我失去了合并单元格的边框。 I am using load_workbook('excel file') to load the Excel file and saving it with a different filename.我正在使用load_workbook('excel file')加载 Excel 文件并使用不同的文件名保存它。

Is there any way to fix this problem?有没有办法解决这个问题?

Actual solution is to patch the libraries code by including this snippet after including the library, it fixes the problem.实际的解决方案是在包含库之后通过包含此代码段来修补库代码,它解决了问题。 (Note: don't worry about missing definitions, eg COORD_RE, ie the patch is self-contained) (注意:不要担心缺少定义,例如 COORD_RE,即补丁是自包含的)

from itertools import product
import types
import openpyxl
from openpyxl import worksheet
from openpyxl.utils import range_boundaries


def patch_worksheet():
    """This monkeypatches Worksheet.merge_cells to remove cell deletion bug
    https://bitbucket.org/openpyxl/openpyxl/issues/365/styling-merged-cells-isnt-working
    Thank you to Sergey Pikhovkin for the fix
    """

    def merge_cells(self, range_string=None, start_row=None, start_column=None, end_row=None, end_column=None):
        """ Set merge on a cell range.  Range is a cell range (e.g. A1:E1)
        This is monkeypatched to remove cell deletion bug
        https://bitbucket.org/openpyxl/openpyxl/issues/365/styling-merged-cells-isnt-working
        """
        if not range_string and not all((start_row, start_column, end_row, end_column)):
            msg = "You have to provide a value either for 'coordinate' or for\
            'start_row', 'start_column', 'end_row' *and* 'end_column'"
            raise ValueError(msg)
        elif not range_string:
            range_string = '%s%s:%s%s' % (get_column_letter(start_column),
                                          start_row,
                                          get_column_letter(end_column),
                                          end_row)
        elif ":" not in range_string:
            if COORD_RE.match(range_string):
                return  # Single cell, do nothing
            raise ValueError("Range must be a cell range (e.g. A1:E1)")
        else:
            range_string = range_string.replace('$', '')

        if range_string not in self._merged_cells:
            self._merged_cells.append(range_string)


        # The following is removed by this monkeypatch:

        # min_col, min_row, max_col, max_row = range_boundaries(range_string)
        # rows = range(min_row, max_row+1)
        # cols = range(min_col, max_col+1)
        # cells = product(rows, cols)

        # all but the top-left cell are removed
        #for c in islice(cells, 1, None):
            #if c in self._cells:
                #del self._cells[c]

    # Apply monkey patch
    worksheet.Worksheet.merge_cells = merge_cells
patch_worksheet()

Source https://bitbucket.org/openpyxl/openpyxl/issues/365/styling-merged-cells-isnt-working来源https://bitbucket.org/openpyxl/openpyxl/issues/365/styling-merged-cells-isnt-working

I know this is old but I got the same issue and the patch didn't work for me so I found a work around by using the function in the documentation that adds a style for merged cells and then looping on all the merged cells and calling the function on each range我知道这是旧的,但我遇到了同样的问题,补丁对我不起作用,所以我通过使用文档中的函数找到了一个解决方法,该函数为合并的单元格添加样式,然后循环所有合并的单元格并调用每个范围的函数

from openpyxl import load_workbook
from openpyxl.styles import Border, Side, PatternFill, Font, GradientFill, 
Alignment

def style_range(ws, cell_range, border=Border(), fill=None, font=None, 
alignment=None):
    """
    Apply styles to a range of cells as if they were a single cell.

    :param ws:  Excel worksheet instance
    :param range: An excel range to style (e.g. A1:F20)
    :param border: An openpyxl Border
    :param fill: An openpyxl PatternFill or GradientFill
    :param font: An openpyxl Font object
    """

    top = Border(top=border.top)
    left = Border(left=border.left)
    right = Border(right=border.right)
    bottom = Border(bottom=border.bottom)

    first_cell = ws[cell_range.split(":")[0]]
    if alignment:
        ws.merge_cells(cell_range)
        first_cell.alignment = alignment

    rows = ws[cell_range]
    if font:
        first_cell.font = font

    for cell in rows[0]:
        cell.border = cell.border + top
    for cell in rows[-1]:
        cell.border = cell.border + bottom

    for row in rows:
        l = row[0]
        r = row[-1]
        l.border = l.border + left
        r.border = r.border + right
        if fill:
            for c in row:
                c.fill = fill

file = 'file.xlsx'

wb = load_workbook(file)
ws = wb['Table 1']

thin = Side(border_style="thin", color="000000")
border = Border(top=thin, left=thin, right=thin, bottom=thin)

for range in ws.merged_cells.ranges:
    style_range(ws, str(range), border=border)

wb.save('newExcel.xlsx')

For me this code work perfectly:对我来说,这段代码工作得很好:

wb = load_workbook(template_path)
ws = wb.active
for merged_cells in ws.merged_cells.ranges:
style = ws.cell(merged_cells.min_row, merged_cells.min_col)._style
for col in range(merged_cells.min_col, merged_cells.max_col + 1):
    for row in range(merged_cells.min_row, merged_cells.max_row + 1): 
        ws.cell(row, col)._style = style

I simply find all merged cells and apply style of left top cell to the rest of the cells.我只是找到所有合并的单元格并将左上单元格的样式应用于其余单元格。

If you are using openpyxl 2.4, the solution offered by micki_mouse may not work.如果您使用的是 openpyxl 2.4,micki_mouse 提供的解决方案可能不起作用。 I managed to make it work by tweaking it this way:我设法通过这种方式调整它使其工作:

from openpyxl import load_workbook
from openpyxl.utils import range_boundaries

wb = load_workbook(template_path)
ws = wb.active
for merged_cells in ws.merged_cell_ranges:
    min_col, min_row, max_col, max_row = range_boundaries(merged_cells)
    style = ws.cell(row=min_row, column=min_col)._style
    for col in range(min_col, max_col + 1):
        for row in range(min_row, max_row + 1):
        ws.cell(row=row, column=col)._style = style

Basically, you have to use "merged_cell_ranges" (deprecated in 2.5.0-b1 ) to correctly access the merged cells within the worksheet.基本上,您必须使用“merged_cell_ranges”(在2.5.0-b1 中已弃用)才能正确访问工作表中的合并单元格。 You also must specify explicitely in the cell constructor the row and column您还必须在单元格构造函数中明确指定行和列

Simple method简单的方法

  1. Go to the folder C:\\Python\\Lib\\site-packages\\ openpyxl\\worksheet folder\\转到文件夹 C:\\Python\\Lib\\site-packages\\ openpyxl\\worksheet folder\\

  2. Open worksheet.py file打开 worksheet.py 文件

  3. In merge_cells function comment the following lines在 merge_cells 函数中注释以下几行

    min_col, min_row, max_col, max_row = range_boundaries(range_string) rows = range(min_row, max_row+1) cols = range(min_col, max_col+1) cells = product(rows, cols) all but the top-left cell are removed for c in islice(cells, 1, None): if c in self._cells: del self._cells[c]
  4. Save it.保存它。

That's all , it works for me.就是这样,它对我有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM