#author("2023-05-01T11:24:04+09:00","default:irrp","irrp")
#author("2023-05-18T16:27:09+09:00","default:irrp","irrp")
→Python関連Tips

→ExcelのVBA

→C#によるExcel制御

#contents


*一般 [#qd2ffbd8]
-PythonによるExcel制御の方法として、Openpyxl, xlwings, pywin32 の3通りがあるが、それぞれ一長一短がある。

-もっとも一般的なのはOpenpyxlだが、もっとも制約が大きい→後述

-Window+Excelインストール済みの環境であることを前提とすれば、pywin32(win32com)を使うことでVBAとほぼ同等の機能が使えるが、openpyxlに比べ情報が少ないと言うデメリットがある。またExcelがインストールされていることが必要。
-pywin32のもう一つの難点として、Pylanceを使った型ヒントアノテーションの仕組みと相性が悪いという問題がある。pywin32で使うCOMオブジェクト類には型ヒントはつけられないと思った方が良さそう。

-[[VBAユーザーのためのPython入門 〜xlwingsでExcelからPythonを呼び出す〜 - Qiita>https://qiita.com/k_maki/items/9efdb70f69d6f3b00f80]] 2022
-[[ExcelからPythonを実行する - Qiita>https://qiita.com/katzhide/items/60d0336b322105bf8fe9]] 2016
-[[ExcelにPython搭載!マクロVBAからプログラムを実行する方法>https://fastclassinfo.com/entry/run_python_from_vba/]] 2023.2
--VBAから xlwings を使ってPythonプログラムを実行する。


*OpenPyxl [#e4f560ef]
-2022.10月時点の調査では、下記の通りです。
--openpyxlの重大な欠点として、セルの計算式の評価がその場でできないというのがある。セルに値を入れてもそれが即計算式に反映されない。
--計算式の値を得るには一度保存した上で、data_only=Trueの引数でオープンする必要があるが、このオプションをつけて開いたファイルをセーブすると、中の計算式が値に変換されて消えるため、data_only=Trueで開いたファイルはセーブしてはならない。
--openpyxlではチェックボックスなどのオブジェクトもまともに使えない。また吹き出しなどの図形オブジェクトはセーブとともに消えてなくなる。
--データ入力規則にも対応しておらず、セーブすると設定が消えてなくなる。
--openpyxlはExcelがインストールされてなくても使える、また単なるファイル処理なので動作が軽い反面、Excelの機能に依存する気の利いたことはできないと思った方が良い。
--ブックの名前やフルパスの取得すらまともにできない。
--.xlsファイルは開けない。pyexel などを使ってあらかじめ .xlsx へ変換する必要がある。
--引いていない罫線が勝手に引かれたり、セルの書式が勝手に変更されて金額のカンマが外されたりするバグがある(2022年末時点)
--これらの制約があるため、使える範囲はVBAよりも限定されると考えるべきだろう

-[[【Python】行・列に色付けする openpyxl基本テクニック | kirinote.com>https://kirinote.com/python-rowcolm-color/]] 2023.1

-[[python3 openpyxl でエクセルに行挿入する - Qiita>https://qiita.com/gameover/items/13868ca9101348bdf4fc]] 2018

-[[openpyxlでExcelを操作する【Python入門】 - RAKUS Developers Blog | ラクス エンジニアブログ>https://tech-blog.rakus.co.jp/entry/20210729/openpyxl]] 2022.9

-[[openpyxlでpythonからエクセルファイルを操作する - Qiita>https://qiita.com/studio_haneya/items/7e49b616d4972fe1804d]] 2021

-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')

--[[io - Python 3 openpyxl UserWarning: Data Validation extension not supported - Stack Overflow>https://stackoverflow.com/questions/53965596/python-3-openpyxl-userwarning-data-validation-extension-not-supported]] 2018

-[[【AI 講師が解説】Python で Excel(エクセル)を読み込む方法 | キカガクの技術ブログ>https://blog.kikagaku.co.jp/python-excel]] 2022.7
--Pandasを使う方法

-[[python 2.7 - openpyxl Set Active Sheet - Stack Overflow>https://stackoverflow.com/questions/41556378/openpyxl-set-active-sheet]]
--wb.active にシートを設定する


*Pywin32 [#j7f8fb5c]
-[[Python pywin32(win32com) Excel 操作備忘録 - Qiita>https://qiita.com/feo52/items/150745ae0cc17cb5c866]] 2022

-[[PythonからExcelをwin32comで操作する - Qiita>https://qiita.com/kumarstack55/items/9ae3432446afca06497f]] 2019

-[[Python(openpyxl)でExcelを出力した際に数式の計算結果が取得できない問題 – 対策編 | ニッチな技術録>https://buzz-server.com/tech/python-openpyxl-no-value-measures/]] 2022.2
--xlwingsでExcelを実行と書いてあるが、それは関係ない。Pywin32を使いCOMで開いてセーブしているだけ
 import win32com.client
 
 app = win32com.client.Dispatch("Excel.Application")
 app.Visible = False
 app.DisplayAlerts = False
 wb = app.Workbooks.Open(exel_file_name)
 wb.SaveAs(exel_file_name)
 app.Quit()

-[[PythonでExcelのチェックボックスのラベル値を取得したいです。>https://teratail.com/questions/331090]] 
 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のプロセスが残ってしまう問題 [#aec4d53c]
参考:C#によるExcel制御

-Workbookをクローズしているのになぜかプロセスが残ってしまう問題がある。
-ReleaseComObject的な処理をしてやれば消えるのだろうが、Pythonでどうやったらいいのか不明。
-とりあえず、Application.Quit()をすればプロセスは消えるが、今度はPythonで起動したExcelとは関係なく別で開いていたExcelまで巻き込んで終了してしまう。
    @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

-その後下記のように修正したところ、プロセスは残らなくなり、終了時に既存のExcelプロセスを巻き込まなくなったが、VisibleをFalseにしてもウィンドウが一瞬表示されてしまう問題が残った。とりあえずこちらの方がマシなのでこちらを採用する。
    @classmethod
    def initExcelApp(cls) -> None:
        cls.__app = win32com.client.Dispatch("Excel.Application")
        pythoncom.CoInitializeEx(pythoncom.COINIT_APARTMENTTHREADED)
 
    @classmethod
    def termExcelApp(cls) -> None:
        if cls.__app is not None:
            # excel が開いているかどうかを確認し、起動している場合はQuitする
            if cls.__app.Workbooks.Count == 0:
                cls.__app.Quit()
  
            pythoncom.CoUninitialize()
            del cls.__app

-さらにこのように修正したところ、Visible=False でも画面が一瞬表示されてしまう問題がなくなった。それでもまだ一瞬ちらつく場合があるが、かなり改善した。
    # アプリの最初に一回呼ぶ
    @classmethod
    def initExcelApp(cls) -> None:
        cls.__app = win32com.client.Dispatch("Excel.Application")
        pythoncom.CoInitializeEx(pythoncom.COINIT_APARTMENTTHREADED)
        cls.__app.Interactive = False
        cls.__app.ScreenUpdating = False
        cls.__app.DisplayAlerts = False
        cls.__app.Visible = False
 
    # アプリの最後に一回呼ぶ
    @classmethod
    def termExcelApp(cls) -> None:
        if cls.__app is not None:
            cls.__app.Visible = True
            cls.__app.DisplayAlerts = True
            cls.__app.ScreenUpdating = True
            cls.__app.Interactive = True
 
            # excel が開いているかどうかを確認し、何もないならQuitする
            if cls.__app.Workbooks.Count == 0:
                cls.__app.Quit()
 
            pythoncom.CoUninitialize()
            del cls.__app


-参考:[[Can't close Excel completely using win32com on Python - Stack Overflow>https://stackoverflow.com/questions/10221150/cant-close-excel-completely-using-win32com-on-python]] 2022
-参考:[[python - COM: excelApplication.Application.Quit() preserves the process - Stack Overflow>https://stackoverflow.com/questions/18421457/com-excelapplication-application-quit-preserves-the-process/18421814#18421814]] 2022.10
-参考:[[python - Using Excel Interop library with python3 ctypes - Stack Overflow>https://stackoverflow.com/questions/66603199/using-excel-interop-library-with-python3-ctypes]] 2022
-参考:[[excel - Python - win32com visible false not working? - Stack Overflow>https://stackoverflow.com/questions/54815848/python-win32com-visible-false-not-working]]
-参考:[[Single-Threaded Apartments - Win32 apps | Microsoft Learn>https://learn.microsoft.com/en-us/windows/win32/com/single-threaded-apartments]]
-参考:[[STAとMTA>http://www.eternalwindows.jp/com/apartment/apartment01.html]]

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS