もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

pythonでclass使ってみた

参考サイト

【Python入門】Pythonにおけるclassの使い方とは?Python 3でのファイルのimportのしかたまとめ

前書き

こんにちは。またまたpythonネタです。

今回は以前やったPythonで縦書き文字を画像合成してみたをclassを使うように変更してみました。

phpのクラスとpythonのクラスは結構やり方?が違って調べまくりましたが、なんとか出来ました。

やってみた

ディレクトリツリーは以下です。

$ tree
.
├── app.py
├── compositor
│   ├── __init__.py
│   └── text_compositor.py
├── font.otf
├── image.jpg
├── main.py
└── tmp

合成を行うクラスファイル(text_compositor.py)です。

# coding: utf-8
from PIL import ImageFont, ImageDraw
import six

class TextCompositor:
    BOLD_LOOP_COUNT = 20 # Boldを再現するループ描画数

    def __init__(self, string, font_size, draw_start_x, draw_start_y, is_bold=False, font_color='#ffffff'):
        self.__string       = string       # 対象文字列
        self.__font_size    = font_size    # フォントサイズ
        self.__draw_start_x = draw_start_x # 描画開始位置X
        self.__draw_start_y = draw_start_y # 描画開始位置Y
        self.__bold         = is_bold      # Boldを行うかどうかのフラグ
        self.__font_color   = font_color   # 文字色


    def getFontfilePath(self):
        return './font.otf'


    def getString(self):
        if (six.PY2 == True):
            # python2系の場合は、unicode変換をかける
            return unicode(self.__string, 'utf-8', 'ignore')

        return self.__string


    def isBold(self):
        return self.__bold


    def composite(self, frame_image, font_color='#ffffff'):
        # 描画用データを取得
        draw_image = ImageDraw.Draw(frame_image)

        # フォントデータを取得
        image_font = ImageFont.truetype(self.getFontfilePath(), self.__font_size)

        if (self.isBold() == True):
            # Bold判定の場合は指定回数分ループして同じ箇所に描画し続ける
            for i in range(self.BOLD_LOOP_COUNT):
                self.__execute(draw_image, image_font)
        else:
            self.__execute(draw_image, image_font)


    def __execute(self, draw_image, image_font):
        # 各文字の描画管理変数
        ix, iy = 0, 0

        for c in self.getString():
            x = self.__draw_start_x - ix * self.__font_size
            y = self.__draw_start_y + (iy * self.__font_size)

            # 今回描画する1文字の詳細なX, Yを設定
            char_width, char_height = image_font.getsize(c)
            x += (self.__font_size - char_width) / 2
            y += self.__draw_start_y

            # 指定の場所へ文字を描画
            draw_image.text((x, y), c, font=image_font, fill=self.__font_color)

            # 次の文字へ
            iy += 1

上記のcompositorを実行していくクラス(app.py)です。

# coding: utf-8
from PIL import Image

class App:
    def __init__(self):
        self.__compositors = []


    def addCompositor(self, compositor):
        self.__compositors.append(compositor);


    def run(self):
        frame_image = Image.open(self.getBaseFilePath())
        for compositor in self.__compositors:
            compositor.composite(frame_image)
        self.__save(frame_image)


    def getBaseFilePath(self):
        return './image.jpg'


    def getOutputFilePath(self):
        return './tmp/output.jpg'


    def __save(self, frame_image):
        frame_image.save(self.getOutputFilePath())
        frame_image.show()

実際に実行されるファイル(main.py)です

# coding: utf-8
import sys
from app import App
from compositor.text_compositor import TextCompositor

STRING_DATA = sys.argv[1]
app_logic   = App()
app_logic.addCompositor(TextCompositor(STRING_DATA, 30, 350, 20, False))
app_logic.addCompositor(TextCompositor(STRING_DATA, 60, 200, 20, False))
app_logic.addCompositor(TextCompositor(STRING_DATA, 80, 60, 20, True, "#FF0000"))
app_logic.run();

実行してみます。

$ python main.py "餌をください"

f:id:kojirooooocks:20180315235132p:plain

こんな感じです。

勉強になったところ

init.py

__init__.py を使ってディレクトリに入っているものをパッケージとして認識してくれるようです。

イコールimportができるようになるということのようです。

インスタンス変数

phpのメンバ変数的な立ち位置です。

self.AAA は public self._BBB は protected self.__CCCはprivate

みたいなイメージでいるんですが、大丈夫でしょうかね。。。?

あとがき

正直ボリューム的に少ないので、クラス化の恩恵はあんまりないですが、それでもpythonでのクラスの取扱が勉強できて良かったです。

__init__.py の存在に気づかず結構ハマってしまったのは内緒です。。。

今後pythonでプログラム書く場合は積極的にclass使っていきたいと思います!

このプログラムもgithubに上げときます。

yudaifujita0121/pillow_draw_text_sample