Pythonでスライド作成の自動化

# どんな記事？
ExcelなどでまとめたデータをPowerPointに自動で移行してくれるツールの作成．
PowerPointで作成するレイアウトが既に決まっていて大量にスライド生産するっていう場合に便利．
![Qiita_fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164046/1452ea52-0d69-48bf-fcca-0fe31d569661.png)

# 今回作成したもの
とある大会の作品ごとのポスターをパワーポイントで自動作成．
ポスターに入れた情報は以下のもの．

* 作品名
* チーム名
* 作品の画像（今回はjpgだがpngでも良い）
* 作品概要（日本語と英語）

# 実行環境
* Python 3.6.3
    * <font color="Red">当初はPython 3.7でやろうとしたがpptxライブラリが使えなかったのでバージョンダウンして3.6に</font>
* macOS High Sierra．ver10.13.6

# 用意するもの  
* pythonファイル
* 全ての作品の画像が入ったフォルダ
* 作品情報が入ったエクセルファイル
* テンプレート用のPowerPointファイル
![Qiita_fig2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164046/444f92c1-dde8-145e-db70-c1055a7f9501.png)

## 作品情報が入ったエクセルファイル
各種情報とシート名を記入．今回は画像をパスで指定．（多分もっといいやり方ある）
![Qiita_fig3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164046/c8b3bdea-7344-fdd2-a13e-806cf4512748.png)

## テンプレート用のPowerPointファイル
スライドマスターで作成．自動生成する際のテキストの位置やフォントもこのスライドマスターに依存される．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164046/cc529755-61db-9e85-a6a5-3c36bfbf9db2.png" width="50%">

# 導入
Pythonでパワーポイントファイルとエクセルファイルを扱うため，pipで`python-pptx`, `openpyxl`をインストール．

```
pip install python-pptx
pip install openpyxl
```

## ライブラリのインポート
pipでインストールした`python-pptx`, `openpyxl`をインポート．
また，センチメートルをPowerPoint上の距離に変換するために`Inches`もインポート

``` python
from pptx import Presentation
from pptx.util import Inches
import openpyxl
```

## 読み込み部分
Pythonファイルと同じ階層にあるエクセルファイルとテンプレートファイルの読み込みを行う．
テンプレートファイルの`パワーポイントを格納した変数.slide_layouts[0]`の引数の部分は引用したいスライドのページを書く．（0は1枚目）

``` python
# ブックを取得
Book = openpyxl.load_workbook(Book_File)

# シートを取得 
Sheet = Book[Book_Sheet]
lastRow = Sheet.max_row # 作品数の格納

# テンプートファイルの取得
prs = Presentation(Template_File)
blank_slide_layout = prs.slide_layouts[0]  # page layout
```

## 書き込み部分
今回は作品の数だけスライドを生成したかったので`for文`で作品数分だけ回す．
`シートを格納した変数.cell(row = ✖, column = △).value`でエクセルファイルからデータを持って来る．
`slide.placeholders[引数]`はテンプレートファイルのテキストボックスなどを取得するための処理．引数の確認の仕方は[トラブル](#トラブル)を参照


``` python
# 全ての作品を書き出すまでループ
for i in range(lastRow - 1):

    # 白紙のスライドの追加
    slide = prs.slides.add_slide(blank_slide_layout)
    shapes = slide.shapes

    # 作品タイトル
    title_shape = shapes.title
    title_shape.text = str(Sheet.cell(row = i + 2, column = 2).value)

    # チーム名
    team_shape = slide.placeholders[14]
    team_shape.text = 'チーム；' + str(Sheet.cell(row = i + 2, column = 3).value)

    # 画像
    pic = slide.shapes.add_picture(Sheet.cell(row = i + 2, column = 7).value, Centis(9.7), Centis(9), height = Centis(10))

    # 作品概要（日本語）
    abst_jp_shape = slide.placeholders[1]
    abst_jp_shape.text = ' ' + str(Sheet.cell(row = i + 2, column = 5).value)

    # 作品概要（英語）
    abst_en_shape = slide.placeholders[15]
    abst_en_shape.text = str(Sheet.cell(row = i + 2, column = 6).value)
```

# 出来上がったコード

``` python
from pptx import Presentation
from pptx.util import Inches
import openpyxl

# 入力部分
Template_File = 'Template.pptx'; # テンプレート元のパワポファイル
Book_File = 'Poster_template.xlsx' # 作品のデータが入ったエクセルファイル
Book_Sheet = 'Template_List' # エクセルファイル内の作品のデータが書いてあるシート名
Output_File = 'OutputPoster.pptx' # 出力するパワポファイル

# センチメートルをPowerPoint上の距離に変換する関数
def Centis(length):
    centi = Inches(length / 2.54)
    return centi

if __name__ == '__main__':
    # ブックを取得
    Book = openpyxl.load_workbook(Book_File)

    # シートを取得 
    Sheet = Book[Book_Sheet]
    lastRow = Sheet.max_row

    # テンプートファイルの取得
    prs = Presentation(Template_File)
    blank_slide_layout = prs.slide_layouts[0]  # page layout

    # 全ての作品を書き出すまでループ
    for i in range(lastRow - 1):

        # 白紙のスライドの追加
        slide = prs.slides.add_slide(blank_slide_layout)
        shapes = slide.shapes

        # 作品タイトル
        title_shape = shapes.title
        title_shape.text = str(Sheet.cell(row = i + 2, column = 2).value)

        # チーム名
        team_shape = slide.placeholders[14]
        team_shape.text = 'チーム；' + str(Sheet.cell(row = i + 2, column = 3).value)

        # 画像
        pic = slide.shapes.add_picture(Sheet.cell(row = i + 2, column = 7).value,Centis(9.7),Centis(9),height = Centis(10))

        # 作品概要（日本語）
        abst_jp_shape = slide.placeholders[1]
        abst_jp_shape.text = ' ' + str(Sheet.cell(row = i + 2, column = 5).value)
        
        # 作品概要（英語）
        abst_en_shape = slide.placeholders[15]
        abst_en_shape.text = str(Sheet.cell(row = i + 2, column = 6).value)

    # ファイルの書き出し
    prs.save(Output_File)
```

上記のコードを走らせると`OutputPoster.pptx`が生成される．
![Qiita_fig4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/164046/ad498da4-19d9-821c-b0b1-6b1bf510d044.png)

# トラブル
* `スライド変数.placeholder[引数]`を使うときに引数に何を入れたらいいのか分からなかった
    * レイアウト中のどのテキストボックスがどのid（引数）に対応しているか分からないので`Template.pptx`に対して下のスクリプトでidをチェックした

``` python
    # スライドレイアウトの要素を確かめるスクリプト
    slide = prs.slides.add_slide(prs.slide_layouts[0])
    for shape in slide.placeholders:
        print('%d %s' % (shape.placeholder_format.idx, shape.name))
```
* 画像を`スライド変数.placeholder[引数]`でinsert_pictureメソッドを使って挿入しようとしたがうまく行かなかった
    * 調べたがよく分からなかったので`add_picture`メソッドを使って指定した座標に画像を挿入した

# 参考にしたサイト
* Pythonでパワポの説明資料（報告書）を生成する
    * https://qiita.com/code_440/items/22e8539da465686496d3
    - 全体的な流れについて書いてある
    - テンプレートの取り扱いも書いてあった
* Pythonで画像ファイルをパワポに貼る
    * https://qiita.com/ceptree/items/ee3dce2924e7ecaa261f
    - 画像ファイルを取り扱うサイト
* python-pptx
    * https://python-pptx.readthedocs.io/en/latest/index.html