Python自体がプログラムをモジュール化して名前空間の汚染を防ぐ仕組みを持っていても、KvLanguageにおいてはその恩恵にあずかれないようです。以下のプログラムで検証してみました。
KvLanguageで書いた部分は合成される
以下はimportした複数のモジュールが、同じ名前のWidgetをKvLanguageで定義していた時にどうなるか確かめるプログラムです。
mywidget_a.py
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string(r"""
<MyWidget>:
Button:
text: r'mywidget_a' #
size_hint: 0.3, 0.1
pos_hint: {r'center_x': 0.25, r'center_y': 0.5} #
""")
class MyWidget(FloatLayout):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
print(r'mywidget_a') #
if __name__ == r'__main__':
runTouchApp(MyWidget())
一旦、単独で実行
mywidget_a
mywidget_b.py
from kivy.base import runTouchApp
from kivy.lang import Builder
from kivy.uix.floatlayout import FloatLayout
Builder.load_string(r"""
<MyWidget>:
Button:
text: r'mywidget_b' #
size_hint: 0.3, 0.1
pos_hint: {r'center_x': 0.75, r'center_y': 0.5} #
""")
class MyWidget(FloatLayout):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
print(r'mywidget_b') #
if __name__ == r'__main__':
runTouchApp(MyWidget())
同じく、単独で実行
mywidget_b
同時にimportすると
from kivy.base import runTouchApp
import mywidget_a
import mywidget_b
runTouchApp(mywidget_b.MyWidget())
mywidget_b
__init__はmywidget_bのものだけが呼ばれている(期待通り)のに対し、子Buttonはmywidget_aのものまで作られてしまいました(期待通りではない)。念のためにConsoleというツールを使ってみると
間違いなく合成されています。ということは
このままだと
合成を防ぐためにプログラム全体に渡ってWidgetのクラス名がかぶらぬ様に気を付けなければならないということになります。
最悪の場合はモジュール名を頭に付けてMyWidgetA_MyWidget,MyWidgetB_MyWidgetのような感じになるかもしれません。
なんとかそうしなくて済む方法はないでしょうか。
kivy.factory.Factory
どうやらこいつがKvLanguageにおけるWidgetの生成に関わってるようです。実際に使ってみます。
まず上のboth.pyは以下のように書く事ができます。
from kivy.base import runTouchApp
from kivy.factory import Factory
import mywidget_a
import mywidget_b
runTouchApp(Factory.MyWidget())
mywidget_a
又、mywidget_a.pyは以下のように書くことができます。
from kivy.base import runTouchApp
from kivy.factory import Factory
class MyWidget(Factory.FloatLayout):
def __init__(self, **kwargs):
super(MyWidget, self).__init__(**kwargs)
button = Factory.Button(
text=r'mywidget_a',
size_hint=[0.3, 0.1],
pos_hint={r'center_x': 0.25, r'center_y': 0.5})
self.add_widget(button)
print(r'mywidget_a')
if __name__ == r'__main__':
runTouchApp(Factory.MyWidget())
見るとButtonやFloatLayoutがそれぞれのimport文無しで利用できているのが分かります。普通は
from kivy.uix.button import Button
from kivy.uix.floatlayout import FloatLayout
の様に利用するものだからです。他にもKvLanguage内で利用できる物はほとんどこのFactoryから利用できるようになっています。この[Package名やModule名が必要無いという利便性]の代償として、[別ModuleのWidgetとの名前衝突に気を付けなければならないという労]を払うと考えればいいのでしょうか?とにかく名前がかぶっていても何も言わないのでやっかいです。
どうやって名前の衝突を避けるか
FactoryにはunregisterというMethodがあり、最初はこれで合成を避けれると思ったのですが違いました。
from kivy.base import runTouchApp
from kivy.factory import Factory
import mywidget_a
Factory.unregister(r'MyWidget') # mywidget_aのMyWidgetを登録解除?
import mywidget_b
runTouchApp(Factory.MyWidget())
mywidget_b
というわけで解決できませんでした。