Python+Kivy入門

kv言語のルートウィジェットとルールの違い【Kivy入門】

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

kv言語でRoot要素に指定することができるClass rulesとRoot rulesの違いについて説明します。またルートウィジェットを理解することはKivyプログラミングでは重要になります。ルートウィジェットの考え方について説明します。

ルートウィジェットは何になるのか

ルートのウィジェットの考え方について説明します。

クラスルールの場合

クラスルールはそれを定義しているクラスが存在しているはずなので、ルートウィジェットはウィジェットを継承したサブクラスのインスタンスになります。下記の場合のルートウィジェットはクラスルールではなくMyLayoutクラスのインスタンスになります。

# kvファイル
<MyLayout>: #ルート要素(クラスルール)
    orientation: 'vertical'

    Label:
        text: "これはクラスルール"
# これがルートウィジェット
class MyLayout(BoxLayout):  
    pass

クラスルールはRoot要素ではあるのですがそれを「ルートウィジェット」とすると語弊が生じます。クラスルールはクラスの設定を定義してるだけなのでMyLayoutのインスタンスが持つ情報にすぎません。なので正確にはクラスルールを定義しているサブクラスのインスタンスがルートウィジェットになるという方が正しいです。

KivyのRootウィジェットになるクラス

kvファイルで定義したウィジェットはMyLayoutクラスに属します。そして、それをbuildメソッドでインスタンス生成されて返ってきたものがオブジェクトになります。オブジェクトはウィジェットとして扱われて、そのウィジェットがウィジェットツリーを構成します。独自に構成したウィジェットツリーなのでカスタムウィジェットと呼ばれています。ウィジェットツリーのルートウィジェットはMyLayoutです。MyLayoutはBoxLayoutを継承しているのでBoxLayoutでもあると言えます。イメージとしては大体このような形になると思います。(これらの表記は間違えている可能性があります)

Kivyのオブジェクトがルートオブジェクトになるイメージ図

ルートルール(ルートクラス名)の場合

ルートルールはbuildメソッドが定義されていないのでわかりにくいかもしれませんが、Kivyによって自動的にbuildメソッドが呼ばれるため、それ自体がルートウィジェットになります。そのため、単に「ルートウィジェット」と呼ばれています。

# kvファイル
MyLayout: #ルート要素(ルートルール(ルートクラス名))
    orientation: 'vertical'

    Label:
        text: "これはルートルール"

お勧めしませんが、次のようにクラスルールとルートルールを混ぜる書き方も可能です。この場合もルートオブジェクトはMyLayoutクラスになります。ルートルールの場合はどんな書き方をしてもkvファイルにルートルールを記述したらそのクラスのインスタンスがルートウィジェットになります。

# kvファイル
MyLayout: #ルート要素(ルートルール(ルートクラス名))
<MyLayout>
        orientation: 'vertical'

    Label:
        text: "これはルートルール"

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

class MyLayout(BoxLayout):
    pass

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

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

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

ルートルールにウィジェットを指定した場合も同様です。

# 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()

上記の場合ルートウィジェットはRoot要素に定義したBoxLayoutウィジェットになります。

レイアウト以外のウィジェットを指定した場合も同様です。

Label:
    text:""

上記のルートウィジェットはLabelウィジェットです。

Button:
    text:""

上記のルートウィジェットはButtonウィジェットです。

ウィジェットを継承したクラスが存在しないため、そのままルート要素がルートウィジェットになります。Appクラスはウィジェットではないのでルートウィジェットではありません。

複雑なクラス構成の場合

いくつかのクラスで構成されたコードの場合はどうなるでしょうか。ここでは複数のクラスルールを定義しています。

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

class Panel1(BoxLayout):
    pass

class Panel2(BoxLayout):
    pass

class Controller(BoxLayout):
    def switch_panel(self):
        pass

class SwitchPanels(App):
    def build(self):
        return Controller()

if __name__ == '__main__':
    SwitchPanels().run()
# kvファイル
<Panel1>:
    id: panel1

<Panel2>:
    id: panel2

<Controller>:
    orientation: 'vertical'

kvファイルに書かれている最初のコードは「Panel1」になりますが、buildメソッドが返しているのはControllerクラスです。なのでルートウィジェットはControllerクラスになりメインのクラスになります。どんなに複雑な構成やファイルを分割して書いた場合でも同じで、buildメソッドが返すクラスは1つのはずなのでそれがルートウィジェットになります。

クラスルールとルートルールのインスタンス生成の違い

クラスルールとルートルールのインスタンスの生成タイミングの違いを検証してみましょう。

クラスルールのサンプル

赤字で「Class Rule」と表示するアプリのサンプルです。

compare_class_rule.py

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

# Root要素になるクラス
class RootWidget(BoxLayout):  
    pass
        
class CompareClassRule(App):
    def build(self):
        return RootWidget()

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

compareclassrule.kv

<RootWidget>:
    orientation: 'vertical'

    Label:
        id: mylabel
        text: "class rule"
        font_size: 50
        color: 1, 0, 0, 1

ルートルールのサンプル

赤字で「Root Rule」と表示するアプリ。

compare_root_rule.py

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

# Root要素になるクラス
class RootWidget(BoxLayout):  
    pass
        
class CompareRootRule(App):
    pass

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

comparerootrule.kv

RootWidget:
    orientation: 'vertical'

    Label:
        id: mylabel
        text: 'Root Rule'
        font_size: 50
        color: 1, 0, 0, 1

2つのアプリの違いはbuildメソッドの有無とRoot要素に「<>」があるかどうかだけになります。クラスルールはbuildメソッドでインスタンスの生成を行います。ルートルールはKivyによって適切なタイミングでインスタンスが自動的に生成されます。

インスタンスの生成を確認する➀

クラスルールにはbuildメソッドを無くして、ルートルールにはbuildメソッドを定義してみましょう。ただしreturnは必要ありません。

# クラスルール
class CompareClassRule(App):
    pass
# ルートルール
class CompareRootRule(App):
    def build(self):
        #return RootWidget() インスタンス生成はKivyが自動で行うのでこれは必要ない
        pass

クラスルールのコードの方は黒い画面のまま何も表示されませんが正常な動きをしています。クラスルールはBuildメソッドが定義されていないのでインスタンスが生成されていません。Buildメソッドでインスタンスを返さないと画面が構成されないことを確認しました。

ルートルールのコードの方は赤字で「Root Rule」と表示され、正常に表示されています。ルートルールでもbuildメソッドは定義しても問題ありません。ただし戻り値は必要ありません。

ここで戻り値を指定すると黒い画面のまま何も表示されません。おそらくルートルールのツリー情報が上書きされてBoxLayoutだけが構築されてるからだと思います。

# ルートルール
class CompareRootRule(App):
    def build(self):
        return RootWidget() # returnを定義

Comment

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