【速習Python7本目】「ドラッグ&ドロップでExcelをA1セルにセット」

こんにちは、くのへです!

今回もPythonを使って、便利なアプリを作ってみましょう。
このアプリを使えば、ドラッグアンドドロップしたExcelファイルの全てのシートのA1セルをアクティブにして保存することができます。

企業によっては、Excelファイルを共有する際に必ずA1セルをアクティブにした状態で保存するというルールがある場合があります。
シート数が多かったり、後から共有が必要になったりすると、手動で全てのシートを開いてA1セルを選択するのは意外と面倒です。
今回のツールを使えば、そんな手間を省くことができます。

今回のアプリ開発を通して、以下の内容を学習できます。

  • comtypes.clientライブラリ
  • COMオブジェクトについて

さあ、一緒に便利アプリを作っていきましょう!

この記事の解説動画はこちらです

Contents

今回のコード

今回作成するPythonコードはこちらです。

import sys
import comtypes.client
# import win32com.client
import tkinter as tk
from tkinter import messagebox

def activate_a1_in_excel(excel_file_path):
    try:
        # Excelアプリケーションを起動
        excel = comtypes.client.CreateObject("Excel.Application")
        # excel = win32com.client.Dispatch("Excel.Application") #win32comライブラリの場合
        excel.Visible = False  # Excelを非表示
        wb = excel.Workbooks.Open(excel_file_path)

        # 最後にこのシートを開いて保存するためアクティブなシートを記憶しておく。
        original_sheet = wb.ActiveSheet

        # 各シートをアクティブにしてA1セルを選択
        for ws in wb.WorkSheets:
            ws.Activate()
            ws.Range("A1").Activate()

        # 最初のシートをアクティブにして保存して閉じる        
        original_sheet.Activate()
        wb.Save()
        wb.Close()
        excel.Quit()

    except:
        messagebox.showerror("エラー", "エラーが発生しました。プログラムを終了します")
        excel.Quit()


if __name__ == "__main__":
    root = tk.Tk()
    root.withdraw()

    if len(sys.argv) == 2:
        file_path = sys.argv[1]
        if file_path.lower().endswith(('.xlsx', '.xlsm')):
            activate_a1_in_excel(file_path)

        # ドラッグアンドドロップされたのがエクセルファイルではない場合
        else:
            messagebox.showinfo("情報", "有効なExcelファイルをドラッグアンドドロップしてください。")

    # ドラッグアンドドロップ以外で実行された場合
    else:    
        messagebox.showinfo("情報", "Excelファイルをドラッグアンドドロップしてください。")

    root.destroy()

このコードは大きく分けて二つの部分に分かれています。

一つは、Pythonファイルを直接ダブルクリックした場合などに実行される if __name__ == "__main__": のブロック。

もう一つは、ドラッグアンドドロップによってExcelファイルが指定された場合に呼び出される activate_a1_in_excel 関数です。

if __name__ == "__main__": ブロックでは、主にメッセージボックスの表示設定と、ドラッグアンドドロップされたファイルパスの取得、そしてExcelファイルかどうかの判定を行っています。
sys.argv を使うことでドラッグアンドドロップされたファイルのパスを取得する方法については、以前の動画(3本目)で詳しく解説しています。

今回のメインの処理は activate_a1_in_excel 関数の中に記述されています。

必要なライブラリのインストール

今回のコードで使用している comtypes.client ライブラリは標準ライブラリではないため、別途インストールが必要です。

ターミナル(コマンドプロンプトやPowerShellなど)を開き、以下のコマンドでインストールしてください。

pip install comtypes

動画では win32com.client ライブラリについても触れられていますが、こちらは pip install pywin32 というコマンドでインストールします。
どちらのライブラリを使ってもCOMオブジェクトを操作できますが、今回のコードでは comtypes.client を使用しています。

COMオブジェクトについて

activate_a1_in_excel 関数のコードを解説する前に、今回の核となる技術である「COMオブジェクト」について説明します。

COMオブジェクトとは、Microsoftが開発したソフトウェアの仕組みの一つで、異なるプログラムを動かす仕組みです。
これを使うと、Excelなどのアプリケーションを「オブジェクト」としてPythonから操ることができます。
しかもこれはプログラミング言語に依らず扱える仕組みです。

Excelを操作する際に、その内部構造を意識したことはあるでしょうか?
実はExcelアプリケーションは、階層構造を持つオブジェクトの集合体として捉えることができます。

一番大きな枠組みが Excel.Application で、これがExcelアプリケーションそのものに相当します。
そのExcel.Applicationの中に、開かれているブックの集合である Workbooks があり、その中には現在アクティブなブックである ActiveWorkBook があります。
さらにWorkBookの中にはシートの集合である Sheets があり、その中には現在アクティブなシートである ActiveSheet があります。
そしてSheetの中にはセルの集合である Cells があり、その中には現在アクティブなセルである ActiveCell がある、といった構造になっています。

Excel VBAを使ったことがある方は、セルに値を入力する際に Cells(1,1).Value = "デモ" のようなコードを書いたことがあるかもしれません(動画 6:38 辺り)。
これは省略せずに書くと、ActiveWorkBook.ActiveSheet.Cells(1,1).Value = "デモ" となります。
さらに完全に省略せずに書くと、Excel.Application.ActiveWorkBook.ActiveSheet.Cells(1,1).Value = "デモ" となります。

Excel VBAではこの Excel.Application.ActiveWorkBook.ActiveSheet の部分を省略して書くことができます。
そして、PythonからExcelを操作する場合、この省略されている部分を明示的に指定することで、異なるプログラムであるPythonからExcelアプリケーションを操れるようになるのです。

PythonでCOMオブジェクトを呼び出す方法

PythonでExcel.ApplicationをCOMオブジェクトとして呼び出すためには、comtypes.client ライブラリ(または win32com.client ライブラリ)を使用します。

comtypes.client を使う場合は、以下のコードでExcelアプリケーションをCOMオブジェクトとして呼び出し、変数にセットできます。

import comtypes.client

# Excelアプリケーションを起動
excel = comtypes.client.CreateObject("Excel.Application")

このようにすることで、excel という変数を通してExcelアプリケーションを操作できるようになります。

あとは、先ほどのExcel VBAの例のように、省略せずに階層構造をたどって操作したい対象を指定すれば良いだけです。

例えば、A1セルに「デモ」と書き込む場合は以下のようになります。

# Excelアプリケーションを起動し、変数excelにセット(comtypes.clientライブラリ使用)
excel = comtypes.client.CreateObject("Excel.Application")

# あとは省略せずにプログラミングするだけ!
excel.ActiveWorkbook.ActiveSheet.Cells(1,1).Value = "デモ"

今回のツールのように、特定のファイルを操作したい場合は、WorkbooksのOpenメソッドを使います。

# Excelブックを開く
wb = excel.Workbooks.Open(excel_file_path)

開いたブックは wb という変数にセットし、その変数を通してブック内のシートなどを操作します(動画 8:45 辺り)。

例えば、指定シートのA1セルをアクティブにするには、以下のように記述します。

# 指定シートのA1をアクティブにする
wb.Sheets("指定シート").Range("A1").Activate()

ここで"指定シート"の部分を操作したいシート名に変更することで、特定のシートを指定できます。

COMオブジェクトの終了処理は忘れずに!

COMオブジェクトを操作する上で、最後に必ず終了処理を行うことが非常に重要です(動画 9:21 辺り)。

起動したCOMオブジェクト(今回の場合はExcelアプリケーション)は、明示的に終了させないと、プログラムが終了してもバックグラウンドで起動しっぱなしになってしまうことがあります(いわゆる「ゾンビプロセス」と呼ばれる状態)。

これを防ぐために、操作が終わったら必ず COM変数.Quit() のように終了命令を実行してください。

今回のコードで言えば、excel.Quit() がそれに該当します。

activate_a1_in_excel 関数の解説

COMオブジェクトの概念と基本的な操作方法が分かったところで、activate_a1_in_excel 関数のコードを見ていきましょう。

def activate_a1_in_excel(excel_file_path):
    try:
        # Excelアプリケーションを起動
        excel = comtypes.client.CreateObject("Excel.Application")
        #excel = win32com.client.Dispatch("Excel.Application") #win32comライブラリの場合
        excel.Visible = False # Excelを非表示
        wb = excel.Workbooks.Open(excel_file_path)

        # 最後にこのシートを開いて保存するためアクティブなシートを記憶しておく
        original_sheet = wb.ActiveSheet

        # 各シートをアクティブにしてA1セルを選択
        for ws in wb.Sheets:
            ws.Activate()
            ws.Range("A1").Activate()

        # 最初のシートをアクティブにして保存して閉じる
        original_sheet.Activate()
        wb.Save()
        wb.Close()
        excel.Quit()

    except:
        messagebox.showerror("エラー", "エラーが発生しました。プログラムを終了します")
        excel.Quit()

この関数は excel_file_path という引数で操作対象のExcelファイルのパスを受け取ります。

まず、try-except 構文で囲んでいます。これは、処理中にエラーが発生した場合でも、Excelが起動しっぱなしにならないように、エラー発生時も必ず excel.Quit() を実行するためです。try-except構文については、以前の動画(5本目)で解説しています。

excel = comtypes.client.CreateObject("Excel.Application") でExcelアプリケーションのCOMオブジェクトを取得し、excel 変数に代入します。

excel.Visible = False で、Excelのウィンドウが表示されないようにしています(バックエンドで処理するため)。

wb = excel.Workbooks.Open(excel_file_path) で、指定されたパスのExcelファイルを開き、そのワークブックオブジェクトを wb 変数に代入します(動画 10:21 辺り)。

original_sheet = wb.ActiveSheet で、ファイルを開いたときに最初にアクティブだったシートを original_sheet という変数に記憶させています。これは、処理後に元のシートをアクティブにした状態で保存するためです(動画 10:51 辺り)。

for ws in wb.Sheets: のループで、ワークブック内の全てのシート(wb.Sheets)を一つずつ取り出し、ws 変数に代入しながら処理を繰り返します。

ループの中では、ws.Activate() で現在のシート(ws)をアクティブにし、ws.Range("A1").Activate() でそのシートのA1セルをアクティブにしています。
これにより、全てのシートのA1セルが順番にアクティブな状態になります。

ループが終わったら、original_sheet.Activate() で最初に記憶しておいたシートを再度アクティブにします。これにより、ファイルを開いたときに最初に表示されるシートが、処理前のシートになります。

wb.Save() で、A1セルをアクティブにした変更をファイルに保存します。

wb.Close() で、開いていたワークブックを閉じます。

最後に 必ず excel.Quit() でExcelアプリケーションを終了させます。

except ブロックでは、tryブロック内でエラーが発生した場合に実行されます。
メッセージボックスでエラーが発生したことを通知し、ここでも忘れずに excel.Quit() を実行してExcelアプリケーションを終了させています。

まとめ

お疲れ様でした!
今回はPythonを使って、Excelファイルの全てのシートのA1セルをアクティブにする便利なツールを作成しました。

このツール開発を通して、以下の内容を学習できました。

  • comtypes.clientライブラリの使い方
  • COMオブジェクトの概念と、PythonからExcelなどのアプリケーションを操作する方法
  • COMオブジェクトの終了処理の重要性

COMオブジェクトを使えるようになると、PythonからExcelだけでなく、OutlookやWordなど、他のMicrosoft Officeアプリケーションや、COMオブジェクトに対応した様々なソフトウェアを自動で操作できるようになります。
これは非常に強力なスキルなので、ぜひ今回の内容を応用して、様々な自動化に挑戦してみてください。

だんだん難しくなってきましたが、一つずつ理解を深めてスキルアップしていきましょう!

またね、バイバイ!

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
Contents