Python+Kivy入門

kvファイルを使用したkv言語の書き方チュートリアル

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

Kivyアプリのkvファイルにkv言語のコードを書くための決まり事を理解するためのチュートリアルになります。kv言語を学ぶための最初の一歩になります。

kvファイルの決まり事

Kivyアプリの書き方のチュートリアルについては下記の記事をご覧ください。

kvファイルを書くときはいくつかの決まり事があります。

  1. ファイル名は「kv」拡張子として、Appクラスを継承したサブクラスの名前にする(必須)
  2. 1行目にKivyバージョンを書く(推奨)
  3. インデントはスペースで最初のインデント数の倍の数にする(必須)
  4. コメントはPythonと同じでシャープを使う(任意)
  5. 1行で完結する。文の途中で改行してはいけない(必須)
  6. Pythonと同じでPEP8命名規則に従う(推奨)
  7. root要素は必ず必要。rootはツリーの始まりの要素になります(必須)

kv言語のファイル名

拡張子は「kv」になります。Appクラスを継承したサブクラスの名前にします。ただし、サブクラスの名前後ろに「App」がついている場合は省略できます。

  • MySampleApp → mysample.kv
  • MyAppSample → myappsample.kv
  • MySample → mysample.kv

1行目にKivyのバージョンを指定する

これは書いても書かなくても動作します。ここでは「#(シャープ)」はコメントではなく必要な文字です。kv言語ではコメント以外でも「#」を使うので注意してください。

#:kivy '2.3.0'

kv言語のインデント

インデントはスペースが推奨されています。Kivyバージョン1.7.0迄はPythonと同じでスペース4つ分が推奨されていましたが、現在は最初にインデントを使用した数を基準にその倍数にします。分かりにくいのでPythonと同じでスペース4つ分で良いと思います。

#:kivy '2.3.0'
# これはコメント

<MyRoot>:
    orientation: 'vertical'
    padding: 10
    spacing: 10
    Label:
        id: my_label
        text: "インデントは同じ数にしよう"
        font_size: 32

kv言語のコメント

コメントは#(シャープ)になります。冒頭の「#」はコメントの他にバージョン指定と、Pythonにあたる変数やimportの定義にも「#」が使われているので注意してください。

1行で完結する

kv言語はif文やfor文などの制御構文も使えますが、どんなに長くても改行してはいけません。ステートメントの終わりは改行しても大丈夫です。値や引数も同様で途中で改行すると値を正確に読み込んでくれない、またはインデントエラーが発生します。

良い例

TextInput:
    on_focus: self.insert_text("Focus" if args[1] else "No focus")

悪い例

TextInput:
    on_focus: self.insert_text(
        "Focus"
        if args[1] else "No focus"
    )

kv言語の命名規則

クラス名・メソッド名・変数名などはPythonと同じでPEP8命名規則に従ったほうがいいでしょう。

kv言語(kvファイル)の構成とルール

uixクラスに属するウィジェットはkvファイルに定義することができます。kvファイルはツリー状にウィジェットを定義していきます。このようなツリーはウィジェットツリーと呼ばれています。

kv言語構成treeサンプル

ルートウィジェット

ルートウィジェットはbuildメソッドで返されたインスタンスを指します。アプリには必ず1つルートウィジェットが含まれます。ツリー構成の一番上のウィジェットに相当するものがルートウィジェットになります。

ruleと要素

ツリー状に定義されたものをルールと呼んでいて、ウィジェットについての情報やスタイル設定を定義します。ツリーの始まりはRoot要素になり、これは必ず1つ必要です。Root要素の下には子要素や他のルールも定義できます。Rootを親要素として親子関係になっていて、更にその下の子要素としてUIウィジェットのプロパティと値を定義します。画面に配置する順番に上から並べて書いていきます。親要素と子要素の間もしくは要素と値の間には「:(コロン)」で区切ります。

kv言語のツリーのルールを表す図

※Kivy公式サイトでは「Root要素」「子要素」という言い方はしていません。Root要素を「ルートウィジェット」呼んでいますが、説明するのに不便なため、当サイトではRoot要素、子要素と呼びます。

kvファイルで定義できるもの

kvファイルは主に下記のものを定義することができます。

  • ルールの定義(クラスルール、動的クラス、その他のルール)
  • Propertyクラスのバインディング
  • 変数の定義
  • importの定義
  • canvasのグラフィック定義

そのほかイベントハンドラを呼び出したり、ifやforなどの制御構文、print文など記述することができます。

ルート要素に指定できるルール

Root要素に指定できるルールはいくつかありますが、基本的にクラスルールを使用します。

  • クラスルール
  • ルートルール(ウィジェット、RootClassName)※
  • その他のルール

※公式サイトではルートルールのルートクラス名、ウィジェットについて明確に呼称されておらず単に「ルートウィジェット」と呼ばれています。説明をわかりやくするために当サイトではこれらで呼称します。

クラスルールはPythonで定義したレイアウトなどを継承したサブクラスをkv言語で構成できます。ルートルールはクラスを指定する方法とウィジェットを指定する2種類の方法があります。ルートクラス名を指定するルートルールはクラスルールと同様ですが役割が違います。

このうちRoot要素として定義できるもので、クラスルールは複数追加できますが、ルートルールはkvファイルの中で1つだけしか定義できません。これらのルールはアプリを起動した時に同じように画面に表示されるので結果的には同じなんですが、内部的には違う動作をしています。

クラスルール

通常はこれをRoot要素に指定します。

レイアウトのウィジェットを継承したサブクラス名を「< >(角括弧)」で囲います。その下に子要素としてウィジェットの定義をします。通常はレイアウトウィジェットから始めます。クラスルールはクラスごとに複数定義できます。

  1. ルート要素にウィジェットを継承したサブクラス名を<>で囲んで指定する。
  2. レイアウトウィジェットを定義する。
  3. レイアウトウィジェットのプロパティを定義する
  4. UIウィジェットを配置したい順番に並べる。
  5. UIウィジェットのプロパティを定義する。
# kvファイル
<RootWidget>: # ➀ルート要素にクラスルールを定義
    orientation: 'vertical' # ➂BoxLayoutのウィジェットを定義(これはプロパティ)

    Label: # ④UIウィジェットを配置したい順に並べる
        text: "これはクラスルール" # ➄UIウィジェットのプロパティを定義

②が抜けている理由は、このコードではクラスでBoxLayoutを継承してるので、<RootWidget>自体がBoxLayoutになるため定義しなくても問題ありません。

# pyファイル
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

# クラスルールになるクラス
class RootWidget(BoxLayout):  
    pass

class MyApp(App):
    # buildメソッドでインスタンスを生成
    def build(self):
        return RootWidget()

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

上記コードはツリー構成にすると下記になります。インデントがツリーを表しているので、インデントの数を間違えるとエラーになるので注意してください。

<RootWidget>
  ∟BoxLayout
    ∟Label

クラスルールは全体のスタイルやUIの設定を指定するためのものです。上記のコードの場合だとbuildメソッドによってRootWidgetのインスタンスが作成されると、このルールに従ったウィジェットツリーが作成されてスタイルが適用されます。

クラスルールはPython側でロジックを書いてkv側で呼び出すこともできます。Kivyのイベントハンドラとも組み合わせられるので万能に使えます。また複数のクラスを定義しているときもそれぞれのクラス毎にスタイルを定義することができます。

ルートルール(ルートクラス名)

ルートルールもスタイルやUIの設定を定義するものです。buildメソッドを定義しなくてもKivyで自動的にインスタンスを生成します。ルートルールは1つだけ定義することができます。クラスルールとルートルールの違いは、前者がスタイルを設定する為に定義するもので、後者がスタイルを定義しKivyが自動的にインスタンスを作成するという役割の違いがあります。

buildメソッドは明示的に定義しても問題ありませんがreturn文は必要ありません。

class MyApp(App):
    def build(self):
        pass

ルートクラス名

通常これはRoot要素として使われませんが、特定のレイアウトやBuilder.load_stringでPythonファイルから読み込む場合に使用すると便利な場合があります。

クラス名の後に「:(コロン)」を付けます。ルートクラス名を指定した場合は「< >(角括弧)」は使用しないので「< >」が付いてるかどうかでクラスルールと見分けられます。

#kvファイル
RootWidget: #ルート要素(ルートルール)
    orientation: 'vertical'

    Label:
        text: "これはルートクラス名"

# pyファイル
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

# ルートルールになるクラス
class RootWidget(BoxLayout):  
    pass

# Kivyがインスタンスを生成するのでbuildメソッドは要らない
class MyApp(App):
    pass

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

Kivyでそのクラスのインスタンスを作成します。kvファイルで自動的にインスタンスが作成されるのでbuildメソッドを定義する必要がありません。クラスルールと同じでPython側でロジックを書いてkv側で呼び出すこともできます。Kivyのイベントハンドラとも組み合わせられるので万能に使えますが、通常はクラスルールの方を使用します。

ルートルール(ウィジェットを指定)

ウィジェットを指定する時はAppサブクラスのみの構成の場合に使われます。

レイアウトを指定する

主にroot要素はレイアウトのウィジェットになります。書き方はルートクラス名を指定した場合と同じでウィジェット名の右側に「:」を付けます。

# kvファイル
BoxLayout: # Root要素
    orientation: 'vertical'

    Label:
        text: "UIをルートにする"
    
# pyファイル
from kivy.app import App

# Kivyが自動的にインスタンスを生成するのでBuildメソッドは要らない。
class MyApp(App):
    pass

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

PythonファイルではKivyアプリを起動する最小限のコードだけを書きます。kv言語のイベントハンドラを使用したり、Appクラスでメソッドを定義することができますが、Appサブクラスでしか動作しないので使用は限定的になります。

その他のUIウィジェットを指定する

レイアウトウィジェット以外の他のUIウィジェットをRoot要素にすることができますが、1つだけしか定義できないのでテストしたい場合やこのサイトのようにサンプルを提示したい場合など、使える用途としてはかなり限定的になります。

Label:
    text: "ウィジェットをRoot要素にする"
Image:
    source: "image.png"

その他のルール

ウィジェット名を<>で囲むとそのウィジェットについてスタイルや設定を定義することができます。また複数のクラス名を<>で囲むとそのクラスで同じ設定を適用することができます。こちらはクラスルールと同じ扱いになりますが明確な呼称はありません。ウィジェットを定義した場合はルート要素としても機能はしますが通常クラスルールと併用して使います。

# kvファイル
<BoxLayout>: 
    orientation: 'vertical'

    Label:
        text: "UIをルートにする"
    

複数のクラスルールをまとめて書くこともできます。

# kvファイル
<MainWidget,SubWidget>: 
    orientation: 'vertical'

    Label:
        text: "UIをルートにする"
    
# pyファイル
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout

class RootWidget(BoxLayout):  
    pass

class MyApp(App):
    # buildメソッドでインスタンスを生成
    def build(self):
        return RootWidget()

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

カスタムウィジェット

ウィジェットを継承したサブクラスはインスタンス化するとオブジェクトになりますが、このオブジェクトはウィジェットとして扱われます。ウィジェットを継承したオブジェクトはやはり「ウィジェット」ということです。複数のウィジェットを独自に構成して作られたのでカスタムウィジェットと呼んでいます。

Comment

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