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

Losing merged cells border while editing Excel file with openpyxl

我在Excel文件中有两张纸,第一张是我不需要编辑的封面。封面中有一些合并的单元格,当我使用openpyxl编辑文件时,甚至不接触封面,我就失去了合并单元格的边界。我正在使用load_workbook('excel file')加载Excel文件并将其保存为其他文件名。

有什么办法可以解决此问题?


实际的解决方案是在包含库之后通过包含此代码片段来修补库代码,从而解决了该问题。 (注意:不要担心缺少定义,例如COORD_RE,即补丁是自包含的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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()

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


我知道这很旧,但是我遇到了同样的问题,并且补丁对我不起作用,因此我找到了一种解决方法,方法是使用文档中的功能,该功能为合并的单元格添加了样式,然后循环所有合并单元格并在每个范围上调用函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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')

如果使用的是openpyxl 2.4,则micki_mouse提供的解决方案可能不起作用。我设法通过以下方式使其起作用:

1
2
3
4
5
6
7
8
9
10
11
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

基本上,您必须使用" merged_cell_ranges"(在2.5.0-b1中已弃用)才能正确访问工作表中的合并单元格。您还必须在单元构造函数中显式指定row和column


对我来说,这段代码可以正常工作:

1
2
3
4
5
6
7
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

我只是找到所有合并的单元格,并将左上单元格的样式应用于其余单元格。


简单方法

  • 转到文件夹C:\\\\ Python \\\\ Lib \\\\ site-packages \\\\ openpyxl \\\\ worksheet文件夹\\\\

  • 打开worksheet.py文件

  • 在merge_cells函数中,注释以下几行

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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]
  • 保存。

  • 仅此而已,对我有用。