はじめに
私はKivyを触り始めて1年半ぐらいなのですが、何となくですが自分なりにKivyがどういった物か摑めてきたと思うので、ここでこれまで自分がつまずいた事をまとめたいと思います。
1. root rule
はinstanceの定義でclass rule
は型の定義
root rule
とはkvファイル内で
Widget:
略
のようにクラス名:
から始まるもので、class rule
は
<MyWidget>:
略
のように<クラス名>:
から始まるものです。このroot rule
というのが分かりにくい名前で、最初の頃は「widget階層の根(root)にあたるwidgetは必ずこれで定義しないといけない?」と思ってました。実際はそんな事とは関係なく、見出しの通り只の生成するinstanceを定義しているだけです。以下にKv言語を使ったコードとそれとほぼ同等のPythonのみのコードを書いたので比べてみて下さい。
まずはroot rule
from kivy.lang import Builder
KV_STRING = r"""
FloatLayout:
Label:
text: 'Label'
"""
instance = Builder.load_string(KV_STRING)
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
instance = FloatLayout()
instance.add_widget(Label(text='Label'))
次はclass rule
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
KV_STRING = r"""
<MyWidget>:
Label:
text: 'Label'
"""
Builder.load_string(KV_STRING)
class MyWidget(FloatLayout):
pass
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class MyWidget(FloatLayout):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.add_widget(Label(text='Label'))
class rule
が型(class)をいじってるのに対し、root rule
はinstanceを作り、その作ったinstanceだけをいじっています。それが分かれば以下のコードにも困りません。
from kivy.lang import Builder
from kivy.base import runTouchApp
from kivy.uix.label import Label
from kivy.uix.boxlayout import BoxLayout
KV_STRING = r"""
<MyLabel>:
text: 'Label'
font_size: '80sp'
MyLabel:
color: 1, 1, 0, 1
"""
class MyLabel(Label):
pass
instance = Builder.load_string(KV_STRING) # A
instance2 = MyLabel() # B
root = BoxLayout()
root.add_widget(instance)
root.add_widget(instance2)
runTouchApp(root)
これは
-
MyLabel
という"型"を定義(class rule
の部分) - その
MyLabel
のinstanceを一つ作り、文字色を黄色にする(root rule
の部分)
というKvのコードになってます。class rule
に書いた物(文字の大きさを80sp, 表示する文字列を'Label')がAとB両方のinstanceに反映されているのに対し、root rule
に書いた物(文字色を黄色)は片方(A)のinstanceにしか反映されていないのが確認できます。
2. widgetの定義の仕方が色々ある
上の章で既に下の2つの仕方がでました。
- pythonとKv言語を両方使う(part2_with_kv.py)
- pythonのみ(part2_pure_python.py)
実は他にもあり、それはKv言語のみです。
from kivy.lang import Builder
KV_STRING = r"""
<MyWidget@FloatLayout>:
Label:
text: 'Label'
"""
Builder.load_string(KV_STRING)
この色々やり方があるというのは初心者を惑わしますね。基本的に全ての事はKv言語を用いなくてもできると思ってます。ですが用いることで大幅にコード量を減らしわかりやすくできるのです。でも逆にKv言語では出来ないor難しくなることもあります。どういった時にどういった方法がいいかは、多分使っていくうちに分かってくると思います。
3.定義されたwidgetへのアクセス方法も色々ある
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.label import Label
class MyWidget(FloatLayout):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
self.add_widget(Label(text='Label'))
instance = MyWidget() # A
instance = Factory.MyWidget() # B
instance = Builder.load_string('MyWidget:') # C
A,B,Cは全てMyWidget
のinstanceを作るのに使える方法です。Aは普通のPythonの方法で、同じmodule内のclassなのでそのまま使えますね。B,CはKivy特有で、Aとは違いどこからでも同じ書き方でMyWidget
を作れます。どこからでもというのは例え違うmodule内だったとしてもという意味です。つまりは定義されたwidget(元からKivyが持っているButton等も含めて)はpackage名、module名など指定することなく作れるのです。じゃあもし同名のwidgetが別の場所で定義されていたらどうなるのか?というとそれに関しては少しこの記事に書いたので参考にしてください。
4. class rule
は厳密には定義の追加
以下のコードの実行結果は
from kivy.lang import Builder
from kivy.factory import Factory
class MyWidget(Factory.Widget):
def on_width(self, __, value):
print('on_width', value)
def add_widget(self, *args, **kwargs):
print('add_widget')
super().add_widget(*args, **kwargs)
def remove_widget(self, *args, **kwargs):
print('remove_widget')
super().remove_widget(*args, **kwargs)
Builder.load_string(r'''
<MyWidget>:
width: 200
Widget:
''')
Builder.load_string(r'''
<MyWidget>: # A
width: 400
Widget:
''')
MyWidget()
以下のようになります。
add_widget
on_width 200
add_widget
on_width 400
同じwidgetに対するclass rule
を複数回読み込ませると、上書きではなく蓄積されます。なのでMyWidget
は初期状態で2つ子を持つことになりますし、width
に関しても最終的に400になってはいるもののその過程で一度width: 200
に設定されているのが分かります。もしこのような蓄積を避けたいのであればA行を<-MyWidget>:
としてください。するとそれまでにあったMyWidget
に関する(Kv言語上の)定義が消され、以下のような結果になります。
add_widget
on_width 400
最後に
長くなりましたがとりあえず今思い出せたのはこれぐらいです。これからKivyを始める人の役に立てれば幸いです。