Python関連Tips

ExcelのVBA

C#によるExcel制御

一般

  • PythonによるExcel制御の方法として、Openpyxl, xlwings, pywin32 の3通りがあるが、それぞれ一長一短がある。
  • もっとも一般的なのはOpenpyxlだが、もっとも制約が大きい→後述
  • Window+Excelインストール済みの環境であることを前提とすれば、pywin32(win32com)を使うことでVBAとほぼ同等の機能が使えるが、openpyxlに比べ情報が少ないと言うデメリットがある。
  • pywin32のもう一つの難点として、Pylanceを使った型ヒントアノテーションの仕組みと相性が悪いという問題がある。pywin32で使うCOMオブジェクト類には型ヒントはつけられないと思った方が良さそう。

OpenPyxl

  • 2022.10月時点の調査では、下記の通りです。
    • openpyxlの重大な欠点として、セルの計算式の評価がその場でできないというのがある。セルに値を入れてもそれが即計算式に反映されない。
    • 計算式の値を得るには一度保存した上で、data_only=Trueの引数でオープンする必要があるが、このオプションをつけて開いたファイルをセーブすると、中の計算式が値に変換されて消えるため、data_only=Trueで開いたファイルはセーブしてはならない。
    • openpyxlではチェックボックスなどのオブジェクトもまともに使えない。また吹き出しなどの図形オブジェクトはセーブとともに消えてなくなる。
    • データ入力規則にも対応しておらず、セーブすると設定が消えてなくなる。
    • openpyxlはExcelがインストールされてなくても使える、また単なるファイル処理なので動作が軽い反面、Excelの機能に依存する気の利いたことはできないと思った方が良い。
    • ブックの名前やフルパスの取得すらまともにできない。
    • これらの制約があるため、使える箇所はかなり限定されると考えるべきだろう
  • openpyxlでExcelを開くとき、Excelにデータ入力制限があると対応していないためワーニングが出るのを抑制する
    • エラー内容:UserWarning: Data Validation extension is not supported and will be removed warn(msg)
    • ワーニング抑制
      import warnings
      warnings.simplefilter(action='ignore', category=UserWarning)
      または↓でもよい
      openpyxl.reader.excel.warnings.simplefilter('ignore')

Pywin32

  • PythonでExcelのチェックボックスのラベル値を取得したいです。
    import win32com.client
    from win32com.client import CDispatch
    from pathlib import Path
    
    def proc_xls(fnam:str) -> None:
       app:CDispatch = win32com.client.Dispatch("Excel.Application")
       print(type(app))
       app.DisplayAlerts = False
       workbook = app.Workbooks.Open(Path(fnam).resolve())
    
       #Excel定数
       EXCEL_xlon = 1
       EXCEL_xlOff = -4146
    
       for chk in workbook.Worksheets("個別契約").Checkboxes:
           print(chk.Name + " のCheck状態は " + str(int(chk.Value) == EXCEL_xlon))
    
       workbook.Close()
       app.DisplayAlerts = True
       app.Quit()

Excelのプロセスが残ってしまう問題

参考:C#によるExcel制御

  • Workbookをクローズしているのになぜかプロセスが残ってしまう問題がある。
  • ReleaseComObject的な処理をしてやれば消えるのだろうが、Pythonでどうやったらいいのか不明。
  • とりあえず、Application.Quit()をすればプロセスは消えるが、今度はPythonで起動したExcelとは関係なく別で開いていたExcelまで巻き込んで終了してしまう。
  • この問題は2022.10時点でまだうまい解決方法が見つからない。
  • 試行錯誤の結果、とりあえず現時点では下記のようなコードでなんとなく動かし、プロセスが残ったら手動でkillしている。
       @classmethod
       def initExcelApp(cls) -> None:
           cls.__app = win32com.client.Dispatch("Excel.Application")
           cls.__app.Visible = False
           cls.__app.DisplayAlerts = False
    
       @classmethod
       def termExcelApp(cls) -> None:
           if cls.__app is not None:
               cls.__app.Visible = True
               cls.__app.DisplayAlerts = True
               if cls.__app.Workbooks.Count == 0:
                   cls.__app.Quit()
                   del cls.__app
    
    
       # 数式を反映させるために実Excelで開いてセーブする    
       @classmethod
       def exel_update(cls, exel_file_name:str) -> None:
           cwd : str = os.getcwd()
           path:str = cwd + '\\' + exel_file_name
           wbs = cls.__app.Workbooks
           wb = wbs.Open(path)
           wb.Save()
           wb.Close(SaveChanges=True)
           wb = None
           del wb
           # wbs.Close()
           # wbs = None
           # del wbs

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2022-11-14 (月) 22:28:34 (12d)