PythonでExcelのグラフを欠損させずシートをコピーする
最終的なコード
import pathlib
import pythoncom
import win32com.client
def copy_sheet(filepath: str, src_sheet: int | str, dst_sheet: int | str, new_sheet_name: str | None = None) -> None:
"""シートをコピーする。
Args:
filepath (str): Excelファイルのパス。(絶対パス)
src_sheet (int | str): コピー元のシート。シート名 or シートのインデックス(1~)で指定する。
dst_sheet (int | str): コピー先のシート位置。シート名 or シートのインデックス(1~)で指定する。
new_sheet_name (str | None, optional): 新しいシートの名前。 デフォルトは None で、 None の場合は変更しない。
"""
xl_app = win32com.client.Dispatch("Excel.Application")
wb = xl_app.Workbooks.Open(filepath)
try:
wb.Worksheets(src_sheet).Copy(After=wb.Worksheets(dst_sheet))
if new_sheet_name:
xl_app.ActiveSheet.Name = new_sheet_name
wb.Save()
except pythoncom.com_error as e:
print(e.excepinfo[2])
finally:
wb.Close(True)
xl_app.Quit()
# 絶対パスでないと受け付けないので注意
src_path = str(pathlib.Path("./tests/test.xlsx").resolve())
copy_sheet(src_path, "1", "3", "new sheet (src.1 dst.3)")
# インデックス指定の場合(1からスタート)
copy_sheet(src_path, 1, 5, "new sheet (src.1 dst.5)")
実行結果
しっかりグラフもコピー出来ています。
背景
Python で Excel を操作しようと思った時、代表的なライブラリとして openpyxl があります。
このライブラリには普段からお世話になっているのですが、1つだけ不満点があります。
それはシートをコピーした時にグラフが消えてしまうという仕様です。
python - Chart can't be copied with Openpyxl - Stack Overflow
業務上、以下のような Excel を扱う機会が多くあるので、この仕様には困らされてきました。
- 大量の計算式とその結果を参照するグラフを持つフォーマット Excel があり、それをコピーして使い回す
- シートごとにグラフが存在する
VBA であれば上のような問題は起きませんが、 csv などからデータを抽出・加工して貼り付けたい時に面倒です。
そこで、どうにか Python だけ で グラフを保持したままシートをコピー出来ないか 試してみました。
解説
準備
pip
かお好きなパッケージ管理ツールで pywin32
を導入します。
私は pipenv を使用しています。
pip install pywin32
Worksheet.Copy
メソッド
Worksheet.Copy メソッド (Excel) | Microsoft Learn を使えばシートをコピー出来ます。
Worksheet.Copy(Before, After)
Before
, After
はどちらかしか指定出来ません。
シートの指定
シートの指定には インデックス (1~) or シート名 のどちらかが使えます。
毎回シートの最後にコピーしたい場合、 len(wb.Worksheets)
などでシート数を取得すれば良いです。
コピー先シートの名前
コピー直後は Application.ActiveSheet
がコピーしたシートに変わっているので、Application.ActiveSheet.Name
を変更することでシート名の変更が可能です。
注意点
- pywin32 は相対パスを受け付けてくれないので絶対パスに変換する必要があります。