# coding=utf-8
# openpyxl-3.0.3
from openpyxl.reader.excel import load_workbook
from openpyxl.cell.cell import TYPE_FORMULA
from openpyxl.worksheet.worksheet import Worksheet
from openpyxl.utils import get_column_letter
from openpyxl.worksheet.cell_range import CellRange
import re
import copy
def insert_rows(self, row_idx, cnt, above=False, copy_style=True, copy_merged_columns=True, fill_formulae=True):
"""Inserts new (empty) rows into worksheet at specified row index.
:param row_idx: Row index specifying where to insert new rows.
:param cnt: Number of rows to insert.
:param above: Set True to insert rows above specified row index.
:param copy_style: Set True if new rows should copy style of immediately above row.
:param fill_formulae: Set True if new rows should take on formula from immediately above row, filled with references new to rows.
Usage:
* insert_rows(2, 10, above=True, copy_style=False)
"""
CELL_RE = re.compile("(?P<col>\$?[A-Z]+)(?P<row>\$?\d+)")
row_idx = row_idx - 1 if above else row_idx
def replace(m):
row = m.group('row')
prefix = "$" if row.find("$") != -1 else ""
row = int(row.replace("$", ""))
row += cnt if row > row_idx else 0
return m.group('col') + prefix + str(row)
# First, we shift all cells down cnt rows...
old_cells = set()
old_fas = set()
new_cells = dict()
new_fas = dict()
for c in self._cells.values():
old_coor = c.coordinate
# Shift all references to anything below row_idx
if c.data_type == TYPE_FORMULA:
c.value = CELL_RE.sub(
replace,
c.value
)
# Here, we need to properly update the formula references to reflect new row indices
if old_coor in self.formula_attributes and 'ref' in self.formula_attributes[old_coor]:
self.formula_attributes[old_coor]['ref'] = CELL_RE.sub(
replace,
self.formula_attributes[old_coor]['ref']
)
# Do the magic to set up our actual shift
if c.row > row_idx:
old_coor = c.coordinate
old_cells.add((c.row, c.column))
c.row += cnt
new_cells[(c.row, c.column)] = c
if old_coor in self.formula_attributes:
old_fas.add(old_coor)
fa = copy.copy(self.formula_attributes[old_coor])
new_fas[c.coordinate] = fa
for coor in old_cells:
del self._cells[coor]
self._cells.update(new_cells)
for fa in old_fas:
del self.formula_attributes[fa]
self.formula_attributes.update(new_fas)
# Next, we need to shift all the Row Dimensions below our new rows down by cnt...
# CHANGED: for row in range(len(self.row_dimensions) - 1 + cnt, row_idx + cnt, -1):
for row in range(list(self.row_dimensions)[-1] + cnt, row_idx + cnt, -1):
new_rd = copy.copy(self.row_dimensions[row - cnt])
new_rd.index = row
self.row_dimensions[row] = new_rd
del self.row_dimensions[row - cnt]
# Now, create our new rows, with all the pretty cells
# CHANGED: row_idx += 1
new_row_idx = row_idx + 1
for row in range(new_row_idx, new_row_idx + cnt):
# Create a Row Dimension for our new row
new_rd = copy.copy(self.row_dimensions[row-1])
new_rd.index = row
self.row_dimensions[row] = new_rd
# CHANGED: for col in range(1,self.max_column):
for col in range(self.max_column):
col = col + 1
cell = self.cell(row=row, column=col)
source = self.cell(row=row_idx, column=col)
if copy_style:
cell.number_format = copy.copy(source.number_format)
cell.font = copy.copy(source.font)
cell.alignment = copy.copy(source.alignment)
cell.border = copy.copy(source.border)
cell.fill = copy.copy(source.fill)
if fill_formulae and TYPE_FORMULA == source.data_type :
s_coor = source.coordinate
if s_coor in self.formula_attributes and 'ref' not in self.formula_attributes[s_coor]:
fa = copy.copy(self.formula_attributes[s_coor])
self.formula_attributes[cell.coordinate] = fa
#print("Copying formula from cell %s%d to %s%d"%(col,row-1,col,row))
cell.value = re.sub(
"(\$?[A-Z]{1,3}\$?)%d" % (row_idx),
lambda m: m.group(1) + str(row),
source.value
)
cell.data_type = TYPE_FORMULA
# Check for Merged Cell Ranges that need to be expanded to contain new cells
for cr in self.merged_cells.ranges:
min_col, min_row, max_col, max_row = cr.bounds
if min_row <= row_idx and max_row > row_idx:
if max_row + cnt >= CellRange.max_row.max:
cr.expand(down = CellRange.max_row.max - max_row)
else:
cr.expand(down = cnt)
elif min_row > row_idx:
if max_row + cnt >= CellRange.max_row.max:
cr.expand(down = CellRange.max_row.max - max_row)
else:
cr.expand(down = cnt)
cr.shrink(top = cnt)
# Merge columns of the new rows in the same way row above does
if copy_merged_columns:
bounds = []
for cr in self.merged_cells.ranges:
if cr.max_row == cr.min_row == row_idx:
bounds.append((cr.min_col, cr.max_col))
for (min_col, max_col) in bounds:
for row in range(new_row_idx, new_row_idx + cnt):
newCellRange = get_column_letter(min_col) + str(row) + ":" + get_column_letter(max_col) + str(row)
self.merge_cells(newCellRange)
# update dataValidation
validations = self.data_validations.dataValidation
for val in validations:
for cr in val.cells:
min_col, min_row, max_col, max_row = cr.bounds
if min_row <= row_idx and max_row >= row_idx:
if max_row + cnt >= CellRange.max_row.max:
cr.expand(down = CellRange.max_row.max - max_row)
else:
cr.expand(down = cnt)
elif min_row > row_idx:
if max_row + cnt >= CellRange.max_row.max:
cr.expand(down = CellRange.max_row.max - max_row)
else:
cr.expand(down = cnt)
cr.shrink(top = cnt)
# update conditional_formatting tow steps
# first get all conditional_formatting need to update
cond_fmts = self.conditional_formatting
upd_cfs = []
for cf in cond_fmts:
for cr in cf.cells:
min_col, min_row, max_col, max_row = cr.bounds
if min_row <= row_idx and max_row >= row_idx:
upd_cfs.append(cf)
break
elif min_row > row_idx:
upd_cfs.append(cf)
break
# second update conditional_formatting
for cf in upd_cfs:
rules = cond_fmts[cf]
del cond_fmts[cf.cells]
for cr in cf.cells:
min_col, min_row, max_col, max_row = cr.bounds
if min_row <= row_idx and max_row >= row_idx:
if max_row + cnt >= CellRange.max_row.max:
cr.expand(down = CellRange.max_row.max - max_row)
else:
cr.expand(down = cnt)
elif min_row > row_idx:
if max_row + cnt >= CellRange.max_row.max:
cr.expand(down = CellRange.max_row.max - max_row)
else:
cr.expand(down = cnt)
cr.shrink(top = cnt)
for r in rules:
cond_fmts[cf] = r
Worksheet.insert_rows = insert_rows
if __name__ == "__main__":
# 注意,keep_vba=True打开的Excel文档,保存之后,MS Office 无法打开
wb = load_workbook(filename='example.xlsx', read_only=False, keep_vba=False, data_only=False, keep_links=True)
ws = wb.active
ws.insert_rows(6, 4, above=True, copy_style=True)
wb.save('new_document.xlsx')
wb.close()