Kivyでは様々なUIウィジェットを提供していますが、独自にカスタマイズしたウィジェットを作ることが可能です。この記事ではカスタムウィジェットの作成方法とカスタムウィジェットからルートウィジェットにアクセスする方法を解説します。
カスタムウィジェットの作り方
Kivyでは様々なUIウィジェットを提供していますが、独自にカスタマイズしたウィジェットを作ることが可能です。
カスタムウィジェットを作るにはウィジェットを継承します。継承したサブクラスはカスタムウィジェットとして扱われます。
例えば、単純に何らかの処理だけを記述したい場合は、Widgetクラスを継承します。WidgetクラスはUIウィジェットの基底になるクラスです。
from kivy.uix.widget import Widget
class CustomWidget1(Widget):
pass
単体のウィジェットを継承したい場合はそのウィジェットを継承します。
from kivy.uix.label import Label
class CustomWidget2(Label):
pass
from kivy.uix.label import Button
class CustomWidget3(Button):
pass
レイアウトを継承した場合もカスタムウィジェットになります。
from kivy.uix.boxlayout import BoxLayout
class CustomWidget4(BoxLayout):
pass
これらのカスタムウィジェットはkvコードでクラスルールなどのルールでスタイルを定義します。(もちろんPythonコードでも書けます。)
# Pythonファイル
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
class CustomWidget1(Widget):
pass
class CustomWidget2(Label):
pass
class CustomWidget3(Button):
pass
class CustomWidget4(BoxLayout):
pass
class RootWidget(BoxLayout):
pass
class MyApp(App):
def build(self):
return CustomWidget()
if __name__ == '__main__':
MyApp().run()
# kvファイル
<CustomWidget1>:
<CustomWidget2>:
text: "Label"
<CustomWidget3>:
text: "Button"
<CustomWidget4>:
Label:
Button:
<RootWidget>: # ルートウィジェットのツリー
CustomWidget1:
CustomWidget2:
CustomWidget3:
CustomWidget4:
Label:
text: "ウィジェットを並べたい順番で配置する。"
Button:
text: "通常のウィジェットとカスタムウィジェットを組み合わせられます。"
カスタムウィジェットを定義したらルートウィジェットのツリーに配置します。ルートウィジェットのツリーは画面を構成します。これをしないとカスタムウィジェットは独立しているため表示されません。
<RootWidget>:
CustomWidget1:
CustomWidget2:
CustomWidget3:
CustomWidget4:
ここではルートウィジェットの「RootWidget」にカスタムウィジェットを表示したい順番で並べています。
kvコードだけでカスタムウィジェットを作る
単純な処理の場合、動的クラス(Dynamic Class)ルールを使用してkvコードだけでカスタムウィジェットを定義することもできます。動的クラスはクラスの継承をkvコードで定義するのでPythonコードの下記の記述と同じ意味を持ちます。
from kivy.uix.label import Button
class CustomWidget3(Button):
pass
<CustomWidget3@Button>:
text: "button"
<RootWidget>:
CustomWidget3:
クラスルールと同じようにルーウィジェットのツリーに配置します。動的クラスの詳細は下記のページで説明しています。
カスタムウィジェットでidを使う場合
カスタムウィジェットでidを定義をする場合、通常はルートウィジェットのツリーで定義します。idにはスコープがあり、ルートウィジェットのツリーでidを定義することによってルートウィジェットから参照できるようになります。カスタムウィジェットはルートウィジェットのツリー内にありますが、ルートウィジェットから枝分かれした別のツリーになります。そのため、それらのツリーはスコープ外になり、互いにアクセスできません。
RootWidget # ルートウィジェット
---+ CustomWidget1 # 他の子を預かっている
---+ CustomWidget2 # 他の子を預かっている
---+ CustomWidget3 # 他の子を預かっている
---+-CustomWidget4 # 更に枝分かれした別のツリー
---+ Label # ルートウィジェットの子
---+ Button # ルートウィジェットの子
ツリーを横に並べると分かりやすいかもしれません。下図の構成の場合、縦ラインのウィジェットには互いにアクセスできますが、横ラインのウィジェットにはアクセスできません。ルートウィジェットとAppはどのウィジェットもアクセスできます。(ただし、アクセスするためのキーワードが異なります)

idについての詳細は下記の記事をご覧ください。
アクセスするためのキーワードについては下記の記事をご覧ください。
下記のようにルートウィジェットのツリーにidを定義します。
<RootWidget>:
CustomWidget1:
CustomWidget2:
id: custom2
CustomWidget3:
id: custom3
CustomWidget4:
id: custom4
Label:
id: root_label
Button:
id: root_button
kvコードでカスタムウィジェットからRootにアクセス
kvコードの場合、カスタムウィジェットから他のクラスのメソッドや変数にアクセスする際も、idと同じで他のツリーのウィジェットのクラスにはアクセスできません。(Pythonコードからはアクセスできます。)自身のクラスとルートウィジェットのクラスのメソッドや変数にアクセスできます。
複雑な構成の場合、そのウィジェットでしか処理できない場合を除いて、各クラスで処理を実装するのではなく、ルートウィジェット方で処理を管理する方が好ましいです。また、アプリ全体に関する処理はAppサブクラスで管理すると良いかもしれません。
以下にカスタムウィジェットからルートにアクセスするサンプルを示します。
custom_widget.py
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.widget import Widget
from kivy.uix.label import Label
from kivy.uix.button import Button
class CustomWidget1(Widget):
def custom1(self):
print("custom1")
class CustomWidget2(Label):
def custom2(self):
print("custom2")
class CustomWidget3(Button):
def custom3(self):
print("custom3")
class CustomWidget4(BoxLayout):
def custom4(self):
print("custom4")
obj = CustomWidget3()
obj.custom3()
class RootWidget(BoxLayout):
def root1(self):
print("Root")
class CustomWidget(App):
def build(self):
return RootWidget()
if __name__ == '__main__':
CustomWidget().run()
customwidget.kv
<CustomWidget1>:
canvas.before:
Color:
rgba: 42/255, 100/255, 89/255, 1
Rectangle:
size: self.size
pos: self.pos
<CustomWidget2>:
id: custom2
text: "custom_widget2"
on_touch_down:
if self.collide_point(*args[1].pos):self.custom2(); self.parent.root1(); app.root.root1()
<CustomWidget3>:
id: custom3
text: "custom_widget3"
on_press:
self.custom3()
self.parent.root1() #下の行と同じ
app.root.root1() #上の行と同じ
<CustomWidget4>:
Label:
id: custom4_label
text: f"custom_widget4"
Button:
id: custom4_button
text: f"custom_widget4 Button"
on_press:
app.root.root1()
root.custom4() #下の行と同じ
self.parent.custom4() #上の行と同じ
<RootWidget>: # ルートウィジェットのツリー
orientation: 'vertical'
CustomWidget1:
CustomWidget2:
id: custom2
CustomWidget3:
id: custom3
CustomWidget4:
id: custom4
Label:
id: root_label
text: "ウィジェットを並べたい順番で配置する。"
Button:
id: root_button
text: "通常のウィジェットとカスタムウィジェットを組み合わせられます。"
Comment