kivyMDチュートリアル其の什肆 Components - Layout篇

みなさん、おはこんばんは。久しぶりの挨拶をしたところで、いかがお過ごしでしょうか。
いやー、GW始まりましたね！緊急事態宣言が出て、出かけることも出来ずフラストレーションが
溜まりそうになりますが、もう少しの辛抱だと思ってお家時間を過ごそうではありませんか(誰が
誰に言ってるんだ)。

今日からはGW特別企画ということでタイトルの通り、今日はKivyMDの数あるレイアウトの総集編
となります。(すっごく余談ですが、GW特別企画は第2~3弾ほど考えています)

レイアウトはマニュアルページでどれだけあるんだろうと数えてみたところ、以下の7つありました。

* Box Layout
* Circular Layout
* Float Layout
* Grid Layout
* Refresh Layout
* Relative Layout
* Stack Layout

本当なら、全て触れ込みたいところですが以下の2つを今回は対象外とすることにします。

* Circular Layout<br>
サンプルコードが動かないというか実装されているか断定できなかったため
* Refresh Layout<br>
触れ込みの対象範囲が大きすぎるため

理由としては上記の通りですが、Circular Layoutについては別モジュールのことも考えて
importなど試してみましたが、見事に動いてはくれませんでした。今後の期待ということに
なりますかね。Refresh Layoutについては少し触れ込みの範囲が大きすぎました。これだけ
で1日を費やすことになるので、また機会があれば別のときにでも。
※ 1日を費やすといってもLayoutの総集編で2~3日ほど費やしています:sob:

ということで残った5つを触れ込むことにします。
分かりやすくシンプルにということで見た目が少し悪いところはご容赦ください。
また、キャプチャが旅行でも行ったの？というくらい出るので容量制限などあれば、省略するかも
なのでこれまたご容赦を。

## Box Layout

ようやく、触れ込めます。

マニュアルを見ると、KivyとKivyMDでのそれぞれの書き方という風に並べられていますね。
\# 後でまとめて「参照」にリンクを載せておきますので適宜そちらから参照ください

端的に言うと、Kivyではこれだけの量がKivyMDだとこれだけ短縮されるんだぜということ
になりますかね。お前は何を言ってるんだという状況になりましたら、以下リンクからもしくは
参照リンクを辿ってみると「あーこういうことね」と分かるかもです。それ以外の方は懐かしく
思えるかもしれません。

[kivyMDチュートリアル其の壱 About&Getting Started篇](https://qiita.com/virty/items/ff08a262053a504222e5)

[参照リンク]

https://kivymd.readthedocs.io/en/latest/getting-started/

使用できるオプションとして以下の3つがあるよということを案内されています。

* adaptive_height<br>
-> "size_hint_y: None"<br>
　　　　　　+<br>
　 "height: self.minimum_height"
* adaptive_width<br>
-> "size_hint_x: None"<br>
　　　　　　+<br>
　 "height: self.minimum_width"
* adaptive_size<br>
-> "size_hint: None, None"<br>
　　　　　　+<br>
　 "size: self.minimum_size"

若干、互換性のところが見にくいかもしれませんが言ってることは以下になります。

> KivyMDでのオプション -> Kivyでのオプション

adaptive_widthなんかはKivyだとwidthオプションじゃね？みたいなツッコミをして
しまいそうですが、これも後述するので一旦放っておきましょう。

少なく書けるということは嬉しいことなのですが、具体的にsize_hint_yだとかheightは
どのようなものなのでしょうかね(heightとかは分かるよ！と言われそうだけど)。これまでも
この辺りはKivyに触れてないので当たり前ですが、触れてこなかったのは事実。

となれば実際に見た方が早いよと言われそうなので実際にコードと実行結果を折り交えながら
見ていきましょうか。以下のサンプルコードをご覧ください。マニュアルには載ってないので
専用に作ったコードになります。

```python:xiv/boxlayout.py
from kivymd.app import MDApp
from kivy.lang import Builder


kv = '''

Screen:    
    
    MDToolbar:
        title: "Box Layout"
        pos_hint: {'top': 1}


    BoxLayout:
        orientation: "vertical"
        
        ### adaptive_height ###
        size_hint_y: None
        height: self.minimum_height
        
        ### adaptive_width ###
        #size_hint_x: None
        #height: self.minimum_width

        ### adaptive_size ###
        #size_hint: None, None
        #size: self.minimum_size

        padding: dp(10)
        spacing: dp(10)

        canvas:
            Color:
                rgba: 0, 1, 1, 1
            Rectangle:
                pos: self.pos
                size: self.size
        
        MDRectangleFlatButton:
            text: "BUTTON 1"
            size: 20, 20

        MDRectangleFlatButton:
            text: "BUTTON 2"
            size: 30, 30

        MDRectangleFlatButton:
            text: "BUTTON 3"
            size: 40, 40

        MDRectangleFlatButton:
            text: "BUTTON 4"
            size: 50, 50

        MDRectangleFlatButton:
            text: "BUTTON 5"
            size: 60, 60
'''

class Test(MDApp):

    def build(self):
        return Builder.load_string(kv)

Test().run()
```

コード自体は特に目新しいものはないので、詳細に触れ込みはしないのですがBoxLayoutが
MDBoxLayoutではないことに注意してください。まずはKivyとしてどのような振る舞いを
するのか見てみましょう。

簡単な説明としては、BoxLayoutの配下に縦積みされるボタンを5つ配置するようになって
います。少し見た目を良くするためにボタン間に適当な余白などを入れています。あとは
adaptive_height/width/sizeなどがどのように変わってくるのかということを見る
ために、コメントアウトにてそれぞれを選ぶように記述しました。その具合を見るために
canvas.Color.rbgaオプションをテーマカラーではなく、緑と青を組み合わせた色に
変更しています。
\# テーマカラーは違いが分かりにくいためです

まぁ、これは見たらこういうことかと分かるはずなので早速みてみましょう。

### adaptive_height

![48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/cadef2f4-eac5-b8be-dc66-38d830fa678e.png)

少し画像のサイズが大きいですが。。
これも理由がありまして、スマホサイズに合わせるとなんか分かりにくくなるためです。
ちなみにkivyでの画面サイズ=800x600から変更せずにお送りいたしております。

全然中身に触れこめませんでしたが、やっと触れ込みます。
キャプチャを見てもらうと、分かりやすいですが絶妙に色分けがされていますね。

どうやら、ボタンなどのウィジェットのサイズと余白のサイズの累積がcanvas.Rectangleの
サイズとなるようです。これをもとに、size_hint(_x/y)の定義に触れますが、簡単に言うと
サイズのヒントになります。説明になってなく、誰かの構文を思い起こさせるかもですがこれは
しょうがありません。なぜなら、Kivyの公式マニュアルでもそう書かれているためなので。。。
> [Widget class]
> https://kivy.org/doc/stable/api-kivy.uix.widget.html

とまぁ、これだけだともの悲しいので少し触れ込みます。と思いましたが以下リンクを参照
ください。なんか触れ込むとBoxLayoutの詳細な触れ込みになりそうな気がしたことと、
すでにある有益な情報は再掲したほうがいいためです。せなさん、いつもお世話になって
います。そしてありがとうございます。

> Kivyのボックスレイアウトの使い方とその仕様
> https://senablog.com/python-kivy-boxlayout/

少し補足すると親ウィジェットの割合に対する比率ということも重要な点になります。あとは、
検索するとsize_hint_xとかの値が1以上に設定されているものを見かけますが、公式マニュ
アルとかは0~1の値を想定されているらしいです。なので、使用する場合は全体の比率を1と
しましょう。

とまぁ、触れ込みは以上となりますがsize_hint_yがNoneなので配下のウィジェットサイズに
合わせ、heightプロパティのself.minimum_heightと合わせると配下のウィジェットのサイズ
の累積が領域として確保されます。よく分からんとなった方は、色々試してみると分かりやすいかも
しれません。

### adaptive_width

これからは説明も淡々とこなしていきます。というか全てやっているといつまで経っても
終わらん。。

![49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/d9b9eb54-7440-c07d-8e57-23903687d3f3.png)

すごく、見かけが悪いですが上記の通りとなります。なぜかMDRectangleFlatButtonのwidthが
上手く反映されていなく、adaptive_heightと違う気もしますがこれはこれでということで。全て
の幅なり高さなりが合っていると同じ領域が確保されるのかな。


### adaptive_size

![50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/f2c110cb-9b52-d78a-7222-af2eeb856d0f.png)

最後になると、確保される領域も最小限となります。見事に小ちゃくなってますね。

また、MDBoxLayoutでも上記と同じになるか見てみます。
コードとしてはマニュアルに書いてある通り、以下のように変更するだけになります。

```python:xiv/boxlayout_md.py
(略)

-    BoxLayout:
+    MDBoxLayout:

-        ### adaptive_size ###
-        #size_hint: None, None
-        #size: self.minimum_size
+        adaptive_size: True

(略)
```

![51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/82ee434b-7819-4e4b-5840-f4fe52727c68.png)

うん、変わりませんね。
\# エビデンスの残し方としてはあまり良くないですが

それでも、ほんとにそうなるの？と疑り深い方はコピペして試されるか、自身のGitHubの
リポジトリからpull or cloneしてみて試してみてください。

GitHubのリンクは以下のリンク(最下部)から
\# ややこしい

[kivyMDチュートリアル其の伍 Themes - Icon Definitions篇](https://qiita.com/virty/items/97499128a205113431d5)

## Float Layout

ここも淡々と触れ込んでいきます。
こちらは絶対座標系の下、サイズの位置を自由に設定してウィジェットを配置できるものです。
BoxLayoutと異なり、adaptive_*というプロパティはないことに注意です。

なんかそれらしいことを言っていますが、説明についてはKivyの書籍から引用しています。他の
レイアウトでもそうですが、この辺りが良くまとまっているのでマニュアル以外何かみれね？と
いう方はこちらを参照してもらうのがおすすめです。

https://www.asakura.co.jp/books/isbn/978-4-254-12896-3/

コードの方も全ては載せません。kv側を一部抜粋します。詳しくはGitHubの方で。

```python:xiv/floatlayout.py
Screen:    
    
    MDToolbar:
        title: "Float Layout"
        pos_hint: {'top': 1}


    FloatLayout:
        canvas:
            Color:
                rgba: 0, 1, 1, 1
            RoundedRectangle:
                pos: self.pos
                size: self.size
                radius: [25, 0, 0, 0]

        MDRectangleFlatButton:
            text: "BUTTON 1"
            pos: 10, 460

        MDRectangleFlatButton:
            text: "BUTTON 2"
            pos: 690, 460

        MDRectangleFlatButton:
            text: "BUTTON 3"
            pos: 360, 240

        MDRectangleFlatButton:
            text: "BUTTON 4"
            pos: 10, 10

        MDRectangleFlatButton:
            text: "BUTTON 5"
            pos: 690, 10
```

特に大きな変更点はありませんが、ボタンの配置方法が変更しています。
それぞれ座標(x, y)となるようにposプロパティを書いています。ボタンの上から、左上より
右下というようにZと文字が書けるような順番としています。まぁ、これも見てもらった方が
良さそうですね。

![52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/c30b3b0b-428d-5415-1b00-684ab5c00163.png)

おおっと、失礼。canvas.RoundedRectangleが画面全体を覆うようになりましたね。
現状これが分かったことで、覆うことはいらないのでcanvasプロパティごとコメントアウトしちゃ
います。

![53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/22cdafea-6b73-818c-b997-f724569eab93.png)

これで綺麗になりました。であとはMDFloatLayoutでも同様かどうか見てみます。
コードの方は、、、いいですかね。。まぁ、GitHub見てもらえれば良いので。
やったことだけ書くと、これも言わずもがなですが接頭辞にMDを付けただけです。

![54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/b7a1f311-3a8b-3154-4f6d-297d3a4c58ee.png)

うーん、差分はなさそう(確かめてはない)。

こちらは少し注意が必要で、画面サイズがバラバラだったりすると以下のようにレイアウトが
崩れる恐れがあります。この点については、実機で試さないことにはなんとも言い難い点があり
ますが。。自動で計算してくれるのかな、この辺りは。

![55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/28f4203c-7fc8-322e-888e-92002fe5f065.png)

## Grid Layout

さくさく進んでいきます。
こちらは格子状に配置させるレイアウトになりますね。例えていうなら、エクセルシートのセルと
言えますでしょうか。あまり例としては使いたくなかったですけどね。。

まぁ、あのような感じです。こちらはadaptive_*プロパティがあるのでご注意を。

これも見てもらったほうがいいのでコードと併せて。
kv以外は一致しているので、kvで定義しているところだけ抜粋します。

```python:xiv/gridlayout.py
Screen:    
    
    MDToolbar:
        title: "Grid Layout"
        pos_hint: {'top': 1}


    GridLayout:
        cols: 2
        
        ### adaptive_height ###
        size_hint_y: None
        height: self.minimum_height
        
        ### adaptive_width ###
        #size_hint_x: None
        #height: self.minimum_width

        ### adaptive_size ###
        #size_hint: None, None
        #size: self.minimum_size

        canvas:
            Color:
                rgba: 0, 1, 1, 1
            Rectangle:
                pos: self.pos
                size: self.size
    
        MDRectangleFlatButton:
            text: "BUTTON 1"

        MDRectangleFlatButton:
            text: "BUTTON 2"

        MDRectangleFlatButton:
            text: "BUTTON 3"

        MDRectangleFlatButton:
            text: "BUTTON 4"

```

変更点はボタンを見やすくするために4つ配置したことと、GridLayoutが持っているプロパティ
colsを使っているくらいになります。rowsという行を表すプロパティもあります。これもどう
なるか見てみましょう。

### adaptive_height

![56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/51a60754-98a3-5d5c-68dd-5cf1c1a56c59.png)

もう、言わずもがな感は出てきていますかね。
えぇ、出ていない？もう一度、BoxLayoutの方に戻ってみましょうw

そこからの差分としては、ちゃんと高さの累積にはなっているようです。

### adaptive_width

![57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/0b1edf26-b046-dfef-576f-c398633b7980.png)

なんか、意味不明なところがありますがこちらも領域については同じです。幅に応じて領域が
確保されています。なんで、上に配置されるのだろう。。

### adaptive_size

![58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/990c81dc-c460-0a28-b39e-0042f5607009.png)

はい、こちらも言わずもがなです。
こちらについても、MDGridLayoutと同じになるか見てみましょう。

![59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/0213a2a2-5ebf-95cb-4baa-ea65f23b059a.png)

まぁ、確かに証明になっていない感はどうしてもあるのですが全て動作確認はしていますので。
詳しくはGitHubのリポジトリの方にて。

## Relative Layout

こちらはFloatLayoutと似ていて、相対座標で配置させるものとなります。引用した本からは
FloatLayoutのサブクラスとしても紹介されています。

こちらもコードと併せてどうなるか見てみましょう。

```python:xiv/relativelayout.py
Screen:    
    
    MDToolbar:
        title: "Relative Layout"
        pos_hint: {'top': 1}

    RelativeLayout:
        canvas:
            Color:
                rgba: 0, 1, 1, 1
            RoundedRectangle:
                pos: (400, 300)
                size: self.size
                radius: [25, ]
        
        MDRectangleFlatButton:
            text: "BUTTON 1"
            pos: 10, 200

        MDRectangleFlatButton:
            text: "BUTTON 2"
            pos: 300, 200

        MDRectangleFlatButton:
            text: "BUTTON 3"
            pos: 10, 10

        MDRectangleFlatButton:
            text: "BUTTON 4"
            pos: 300, 10

```

FloatLayout同様、というか当たり前ですがadaptive_*プロパティはありません。

RoundedRectangleの基準点(四角形左下の点)を(400, 300)としてそこから相対座標
を設定してそれぞれのウィジェットを配置させることを目的としています。それぞれの
ウィジェットは画面右上あたりに表示されるはずです。

ではどうなるか見てみましょう。

![60.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/012b0cb8-e72a-df2a-52da-cc614d88d4d8.png)

あれーww出来てないぞーwww
うーん、これは困った。。MD側のボタンの仕様なのか、それともLayout側の仕様なのか
分かりません。ただし、領域については狙い通りとなってそうです。

追加調査でsize_hintで両方ともNone指定をしましたが、結果としては変わらず。
このことについては、継続調査が必要そうですが今は時間切れということでタイムアップ。。
時間がある際、もしくは今後のサンプルコードでこちらが出た場合に改めてということで、
許してください。。

あ、ちなみにですがMDRelativeLayoutに変更して実行してみると動作できませんでした。
今後に期待ということも言えますが、実装自体必要かなとか思えてしまいます。すでにMDFloat-
Layoutはあるわけなのですが。

## Stack Layout

最後になります。こちらは上下左右からそれぞれのウィジェットを積み上げるレイアウトと
なります。マニュアル(KivyMD)からはBoxLayoutなどと変わりなく書かれていますが、
プロパティなどが少し変わります。BoxLayoutはorientationプロパティがverticalか
horizontalの2つが選択できますが、StackLayoutは'lr-tb'を筆頭に左右どちらから
始めるかという選択もできるようになります。
\# 詳しくは後ほど参照する公式マニュアルを参照ください。

こちらについてはadaptive_*プロパティが存在します。
コードについては一部だけ載せておきます。

```python:xiv/stacklayout.py
    StackLayout:
        orientation: "lr-tb"
```

はい、本当にこれだけになります。詳しくはGitHubリポジトリの方で(何回言うんだって話)。
ボタンの仕様に関しては、BoxLayoutの方と合わせています。

### adaptive_height

![61.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/0a54c92b-8074-8b25-d37f-9b05aae63349.png)

こんな感じになりました。
"lr-tb"となるので左上から右に配置させることになります。高さを一定化させるのでこのように
なります。ペチャーっとなっていますね。

### adaptive_width

![62.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/d46b652b-33cd-85cd-1555-877144b8e326.png)

続いてはこちら。
こちらも上側に吸い寄せられています。まぁ、左上から右で横幅が一定なので合ってはいるん
ですけどね。でもなぜ上側に吊し上げられるのか。

### adaptive_size

![63.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/1c7a59d0-b98f-f6e7-4d51-d1cadb721b38.png)

こちらはこうなりました。adaptive_widthとどこでどう変わったんだああぁぁぁ！と叫びたい
気持ちを一旦押し殺して、観察してみます。いや、本当に何が変わった...？結果からは何ももの
申すことは出来ません。

では、本当にMDStackLayoutと同様になるかどうか見てみます。
こちらはMDを接頭辞に付けただけになります。

![64.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/350fbe8c-fe86-4812-0d50-364ea506ae11.png)

はい、一致しました。異論は認めません。

最後にMDStackLayoutで書いたものを"bt-rl"とするとどうなるか見てみます。

![65.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/886bd676-017b-64ed-1810-365f9a18e6fd.png)

このように右下に配置することもできます。
StackLayoutの注意点としてはKivy1.5.0からorientationの変更があったりすることと、
adaptive_*との組み合わせによって結果が結構異なったりすることになります。

## まとめ

いや、疲れたあぁぁッッ！！！！
すみません、心の声が出ちゃいました。

なんせ、記載量少ないし一気にやっちゃえ○産ということで気軽に考えてましたが
Kivyの仕様をあまり把握してないので、時間が大幅に掛かりました。。でも良いの
です。工数としては5→1と約1ヶ月ほど短縮されました。これについてはあっぱれ。

いかかでしたかね。触れ込みというか備忘録に近い内容となってしまいましたが、
参考になりましたかね。ならなかったという方はバッドボタンが実装されましたら
容赦なく押してもらえれば。あるのかな、Qiitaにそんな実装が。

まぁ、あんまり良く分からんという方がいましたら、下記の参照でKivyの公式
マニュアルも参照してもらえればと思います。本当ならこの辺もしっかり触れ込み
たいところなのですがね。

ということで、長く長くなってきましたので今日はこの辺にて〜。
次回のGW特別第2企画はButton篇となります。お楽しみに〜。

それでは、ごきげんよう。

## 参照

### KivyMD
> Components » Box Layout
> https://kivymd.readthedocs.io/en/latest/components/box-layout/

> Components » Float Layout
> https://kivymd.readthedocs.io/en/latest/components/float-layout/

> Components » Grid Layout
> https://kivymd.readthedocs.io/en/latest/components/grid-layout/

> Components » Relative Layout
> https://kivymd.readthedocs.io/en/latest/components/relative-layout/

> Components » Stack Layout
> https://kivymd.readthedocs.io/en/latest/components/stacklayout/

### Kivy
> Programming Guide(翻訳済み) » Widgets(翻訳済み)
> https://pyky.github.io/kivy-doc-ja/guide/widgets.html

> Box Layout
> https://kivy.org/doc/stable/api-kivy.uix.boxlayout.html

> Float Layout(翻訳済み)
> https://pyky.github.io/kivy-doc-ja/api-kivy.uix.floatlayout.html

> Grid Layout
> https://kivy.org/doc/stable/api-kivy.uix.gridlayout.html

> Relative Layout
> https://kivy.org/doc/stable/api-kivy.uix.relativelayout.html

> Stack Layout
> https://kivy.org/doc/stable/api-kivy.uix.stacklayout.html

※ 翻訳済みに関してはver1.10~と少し情報が古い可能性があります