PythonでExcelファイルに画像を挿入する/列の幅を調整する

# Pythonを使ってExcelファイルに画像を挿入する方法
どうもhinaateです。
この記事では、openpyxlとPillowを使用して、Excelファイルに画像を挿入する方法を説明します。

# 環境構築
まず、openpyxlとPillowのインストールが必要です。

# openpyxlのインストール
以下のコマンドをコマンドプロンプトで実行して、openpyxlをインストールします。
```
pip install openpyxl
pip install Pillow
```

# コードの全体像
以下は、Excelファイルに画像を挿入するためのコードの全体像です。このコードを実行することで、Excelファイルに画像が挿入されます。

```python
import os
from openpyxl import Workbook
from openpyxl.utils import get_column_letter
from openpyxl.drawing.image import Image as xlImage
from PIL import Image as PILImage

# 画像が保存されているフォルダのパス
pic_folder_path = "pic"

# Excelファイルを作成
workbook = Workbook()

# アクティブなシートを取得
sheet = workbook.active

# 列"A"の幅を設定
sheet.column_dimensions["A"].width = 20

# 画像ファイルのリストを取得
image_files = [f for f in os.listdir(pic_folder_path) if f.endswith(".jpg") or f.endswith(".png")]

# 画像ファイルをExcelに挿入
for i, image_file in enumerate(image_files):
    # 画像をPILで開いてサイズを取得
    pil_image = PILImage.open(os.path.join(pic_folder_path, image_file))
    width, height = pil_image.size

    # 画像をExcelのセルに挿入
    img = xlImage(os.path.join(pic_folder_path, image_file))
    sheet.add_image(img, f"B{i+1}")

    # セルの幅および高さを設定
    sheet.column_dimensions[get_column_letter(i+2)].width = width / 7
    sheet.row_dimensions[i+1].height = height / 3

# Excelファイルを保存
workbook.save("example_with_image.xlsx")
```

# コードの解説
詳細にコードを見ていきいます。
#### ライブラリのインポート
```python
from openpyxl import Workbook
from openpyxl.drawing.image import Image
from openpyxl.utils import get_column_letter
from openpyxl.styles import Alignment, Font, PatternFill, Border, Side
from PIL import Image as PILImage
```
openpyxlライブラリから、Workbook、Image、get_column_letter、Alignment、Font、PatternFill、Border、Sideをインポートしています。

また、PILライブラリから、Imageをインポートしています。

#### Excelファイルをアクティブにする
```python
workbook = Workbook()
sheet = workbook.active
```
Workbookを使用して新しいExcelファイルを作成し、アクティブなシートを取得します。

#### 画像ファイルの取得とソート
```python
image_files = [f for f in os.listdir(folder_path_pic) if f.endswith(".jpg") or f.endswith(".png")]

def sort_filename(filename):
    return int(filename.split('_')[0])

image_files = sorted(image_files, key=sort_filename)
```
指定したフォルダ内にある、拡張子が.jpgまたは.pngである画像ファイルを取得します。

ファイル名を数字でソートするために、sort_filename関数を定義し、sorted関数を使用してファイル名をソートします。
（この工程は適宜省略可能です。）

#### 一時的にファイルを保存するフォルダの作成(画像の縮尺を変えるために一時的なフォルダのを作っておきます)
```python
temp_dir = "temp_images"
os.makedirs(temp_dir, exist_ok=True)
```
一時的に保存する画像ファイルを格納するディレクトリを作成します。exist_ok=Trueは、ディレクトリがすでに存在している場合に、何もしないことを示します。

#### 画像をExcelのセルに挿入

```python
for i, image_file in enumerate(image_files):
    image_path = os.path.join(pic_folder_path, image_file)

    # 画像をPILで開いて0.2倍に縮小
    pil_image = PILImage.open(image_path)
    pil_image = pil_image.resize((int(pil_image.width * 0.2), int(pil_image.height * 0.2)))
    
    # PIL Imageを一時的なファイルに保存してからOpenpyxl Imageに変換
    temp_image_path = os.path.join(temp_dir, f"temp_image_{i+1}.png")
    pil_image.save(temp_image_path)
    img = Image(temp_image_path)
    
    # 画像をExcelに挿入
    sheet.add_image(img, f"A{i+2}")
    
    # セルの幅および高さを設定
    sheet.row_dimensions[i+2].height = pil_image.height * 0.8
```
こちらのコードで画像を開き、0.2倍にして一時的なファルダに格納しておきます。
そしてその画像のパスを参照し、画像をExcelに挿入していきます。今回はA列の2行目から行ごとに画像を挿入していくことをしています。このあたりは必要に応じてアレンジしてみてください。


#### 列の幅の調整
```python
# 列の幅を自動調整
for col in sheet.columns:
    max_length = 0
    for cell in col:
        try:
            if len(str(cell.value)) > max_length:
                max_length = len(cell.value)
        except:
            pass
    adjusted_width = (max_length + 2) * 1.2 # 調整後の幅を計算
    col_letter = col[0].column_letter
    sheet.column_dimensions[col_letter].width = adjusted_width
```
セルの文字数に応じてセルの幅を調整していきます。
列の最大の文字数を取得し、その文字数を参考にしてセルの幅を決めています。
セル内で文字が改行されている場合はうまく動かない説がありますが、そのあたりはご愛敬ということで....。

```python
# 列"A"の幅を調整
sheet.column_dimensions["A"].width = pil_image.width * 0.14
```
画像を挿入したA列は画像の大きさに合わせてセルの幅を大きくしておきます。


#### Excelの体裁を整える
```python
# 一行目の背景色をグレーに設定
gray_fill = PatternFill(start_color='C0C0C0', end_color='C0C0C0', fill_type='solid')
for cell in sheet[1]:
    cell.fill = gray_fill

# 一行目のすべてのセルに下線を引く
thin_border = Border(left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin'))
for cell in sheet[1]:
    cell.border = thin_border

# 一行目のテキストを中央揃えにする
for cell in sheet[1]:
    cell.alignment = Alignment(horizontal='center', vertical='center')
    sheet.cell(row=1, column=1, value="image")

# 一行目のすべてのセルに太字のフォントを設定
for cell in sheet[1]:
    cell.font = Font(bold=True)
    cell.alignment = Alignment(horizontal='center')
```
上記のように書いてあげると、諸々の体裁を調整してあげることができます。

#### Excelファイルの保存
```python
file_name_image = 'example_with_image.xlsx'

# ファイルのフルパスを作成
file_path_image = os.path.join(folder_path, file_name_image)

# Excelファイルを保存
workbook.save(file_path_image)
```
保存先のパス、ファイル名を指定してworkbook.saveでエクセルファイルとして書き出すことができます。

#### 一時的なファイルの削除
```python
# 一時的なファイルを削除
for i in range(len(image_files)):
    temp_image_path = os.path.join(temp_dir, f"temp_image_{i+1}.png")
    os.remove(temp_image_path)
os.rmdir(temp_dir)
```

最後に一時的に作成していたファイル・ファルダを削除してあげましょう。

以上です。それではまたQiitaで会いましょう。🦛（←カバ）