Quick search

Kv language(翻訳済み)

Concept behind the language(背景とコンセンプト)

アプリケーションがより複雑になるにつれて、widgetツリーとバインディングの明示的な宣言の構築は、一般的に冗長になり保守が困難となります。KV Language はこの問題を克服しようとする試みです。

KV language(kvlang、またはkivy言語と呼ばれることがあります)、 宣言型でwidgetツリーを作成し、自然な方法でwidgetのプロパティを 相互またはコールバックしてバインドできます。 これはUIにアジャイル的な変更と非常に高速なプロトタイプピングを可能にします。アプリケーションのロジックとユーザーインタフェース間の分離を容易にします。

How to load KV(KV のロードについて)

アプリケーションにKvコードをロードする方法は2つあります。

  • 命名規則から:

    ‘App’で終わる場合、Kivyはアプリのクラスから’App’を引いた、小文字で同じ名前を持つKvファイルを検索します。例えば:

    MyApp -> my.kv
    

    このファイルは`Root Widget`を定義している場合には、アプリケーションの root 属性に所属しており、アプリケーションのwidgetツリーのベースとして使用されます。

  • Builder: 文字列またはファイルをロードしてKivyに直接伝えられます。文字列またはファイルがroot widgetを定義している場合は、メソッドとして返されます。:

    Builder.load_file('path/to/file.kv')
    

    または:

    Builder.load_string(kv_string)
    

Rule context(ルールコンテキスト)

KvソースはWidgetのコンテンツを記述するために使用されるルールの構成は、1つの`root` ルール、およびの任意の`class` または`template`ルールを有します。

root ルールは、任意のインデントなしで、「:」と宣言することによって、あなたのroot widgetクラスを宣言が続いている、Appインスタンスのルート属性として設定されます。:

Widget:

class ルールは、< > の中で、widgetクラスの名前を宣言し、「:」の後は、クラスのすべてのインスタンスをグラフィカルに表現する方法を定義します。

<MyWidget>:

ルールはPythonのような区切りのためにインデントを使用し、インデントはPythonのグットプラクティスの推奨設定のように、レベルごとに4つのスペースでなければなりません。

Kv languageには固有の3つのキーワードがあります:

  • app: 常にアプリケーションのインスタンスを参照します。

  • root: 現在のルールでベースの「widget/template」を参照します。

  • self: 常にカレントのwidgetを参照します。

Special syntaxes(特別な構文)

Kvコンテキスト全体を定義するには、2つの特別な構文があります:

KvからPythonモジュールとクラスにアクセスするには:

#:import name x.y.z
#:import isdir os.path.isdir
#:import np numpy

これは以下と同等です:

from x.y import z as name
from os.path import isdir
import numpy as np

Pythonではこうなります。

グローバルな値をセットするには:

#:set name value

これは以下と同等です:

name = value

Pythonではこうなります。

子をインスタンス化

widgetが子widgetを持ついくつかのクラスのインスタンスを宣言するためには、ルールの内部にその子を宣言します:

MyRootWidget:
    BoxLayout:
        Button:
        Button:

上記の例では、root widget、MyRootWidget`のインスタンスは、:class:`~kivy.uix.boxlayout.BoxLayout`の子のインスタンスを有しています 。BoxLayoutは、さらに、:class:`~kivy.uix.button.Button クラスのインスタンスである2つの子の親です。”

Pythonコードでの同じ内容は次のようになります:

root = MyRootWidget()
box = BoxLayout()
box.add_widget(Button())
box.add_widget(Button())
root.add_widget(box)

どぢらでも読み込み、書き込みできます。

Of course, in python, you can pass keyword arguments to your widgets at creation to specify their behaviour. For example, to set the number of columns of a gridlayout, we would do:

grid = GridLayout(cols=3)

KVで同じことを行うには、ルールで直接、子のwidgetのプロパティを設定できます:

GridLayout:
    cols: 3

値はPythonの式として評価され、式の中で使用されるすべてのプロパティが表現され、Pythonでもいくつかを表現出来ることを意味します。(self widget は ListProperty の`data` を保持していると推測):

grid = GridLayout(cols=len(self.data))
self.bind(data=grid.setter('cols'))

現在のデータの変更がされたときに、表示が更新されます:

GridLayout:
    cols: len(root.data)

注釈

プロパティ名は先頭小文字で、wigdet名の先頭は大文字で書く必要があります。以下の PEP8の命名規則 が奨励されています。

Event Bindings(イベントバインディング)

コールバックをイベントに関連付ける “:”構文を使用して、Kvのイベントにバインドできます。

Widget:
    on_size: my_callback()

args キーワードを使用することで、シグナルによってディスパッチした値を渡せます:

TextInput:
    on_text: app.search(args[1])

より複雑な式は次の様に使用できます:

pos: self.center_x - self.texture_size[0] / 2., self.center_y - self.texture_size[1] / 2.

この式は center_xcenter_ytexture_size の変化を表示しています。一方が変化した場合、再評価され, pos フィールドは更新されます。

kv language内で on_ イベントをハンドル出来ます。 例えばTextInputクラスが持つ`focus``プロパティは、自動生成される ``on_focus``イベントでkv language の内部にアクセスできます。

TextInput:
    on_focus: print(args)

キャンバスを拡張する

KV langは、このようなwidgetのキャンバス命令を定義するために使用できます:

MyWidget:
    canvas:
        Color:
            rgba: 1, .3, .8, .5
        Line:
            points: zip(self.data.x, self.data.y)

“プロパティの値が変更されたときに更新されます。

もちろん、canvas.before`canvas.after`を使用できます 。

Referencing Widgets(Widgetを参照する)

widgetツリーで、他のwidgetにaccess/referenceするのが必要になることがあります。Kv Languageは「ID」を使用して実行する方法を提供しています。Kv Languageのみで使用でき、クラスレベルの変数と考えます。次の点を考慮します:

<MyFirstWidget>:
    Button:
        id: f_but
    TextInput:
        text: f_but.state

<MySecondWidget>:
    Button:
        id: s_but
    TextInput:
        text: s_but.state

id``はルール内で制限されており、上記のコードでは``s_but``は ``<MySecondWidget> ルールの外にはアクセス出来ません:

警告

idに値を代入するときは、その値が文字列ではないことに注意してください。引用符(’)はありません:良い例 - > id:value、b悪い例 - > id: ‘value’

id はwidgetの``weakref(弱い参照)`` でありwidget自身ではありません。その結果、id はガベージコレクションを行ってからの保存は十分ではありません。実証するためには:

<MyWidget>:
    label_widget: label_widget
    Button:
        text: 'Add Button'
        on_press: root.add_widget(label_widget)
    Button:
        text: 'Remove Button'
        on_press: root.remove_widget(label_widget)
    Label:
        id: label_widget
        text: 'widget'

`MyWidget``内の``label_widget``参照は 、weakref(弱い参照)のみなので、他の参照が削除された後に生存しているオブジェクトを保持するのに十分ではないです。したがって、削除ボタンをクリックして(ウィジェットへの直接参照を除去する)、ウィンドウがリサイズされ(``label_widget``の欠失をもたらすガベージコレクタを呼び出す)、追加ボタンがクリックされ、widgetを追加するために戻ると、 ``ReferenceError: weakly-referenced object no longer exists``がスローされます。

生存しているウィジェットを保つために、label_widget widget への直接参照は維持されなければなりません。 このケースでは使用して達成した、id.__self__ または``label_widget.__self__``です。これを行うための正しい方法は次のようになります:

<MyWidget>:
    label_widget: label_widget.__self__

PythonコードからKv lang の内部で定義されたwigetsにアクセスする

my.kvで次のコードを考えてみます:

<MyFirstWidget>:
    # both these variables can be the same name and this doesn't lead to
    # an issue with uniqueness as the id is only accessible in kv.
    txt_inpt: txt_inpt
    Button:
        id: f_but
    TextInput:
        id: txt_inpt
        text: f_but.state
        on_text: root.check_status(f_but)

myapp.py では:

...
class MyFirstWidget(BoxLayout):

    txt_inpt = ObjectProperty(None)

    def check_status(self, btn):
        print('button state is: {state}'.format(state=btn.state))
        print('text input text is: {txt}'.format(txt=self.txt_inpt))
...

txt_inpt`は:class:`~kivy.properties.ObjectProperty`で定義されてクラス内で `None に初期化されます。

txt_inpt = ObjectProperty(None)

この時点でself.txt_inptは`None`です。Kv lang プロパティはid `txt_inpt`によって参照された :class:`~kivy.uix.TextInput`インスタンスを保持するように更新されます:

txt_inpt: txt_inpt

これ以降、self.txt_inpt`は、ID `txt_input`によって識別されるwidgetへの参照を保持し、`check_status`機能のように、クラス内のどこにでも使用することができます。 この方法とは対照的に、また、単に上記のコードで `f_but の場合のように、使用する必要がある関数にIDを渡せます。

参照オブジェクト`ids` を使用して、Kvで`id`タグを持つオブジェクトにアクセスするための簡単な方法があります。 ” “次のように行えます:

<Marvel>
  Label:
    id: loki
    text: 'loki: I AM YOUR GOD!'
  Button:
    id: hulk
    text: "press to smash loki"
    on_release: root.hulk_smash()

Pythonコードでは:

class Marvel(BoxLayout):

    def hulk_smash(self):
        self.ids.hulk.text = "hulk: puny god!"
        self.ids["loki"].text = "loki: >_<!!!"  # alternative syntax

KVファイルがパース(解析)されると、kivyは、IDでタグ付けされたすべてのwidgetを収集し、dict型のプロパティ self.ids に配置します。また、widgetを反復処理し、dict形式でアクセスできることを意味します:

for key, val in self.ids.items():
    print("key={0}, val={1}".format(key, val))

注釈

「self.ids」 の使用する方法は非常に簡潔であるが、通常はObjectPropertyを使用する方が「ベストプラクティス」です。 より高速なアクセスが提供されてより明示的になります。

Dynamic Classes(ダイナミッククラス)

以下のコードを考えてみましょう:

<MyWidget>:
    Button:
        text: "Hello world, watch this text wrap inside the button"
        text_size: self.size
        font_size: '25sp'
        markup: True
    Button:
        text: "Even absolute is relative to itself"
        text_size: self.size
        font_size: '25sp'
        markup: True
    Button:
        text: "Repeating the same thing over and over in a comp = fail"
        text_size: self.size
        font_size: '25sp'
        markup: True
    Button:

代わりにテンプレートを使用することで、すべてのボタンに対して同じ値を返せます:

<MyBigButt@Button>:
    text_size: self.size
    font_size: '25sp'
    markup: True

<MyWidget>:
    MyBigButt:
        text: "Hello world, watch this text wrap inside the button"
    MyBigButt:
        text: "Even absolute is relative to itself"
    MyBigButt:
        text: "repeating the same thing over and over in a comp = fail"
    MyBigButt:

このルールの宣言によって作成されたクラスは、Buttonクラスを継承し、デフォルト値を変更し、Python側で任意の新しいコードを追加することなく、すべてのインスタンスのバインディングを作成できます。

Re-using styles in multiple widgets(複数のwidgetでのスタイルの再使用)

my.kvで次のコードを考えてみます:

<MyFirstWidget>:
    Button:
        on_press: root.text(txt_inpt.text)
    TextInput:
        id: txt_inpt

<MySecondWidget>:
    Button:
        on_press: root.text(txt_inpt.text)
    TextInput:
        id: txt_inpt

myapp.py では:

class MyFirstWidget(BoxLayout):

    def text(self, val):
        print('text input text is: {txt}'.format(txt=val))

class MySecondWidget(BoxLayout):

    writing = StringProperty('')

    def text(self, val):
        self.writing = val

両方のクラスで同じ.kvスタイルを共有するため、両方のwidgetのスタイルを再利用する場合は、設計を簡素できます。次のように.kvで行えます。my.kvだと:

<MyFirstWidget,MySecondWidget>:
    Button:
        on_press: root.text(txt_inpt.text)
    TextInput:
        id: txt_inpt

カンマでクラス名を分離することで、宣言に記載されているすべてのクラスが同じkv プロパティを持つことになります。

Designing with the Kivy Language(Kivy Languageを使用した設計)

Kivy language の目的の一つは、プレゼンテーションとロジックの`関心の分離<hhttps://ja.wikipedia.org/wiki/%E9%96%A2%E5%BF%83%E3%81%AE%E5%88%86%E9%9B%A2>`_ 、を実現することにあります。 プレゼンテーション(レイアウト)側は、あなたのkvファイル、ロジック側はpyファイルによって対処されます。

The code goes in py files(pyファイルのコードにいく)

少し例を見てみましょう。 まず、main.pyという名前のPythonのファイル:

import kivy
kivy.require('1.0.5')

from kivy.uix.floatlayout import FloatLayout
from kivy.app import App
from kivy.properties import ObjectProperty, StringProperty


class Controller(FloatLayout):
    '''Create a controller that receives a custom widget from the kv lang file.

    Add an action to be called from the kv lang file.
    '''
    label_wid = ObjectProperty()
    info = StringProperty()

    def do_action(self):
        self.label_wid.text = 'My label after button press'
        self.info = 'New info text'


class ControllerApp(App):

    def build(self):
        return Controller(info='Hello world')


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

この例では、2つのプロパティを持つControllerクラスを作成しています:

  • テキストを受信するための``info``

  • label widgetを受信するための``label_wid``

加えて、do_action() メソッドで両方のプロパティを作成している。これは``info`` テキストの変更と label_wid widgetのテキスト変更。

The layout goes in controller.kv(レイアウトはcontroller.kvに行く)

対応する`.kv` ファイルを使用せずにアプリケーションを実行すると動作はしますが画面に何も表示されません。期待されている``Controller``クラスは、FloatLayout``のようなwidgetを持っていないためです 。``Controller``クラス周りのUI、ファイル名が`controller.kv`に実行時にロードする``ControllerApp を作成できます。ファイルのロードや実行方法は kivy.app.App.load_kv() メソッドで説明されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#:kivy 1.0

<Controller>:
    label_wid: my_custom_label

    BoxLayout:
        orientation: 'vertical'
        padding: 20

        Button:
            text: 'My controller info is: ' + root.info
            on_press: root.do_action()

        Label:
            id: my_custom_label
            text: 'My label before button press'

vertical な``BoxLayout``で一つのラベルと一つのボタンです。 ”非常に単純です。 同時に3つの事が起こっています:

  1. text: ‘My controller info is: ‘ + root.info`` は自動的にテキストに変更されて”Buttonは再評価され、``info``プロパティはすぐにコントローラに変化します。

  2. Giving data to the Controller. The expression id: my_custom_label is assigning the created Label the id of my_custom_label. Then, using my_custom_label in the expression label_wid: my_custom_label gives the instance of that Label widget to your Controller.

  3. Controller``の``on_press``方法を使用して``Button のカスタムコールバックを作成します。

    • rootself は予約済みのキーワードで任意の場所で使用可能です。 root はルールでトップwidgetを表し、 self は現在のwidgetを表します。

    • rootself 同じルールで宣言されたIDを使用できます。たとえば、”on_press()” で行えます :

    Button:
        on_press: root.do_action(); my_custom_label.font_size = 18
    

これでおしまいです。main.py`を実行したとき、`controller.kv がロードされて”Button``Label``が表示され、タッチイベントに応答します。