kivyMDチュートリアル其の参什 Components - TapTargetView篇

ハローハロー、お勤めご苦労様です。
なんか2国間のあいさつをした(出来ていない)ところで、いかがお過ごしでしょうか。

すっかりお盆の時期になりましたね。こういったときは祝日にするべきだ(過激論)と
思いますが、お仕事されている方は意外といるのではないでしょうか。はい、私は
言うまでもないですね。

週初めとかはなんか天気良かったのに、急に週末天気がグズついてきましたね。
体調管理はしっかりしたいものですね。何かこういう管理をしてるよという健康
オタクのかた、コメントをお待ちしています。

ということで、このKivymdに関する投稿はお休みしないので、ご心配なく(誰も
していない)。というわけで今週も空元気でやっていきますね。今日はTapTarget-
View篇となります。

## TapTargetView

まぁ、恒例行事のようにリンクはスキップするのですが、なにやらいつもと様子が
異なります。マテリアルデザインのリンク先がアーカイブ扱いとなっていますね。
それだけ古いコンポーネントだよということでしょうか。

アーカイブになっている理由などは分かりませんでしたが、詳細な仕様などが書か
れていることは変わりありません。気になる方は見て頂ければと思います。

KivyMDの方ではこのように書かれています。

> Provide value and improve engagement by introducing users 
> to new features and functionality at relevant moments.

つまりどういうことだってばよ、と私はなったのですぐさま依頼しました。
依頼結果はこちらとなります。

> 適切なタイミングでユーザーに新機能を紹介することで、価値を提供し、
> エンゲージメントを向上させます。

なるほど...うぅん?となってしまいましたが、少し抽象的な表現となる
でしょうか。

実際に今のGoogleのマップアプリだと、当然っちゃ当然ですが機能としては
ありません。なのでマテリアルデザインでもアーカイブとなるのはそのためで
しょうか。少しレガシーな機能ということでしょうかね。

機能自体の概略としては、アイコン付きのボタンなどがあり、それを押すと
テキストなどのコンテンツが表示されそれ自体が「新機能を紹介することで
、価値を提供し、エンゲージメントを向上」させるということでしょうか。
主に新機能を取り込んだ用というかそのような位置付けになります。

まぁ、要は見てみないとなんだかよくわからんとなりそうなので、さっそく
見ていきましょう。

### Usage

はい、使用方法ですね。こちらもサンプルがあるので見ていきましょう。

```python:xxx/taptargetview.py
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.taptargetview import MDTapTargetView

KV = '''
Screen:

    MDFloatingActionButton:
        id: button
        icon: "plus"
        pos: 10, 10
        on_release: app.tap_target_start()
'''


class TapTargetViewDemo(MDApp):
    def build(self):
        screen = Builder.load_string(KV)
        self.tap_target_view = MDTapTargetView(
            widget=screen.ids.button,
            title_text="This is an add button",
            description_text="This is a description of the button",
            widget_position="left_bottom",
        )

        return screen

    def tap_target_start(self):
        if self.tap_target_view.state == "close":
            self.tap_target_view.start()
        else:
            self.tap_target_view.stop()


TapTargetViewDemo().run()
```

#### import文

で、さっそくimport文に入りますが、以下に抜粋を載せておきます。

```python
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.taptargetview import MDTapTargetView
```

BuilderやMDAppモジュールはいつも出てくるものですよね。また、MDTap-
TargetViewはTapTargetViewDemoクラス側で使用するので、ここでimport
しています。

#### kv側

ここは特にとりとめがないところですが、一応クラス側にもつながるところなので
再度以下に該当部分を抜粋しておきます。

```python
KV = '''
Screen:

    MDFloatingActionButton:
        id: button
        icon: "plus"
        pos: 10, 10
        on_release: app.tap_target_start()
'''
```

ここではMDFloatActionButton1個が定義されているのみになります。Buttonに
ついては以下でチュートリアルを済ませているので、参考までに見ていただければ。
分かりにくっとなった方は公式マニュアルの該当ページをご覧ください。

https://qiita.com/virty/items/29d792d5ecf4ad0f86be

代わり映えのあるところで言うと、idとon_releaseプロパティがあるくらいになり
ますかね。これらは後のクラス側でも出てくるので、詳細は後ほど。あと、posプロ
パティはこれまた後ほど触れるかもしれませんが、絶対座標方式で位置を定義する
ものになります。

#### クラス側

ここが結構、コンポーネントのキモとなるのでじっくり見ていきましょう。
コードを抜粋します。

```python
class TapTargetViewDemo(MDApp):
    def build(self):
        screen = Builder.load_string(KV)
        self.tap_target_view = MDTapTargetView(
            widget=screen.ids.button,
            title_text="This is an add button",
            description_text="This is a description of the button",
            widget_position="left_bottom",
        )

        return screen

    def tap_target_start(self):
        if self.tap_target_view.state == "close":
            self.tap_target_view.start()
        else:
            self.tap_target_view.stop()
```

まずはbuildメソッドから。基本的な使い方としてはMDTapTargetViewインスタンス
を直接TapTargetViewDemoのプロパティとして格納する形となります。これは特に
初めてというわけでもなく、Dialogとかもその形でしたね。

あとは、MDTapTargetViewインスタンスを生成するときに、引数として使用したい
オプションなどをじゃんじゃん入れ込んでいきます。サンプルとしては、title_text、
description_text、widget_positionなどがありますね。

*_textに関しては、なんとなく分かるかと思いますが、widgetに関してはTapTarget-
Viewを発動させたい起点となる他のコンポーネントを基本的には指定します。この場合は
MDFloatingActionButtonになっていますね。

widget_positionはコンテンツを入れ込んだ外円をどこに表示させたいかということを
考慮する必要があります。まぁ、端的に言うと画面上で起点となるコンポーネント(この
場合ボタン)がどこにあるかということになりそうですが。今回のボタンは画面左下に
配置させたので、外円は右上に表示させないと見ることができません。ということで、
"left_bottom"を値として入れているということがあります。

残るは、tap_target_startメソッドですが、これはkv側でもありましたね。そう
です、気づいた方は大勢いらっしゃるかもしれませんが、これはon_release(kv側)
プロパティのコールバックメソッドになります。

中身も至って単純ですね。先程インスタンス化したtap_target_viewの状態を見て
それを元に条件分けをしていることになります。ボタンが押されたときに、クローズ
していれば、オープンするように。オープンしているときは、また逆のように。

### 結果

ということで、さっそく結果の方を見てみましょう。論より証拠を。

![139.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/e04e30af-ef43-2403-71d2-e20ebf364c4a.png)

![140.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/a251405c-3bc4-1b66-57bc-387e1ca3439d.png)

マニュアルから至って変わりない様子ですが、ちゃんと機能としては使用することが
出来ます。外円も右上にちゃんと表示されていますね。タイトルと説明文も意図通り
表示されています。問題はなさそうですね。

## Breeding

というわけで、今日はここまで！

という風に打ち切る予定でしたが、少し気になることもあり、色々見てみたいという
ことでここからはオリジナル節となります。

:::note
Breedingは動物に対する進化、品種改良(最初に検索したのはこちら)という意味で
ありますが、ここでもコードは動物でしょ?(意味不明)ということから名付けられ
ました。
:::

というか品種改良って生物だけかなと思っていましたが、どうやらそのようではない
みたいです。

まぁ、重要なところはこの後のコードになりますが、少し変更を加えたコードをさっ
そく載せておきます。

```python:xxx/taptargetview_breeding.py
from kivy.lang import Builder

from kivymd.app import MDApp
from kivymd.uix.taptargetview import MDTapTargetView

KV = '''
Screen:

    MDFloatingActionButton:
        id: button_left_top
        icon: "plus"
        pos: 50, 480
        on_release: app.tap_target_view_lt.start() if app.tap_target_view_lt.state == "close" else app.tap_target_view_lt.stop()
    
    (略)

'''

class TapTargetViewDemo(MDApp):
    def build(self):
        screen = Builder.load_string(KV)
        self.tap_target_view_lt = MDTapTargetView(
            widget=screen.ids.button_left_top,
            title_text="Title text",
            title_text_size="36sp",
            description_text="Description text",
            description_text_color=[1, 0, 0, 1],
            widget_position="left_top",
        )

    (略)

        return screen

TapTargetViewDemo().run()
```

簡単にどんな変更を加えたかということを言いますと、まずボタンを8個ほど追加を
しています。これはUsageであったMDTapTargetViewオブジェクトのwidget_po-
sitionを全て列挙できるかどうかということを見ています。left-top、topだとか
の定義していなかったものを全て指定してみたということですね。

お次はというと、まずUsageでMDTapTargetViewオブジェクトの指定していたオプ
ションをベースとします。分かりやすくするために以下に再掲しておきます。

```python
self.tap_target_view = MDTapTargetView(
    widget=screen.ids.button,      // #以下のプロパティは各ボタンによりけり
    title_text="This is an add button",
    description_text="This is a description of the button",
    widget_position="left_bottom",
)
```

widget(_position)は、作成するボタンによりけりなので可変ということは分かり
やすいとは思いますが、これら4つのプロパティをこのままにしておいて値だけを変更
したものをベースとします。

そこからさらにタイトルの文字サイズ、色だとかを変更したものを9個全て定義してみま
した。具体的な仕様としては以下のようになります。

```yaml
Screen:
  MDFloatingActionButton_left_top:
    - タイトルテキストサイズを36dpに変更
    - 説明文の色を赤文字に変更

  MDFloatingActionButton_top:
    - 外円を赤色に変更

  MDFloatingActionButton_right_top:
    - target_circle(内円と言った方がいいでしょうか)の色を緑に変更

  MDFloatingActionButton_left:
    - 外円の半径を150dpに変更

  MDFloatingActionButton_center:
    - タイトルの位置を左上に設定(プロパティ定義は必須)

  MDFloatingActionButton_right:
    - 内円の半径を30dpに変更

  MDFloatingActionButton_left_bottom:
    - タイトルの太文字をFalseに変更

  MDFloatingActionButton_bottom:
    - 説明文のサイズを5dpに変更

  MDFloatingActionButton_right_bottom:
    - 説明文の文字を太文字に変更

```

ふー、大変。なぜyaml形式にしたのかと言われると真っ先に出てくるのはやってみた
かったということですねw

という冗談みたいなことは置いておいて、ざっとの仕様はこんな感じになります。
書いてみて、結構kv言語とyamlは相性がいいなぁと思ったことも置いておいて、
チュートリアルでスクショが貼ってあったものは全て見ているかと思います。
漏れていたらすみません・・

注目ポイントとしては、センター配置のときにタイトルの位置を固定化しなければ
いけないという仕様ですね。これがないと動きません。私もまんまと引っかかり
ました。チュートリアルにも以下のように記載があります。

> If you use the widget_position = "center" parameter then 
> you must definitely specify the title_position.

> from Widget position - widget_position="center"

ですって。あ、あと言い忘れていましたが、コールバックメソッドはプロパティ
の値として定義しているということも変更点になります。分かりにくいという方は
クラス側の方でbuildメソッド以外のメソッドが亡くなったと思ってもらえれば.

ということで、結果を見てみたいという方が大半と思うので、さっそく結果の方に
移ります。あ、コードの方はGitHubにプッシュするようにしますので、ご安心を。
というか全文見たい方はそちらをご参照ください。

### 結果

お待ちかね、結果になります。
ここは動画キャプチャでの案内になります。

![141.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/7c30e31c-5443-3227-aa2d-082fb6fe1f56.gif)

なんか画質も荒いし、撮り方も下手くそですね。。もう少し練習しますね。何か良い
方法をまとめたページどこかに落ちていないかな。

ということは置いておいて、yamlでまとめた仕様を具現化したものが上記キャプチャ
になります。照らし合わせて見ていただくと、スッと入ってきやすいのではと思って
おります。

[追記 2021/08/15 17:22]
少し書き忘れていたことがあって、以前にも述べていたことかもしれませんがpos_size
プロパティは使用しないほうがいいかもしれません。なぜならtablet表示などでは以下の
ように位置ずれが発生するためになります。

![142.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/40e9a043-6413-7e52-5629-ab8722aafa98.png)


## API - kivymd.uix.taptargetview

まとめに入る前に、これも恒例行事ですが使用したAPIをまとめておきます。

### class kivymd.uix.taptargetview.MDTapTargetView(**kwargs)

> Rough try to mimic the working of Android’s TapTargetView.

自身の英語力ではちんぷんかんぷんであったので、依頼してみました。結果を見た
瞬間にあぁなるほどwという気持ちになりましたね。

> AndroidのTapTargetViewの動作を真似てみました。

`widget`

> Widget to add TapTargetView upon.

> widget is an ObjectProperty and defaults to None.

オブジェクトをインスタンス化をするときに引数で指定していたプロパティでしたね。
Usage・Breedingどちらもボタンのid値を指定していました。

`outer_radius`

> Radius for outer circle.

> outer_radius is an NumericProperty and defaults to dp(200).

外円の半径です。Breedingでは左にボタンを配置したところで指定していました。
デフォルトは200dpで、Breedingでは150dpに変更していました。

`outer_circle_color`

> Color for the outer circle in rgb format.

> outer_circle_color is an ListProperty and defaults to theme_cls.primary_color.

外円の色指定です。Breedingでは上にボタンを配置したところで指定していました。
リストプロパティでデフォルトテーマカラーを引き継いでいます。Breedingでは(1,0,0)
つまりは赤に変更していました。

`target_radius`

> Radius for target circle.

> target_radius is an NumericProperty and defaults to dp(45).

ターゲット円、つまりは内円の半径になります。Breedingでは右配置のボタンで
間接的に指定していました。デフォルトは45dpで、Breeding時の指定は30dpでした。

`target_circle_color`

> Color for target circle in rgb format.

> target_circle_color is an ListProperty and defaults to [1, 1, 1].

これまた内円で、その色指定ですね。今回は右上配置のボタンに関連して、(0,1,0)
つまり緑色に指定していました。プロパティの仕様、デフォルト値は上記の通りです。

`title_text`

> Title to be shown on the view.

> title_text is an StringProperty and defaults to ‘’.

これは言わずもがなかもですね。タイトルのデフォルト値は何も入っていないのでご注意を。

`title_text_size`

> Text size for title.

> title_text_size is an NumericProperty and defaults to dp(25).

今度はタイトルのサイズになります。Breedingでは左上配置のボタンに関連して
36dpと指定していました。プロパティの仕様、デフォルト値は上記の通りです。

`title_text_color`

> Text color for title.

> title_text_color is an ListProperty and defaults to [1, 1, 1, 1].

今度はタイトルの色指定になります。Breedingでは指定はしていませんでしたが、これも
指定できるよーという周知も込めて。プロパティの仕様、デフォルト値は上記の通りです。

`title_text_bold`

> Whether title should be bold.

> title_text_bold is an BooleanProperty and defaults to True.

タイトルの太文字選択ですね。これは今回使用していませんが、デフォルトは上記の
通りとなっているので注意が必要そうです。


`description_text`

> Description to be shown below the title (keep it short).

> description_text is an StringProperty and defaults to ‘’.

`description_text_size`

> Text size for description text.

> description_text_size is an NumericProperty and defaults to dp(20).

`description_text_color`

> Text size for description text.

> description_text_color is an ListProperty and defaults to [0.9, 0.9, 0.9, 1].

`description_text_bold`

> Whether description should be bold.

> description_text_bold is an BooleanProperty and defaults to False.

これら4つはタイトルと同じで、その説明文版と見てもらって良いかもしれません。
これらはUsage・Breedingともに使用している・していないということがあり
ますが、ウォーリーを探せみたいにどこで使用されているか探してみるのもよい
トレーニングになるかもしれません。\# 省エネモード突入!

`widget_position`

> Sets the position of the widget on the outer_circle. Available options are ‘left’, ‘right’, ‘top’, ‘bottom’, ‘left_top’, ‘right_top’, ‘left_bottom’, ‘right_bottom’, ‘center’.

> widget_position is an OptionProperty and defaults to ‘left’.

一言で表すなら、外円を表示させるウィジェットがどこにあるかという位置決め
ですが、このデフォルトは'left'なことは注意が必要そうです。

`title_position`

> Sets the position of :attr`~title_text` on the outer circle. Only works if :attr`~widget_position` is set to ‘center’. In all other cases, it calculates the :attr`~title_position` itself. Must be set to other than ‘auto’ when :attr`~widget_position` is set to ‘center’.

> Available options are ‘auto’, ‘left’, ‘right’, ‘top’, ‘bottom’, ‘left_top’, ‘right_top’, ‘left_bottom’, ‘right_bottom’, ‘center’.

> title_position is an OptionProperty and defaults to ‘auto’.

今度はタイトルの位置決めになりますね。デフォルトは'auto'なので特に気にする
ことはないのかと思われるかもしれません。先述の通りですがwidget_position
プロパティをcenterに配置したときはこちらのプロパティが必須項目となることに
注意が必要です。

`state`

> State of MDTapTargetView.

> state is an OptionProperty and defaults to ‘close’.

`stop(self, *args)`

> Starts widget close animation.

`start(self, *args)`

> Starts widget opening animation.

MDTapTargetViewインスタンスの状態及び併せ持つメソッドになります。
注意としてはデフォルト時はこれが閉じた状態にあるという点になります。

## まとめ

いやー、長い！打ち切っとけばよかった！
という本当か分からない心の内は閉まっておくのがベストですね。

さぁ、どうでもよいことは置いておいて、いかがだったでしょうか。

試してみると分かるかもしれませんが、結構デフォルトは計算されているので
特にカスタマイズ要件などがない限りは、そのまま使用した方がいいかもですね。
実行結果の下ボタン開いたときなんかは、説明文が見えないしで。。

ということで、長文になったことは思ってもみなかったことですが、参考になれば
この上なく嬉しいです。ということで今日はここまで！

来週は順番通りTextField篇となります。終わりも見えてきて、これをやると結構
アプリ開発が本格的にスタートできるのではという感じになってきます。バージョン
アップが先だけどね。ということで、来週もお楽しみにー。

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

## 参照

> Components » TapTargetView
> https://kivymd.readthedocs.io/en/latest/components/taptargetview/

> DeepL 翻訳ツール
> https://www.deepl.com/ja/translator