Pythonで作るGUIアプリ with kivy kivy語編

# はじめに
本記事は、PythonのGUI作成ライブラリであるkivyの使い方について解説する記事です。

よろしければ、以前の記事もご参照ください。

[Pythonで作るGUIアプリ with kivy 導入編](https://qiita.com/phorizon20/items/ff352ef1ad15b62ff266)
[Pythonで作るGUIアプリ with kivy 環境構築編](https://qiita.com/phorizon20/items/3fe094186d44d620b661)
[Pythonで作るGUIアプリ with kivy 基礎編](https://qiita.com/phorizon20/items/21edc30b2d5afaa606c8)


# kivy Languageについて
前回[はじめてのkivyアプリに毛を生やしたヤツ]()では、下記のようにして`BoxLayout`を使って部品を配置する様子を見ていきました。

```py
# coding:utf-8

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

class MainScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        self.orientation = "vertical"

        btn = Button(text="hello")
        self.add_widget(btn)

        btn2 = Button(text="everyone")
        self.add_widget(btn2)

        btn3 = Button(text="how")
        self.add_widget(btn3)

        btn4 = Button(text="are")
        self.add_widget(btn4)

        btn5 = Button(text="you")
        self.add_widget(btn5)

        btn6 = Button(text="today?")
        self.add_widget(btn6)


class MainApp(App):
    def build(self):
        MS = MainScreen()
        return MS

if __name__=="__main__":
    MainApp().run()
```

さて、これからここに改造に改造を加えて最高にCOOLなアプリを作っていく訳ですが、勘のいい方はもうお解りの通り、これ、この調子で書いて行くと、**コードが凄まじく長くて読み辛いモノになっていきます。**

というのも、ボタンを一つ追加するだけでも、

1. Widgetをインスタンス化
1. インスタンス化したwidgetのプロパティを変更
1. `add_widget`

この作業をひたすら繰り返していく訳です。その際、「このWidget、どのWidgetにaddすれば良いんだっけ…?」「（画面を見ながら）あれ、このボタン、コードで何行目で作ったWidgetだっけ…?」を、ながーーーくてふくざーーーつなコードを逐次読み解きながら改造を加えていく訳です。

正直やっていられませんよね。COOLなアプリを完成させる前に心が折れそうです。

そんな時に登場するのが、`kivy language`という、kivy独特の機構です。

これは画面（＝Widgetたち）を、ある意味Python風に、ある意味HTML風に、記述するためのツールです。

使い方は実に簡単で、Pythonに慣れていれば、いや慣れていなくても、割りとすぐに理解できるかと思います。早速見てみましょう。


## kivy語の基本的な使い方
言葉で説明するよりもコードを見ながらの方が解り易いかと思いますので、まずは何も考えずに動かしてみます。

以前に作った[はじめてのkivyアプリ]()は、下記のようなコードでした。

```py
# coding:utf-8

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

class MainScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        btn = Button(text="hello")
        self.add_widget(btn)

class MainApp(App):
    def build(self):
        MS = MainScreen()
        return MS

if __name__=="__main__":
    MainApp().run()
```

これをkivy語を使って書くと、下記のようになります。

```py
# coding:utf-8

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

from kivy.lang.builder import Builder

Builder.load_string('''
<MainScreen>:
    Button:
        text: "hello"
''')

class MainScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

class MainApp(App):
    def build(self):
        return MainScreen()

if __name__=="__main__":
    MainApp().run()
```

７～９行目が追加され、ボタンのインスタンス化＋add_widgetの部分が無くなっています。

次に、ボタンを１つ追加した版を見てみます。
`Builder.load_string('''  ''')`で囲まれた部分にご注目ください。

```py
# coding:utf-8

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

from kivy.lang.builder import Builder

Builder.load_string('''
<MainScreen>:
    Button:
        text: "hello"
    Button:
        text: "everyone"
''')

class MainScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

class MainApp(App):
    def build(self):
        return MainScreen()

if __name__=="__main__":
    MainApp().run()
```

次に、ボタンの配置を縦方向にしてみます。

```py
# coding:utf-8

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

from kivy.lang.builder import Builder

Builder.load_string('''
<MainScreen>:
    orientation: "vertical"
    Button:
        text: "hello"
    Button:
        text: "everyone"
''')

class MainScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

class MainApp(App):
    def build(self):
        return MainScreen()

if __name__=="__main__":
    MainApp().run()
```

最後に、[はじめてのkivyアプリに毛を生やしたヤツ]()をkivy語で書いてみます。

```py
# coding:utf-8

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

from kivy.lang.builder import Builder

Builder.load_string('''
<MainScreen>:
    Button:
        text: "hello"
    Button:
        text: "everyone"
    BoxLayout:
        orientation: "vertical"
        Button:
            text: "how"
        Button:
            text: "are"
        Button:
            text: "you"
    Button:
        text: "today?"
''')

class MainScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

class MainApp(App):
    def build(self):
        return MainScreen()

if __name__=="__main__":
    MainApp().run()
```

おわかりいただけたでしょうか。

kivy語では上記の様にして、インデントを用いて

1. Widgetを入れ子にして配置
1. Widgetのプロパティ設定

する事が可能です。

大文字から始まるモノはWidget、小文字から始まるモノはプロパティとなります。Widgetには２種類の系統（レイアウト系と部品系）ある点については、kivy語で書こうが書くまいが同じ話ですね。

もちろん、kivy語で書く方法と通常通りの方法（インスタンス化・プロパティ設定・add_widgetする方法）は併用する事が可能です。

```py
# coding:utf-8

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

from kivy.lang.builder import Builder

Builder.load_string('''
<MainScreen>:
    Button:
        text: "hello"
''')

class MainScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        btn = Button(text="everyone")
        self.add_widget(btn)

class MainApp(App):
    def build(self):
        return MainScreen()

if __name__=="__main__":
    MainApp().run()
```

後々、ボタンをクリックした際に何かWidgetを追加/削除させたりする時には通常通りの方法を使うことの方がむしろ多いので、kivy語での記述と併せて書くことに慣れておくといいかもしれません。

説明が多少前後してしまいますが、kivy語内の`<  >`で囲まれた部分は、その挙動を定義しているクラス名と同一である必要があります。

上記の例では、１０行目の`<MainScreen>`と、１５行目の`class MainScreen(BoxLayout):`が、ちゃんと同じ名前になっています。つまり、`MainApp`が立ち上がり、`MainScreen()`をインスタンス化する際に、書かれたkivy語`Builder.load_string('''  ''')`を読み込んで、同一の名前のモノがあれば、kivy語を解読して画面を構築する、みたいな流れです。

参考までに、２つのクラスを作ったとき、kivy語側がどんな風になるかを見てみます。

```py
# coding:utf-8

from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button

from kivy.lang.builder import Builder

Builder.load_string('''
<MainScreen>:
    orientation: "horizontal"
    Button:
        text: "hello"
    Button:
        text: "everyone"

<SubScreen>:
    orientation: "vertical"
    Button:
        text: "how"
    Button:
        text: "are"
    Button:
        text: "you"
    Button:
        text: "today?"
''')

class MainScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

class SubScreen(BoxLayout):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)


class MainApp(App):
    def build(self):
        # hoge = True
        hoge = False

        if hoge:
            return MainScreen()
        else:
            return SubScreen()

if __name__=="__main__":
    MainApp().run()
```

すごく簡単というか雑な例で恐縮ですが、このようにして複数のWidgetの塊（`MainScreen`や`SubScreen`のような）をkivy語で定義しておくことも可能です。


このようにkivy語という機構は本当に便利なのですが、その便利さについては実際にガリガリとコードを書き始めた後に実感として湧いてくる類の物かと思いますので、是非ご自身で感じてみて頂ければと思います。


今回はこれにて。次回は、Widgetのサイズや色を操作する方法を見て行きたいと思います。

以上、ありがとうございました。