Python pptxで簡単な自動スライド作成アプリを作成してみた。

# python-pptx
皆さんはパワーポイントを自動で作成したいと思ったことはありませんか？
というより、作成したいと思ったからこのページへと飛んできているんですよね。
そこで便利なのが、「python-pptx」。
こいつを使えば、オブジェクト指向でどんなスライドでも作成することができます。

今回はその中でも私が個人的に使いたい要素として、
1. タイトルスライド
2. 画像貼り付け
3. テーブル貼り付け

の三種類を実装しようと思います。
とはいえ、備忘録のための駆け足実装ですので、説明不足感が否めないですがご容赦ください。

では行きましょう。


# python-pptxの導入
導入はおなじみ、pipで簡単にinstallできます。

```bash
$pip install python-pptx
```

#0. 共通設定
タイトルスライドを入れる前に、必要な設定は先にしてしまいましょう。

```python:sample.py
from pptx import Presentation
from pptx.util import Cm, Pt          # 単位変換機能
from pptx.dml.color import RGBColor   # 色指定用
from PIL import Image                 # 画像貼り付けに必要
import pandas as pd

OUTPUT_PATH = "test.pptx"

ppt = Presentation()                  # ここにテンプレートにしたいpptxファイルを指定できる。今回はデフォルトで行う。
SLIDE_WIDTH = ppt.slide_width         # スライドの幅を取得
SLIDE_HEIGHT = ppt.slide_height       # スライドの高さを取得
```

それぞれ何をやっているのかわかりにくいので、簡単に解説します。
最初の```from pptx import Presentation```はベースとなるPresentationクラスをimportしています。ほぼ全てのオブジェクトがこのクラスに格納されているので、このimportは必須です。

次は```from pptx.util import Cm, Pt```です。
PowerPointの長さの単位は㎝でもinchでもないようです。そのため、人間にわかりやすいようにCmというメソッドをimportして変換します。
Ptも同様で、こちらはフォントサイズの意味です。

色の指定に```RGBColor```を使用します。あとで出てきますが、RGBそれぞれ0~255で指定して色を決めるようになっています。

画像の取得のために必要なのが、```from PIL import Image```です。
こちらはPillowというPackageが必要なのですが、python-pptxに依存してインストールされるので気にする必要はありません。

pandasはわかると思うので割愛します。

```ppt = Presentation()```の部分はクラスを使う時と同じ感覚です。
その下はPresentationクラスからスライドの高さと幅を引っ張ってきています。16:9なら縦x横:19.05x33.867cmのようです。

ではまずは、タイトルスライドを作成しましょう。

#2. タイトルスライド
目的のスライドは下の感じ。ずれているのが気になるけど、後々補正する方法もお伝えしますのでご容赦を。。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405454/e0624814-26cb-d442-11bc-b188fe120b5d.png)
さて。
パワポのタイトルスライドってデフォルトだと次のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405454/62196c91-0cdc-72de-05ea-84745ca70814.png)
これって、入力箇所が二つしかありません。そのため、二つの入力箇所を指定してやれば良いです。
入力箇所の指定は「placeholder」というメソッドを使用します。
順番を追ってみていきましょう。

```python:sample.py
def titleslide(title_message, sub_message):
    slide = ppt.slides.add_slide(ppt.slide_layouts[0]) # 1
    title = slide.placeholders[0]                      # 2-1 ここでtitleの場所を指定。
    title.text = title_message                         # 3-1
    subtitle = slide.placeholders[1]                   # 2-2 ここでsubtitleの場所を指定。
    subtitle.text = sub_message                        # 3-2

titleslide("pptx-practice", "I want to automize all.")
ppt.save(OUTPUT_PATH)
```
1ではslideを追加しています。
本当なら```title_slide = ppt.slide_layouts[0]```なんて感じに一段階噛ませるのでしょうけど、今回は省いています。
2ではslideのテンプレートにあるテキストボックスを指定しています。1番目だったら上、2番目だったら下、と言う感じです。
そして、指定したテキストボックスに3でメッセージを入れています。
今回は関数化したので、何か適当にコメントを定義してあげれば楽になるはずです。

#2. 画像貼付
お次は画像貼り付けです。
画像貼り付けを採用している理由は、matplotlibで出力された画像ファイルを貼り付けるためです。
直接グラフを入れてもいいのですが、matplotlibの画像の方が綺麗な気がするので、私はいつも出力→貼り付けを採用しています。
例となるスライドはこちら。（趣味丸出し、、、）
![スクリーンショット 2021-08-20 18.18.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405454/a4056447-c707-076d-6c77-c46b58506ae8.png)

ポイントは二つです。
1. テキストボックスの挿入。
2. 画像貼り付け(配置整理も含む)
では、コードの方を見ていきます。

```python:sample.py
pic_list = [file_path_0, file_path_1]                                  # イメージファイルの場所をリストで指定しておく。

def layout5slide_add_2_image(title_message, main_message, pic_list):
    slide = ppt.slides.add_slide(ppt.slide_layouts[5])
    title = slide.placeholders[0]
    title.width = Cm(31)
    title.height = Cm(3)
    title.text = title_message
    shapes = slide.shapes                                             # 1 shapesメソッドの設定。ここ重要！
　　　　　　　　# テキストボックスの追加
    textbox1 = shapes.add_textbox(Cm(3), Cm(4), Cm(25), Cm(3))
    textbox1.text = main_message
    for i, pic_path in enumerate(pic_list):
        im = Image.open(pic_path)
        im_width, im_height = im.size
        aspect_ratio = im_width/im_height
        display_height = IMG_H_SIZE
        display_width = IMG_H_SIZE * aspect_ratio
        left = (SLIDE_WIDTH / len(pic_list) - display_width) / len(pic_list) + SLIDE_WIDTH * i / len(pic_list)
        top = (SLIDE_HEIGHT / 2 - display_height) / 2 + Cm(8)
　　　　　　　　　　　　　　　　# イメージファイルの追加。
        shapes.add_picture(pic_path, left, top, height = IMG_H_SIZE)

layout5slide_add_2_image('趣味丸出しのスライド', '左が瑞牆山...', pic_list)
ppt.save(OUTPUT_PATH)

```
重要なのは```slide.shapes```です。これはスライドにいろんなものを追加できます。
今回はこのshapesメソッドを使用して、テキストボックスと画像を追加しています。
テキストボックスの場合は```shapes.add_textbox(left, top, width, height)```
画像の場合は```shapes.add_picture(path, left, top, width, height)```となっています。
引数はそれぞれ次のようになっています。
<ul><li>left  : スライド左端からコンテンツの左端までの距離</li><li>top   : スライド上端からコンテンツの上端までの距離</li><li>width : コンテンツの幅</li><li>height: コンテンツの高さ</li><li>path  : 画像ファイルのパス</li></ul>
わかりやすく図にするとこうなります。![スクリーンショット 2021-08-20 18.44.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405454/2e0beefc-2bb8-3add-ea09-92c73cfb7c57.png)
あとはこれをひたすらすきなところに追加すれば良いわけです。
一見複雑そうに見えるのは配置を計算で出しているだけで、大したことはしていません。

例えば、これだけでもテキストボックスと画像は追加されます。

```python:sample.py
slide = ppt.slides.add_slide(ppt.slide_layouts[5])
shapes = slide.shapes
textbox1 = shapes.add_textbox(Cm(3), Cm(4), Cm(25), Cm(3))
shapes.add_picture(pic_path, 10, 8, 8)
ppt.save(OUTPUT_PATH)
```
python-pptxで遊ぶだけなら直上のコードでも十分ですが、体裁を整えるとなると、いろいろ付け加える必要があるんですね。。。

さて、最後に表の追加をしていきましょう。

#3. 表を作る。
表を作る前に、表に使うデータを載せておきます。同じディレクトリにてきとうにCSVファイルを作ってください。

```csvs:samplr.csv
name,age,state,point
Alice,24,NY,64
Bob,42,CA,92
Charlie,18,CA,70
Dave,68,TX,70
Ellen,24,CA,88
Frank,30,NY,57
```
そして完成像がこちら。
![スクリーンショット 2021-08-20 19.36.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/405454/a57221dd-8602-ce1e-4efc-bad1b0c1a61e.png)
ある程度応用が効かせられたら良いなと思い、条件式を組み入れたりしています。

コードはこちら。

```python:sample.py
def layout5slide_add_table(title_message, df, table_setting):
    # title
    slide = ppt.slides.add_slide(ppt.slide_layouts[5])
    title = slide.placeholders[0]
    title.width = Cm(31)
    title.height = Cm(3)
    title.text = title_message
    
    # table
    # setting
    shapes = slide.shapes
    index_num  = len(df.index)+1
    column_num = len(df.columns)+1
    cell_font    = table_setting[0]
    table_width  = table_setting[1]
    table_height = table_setting[2]
    table_left = (ppt.slide_width  - table_width ) / 2
    table_top  = (ppt.slide_height - table_height) / 2
    table_midashi_color = RGBColor(50,150,225)
    table_cell_color = RGBColor(150,200,220)
    table_error_color = RGBColor(200,50,50)
    # table挿入
    table = shapes.add_table(index_num, column_num, table_left, table_top, table_width, table_height).table
    # (0,0) cellは別で色を入れる。
    cell00 = table.cell(0,0)
    cell00.fill.solid()
    cell00.fill.fore_color.rgb = table_midashi_color
    # index
    for i in range(1,index_num):
        index = table.cell(i,0)
        index.text = str(list(df.index)[i-1])
        index.fill.solid()
        index.fill.fore_color.rgb = table_midashi_color
    # column
    for i in range(1,column_num):
        column = table.cell(0,i)
        column.text = str(list(df.columns)[i-1])
        column.fill.solid()
        column.fill.fore_color.rgb = table_midashi_color
    # cell
    for row in range(1,index_num):
        for col in range(1,column_num):
            cell = table.cell(row,col)
            cell.text = str(df.iloc[row-1,col-1])
            cell.text_frame.paragraphs[0].font.size = cell_font
            cell.fill.solid()
            # 条件分岐 カラーリング
            if col == column_num-1:
                print(df.iloc[row-1,col-1],type(df.iloc[row-1,col-1]))
                if df.iloc[row-1,col-1] < 75:
                    cell.fill.fore_color.rgb = table_error_color
                else:
                    cell.fill.fore_color.rgb = table_cell_color
            else:
                cell.fill.fore_color.rgb = table_cell_color
df = pd.read_csv(sample.csv)
layout5slide_add_table('表(sample)', df, table_setting=[Pt(20),Cm(20),Cm(10)])
ppt.save(OUTPUT_PATH)
```

注目Pointは```shapes.add_table(index_num, column_num, table_left, table_top, table_width, table_height).table```です。この部分でtableを挿入しています。
ただし、このままだと中身は空っぽなので、cellメソッドを使って各セルに値を挿入しています。
今回は全てcellメソッドを使って入れています。colメソッドやrowメソッドを使うと、indexとcolumnがうまく入れられないので避けました。
あと、どうやらint型はcellメソッドには使えないようです。必ずstr型に変換する必要があります。

cellメソッドでは主に```text```,```fill```の二つのメソッドを使用しています。```text```メソッドは言葉の通りなので良いのですが、```fill```メソッドはよくわからないと思います。
この```fill```メソッドはセルの塗りつぶしに使用しています。```fill.solid```で塗りつぶしをするよーと宣言し、```fill.fore_color.rgb```で塗りつぶしの色を設定するようです。従って両方とも必要になります。

また、下の方で、セルの値によって塗りつぶしの色を分けています。こうすることで、より仕事の自動化などが捗ると思います。

# まとめ
pythonでスライド作成を駆け足でまとめてみました。
ほかにも機能はいろいろありますが、残りの編集は大抵が微調整だと思うので、直接編集した方が早い気がします。
ただ、こんな感じにざっくりとしたベースを自動生成できるようにすれば、かなり短縮されるのではないでしょうか。
もし他の機能が必要ならば、それらはドキュメントを実際に見てみてください。