Excelをテンプレートとして読み込んで、編集した後に出力したかったのでOpenPyXLを使ってみたのでメモ書き程度に使ってみた結果を書きとどめてみた。
基本的な使い方は下記を参照。
https://openpyxl.readthedocs.io/en/stable/
結合されたセルの罫線が消える
load → saveで結合されたセルの罫線の一部が消える。
from openpyxl import load_workbook
wb = load_workbook('./test.xlsx')
wb.save('./test2.xlsx')
ドキュメント読むとそういうものなので諦めるしか無いみたい。
http://openpyxl.readthedocs.io/en/default/styles.html#styling-merged-cells
なのでloadするexcelでは結合せずに罫線を引いておいて、load後にmerge_cells()
で結合するしかない。。めんどくさい。
スタイルのコピー
copy
を使ってできるらしい。
http://openpyxl.readthedocs.io/en/default/styles.html#copying-styles
けど全てのスタイルをコピーしたい場合にどうしたらいいのかよくわからなかったので調べるとborderとかfontとか指定してコピーしたらって提案がされている。
http://stackoverflow.com/questions/23332259/copy-cell-style-openpyxl#answer-34838233
for row in default_sheet.rows:
for cell in row:
new_cell = new_sheet.cell(
row=cell.row_idx, col=cell.col_idx, value= cell.value)
if cell.has_style:
new_cell.font = cell.font
new_cell.border = cell.border
new_cell.fill = cell.fill
new_cell.number_format = cell.number_format
new_cell.protection = cell.protection
new_cell.alignment = cell.alignment
正直めんどいし、これがスタイルの全てかわからんなぁ、って思ってopenpyxlのソースコード見てたらopenpyxl.worksheet.copier._copy_cells
でtarget_cell._style = copy(source_cell._style)
ってしているのを見つけた。
https://bitbucket.org/openpyxl/openpyxl/src/644ea21bb4056f93184d3e743f4abf05c51f84af/openpyxl/worksheet/copier.py?at=default&fileviewer=file-view-default#copier.py-54
ということでスタイルを全コピーする場合は下記で対応する。
to_cell._style = copy(cell._style)
というかセルをコピーする場合はopenpyxl.worksheet.copier._copy_cells
を参考にするといいと思う。
https://bitbucket.org/openpyxl/openpyxl/src/644ea21bb4056f93184d3e743f4abf05c51f84af/openpyxl/worksheet/copier.py?at=default&fileviewer=file-view-default#copier.py-46:60
row_dimensionsは1番目から設定されている
テンプレートから行単位でコピーする場合に行の高さを取得してコピー先の行に設定したくなったりするのだけれども、そんなときにworksheetのrow_dimensionsにアクセスする必要がある。
ただ、1行目のrow_dimensionsはrow_dimensions[0]ではなくrow_dimensions[1]に入っているので間違えないようにする。
範囲指定してコピーする
とあるデータの件数分テンプレートのシートから行をコピーして出力したかったので範囲指定してコピーする関数を実装してみた。
def copy_cell_range(from_sheet, from_range_string, to_sheet, to_cell_string='A1'):
to_cell = to_sheet[to_cell_string]
to_row = to_cell.row
to_col = to_cell.col_idx
for row_num, row in enumerate(from_sheet[from_range_string]):
if not row:
continue
# 行の高さをコピー
to_row_dimension = to_sheet.row_dimensions[to_row + row_num]
from_row_dimension = from_sheet.row_dimensions[row[0].row]
to_row_dimension.height = from_row_dimension.height
for col_num, cell in enumerate(row):
to_cell = to_sheet.cell(row=to_row + row_num, column=to_col + col_num)
# cellの値をコピー
to_cell.value = cell.value
# cellのスタイルをコピー
if cell.has_style:
to_cell._style = copy(cell._style)
row_dimensionもcopyできるんじゃないかと思うけど未確認です。
cellのスタイル以外も必要ならコピーすると良いと思う。