Kivyアプリの基本的な考え方と最小構成にしたPythonコードと、kvファイルを使用したkv言語のコードの書き方を学ぶためのチュートリアル。Kivyアプリ作成のための最初の一歩になります。
Kivyアプリケーションの基本的な考え方
KivyではボタンやラベルなどのUIコンポーネントをウィジェットと呼んでいます。ボタンやラベル以外のレイアウトパターンもウィジェットになります。ウィジェット=オブジェクト=Classと思ってくれて大丈夫です。
基本的にAppクラスを継承してアプリケーションを作成します。AppクラスはKivyアプリケーションを作るためのベースになり上位クラスに位置します。
UIコンポーネントのウィジェットは、それぞれのUIウィジェットの基礎となるウィジェットクラスがあります。またレイアウトの種類はいくつかありますがレイアウトの基礎となるレイアウトクラスがあります。
クラスの名前空間
UIコンポーネントやレイアウトは「uix」に属してます。
これらのクラスの名前空間を見てもらえばわかるかと思いますが、レイアウトウィジェットとその他のUIウィジェットには親子関係はありません。しかしアプリケーションを構築する際はこれらをツリー状に構成していくので親子関係が成り立ちます。
uixに属するウィジェット
uixに属するウィジェットをみてみましょう。UIウィジェットはKivy公式サイトでは「UXウィジェット」と呼ぶみたいですが、あまり浸透していないので当サイトではUIウィジェットで統一します。
UIコンポーネントをUXウィジェットと呼んでいます。
ラベル、ボタン、 チェックボックス、 画像、スライダー、 プログレスバー、テキスト入力、 トグルボタン、スイッチ、 ビデオ
複数のUXウィジェットで組み立てられて1つのUIとなったものになります。
バブル、ドロップダウンリスト、 ファイル選択、ポップアップ、 スピナー、 リサイクルビュー、 タブパネル、ビデオプレーヤー、 VKeyboard
AnchorLayout、BoxLayout、 FloatLayout、 GridLayout、PageLayout、 RelativeLayout、ScatterLayout、 StackLayout
タッチすると動作するウィジェットになります。
散布図、ステンシルビュー
複数の画面を管理するウィジェットです。
スクリーンマネージャー
よく使うウィジェット
Kivyを初めて学習する方はAppクラスとBoxLayout、Label、Bottunのウィジェットだけでも進めることができます。これらのウィジェットの説明は別の記事で説明しますが、取り急ぎざっくり説明しておきます。
Appクラス
Appクラスはアプリケーションの土台になるものです。BoxLayoutはウィジェットの配置をするための土台になります。その土台の中にUIウィジェットを配置していく形になります。
BoxLayout
BoxLayoutは垂直方向または水平方向にウィジェットを並べるレイアウトです。入れ子にもできるので広く活用できます。(複数の入れ子は重くなるので程々にしてください)
Label
Labelはテキストを表示するためのウィジェットです。テキスト表示だけでなく、クリックイベントにも対応できてボタンのように扱うことができます。
Button
Buttonはイベントを発生させるために使われます。イベントはクリックしたり、マウスカーソルをあてるなどユーザーが何らかの動作をすると、動作に応じた処理を記述した関数を呼び出す仕組みです。例えばButtonをクリックするとLabelの文字を変更するといった感じです。Buttonに限らずKivyのUIウィジェットは何らかのイベントメソッドが標準で組み込まれています。コードの中でそれを紐づけて、処理を記述した関数を呼び出します。
tree構成
Kivyアプリは構成するウィジェットを組み合わせて下図のサンプルのようにツリー構成を作ります。ツリーの最上位をルートウィジェットを呼んでいてアプリケーションに1つ存在します。
Kivyアプリの最小構成(Python)
KivyアプリケーションはPython言語のみで書くこともできますが、Kivyでは独自の言語でkv言語(またはKivy言語)があって、アプリケーションの設計に応じて様々な書き方ができるようになっています。色々な書き方がある中でおそらく一番使われている基本的な書き方を説明します。
Pythonで書くKivyアプリの基本的な書き方①
Pythonで書くKivyアプリの最小構成は下記にようにシンプルなものです。これが基本構成になります。
- Appクラスと使用するクラスをインポートする。
- Appクラスを継承したサブクラスを定義する。
- build()メソッドを実装する。
その中にUIウィジェットを記述しそのインスタンスを返す。 - run() メソッドを呼び出す。
実際にコードにしてみましょう。ラベルウィジェットに「hello world」の文字列を表示するアプリになります。「first_py1_app.py」としてプロジェクトフォルダーに保存してください。
first_py1_app.py
# ①AppクラスとLabelクラスのインポート
from kivy.app import App
from kivy.uix.label import Label
# ②kivyのAppクラスを継承したサブクラスを作成する。C
class FirstPy1App(App):
# ③build()メソッドを実装する。
# この例ではLabelウィジェットを初期化してインスタンスを返す。B
def build(self):
return Label(text='hello world')
if __name__ == '__main__':
# ④run()メソッドを呼び出す。A
FirstPy1App().run()
実行するとアプリが立ち上がります。実行されるとA→Cの順で呼ばれます。これがPythonファイルで構成された最小のコードになります。
Pythonで書くKivyアプリの基本的な書き方➁
上記のコードではbuildメソッドの中にLabelウィジェットを書きましたが、別のクラスを作って呼び出す方法もあります。こちらの書き方の方が一般的で、クラスはレイアウトやWidgetクラスを継承します。
- Appクラスと使用するクラスをインポートする。
- Appクラスを継承したサブクラスを定義する。
- レイアウトやWidgetクラスを継承したサブクラスを定義する。
- コンストラクタでウィジェットのプロパティを定義する。
- レイアウトのプロパティを定義する。
- UIウィジェットを定義する。
- add_widget()メソッドで親ウィジェットにUIを追加する。
- build()メソッドを実装し、③で定義したインスタンスを返す。
- run() メソッドを呼び出す。
※Kivyにおけるプロパティはクラスやメソッドの属性もしくは属性値。またはPropertyクラスを指します。
first_py2_app.py
# ①App、BoxLayout、Labelクラスのインポート
from kivy.app import App
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
# ③ここではBoxLayoutクラスを継承しています。E
class MyRootWidget(BoxLayout):
# ④コンストラクタでウィジェットのプロパティを定義する。F
def __init__(self, **kwargs):
super().__init__(**kwargs)
# ⑤レイアウトのプロパティを定義する。
self.orientation = 'vertical'
# ⑥UIウィジェットを定義する。
self.label = Label(text='hello world')
# ⑦add_widget()メソッドで親ウィジェットにUIを追加する。
self.add_widget(self.label)
# ②kivyのAppクラスを継承したサブクラスを作成する。B
class FirstPy2App(App):
# ⑧build()メソッドを実装する。C
# この例ではレイアウトを継承したサブクラスのインスタンスを返す。
def build(self):
return MyRootWidget()
if __name__ == '__main__':
# ⑨run() メソッドを呼び出す。A
FirstPy2App().run()
実行するとA→Fの順に呼ばれます。
コードの解説とツリー構成
このコードは下記のツリー構成になっています。ルートウィジェットはMyRootWidgetクラスです。このようにウィジェットをツリー構成にして考え、何がルートウィジェットになるのか把握しておくとコードが書きやすくなります。
<MyRootWidget>
└─ BoxLayout
└─ Label
- ①Appクラスと使用するクラスをインポートする。
AppクラスとBoxLayoutクラス、Labelクラスをインポートしています。
from kivy.app import App from kivy.uix.label import Label from kivy.uix.boxlayout import BoxLayout
- ②Appクラスを継承したサブクラスを定義する。
Appクラスを継承したサブクラスFirstPy2Appを定義します。
class FirstPy2App(App):
- ③レイアウトやWidgetクラスを継承したサブクラスを定義する。
今回はBoxLayoutクラスを継承しています。BoxLayoutは垂直または水平にウィジェットを並べるレイアウトです。
class MyRootWidget(BoxLayout):
- ④コンストラクタでウィジェットのプロパティを定義する。
コンストラクタでウィジェットの定義を初期化します。
def __init__(self, **kwargs): super().__init__(**kwargs)
- ⑤レイアウトのプロパティを定義する。
今回は垂直になるようにorientationプロパティを’vertical’に設定してます。selfは自身のオブジェクトを指していてこの場合はMyRootWidgetクラスのオブジェクト(つまりBoxLayout)になります。
self.orientation = 'vertical'
- ⑥UIウィジェットを定義する。
今回はLabelウィジェットを定義していて、引数にはLabelウィジェットのtextプロパティの値に’hello world’を設定しています。
self.label = Label(text='hello world')
- ⑦add_widget()メソッドで親ウィジェットにUIを追加する。
PythonでUIウィジェットを定義する場合はadd_widgetメソッドで1つずつ追加する必要があります。
self.add_widget(self.label)
ここで言う親ウィジェットとはツリー構成にした時の親という意味で、そのウィジェットをどこに追加したいのかということです。今回はBoxLayoutになります。例えばLabelをButtonウィジェットに追加したい場合は親はButtonになります。
わかりやすくするために分割しましたが下記の様に1行で書くこともできます。self.add_widget(Label(text='hello world'))
- ⑧build()メソッドを実装し、サブクラスのインスタンスを返す。
buildメソッドを実装して、戻り値としてMyRootWidgetクラスを返します。
def build(self): return MyRootWidget()
- ⑨run() メソッドを呼び出す。
FirstPy2Appサブクラスを指定してrunメソッドを実装します。
if __name__ == '__main__': FirstPy2App().run()
Kivyアプリの最小構成(kv言語)
次にkv言語で書いたコードをみてみましょう。
kv言語のチュートリアルは下記の記事で紹介しています。
Kivyではkv言語が存在する
最初のサンプルコードではPython言語のみで書いていましたが、kv言語を使用して書くこともできます。kv言語はUIウィジェットに関連する事を書く為のメタ言語になります。メタ言語とは特定の言語を作成する為の言語で、この場合Python言語を作成する為のkv言語という事になります。kv言語だけではアプリケーションを動かす事はできません。Python言語を補完する言語といった感じです。
kvファイルの命名規則
kv言語で書かれたコードは「kv」の拡張子にします。ファイル名については決まりがあって、Appクラスを継承したサブクラスの名前にします。クラス名の後方に「App」があった場合はその部分は省略する事ができます。このサンプルではクラス名が「TestApp」なので「App」を省略して「test.kv」になります。Pythonファイルは「main.py」がよく使われていますが、ファイルが2つになるので管理しやすいようにkvファイルと同じ名前でも何でも大丈夫です。そのほかの命名規則はPythonの規則に従います。
これは必須ではありませんが、ファイル名に大文字を入れるとモバイルアプリに変換する際にKivyは正しくファイル名を検索できません。普段から小文字のみを使用すると訳の分からない罠に陥らずにすみます。
下記のコードをpyファイルとkvファイルとして別々のファイルで保存します。
kv言語で書くKivyアプリの基本的な書き方
kivyでは構成によりいくつかの書き方がありますが、基本的な書き方を説明します。せっかくなのでButtonも追加してみましょう。
first_kv_app.py
# ➀AppクラスとBoxLayoutクラスをインポート
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
# ➂レイアウトやWidgetクラスを継承したサブクラスを定義する
class MyRoot(BoxLayout):
pass
# ➁Appクラスを継承したサブクラスを定義する
class FirstKvApp(App):
# ➃buildメソッドを実装する
def build(self):
return MyRoot()
# ➄run()メソッドを呼び出す
if __name__ == '__main__':
FirstKvApp().run()
firstkvapp.kv
<MyRoot>: #ルート要素を定義
orientation:'vertical' # BoxLayoutを垂直方向に設定
Label: # Labelウィジェットを定義
id: mylabel # LabelウィジェットにIDを設定
text: 'hello world' # Labelに設定するテキスト
Button: # Buttonウィジェット
text: "ボタンを押して" # Buttonウィジェットに設定するテキスト
on_press: mylabel.text = "テキストが変わったよ" #ボタンイベントを定義
すごくスッキリしましたね!(コメントでごちゃごちゃしてるのはご容赦ください)
このコードの書き方ではコード構成はPythonと同じです。ただウィジェットの定義をkvファイルの方で分離して書いています。このようにUIとロジックを分離して書く書き方は管理がしやすくコードが読みやすくなるのがメリットです。
コード解説とツリー構成
このコードは下記のツリー構成になっています。ルートウィジェットはMyRootWidgetクラスになります。kv言語の書き方は別の記事で解説するため何をしてるか程度にとどめたいと思います。
<MyRootWidget>
└─ BoxLayout
└─ Label
└─ Button
- ①Appクラスと使用するクラスをインポートする。
AppクラスとBoxLayoutクラスをインポートします。他のUIウィジェットはkvファイルの方で記述しているのでインポートする必要はありません。
- ②Appクラスを継承したサブクラスを定義する。
Appクラスを継承したサブクラスFirstKvAppを定義します。
- ③レイアウトやWidgetクラスを継承したサブクラスを定義する。
今回はBoxLayoutクラスを継承しています。UIウィジェットはkvファイルで定義するため中身はありません、「pass」を記述することで中身がないクラスを作成できます。中身がなくても土台は必要なのでこのように定義しています。
- ④build()メソッドを実装し、サブクラスのインスタンスを返す。
buildメソッドを実装して、戻り値としてMyRootWidgetクラスを返します。
- ⑤run() メソッドを呼び出す。
FirstPy2Appサブクラスを指定してrunメソッドを実装します。
- ①Kivyのバージョン情報
明示的にkivyのバージョン情報を書いています。
#:kivy 2.3.0 #Kivyのバージョン情報
- ②ルート要素の定義
ルート要素にルールを定義しています。
<MyRoot>: #ルート要素を定義
- ③レイアウトのプロパティを設定
BoxLayoutを垂直方向に設定しています。
orientation:'vertical' # BoxLayoutを垂直方向に設定
- ④UIウィジェットの定義
LabelやButtonのプロパティを定義しています。
Label: # Labelウィジェットを定義 id: mylabel # LabelウィジェットにIDを設定 text: 'hello world' # Labelに設定するテキスト Button: # Buttonウィジェット text: "ボタンを押して" # Buttonウィジェットに設定するテキスト
- ⑤Buttonイベントを定義
Buttonイベントを定義しています。kvファイルを使用した場合、複雑な処理をしなければkv言語だけで完結できます。複雑な処理をしたい場合はPythonファイルの方でメソッドを定義して呼び出します。on_pressメソッドはKivyの標準イベントハンドラー(イベントアクションを定義した関数)でクリックのアクションが発生すると呼び出されます。ここでLabelのテキストを変更する処理を記述しています。
on_press: mylabel.text = "テキストが変わったよ" #ボタンイベントを定義
どっちの書き方が良いのか?
Python言語のみで書くよりも、kv言語と併用した書き方を強くお勧めします。Kivyアプリケーションではkv言語で書くとKivyの設計の仕組みにより色々と自動的で行ってくれるので、GUI開発にKivyを選択した恩恵が最大限に受けられます。またPythonコードに比べると遥かにコード量が減ります。kv言語の書き方を覚えなくてはいけませんがそのメリットは大きいです。当サイトでは主にkv言語を使用した書き方で説明しているので是非覚えて頂けたらと思います。
buildメソッドの役割
buildメソッドは大事な役割を持っていて、アプリが開始すると一度だけ呼ばれてインスタンスの生成をおこないます。Appクラス内でオーバーライドして、アプリ全体のツリー構成の最上位にあるルートウィジェットを作成して返します。
※kv言語を使用したコードではbuildメソッドを定義する必要がない書き方も存在します。
buildメソッドは下記の3つの役割を持っています。
「__name__ == ‘__main__’」は何か?
[__name__]はPythonの特殊属性で[__name__]の値は拡張子を除いたPythonファイル名が入ります。[__main__]は、Pythonファイルがスクリプトとして直接実行されたときに[__name__]に値を入れます。つまり、Pythonのファイル名がスクリプトとして実行されればrun()をします。ファイルがインポートされた場合はこれは無視されます。ここは謎の呪文だと思って毎回書いておいてください。
まとめ
Kivyを使ったアプリケーション開発の基本的な部分とPythonコードとkvコードで書くアプリの最小構成の書き方について説明しました。
Comment