PR
Python+Kivy入門

Kivyの画像表示1: 画像をボタンにする&画像サイズの変更【kv言語】

この記事は約12分で読めます。

Kivyアプリで画像を表示し、画像サイズの変更やアニメーション画像、キャッシュなどの設定について説明します。また、画像をボタンとして使う方法や画像にテキストや画像を重ねる方法についても解説しています。

Kivyで画像を表示する

Kivyで画像を表示するにはImageクラスを使います。

kivy.uix.image

Kivyで扱える画像の種類

Kivyがデフォルトで対応している画像形式(拡張子)は下記になります。

  1. PNG (.png):透過pngもサポートしている。
  2. JPEG (.jpg, .jpeg)
  3. GIF (.gif):アニメーションgifや透過gifもサポートしている。
  4. BMP (.bmp)
  5. ICO (.ico)
  6. SVG (.svg)

Pillowライブラリを使用してAtlasも扱うことができます。

画像を表示する方法

画像を表示するにはsourceプロパティで画像のパスを指定します。

画像はウィジェットボックスに収められ、デフォルトではウィジェットボックスに合わせたサイズで表示されます。ウィジェットボックスより画像の方が小さい場合は元の画像サイズを維持します。その際ウィジェットのボックスの画像ではない部分は空白の領域になり背景色が表示されます。

<RootWidget>:
    Image:
        source: 'images/icon-001.png'

上記のコードはソースコードと同じ場所にimagesフォルダを置いています。モバイルアプリの場合、相対パスよりは絶対パスを取得する方法で記述することをお勧めします。

プロジェクトフォルダー
+--- Imagesフォルダー
+---+--- image.png
+--- pyファイル
+--- kvファイル

画像を扱う

Imageクラスには画像を扱うためのメソッドやプロパティがいくつか用意されています。これらの中でよく使われるものを紹介します。

  1. fit_mode:画像サイズを変更する。
  2. color:画像に色を付ける。
  3. anim_delay:アニメーションの再生速度を設定する。
  4. anim_loop:アニメーションのループ回数を設定する。
  5. nocache:内部キャッシュの設定をする。
  6. remove_from_cache():キャッシュを削除する。
  7. reload():ディスクからイメージを再読み込みする。

画像サイズを変更する

fit_modeプロパティは画像サイズを変更するための4つのモードがあります。

  1. scale-down:画像はウィジェットボックスに合わせて縮小されます。
  2. fill:画像はウィジェットボックスに合わせて拡大します。
  3. contain:画像はウィジェットボックスに合わせてリサイズされます。
  4. cover:画像は水平または垂直に引き伸ばされ、ウィジェットボックスに収まります。

同じように画像サイズを変更するプロパティのallow_stretchkeep_ratioがありますが、Kivy2.2.0より非推奨になりました。Kivy2.3.1では使用できますが警告が表示されます。将来、削除される可能性があります。

scale-down

scale-downはウィジェットボックスに収まるように縮小されます。画像サイズがウィジェットより小さい場合、元のサイズで表示されます。画像のアスペクト比(縦横の比率)がウィジェットと異なる場合、ウィジェットボックスに空白領域ができて背景色が表示されます。このscale-downがデフォルトです。

Kivyで画像表示。fit_modeのscalle_downを設定したアプリ

fill

fillは画像はウィジェットボックスの縦横のサイズに合わせて拡大します。アスペクト比は無視されるので画像表示が崩れる可能性があります。

Kivyで画像表示。fit_modeのfillを設定したアプリ

contain

containでは画像はウィジェットボックスに合わせてリサイズされますが、アスペクト比は維持されます。画像サイズがウィジェットボックスより大きい場合は縮小されます。画像サイズがウィジェットボックスより小さい場合、画像はウィジェット内に収まるようにリサイズされます。画像のアスペクト比がウィジェットと異なる場合、ウィジェットボックスに空白の領域ができて背景色が表示されます。

Kivyで画像表示。fit_modeのcontainを設定したアプリ

cover

coverは画像は水平または垂直に引き伸ばされ、ウィジェットボックスに収まります。これはアスペクト比を維持したまま、水平または垂直のどちらかのサイズを基準にウィジェットボックスを埋めます。画像の縦横比がウィジェットと異なる場合、画像は切り取られて表示されます。

Kivyで画像表示。fit_modeのcoverを設定したアプリ

画像に色を付ける

画像に色を付けるにはcolorプロパティでRGBA値を指定します。これは、画像の色を変更するためではなく、元の画像の上に色を付けています。ただし、画像がグレーまたは白でない場合、色は期待どおりに機能しません。

color: 1, 0, 1, 1
kivyアプリで画像に色を付ける設定をした

アニメーション画像を操作する

アニメーションGIFなどのアニメーション速度や再生数を設定することができます。

アニメーションの再生速度を設定を設定する

アニメーションの再生速度を設定するにはanim_delayを定義します。デフォルトは0.25 (4 FPS)です。-1を指定した場合はアニメーションを停止し静止画像にします。

anim_delay: 1.0

アニメーションのループ再生数を設定する

アニメーションのループ回数を設定するにはanim_loopプロパティを定義します。デフォルトは0でアニメーションは永続します。

anim_loop: 5

キャッシュの設定をする

Kivyはデフォルトで内部キャッシュを使用していますが、これを無効にすることができます。また、remove_from_cache()でキャッシュの削除を行うことができます。

内部キャッシュを無効にする

内部キャッシュを使用するかどうかをnocacheTrueまたはFalseで指定します。Trueで内部キャッシュを無効にし、デフォルトはFalseで内部キャッシュを有効にします。

nocache: True

キャッシュを削除する

キャッシュを削除するにはremove_from_cache()を使用します。下記は画像キャッシュを削除する例を示します。

from kivy.uix.image import Image

class RootWidget(BoxLayout):
    def on_stop(self):
        # 画像キャッシュをクリアする
        for image in Image._image_cache:
            Image._image_cache[image].remove_from_cache()
        Image._image_cache.clear()

このコードでは、アプリケーション終了時に画像のキャッシュを削除しています。on_stop()はアプリケーションが終了するときに呼ばれるKivyのイベントコールバックです。for文では下記を実行しています。

  1. 画像キャッシュの確認
  2. 画像キャッシュの削除
  3. 画像キャッシュの辞書を削除
画像キャッシュの確認

Image._image_cacheは、Kivyが内部的に使用する画像キャッシュを保持する辞書です。ここには、アプリケーションが使用するすべての画像がキャッシュされています。

for image in Image._image_cache:
画像キャッシュの削除

各画像に対して、remove_from_cache()を呼び出して、画像をキャッシュから削除しています。

Image._image_cache[image].remove_from_cache()
画像キャッシュの辞書を削除

clear()を呼び出して、キャッシュ辞書そのものをクリアします。これにより、キャッシュ内のすべての画像キャッシュが削除されます。

Image._image_cache.clear()

画像を表示するサンプルコード

ここまで説明した内容をサンプルコードで確認しましょう。設定を変えて試してみてください。

images1.py

from kivy.app import App
from kivy.uix.gridlayout import GridLayout
from kivy.uix.image import Image

class RootWidget(GridLayout):
    def on_stop(self):
        # 画像キャッシュをクリアする
        for image in Image._image_cache:
            Image._image_cache[image].remove_from_cache()
        Image._image_cache.clear()

class Images1(App):
    def build(self):
        return RootWidget()

if __name__ == '__main__':
    Images1().run()

images1.kv

<RootWidget>:
    cols: 2
    Image:
        source: 'images/image-007.jpg'
        nocache: True
    Image:
        canvas.before:
            Color:
                rgba: 222/255, 228/255, 215/255, 1
            Rectangle:
                size: self.size
                pos: self.pos

        source: 'images/image-007.jpg'

        fit_mode: 'scale-down' # default
        #fit_mode: 'fill'
        #fit_mode: 'contain'
        #fit_mode: 'cover'

        #color: 1, 0, 1, 1

        #anim_delay: 0.1
        #anim_loop: 5

画像をボタンとして使う

画像をボタンとして使えるようにするにはKivyのBehaviorモジュールのButtonBehaviorクラスを使用します。

KivyのBehaviorモジュールには、ボタンやラベルなどの基本的なウィジェットに何らかの動作を組み合わせるmixinクラスがいくつか用意されています。その中でもButtonBehaviorクラスはウィジェットをボタンと同じように扱える機能を提供します。

images2.py

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

class RootWidget(BoxLayout):
    pass

class Images2(App):
    def build(self):
        return RootWidget()

if __name__ == '__main__':
    Images2().run()

images2.kv

<ImageButton@ButtonBehavior+Image>
    source: 'images/image-ts-005.png'

<RootWidget>:
    Label:
        id: label
        text: 'You can click on the image'

    ImageButton:
        on_press: label.text = 'ImageButton Pressed'
        on_release: self.source = 'images/image-ts-006.png'
        #on_touch_down: label.text = 'ImageButton touched'

この例では、on_press()が呼ばれるとメッセージを表示し、on_release()が呼ばれると別の画像を表示します。

kvコードの1行目は動的クラスルールでButtonBehaviorクラスとImageクラスの多重継承してカスタムウィジェットを作成しています。動的クラスルールについては下記の記事をご覧ください。

画像にオーバーレイテキストを表示する

画像の上にテキストを重ねることができます。テキスト以外でも画像も重ねられます。これは自由な位置にウィジェットを配置できるレイアウトで可能です。下記は単体でレイアウトを使用し、これが可能かどうかを示しています。

  • FloatLayout
  • RelativeLayout
  • AnchorLayout
  • ScatterLayout

このコードを実行すると下図のように表示されます。

Kivyアプリで、画像にテキストと画像を重ねているアプリ

images3.py

from kivy.app import App
from kivy.uix.floatlayout import FloatLayout

class RootWidget(FloatLayout):
    pass

class Images3(App):
    def build(self):
        return RootWidget()

if __name__ == '__main__':
    Images3().run()

images3.kv

<RootWidget>:
    Image: 
        source: 'images/image-007.jpg'
    Image:
        source: 'images/icon-001.png'
        #pos_hint: {"center_x": 0.6, "center_y": 0.4}
        x: int(self.x / 2 + 80)
        y: int(self.y / 2 - 50)
    Label:
        text: 'Overlay Text'
        color: 1, 0, 1, 1
        font_size: 40
        size_hint: None, None
        size: 300, 50
        pos_hint: {"center_x": 0.5}
        y: int(self.parent.height / 2 + 90)

単純にpos_hintで位置を調整してるだけです。おそらくcanvasを使用するとより複雑なことができると思います。

ウィジェットのサイズと位置の設定については下記の記事をご覧ください。

画面サイズが変わると、画像やラベルのテキストが動いてしまうので、自身の位置や親のサイズを基準に位置を計算しています。

x: int(self.x / 2 + 80)
y: int(self.y / 2 - 50)
y: int(self.parent.height / 2 + 90)

Comment

タイトルとURLをコピーしました