kivyMDチュートリアル其の弐 Themes - Theming篇

はい、今日も順調にやって参りました。
一応、3日坊主ならぬ3回坊主は回避できたのではないかと。
今日もコピペで1000万エンジニアを目指して！

少し、kotlinに浮気していてフラフラとしていますが、
そっちが本命にならないよう頑張ります(何を)。
\# 先にFlutterをやらねば

## Themes

Themesは以下の構成になります。
全て1回でまとめるのは不可能なので本日はThemingだけで。

* Theming
* Material App
* Color Definitions
* Icon Definitions
* Font Definitions

### Theming

さて、本題に入りましょうか。
冒頭ではこんなことから始まっています。

> See also
> Material Design spec, Material theming

マテリアルデザインのスペックとテーマを見ろよということですが、
スペックというのはどういうものなのでしょうね。

テーマはこれかな。
https://material.io/design/material-theming/overview.html#material-theming

ここでは、とりあえずkivyMDについてのことなのでMaterialDesignについては言及しません。
1度別の機会で取り扱ってもいいかもですね。淡い期待を持っていただければ！

あとはスペックについては、本当にどこにあるのでしょうね。
と思っていたら、我らがクラスメソッドさんの記事で次のような引用がありました。
\# いつもお世話になっております！
> Google の新しいデザインガイドライン「Material Design」
> This spec is a living document that will be updated as we continue to develop the tenets and specifics of material design.
> https://dev.classmethod.jp/articles/google-material-design/

google先生に和訳を頼むと、次のような回答が得られました。
> この仕様は、マテリアルデザインの信条と詳細を開発し続けるにつれて更新される、生きたドキュメントです。

あ、specって仕様なのですね。勝手にハードウェア要件とかあるのかなとか勘違いしていました(照)。
仕様についてもここでは割愛させていただきます。ご了承のほどを。でも、それにしても先ほどの記事は
わかりやすくてほぇーとなりました。面接だとこの点に惹かれて・・・ていう志望理由を言いそうになり
ますが、まぁそんなことはどうでもよくて。パッと概要を掴みたいとかあれば先ほどの記事は必読になり
そうです。では一旦kivyMDに戻ります！

#### Material App

こちらの文も依頼してみます。

> The main class of your application, which in Kivy inherits from the App class, 
> in KivyMD must inherit from the MDApp class. The MDApp class has properties that 
> allow you to control application properties such as color/style/font of interface elements and much more.

> KivyではAppクラスから継承するアプリケーションのメインクラス、KivyMDではMDAppクラスから継承する
> 必要があります。MDAppクラスには、インターフェイス要素の色/スタイル/フォントなどのアプリケーション
> プロパティを制御できるプロパティがあります。

この辺りは前回の記事では触れていませんでした(時間が足りなかったという勝手な言い訳でお許しください)。
kivyでもそうだったのですが、確かにMainAppクラスはそれぞれ必要となるクラスを継承する必要があります。
テストがあればおそらく高確率で出てくるところになるかと。後の、プロパティについては後述ということで。


#### Control material properties

ここも依頼を。

> The main application class inherited from the MDApp class has the theme_cls attribute, 
> with which you control the material properties of your application.

> MDAppクラスから継承されたメインアプリケーションクラスには、アプリケーションのマテリアルプロパティを
> 制御するtheme_cls属性があります。

これを見た瞬間はへーとしか言いようがありませんでした。
これも後で、出てきたり出てこなかったり。

#### Changing the theme colors

同じく。

> The standard theme_cls is designed to provide the standard themes 
> and colors as defined by Material Design.

> We do not recommend that you change this.

> However, if you do need to change the standard colors, for instance 
> to meet branding guidelines, you can do this by overloading the 
> color_definitions.py object.

> Create a custom color defintion object. This should have the same format 
> as the colors object in color_definitions.py and contain definitions for 
> at least the primary color, the accent color and the Light and Dark backgrounds.

> 標準のtheme_clsは、マテリアルデザインで定義されている標準のテーマと色を
> 提供するように設計されています。

> これを変更することはお勧めしません。

> ただし、ブランドガイドラインを満たすためなど、標準の色を変更する必要がある場合は、
> color_definitions.pyオブジェクトをオーバーロードすることで変更できます。 

> カスタム色定義オブジェクトを作成します。 これは、color_definitions.pyのcolorsオブジェクト
> と同じ形式で、少なくとも原色、アクセントカラー、明るい背景と暗い背景の定義が含まれている必要があります。

つまりは、どういうことだってばよっていう風に言いそうになりますが、要は標準の色
変えんじゃねーよということでしょうかね。どうしても変えたければcolor_definitions.py
オブジェクトをオーバーロードして変更するなりカスタム色定義オブジェクトなり作れよということ
なのでしょうか。

なるほど、コーポネートカラーとかが厳密になるのであれば変更する余地があるのかな。
えぇ〜、そんな案件近づきたくもなく関わりたくもないですね・・・まぁ、そんな状況
であれば、マテリアルデザインを採用するんじゃないよと言いたくなりそうですが。
まぁ決まってしまればやってよねと言われそう・・・

あとはこういったInfoもあります。
\# どうでもいいですがqiitaってinfoとかのマークダウンタグないのかな

> Note
> Your custom colors must use the names of the existing colors 
> as defined in the palette e.g. You can have Blue but you cannot have NavyBlue.

> カスタムカラーは、パレットで定義されている既存のカラーの名前を使用する必要があります。 
> ブルーは持てますが、ネイビーブルーは持てません。

これも色にこだわりすぎる案件だと向いていない気もしますね。
個人的にはネイビーブルー好きなのですが。まぁ、こんなところで
文句言っても仕方ない気がしますね。

次は、あぁカスタムの仕方書いているのね。
結構親切だなぁ。

> Add the custom theme to the MDApp as shown in the following snippet.

> 次のスニペットに示すように、カスタムテーマをMDAppに追加します。

コードに続きます。

```python
(省略)
```

なぜ省略なの？と思われるかもしれませんが、動きませんでしたー！というためですw
コピペエンジニアは改修しようとは全然思わないのですね。というか仕様わかってない
のに改修できたら苦労しないよと。まぁでも今後わかってきて後でこれは実はこういう
ことが原因なのだよと振り返りできればそれはそれでいいかもですね。今後これと同様で
動かない場合はコードの掲載を勝手ながら省略することにします。
\# 後に続く補足説明も省略ということで
\# まぁカスタムすることもできるんだぜということがわかってもらえれば！

### API

ここからが翻訳するところの山場ですね。
ここはしっかりといきたいと思います。

####  class kivymd.theming.ThemeManager(**kwargs)

クラス名が書かれていますが、実際にこちらを定義したり呼び出したりする訳ではないようです。
こちらは先述したMDAppの中で定義されていてインスタンス化されたそのプロパティを実際にいじるのかな？
すみません、実際に中身を見た訳ではないので断定はできません。でも、次の様子でそのことが分かるかなと。

`primary_palette`
また依頼してみます。

> The name of the color scheme that the application will use. All major material components
>  will have the color of the specified color theme.

> Available options are: ‘Red’, ‘Pink’, ‘Purple’, ‘DeepPurple’, ‘Indigo’, ‘Blue’, ‘LightBlue’,
> ‘Cyan’, ‘Teal’, ‘Green’, ‘LightGreen’, ‘Lime’, ‘Yellow’, ‘Amber’, ‘Orange’, ‘DeepOrange’, 
> ‘Brown’, ‘Gray’, ‘BlueGray’.

> To change the color scheme of an application:

> アプリケーションが使用する配色の名前。 すべての主要なマテリアルコンポーネントは、
> 指定されたカラーテーマのカラーになります。

> 使用可能なオプションは、「赤」、「ピンク」、「紫」、「ディープパープル」、「インディゴ」、
> 「青」、「ライトブルー」、「シアン」、「ティール」、「グリーン」、「ライトグリーン」、「ライム」です。 
> '、'イエロー '、'アンバー '、'オレンジ '、'ディープオレンジ '、'ブラウン '、'グレー '、'ブルーグレー '。

> アプリケーションの配色を変更するには：

途中、なんか変な翻訳になってますが、色自体は上記書かれているものは使えるかと。
と、ここでまたコードにつながります。

```python:ii/primary_palette.py
from kivy.uix.screenmanager import Screen

from kivymd.app import MDApp
from kivymd.uix.button import MDRectangleFlatButton


class MainApp(MDApp):
    def build(self):
        self.theme_cls.primary_palette = "Green"  # "Purple", "Red"

        screen = Screen()
        screen.add_widget(
            MDRectangleFlatButton(
                text="Hello, World",
                pos_hint={"center_x": 0.5, "center_y": 0.5},
            )
        )
        return screen


MainApp().run()
```

今度は上手くいきました。
下がその様子です。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/5610a4c2-914b-c334-a544-d6ed24786930.png)

前回は画像サイズが大きかったので小さくしました。
なぜか手持ちの画像はサイズが変更できなかったのでこうするしかなかったのですよね。
とまぁこんなことはどうでもよくてですね、画像からはプロパティを設定して反映された
その様子が伺えますね。

他の色の様子はマニュアル通りですが、デフォルトはどうなってんのと思ったところ
以下のように定義されていましたね。

> primary_palette is an OptionProperty and defaults to ‘Blue’.

まさかの翻訳なし。これくらいであれば依頼することはありません。
で、様子は以下の通りです。まちがいない。
※self.theme_cls.primary_palette = "Green"をコメントアウトしました

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/c05c20b0-e4f2-ac39-dc4c-98f43c70c46f.png)

`primary_hue`
続いてはこちらになります。
> The color hue of the application.

> Available options are: ‘50’, ‘100’, ‘200’, ‘300’, ‘400’, ‘500’, ‘600’, 
> ‘700’, ‘800’, ‘900’, ‘A100’, ‘A200’, ‘A400’, ‘A700’.

> To change the hue color scheme of an application:

> アプリケーションの色相。

> 使用可能なオプションは、「50」、「100」、「200」、「300」、「400」、「500」、「600」、
> 「700」、「800」、「900」、「A100」、「A200」です。 '、' A400 '、' A700 '。

> アプリケーションの色相の配色を変更するには： 

hueってなんぞそれ？って思ってたのですが、上記でもある通り
色相とか色調とかなのですね。いかにデザイン力がないのが丸わかり・・・
こちらは50からA200と数値と数値+文字を組み合わせた文字が入ります。
hueのとりうる値としてこう決まってんのかな。

まぁ、でもkivyMDを触ることによってデザインの知識も増えるとなると
一石二鳥かもしれません。ここでデザイン力を鍛えることによってより豊かな
アプリケーションを目指せるかもしれないと勝手に思い込んでいます。
というかkivyMDを触るきっかけってのもありますが。。
人は見た目で人を選ぶといいますからね・・・悲しいことに。
アプリも同じような気がします。。

とまぁ長くなってきたので、ここでコードの方に入ります。

```python:ii/primary_hue.py
from kivy.uix.screenmanager import Screen

from kivymd.app import MDApp
from kivymd.uix.button import MDRectangleFlatButton


class MainApp(MDApp):
    def build(self):
        self.theme_cls.primary_palette = "Green"  # "Purple", "Red"
        self.theme_cls.primary_hue = "200"  # "500"

        screen = Screen()
        screen.add_widget(
            MDRectangleFlatButton(
                text="Hello, World",
                pos_hint={"center_x": 0.5, "center_y": 0.5},
            )
        )
        return screen


MainApp().run()
```

こちらも該当のようにプロパティを設定して数値を入れておきます。
一応上記と同じく200を入れてみた結果が以下の通りです。
![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/9becfbdd-dcb8-ece4-afd0-d2468614be66.png)

こちらはマニュアル通りかなと。
ここでじゃあ最小値・最大値でどう変わるんじゃいとなったので
その様子を下に貼り付けておきます。

50
![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/016a18dc-f75b-bd43-92a9-22e0586e85ef.png)

A700
![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/14c5a717-ff30-1f9c-dc23-53aaec346d4a.png)

50に至ってはもう薄っすらというか見えないですね。逆にA700とかだと強いイメージを
受けるのは私だけでしょうか。これも個人、組織など考え方や文化など変わってくると思うので
適した値を入れるようにした方がいいですね。マテリアルデザインはこういうほうがいい！
というのはないのですかね。

あ、あと英文は明記していませんがデフォルトは500のようです。

`primary_light_hue`
`primary_dark_hue`
`primary_color`
`primary_dark`

この辺りに関しては少し大変になってきたので省略をします。\# 疲れてきた
primary_colorなどに関しては後述するprimary_lightの方で出てくるので
そちらの方で。

`primary_light`
説明ではこうあります。

> Colors of the current application color theme in rgba format (in lighter color).

> primary_light is an AliasProperty that returns the value of 
> the current application theme (in lighter color), property is readonly.

> rgba形式（明るい色）の現在のアプリケーションの色テーマの色。

> primary_lightは、現在のアプリケーションテーマの値を（明るい色で）返す
> AliasPropertyであり、プロパティは読み取り専用です。

とのようです。このことは自分でも噛み砕けなかったので
コードを見るとわかりやすいかもですね。

```python:ii/primary_light.py 
from kivy.lang import Builder

from kivymd.app import MDApp


KV = '''
Screen:

    MDRaisedButton:
        text: "primary_light"
        pos_hint: {"center_x": 0.5, "center_y": 0.7}
        md_bg_color: app.theme_cls.primary_light

    MDRaisedButton:
        text: "primary_color"
        pos_hint: {"center_x": 0.5, "center_y": 0.5}

    MDRaisedButton:
        text: "primary_dark"
        pos_hint: {"center_x": 0.5, "center_y": 0.3}
        md_bg_color: app.theme_cls.primary_dark
'''


class MainApp(MDApp):
    def build(self):
        self.theme_cls.primary_palette = "Green"
        return Builder.load_string(KV)


MainApp().run()
```

なるほど、今度はbuild関数の中に埋め込むのではなくてkvのそれぞれの
パーツに定義する形となるのですね。というかmd_bg_colorどっから出てきた・・・
読み取りというのはまさにその通りでそれぞれのパーツでこれを使ってくださいよ
ということをmd_bg_colorプロパティに指定する形となっています。上記から見るに
デフォルトではprimary_colorが選択する形となるのですかね。

この手前でMDRectangleFlatButtonオブジェクトを生成していたのに急に
kvで定義され少し動揺してしまいそうですが、これはこうでということで。
以前から複数個ボタンを用意したい場合はどうすんのよとモヤモヤしていましたが、
その場合はどうやらkvの方で定義はした方がいいようですね。というかkv使う理由って
1番はそこだったりするしで。あとはkvファイル分けたいのだけど！という方もいる
とは思いますが、現段階では少しこのまま一緒に1ファイルとしてマニュアルのまま
記載していきます。というかまだわかっていないので今後その方法はご期待ということで。
\#この辺りはkivyと同じとは思いますが

あ、あと肝心なところ忘れるところだった。
試したキャプチャは下の通りです。

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/74477157-a60e-2c90-e064-1febf26127cb.png)

`accent_palette`
~
`accent_dark`

この辺りも省略します。この辺はMDTabsBarを触っていないとちんぷんかんぷんなので。。
おそらく同じような感じでツールバーの色合いとかを明るくする暗くするとかだとは思いますが
この辺はMDTabsBarを触ったときにということで。

`theme_style`
文字列から把握しやすいですね。これだけ簡潔に言い表されていると。

> App theme style.

> theme_style is an OptionProperty and defaults to ‘Light’.

んー、これは説明も明白。これだと依頼はする必要はなさそうです。

```python:ii/theme_style.py
from kivy.uix.screenmanager import Screen

from kivymd.app import MDApp
from kivymd.uix.button import MDRectangleFlatButton


class MainApp(MDApp):
    def build(self):
        self.theme_cls.theme_style = "Dark"  # "Light"

        screen = Screen()
        screen.add_widget(
            MDRectangleFlatButton(
                text="Hello, World",
                pos_hint={"center_x": 0.5, "center_y": 0.5},
            )
        )
        return screen


MainApp().run()
```

んー、というか当たり前ですがコードも実にシンプル。
theme_styleでDarkもしくはLightを指定するだけです。
これまでやってきたし、説明の通りですがデフォルトはLightです。
なので試した様子はDarkだけを。

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/1cf60f75-adb4-475f-3ae4-2237a2fe20ae.png)

んー、かっこいい！

`bg_darkest`
`opposite_bg_darkest`
`bg_dark`
`opposite_bg_dark`
`bg_normal`
`opposite_bg_normal`
`bg_light`
`opposite_bg_light`

思ったよりしんどくなってきました・・・
なのでここはまとめていきたいと思います。

疲れてきたので説明などは飛ばします。ご了承を。
簡単に言うと、背景の程度を指定するといったことが正しいですかね。
背景でも明るいのも暗いのもあるよーということか。
マニュアルの通りやっても面白みがないのでここではoppositeを選ぶと
どうなるか見ようかと思います。

oppositeはお分かりの通りかもですが反対という意味ですね。
\#いや、そ、そんな翻訳サイト見たなんてそんなことは・・・

論より証拠。コードとキャプチャを一気通貫します。

```python:ii/opposite_bg_asterisk.py
KV = '''
<Box@BoxLayout>:
    bg: 0, 0, 0, 0

    canvas:
        Color:
            rgba: root.bg
        Rectangle:
            pos: self.pos
            size: self.size

BoxLayout:

    Box:
        bg: app.theme_cls.opposite_bg_light
    Box:
        bg: app.theme_cls.opposite_bg_normal
    Box:
        bg: app.theme_cls.opposite_bg_dark
    Box:
        bg: app.theme_cls.opposite_bg_darkest
'''

from kivy.lang import Builder

from kivymd.app import MDApp


class MainApp(MDApp):
    def build(self):
        self.theme_cls.theme_style = "Dark"  # "Light"
        return Builder.load_string(KV)


MainApp().run()
```

![12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/c7d06a75-b6bc-a46f-316c-26c0401bb9f6.png)

おや、と思いましたがこれはこれで正解なようです。
まぁ、当たり前ですがDarkの反対はなんだって考えたときにLightでしょ！
と某先生は言いそうですが、そういうことになります。oppositeを使う理由は
わかりません。これであればopposite付かないもの指定した方が・・・と思い
ますがすべてのものに意味はある！と信じて疑わないので追求できればしようと思います。
現状は、んーわからないw

`divider_color`
~
`set_clearcolor`

これらも、もうすでに力を尽きた感があるので省略します。
許して。。。
これらは、それぞれのパーツ(icon, textfieldとか)が出てきたときに
改めて紹介するようなしないような・・・

`font_styles`
ここも時間等、制約がなければ触っていたのですがフォントを用意できていないので
割愛します。日本語に関しては入力してみましたが、kivyと同じフォントを自前で
用意しないといけなくて用意しないと文字化けを起こしました。kivyにも共通する話
ですが、ここあたりはなんとかならんかねと思いますが偉そうに言えたタチではないので
触れないようにします。

`on_theme_style`
`set_clearcolor_by_theme_style`

ここは説明なしなので省略。
というか用意自体はされているのかな。
\# ふー、なんとか乗り切ったぜ

`set_colors`
説明もしようと思ったのですが、こちらはサンプルが動かなく
なくなく断念。いやー、残念だなぁー、動かしたかったなぁー。
と誰でもわかる虚勢ですが、また別途調査対象ということで。

####  class kivymd.theming.ThemableBehavior(**kwargs)

この辺りもTBDで余力あれば調査します泣！
今回は省略します！

## まとめ

いやー、長い！
とてもじゃないけど、毎回毎回こんな感じだと
疲れて断念しそうでした。でも来週からはもう少し
分量が小さくなるので、耐えます。今日は疲れましたので
これにて失礼します。

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

## 参照

> Themes » Theming
> https://kivymd.readthedocs.io/en/latest/themes/theming/