めも

メモ.

python-pptxの使い方のメモ

この記事は何

python経由でpptx(パワーポイント)ファイルを操作するライブラリであるpython-pptxについて、普段パワポ触ることがほとんどないのでパワーポイントの基本的な使い方も調べつつメモした記事。

pptx

Microsoft PowerPoint - Wikipedia

テンプレート・デザイン

www.slideshare.net

互換性

keynote・google drive・libreofficeなどを利用している人もいるが、python-pptxで操作できる基本的な図形はどのソフトでも対応していると思われます(詳細は未確認)。

phton-pptx

まず初めにpython-pptxのドキュメントのGetting Startedにていくつか事例が紹介されているのでこちらを参照しつつスライドをいくつか生成して見る。

プレゼンテーションにスライドを追加する

ドキュメント参照ページSlides — python-pptx 0.6.18 documentation

prs = Presentation()としてパワーポイントを作成後、slides.add_slideでパワポのレイアウトを指定してスライドを追加する。

from pptx import Presentation


def set_title_text(slide, text):
    shapes = slide.shapes
    shapes.title.text = text


def get_title_text(slide):
    return slide.shapes.title.text


prs = Presentation()
slide1 = prs.slides.add_slide(prs.slide_layouts[0])
set_title_text(slide1, "test")
slide2 = prs.slides.add_slide(prs.slide_layouts[1])
set_title_text(slide2, "test2")
prs.save("temp.pptx")

の出力結果は下のようになる。

f:id:misos:20210223002708p:plain
出力結果

テキストの箇条書きのスライドを作る

ドキュメント参照ページGetting Started — python-pptx 0.6.18 documentation

from pptx import Presentation


def set_title_text(slide, text):
    shapes = slide.shapes
    shapes.title.text = text


def get_title_text(slide):
    return slide.shapes.title.text


def set_paragraphs(slide, paragraphs):
    shapes = slide.shapes
    body_shape = shapes.placeholders[1]
    text_frame = body_shape.text_frame

    for level, text in paragraphs:
        p = text_frame.add_paragraph()
        p.level = level
        p.text = text

    return slide


prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[1])
# タイトル追加
set_title_text(slide, 'スライドのタイトル')
# テキスト追加
paragraphs = [
    (0, "テキストフレームのトップ"),
    (1, "テキストフレームの1段落目"),
    (2, "テキストフレームの2段落目"),
    (0, "テキストフレームの0段落目-2"),
    (2, "テキストフレームの2段落目-2"),
]
set_paragraphs(slide, paragraphs)
prs.save("temp.pptx")

f:id:misos:20210223010002p:plain
出力結果

テキストと画像が並んだスライドを作る

参照元ドキュメント:Source code for pptx.shapes.shapetree

add_picture関数が存在し、

    def add_picture(self, image_file, left, top, width=None, height=None):
        """Add picture shape displaying image in *image_file*.

        *image_file* can be either a path to a file (a string) or a file-like
        object. The picture is positioned with its top-left corner at (*top*,
        *left*). If *width* and *height* are both |None|, the native size of
        the image is used. If only one of *width* or *height* is used, the
        unspecified dimension is calculated to preserve the aspect ratio of
        the image. If both are specified, the picture is stretched to fit,
        without regard to its native aspect ratio.
        """
  • 画像ファイルパス、左からの位置、上からの位置を指定して画像を出力
  • height/widthで画像のサイズを指定
  • height/widthが指定なしの場合は画像の元サイズを使用

すれば良いとあるので試して見る。位置を指定するために新たに Inchesをインポートする。

from pptx import Presentation
from pptx.util import Inches

import numpy as np
import matplotlib.pyplot as plt


def creage_sample_fig(filepath):
    xs = [np.random.rand()**2 for _ in range(1000)]
    ys = [np.random.rand()**2 for _ in range(1000)]
    plt.scatter(xs, ys)
    plt.savefig(filepath)


def set_title_text(slide, text):
    shapes = slide.shapes
    shapes.title.text = text


def get_title_text(slide):
    return slide.shapes.title.text


def set_paragraphs(slide, paragraphs):
    shapes = slide.shapes
    body_shape = shapes.placeholders[1]
    text_frame = body_shape.text_frame

    for level, text in paragraphs:
        p = text_frame.add_paragraph()
        p.level = level
        p.text = text

    return slide


def add_picture(slide, img_filepath, position, imgsize):
    left, top = position
    width, height = imgsize
    pic = slide.shapes.add_picture(img_filepath, left, top, height=height, width=width)
    return slide


prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[3])

# タイトル追加
set_title_text(slide, 'スライドのタイトル')
# テキスト追加
paragraphs = [
    (0, "テキストフレームのトップ"),
    (1, "テキストフレームの1段落目"),
    (2, "テキストフレームの2段落目"),
]
set_paragraphs(slide, paragraphs)
# 画像の追加
img_filepath = "sample.png"
creage_sample_fig(img_filepath)
add_picture(slide, img_filepath, (Inches(5), Inches(2)), (Inches(5), Inches(4)))

prs.save("temp.pptx")

f:id:misos:20210223013351p:plain
出力結果

テーブルを含んだスライドを作る

ドキュメント参照ページGetting Started — python-pptx 0.6.18 documentation

table.cell(i+is_column_exists, j+is_index_exists).text = str(rij)の箇所でデータを文字列に変換しているのは .text 属性に指定できるデータが文字列であるため。add_table(slide, tabledata, position, tablesize, columns=None, indices=None) で指定したスライドにテーブルデータをプロットする。カラム・インデックスが渡されているときはそれをテーブルのはじめの行・列に表示するようにする。

from pptx import Presentation
from pptx.util import Inches

import numpy as np
import matplotlib.pyplot as plt


def creage_sample_fig(filepath):
    xs = [np.random.rand()**2 for _ in range(1000)]
    ys = [np.random.rand()**2 for _ in range(1000)]
    plt.scatter(xs, ys)
    plt.savefig(filepath)


def set_title_text(slide, text):
    shapes = slide.shapes
    shapes.title.text = text


def get_title_text(slide):
    return slide.shapes.title.text


def set_paragraphs(slide, paragraphs):
    shapes = slide.shapes
    body_shape = shapes.placeholders[1]
    text_frame = body_shape.text_frame

    for level, text in paragraphs:
        p = text_frame.add_paragraph()
        p.level = level
        p.text = text

    return slide


def add_picture(slide, img_filepath, position, imgsize):
    left, top = position
    width, height = imgsize
    pic = slide.shapes.add_picture(img_filepath, left, top, height=height, width=width)
    return slide


def add_table(slide, tabledata, position, tablesize, columns=None, indices=None):
    tabledata = np.array(tabledata)
    left, top = position
    width, height = tablesize
    is_index_exists = 1 if indices is not None else 0
    is_column_exists = 1 if columns is not None else 0
    

    shapes = slide.shapes
    colnum = tabledata.shape[1]+1 if is_index_exists else tabledata.shape[1]
    rownum = tabledata.shape[0]+1 if is_column_exists else tabledata.shape[0]
    table = shapes.add_table(rownum, colnum, left, top, width, height).table

    # 列幅を等間隔にする
    for i in range(colnum):
        table.columns[i].width = Inches(width.inches/colnum)

    # カラム名を指定する
    if is_column_exists:
        for i, ci in enumerate(columns):
            table.cell(0, i+is_index_exists).text = str(ci)

    # インデックス名を指定する
    if is_index_exists:
        for i, ii in enumerate(indices):
            table.cell(i+is_column_exists, 0).text = str(ii)

    # インデックスを指定する
    for i, ri in enumerate(tabledata):
        for j, rij in enumerate(ri):
            table.cell(i+is_column_exists, j+is_index_exists).text = str(rij)


prs = Presentation()
slide = prs.slides.add_slide(prs.slide_layouts[3])

# タイトル追加
set_title_text(slide, 'スライドのタイトル')

# テキスト追加
paragraphs = [
    (0, "テキストフレームのトップ"),
    (1, "テキストフレームの1段落目"),
    (2, "テキストフレームの2段落目"),
]
set_paragraphs(slide, paragraphs)

# 画像の追加
img_filepath = "sample.png"
creage_sample_fig(img_filepath)
add_picture(slide, img_filepath, (Inches(5), Inches(2)), (Inches(5), Inches(4)))

# テーブルの追加
columns = ["カラム1", "カラム2", "カラム3"]
indices = ["1行目", "2行目", "3行目"]
tab_position = (Inches(1), Inches(5))
tab_size = (Inches(8), Inches(1))
table = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
]
add_table(slide, table, tab_position, tab_size, columns=columns, indices=indices)

prs.save("temp.pptx")

f:id:misos:20210223021959p:plain
出力結果

グラフを含んだスライドを作る

ドキュメント参照ページ: - Working with charts — python-pptx 0.6.18 documentation - Charts — python-pptx 0.6.18 documentation

上記ページにて棒グラフ・円グラフなどの例がある。 ただ詳細なグラフを作るたびに上記APIを見るのが大変そうだったので、 複雑なグラフは画像で出力+生データをcsvなどで出力する方針にしたので割愛。

他の方の使用例

python-pptxで検索していた際に見たサイト一覧です。