kv言語でウィジェットを参照するために使うroot・self・app・app.rootキーワードについて説明します。
ウィジェットを参照する3つのキーワード
kv言語ではウィジェットを参照するためのキーワードが3つあります。Pythonで使われているselfやsuperと似たようなもので、Kivyではウィジェットやクラスのインスタンスに対して使われますがどの立場から見たかでキーワードが変わってきます。
- self:そのウィジェット自身を指します。Pythonコードの場合は通常のインスタンス参照になります。そのクラスのインスタンスやウィジェットを参照するために使うキーワードです。
- root:アプリケーションのルートウィジェットを指します。rootはbuildメソッドで返されるウィジェットです。
- app:現在実行中のアプリケーションのインスタンスを指します。Appクラスを継承して定義されたクラスのインスタンスです。
selfキーワード
Pythonコードの場合のselfは通常のインスタンス参照になり、そのクラスのインスタンスやウィジェットを参照するために使うキーワードです。Kvファイルの場合はウィジェット自身を指します。例えば、Labelウィジェットの定義の中でselfを使うと、そのLabel自身を指します。
example_keywords1.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class MyWidget(BoxLayout):
def update_label1(self):
# ルートウィジェットから見たLabelウィジェット
self.ids.label1.text = "Label①のtextを変更したよ"
class ExampleKeywords1(App):
def build(self):
return MyWidget()
if __name__ == "__main__":
ExampleKeywords1().run()
examplekeywords1.kv
<MyWidget>:
orientation: "vertical"
Label:
id: label1
text: "クリックしてみて"
Label:
id: label2
text: self.text # 自分自身のプロパティを参照
on_touch_down: self.text = "クリックされたよ" # 自分自身を更新
Button:
text: "押してみて"
on_press: root.update_label1()
kvファイルから見たselfキーワード
ウィジェット自身のプロパティを参照したい時に使います。
text: self.text
自身のtextプロパティを参照しているので、キーワードはselfになります。
on_touch_down: self.text = "クリックされたよ"
自身のtextを更新しているので、キーワードはselfになります。
pythonから見たselfキーワード
Pythonから見たselfは現在のクラスのインスタンス (MainWidget)になります。つまり、rootから見てLabelやButtonは自身のツリー配下にあるのでMainWidgetから見たselfになります。
self.ids.label1.text = "Label①のtextを変更したよ"
LabelウィジェットはMainWidgetに属しているのでルートウィジェットから見るとselfになります。
rootキーワード
アプリケーションのルートウィジェットを指します。rootはbuildメソッドで返されるウィジェットを継承したサブクラスです。kvファイルでは定義されたルートウィジェットや他のウィジェットを指します。またルートウィジェットのインスタンス(Pythonコードで書いたウィジェットを継承したサブクラスのインスタンス)のメソッドなどを参照するために使います。
example_keywords2.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
class MainWidget(BoxLayout):
label_text = "これはルートウィジェットのクラス変数の文字列"
def on_button_click(self):
self.ids.mylabel.text = "rootのLabelのtextを変更したよ"
class ExampleKeywords2(App):
def build(self):
return MainWidget()
if __name__ == "__main__":
ExampleKeywords2().run()
examplekeywords2.kv
<MainWidget>:
orientation: 'vertical'
Label:
id: mylabel
text: "ルートウィジェットから取得: " + root.label_text
Button:
text: "押してみて"
on_press: root.on_button_click()
kvファイルから見たrootキーワード
kvファイルから見たrootはルートウィジェットになります。ここでのルートウィジェットはMainWidgetクラスです。
<MainWidget>
+--- BoxLayout
+---+--- Label
+---+--- Button
このサンプルコードは上記のtree構成になっています。記述されている場所のウィジェットから他のウィジェットを参照する場合はrootになります。つまり、Buttonから見たLabelはroot、Labelから見たButtonはrootになります。
MainWidgetのクラス変数やメソッドを参照する場合もrootになります。
text: "ルートウィジェットから取得: " + root.label_text
MainWidgetのlabel_text変数を参照しているのでキーワードはrootになります。
on_press: root.on_button_click()
MainWidgetのon_button_clickメソッドを呼び出しているので、キーワードはrootになります。
pythonから見たrootキーワード
この場合は自身がrootなのでselfに置き換わります。
self.ids.mylabel.text = "rootのLabelのtextを変更したよ"
LabelウィジェットはMainWidgetに属しているのでルートウィジェットから見るとselfになります。
appキーワード
appキーワードは、現在実行中のAppクラスのインスタンスを参照します。つまり、Appクラスで定義した変数やメソッドを参照する際に使われます。appを使うかどうかは設計によって変わってきます。selfやrootがローカルスコープでの参照をするのに対し、appはアプリ全体での操作に適しています。アプリケーション全体で使うデータやアプリを制御するメソッドを管理したい場合はAppクラスを継承したサブクラスに記述すると良いでしょう。
example_keywords3.py
from kivy.app import App
class ExampleKeywords3(App):
app_value = "Appサブクラスの変数だよ"
def on_button_press(self):
self.root.ids.my_label.text = "ボタンが押されたよ"
if __name__ == "__main__":
ExampleKeywords3().run()
examplekeywords3.kv
BoxLayout:
orientation: "vertical"
Label:
id: my_label
text: app.app_value # Appサブクラスの変数を参照
Button:
text: "ボタン"
on_press: app.on_button_press() # Appサブクラスのメソッドを呼び出す
kvファイルから見たappキーワード
Appサブクラスのクラス変数やクラスメソッドを参照する場合はappになります。
text: app.app_value
Appサブクラスのクラス変数を参照しているのでキーワードはappになります。
on_press: app.on_button_press()
Appサブクラスのクラスメソッドを参照しているのでキーワードはappになります。
Pythonファイルから見たappキーワード
Appサブクラスからルートウィジェットのウィジェットを参照するには「self.root」になります。次の章のapp.rootと同じ意味を持ちます。ここではappは自分自身になるのでself.rootになります。
self.root.ids.my_label.text = "ボタンが押されたよ"
app自身が持っているルートオブジェクトに属するLabelウィジェットを参照するため、キーワードは「self.root」になります。
app.rootキーワード
違うクラスの参照やツリー構成の階層が深いとスコープや親子関係の理由で参照できない場合があります。その時はapp.rootを試してみてください。app.rootは現在実行中のアプリケーションインスタンス(app)のルートウィジェット(root)を参照します。つまりapp.rootを使うことで、appを経由してrootにアクセスできます。
以下にapp.rootを確認するサンプルコードを示します。コンソール上にメッセージが表示されます。
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.lang import Builder
from kivy.uix.widget import Widget
# Pythonファイル内でkv言語を直接記述
kv_code = '''
<MyWidget>:
Button:
text: "押してみて"
on_press: app.root.other_widget.scope_test1()
'''
Builder.load_string(kv_code)
class OtherWidget(Widget):
def scope_test1(self):
print("OtherWidgetを参照")
app = App.get_running_app() #現在のアプリケーションインスタンスを取得
app.root.scope_test2()
class MyWidget(BoxLayout):
other_widget = OtherWidget() # OtherWidgetのインスタンスを作成
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add_widget(self.other_widget) # OtherWidgetをMyWidgetに追加
def scope_test2(self):
print("MyWidgetを参照")
class ExampleKeywords4(App):
def build(self):
return MyWidget()
if __name__ == "__main__":
ExampleKeywords4().run()
OtherWidget()から他のクラスに直接アクセスできないのでapp.rootを使用します。
app.root.other_widget.scope_test1()
kvコードでは、ルートウィジェットであるMyWidget()から、スコープ外のOtherWidget()のscope_test1メソッドを呼び出しているので、app.rootを使用しています。インスタンス変数のother_widgetにはOtherWidget()のインスタンスが代入されているので、名前空間の順に呼び出すと上記の書き方になります。
Appアプリケーション→ルートウィジェット→OtherWidget()のインスタンス→OtherWidget()のメソッド
app = App.get_running_app() #現在のアプリケーションインスタンスを取得
app.root.scope_test2()
Pythonコードでは、OtherWidget()から見てExampleKeywords4()はスコープ外なので参照できません。その為、Appサブクラスを参照するためにAppのインスタンスを取得する必要があります。Appのインスタンスを取得するにはApp.get_running_app() を使用します。ここではappインスタンス変数に代入しています。後はこれらを名前空間の順番に呼び出すだけです。
Appアプリケーション→ルートウィジェット→MyWidget()のメソッド
app.rootを使用した良い例が下記の記事にあるので合わせてご覧ください。
まとめ
このようにどのウィジェットから見たかでキーワードが異なりますので注意してください。
- 自身のウィジェットはself。
- 自身以外を参照するときはrootかapp。
- ルートウィジェットから見たウィジェットはself。
- Appからウィジェットを参照する場合は、self.root。
Comment