読者です 読者をやめる 読者になる 読者になる

Kivyで簡単なアプリを作ってみる

ここのところKivyのサンプルを動かしたりソースコードを読んだりしているのですが、 仕様がムダなく洗練されていていい感じですね。モバイル環境ではまだ試せていないのですが、Kivyがいろんな用途で今後もっと広まっていくといいなと思います。

今日はこれまで勉強したことの復習も兼ねて、シンプルなアプリを作ってみたいと思います。

今回作るアプリ

電卓でも作ろうかと思ったのですが、ボタンの数が多くて面倒そうだったので、今回は下のイメージのようなシンプルなタイマーアプリを作ることにします。

f:id:tkitao:20141007204324p:plain

+1 minuteボタンで残り時間を60秒追加、Start/Stopボタンでカウントダウンの開始と停止。Resetボタンで残り時間を0に戻す単純な仕様です。

アプリのコード

アプリのコード全文は以下のとおりです。

from kivy.config import Config
Config.set('graphics', 'width', '300')
Config.set('graphics', 'height', '150')

from kivy.lang import Builder
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.uix.button import Button
from kivy.properties import BooleanProperty
from kivy.properties import NumericProperty
from kivy.clock import Clock
from kivy.app import App


Builder.load_string('''
#:kivy 1.8.0

<KivyTimer>:
    BoxLayout:
        orientation: 'vertical'
        pos: root.pos
        size: root.size

        Label:
            text: str(root.left_time)
            font_size: 100

        BoxLayout:
            orientation: 'horizontal'
            size_hint: 1.0, 0.3

            Button:
                text: '+1 minute'
                font_size: 16
                on_press: root.on_command('+1 minute')

            Button:
                text: 'Stop' if root.is_countdown else 'Start'
                font_size: 16
                on_press: root.on_command('start/stop')

            Button:
                text: 'Reset'
                font_size: 16
                on_press: root.on_command('reset')
''')


class KivyTimer(Widget):
    is_countdown = BooleanProperty(False)
    left_time = NumericProperty(0)

    def on_command(self, command):
        if command == '+1 minute':
            self.left_time += 60
        elif command == 'start/stop':
            if self.is_countdown:
                self.stop_timer()
            elif self.left_time > 0:
                self.start_timer()
        elif command == 'reset':
            self.stop_timer()
            self.left_time = 0

    def on_countdown(self, dt):
        self.left_time -= 1
        if self.left_time == 0:
            self.is_countdown = False
            return False

    def start_timer(self):
        self.is_countdown = True
        Clock.schedule_interval(self.on_countdown, 1.0)
        pass

    def stop_timer(self):
        self.is_countdown = False
        Clock.unschedule(self.on_countdown)
        pass


class KivyTimerApp(App):
    def build(self):
        return KivyTimer()


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

ポイントを順に解説します。

ウィンドウサイズの設定

Kivyでは専用の設定ファイル(.iniファイル)でウィンドウサイズの設定を行いますが、今回はファイルを一つにまとめるために、Cofigクラスを使用して直接パラメータを設定しています。

Config.set('graphics', 'width', '300')
Config.set('graphics', 'height', '150')

Configの値が他のモジュールに影響を与える場合があるため、設定は他のモジュールをインポートする前に行います。

ウィジェットの配置

ウィジェット(UI部品)の配置は専用のKV言語を使用して行います。こちらも別ファイルに分けることができるのですが、今回はBuilderクラスを使用してコード内に直接KV言語を記述しています。

Builder.load_string('''
#:kivy 1.8.0
ここにKV言語を記述
''')

KV言語内ではアプリケーションとデータのやり取りを行うために、ウィジェットのパラメータとしてルートウィジェット(KivyTimerクラス)のプロパティやメソッドroot.で指定しています。

# ラベルに残り時間を表示する 
Label:
    text: str(root.left_time)
# ボタン押下時にルートウィジェットのon_commandメソッドを呼ぶ
Button:
    on_press: root.on_command('+1 minute')
# ルートウィジェットのis_counddownプロパティに応じてボタンの表記を変更する
Button:
    text: 'Stop' if root.is_countdown else 'Start'

アプリケーションコードの記述

アプリケーションの動作内容は、ルートウィジェットとして使用するKivyTimerクラス内で記述します。

クラスの先頭でKV言語で引用しているプロパティを定義します。

class KivyTimer(Widget):
    is_countdown = BooleanProperty(False)
    left_time = NumericProperty(0)

ボタンが押された際に呼ばれるコールバック用のメソッドも定義します。ここでは引数としてボタンの種類を表す文字列を受け取り、値に応じて処理を分岐させています。

    def on_command(self, command):
        if command == '+1 minute':
            self.left_time += 60

終わりに

Kivyで簡単なアプリを作ってみました。いつものProcessingのサンプルと比べると、クラス定義などのためコードのボリュームがそれなりにありますが、アプリに必要な要素を一通り押さえていると考えると、かなり簡潔な記述が出来ていると思います。

また、KV言語のおかげで直感的にウィジェットの設定ができる一方、プロパティの自動連動等、暗黙の仕様も多いので、そのあたりの理解がKivy習熟の早道のように感じました。