kivyに関する備忘録

Androidアプリを作っている際にpythonでGUIを作成したいなと思ったので、kivyというライブラリを使ってGUIを作成することにしました！
今回はメモ代わりに書いているので、正直書いている項目に順番はありません。

また、今回は`Widgetクラス`と`ウィジェット`を区別して書くことにします。カタカナで`ウィジェット`になっている場合は、ButtonやLabelなどのウィジェットだと思ってください。

# 開発環境
* Visual Studio Code 1.53.2
* MacOS Darwin x64 20.3.0
* python 3.7.7(anaconda)

# 参考記事
こちらの記事がわかりやすかったので、参考にさせていただきました。
[Kivy 超入門(6)：動的配置 – float レイアウト](http://iatlex.com/python/first_kivy_6)
[Python: Kivy と Matplotlib でデータセットの確認ツールを書いてみる](https://blog.amedama.jp/entry/2019/07/25/060903)
[Kv Languageを使ったKivyの動かし方](https://senablog.com/python-kivy-used-kvlanguage/)
[Kv言語の基本](https://takeshid.hatenadiary.jp/entry/2016/06/19/151827)
[Python: Kivy で Matplotlib のグラフをプロットする](https://blog.amedama.jp/entry/kivy-matplotlib)
[Python3入門〜Kivy による GUI アプリケーション開発，サウンド入出力，ウェブスクレイピング〜](http://www.k-techlabo.org/www_python/python_main.pdf)


# GUIを構築するためのプログラミング言語
まず、最初にAndroidアプリを作る時には、Androidアプリを作るための統合開発環境である、**Android Studio**というものが良く使われているみたいです。

しかし、これで作る場合は、言語はJavaかKotlinというもので作る必要があるのですが、自分はPythonで書く方が慣れているため、なんとかpythonで作る方法を探してみたところ、見つかったのが、この**kivy**というライブラリです。

# kivyの役割
kivyライブラリを使って、GUIを作成する際は、
>①pythonファイルだけを作成して、そこにkivyライブラリを読み込んで、GUIを作っていく方法
②pythonファイルとkvファイルの2つを作成して、pythonファイルには機能だけを、kvファイルにはレイアウトだけを加えていき、GUIを作成していく方法(この場合もpythonファイルにはkivyライブラリを読み込む)

②の場合は、**HTMLとCSS**のような書き方になっておりますね。

# 日本語表記するための最強のライブラリ
KV Languageを使って日本語表記を行おうと思っている方は多いと思います。
しかし、kvはデフォルトでは英語表記になっておりますので、日本語表記にするために色々面倒くさそうなことをしなければありません。

そこで活躍するのが、 `japanize_kivy`になります！

```
!pip install japanize-kivy
import japanize_kivy
```

これを行うだけで、他には何もしなくても日本語で表示することが可能です。

[Python: インポートするだけで Kivy が日本語を表示できるようになる japanize-kivy を作った](https://blog.amedama.jp/entry/japanize-kivy)

# Widgetとは
まず、Kivyを勉強していて思ったのが、`Widgetクラス`を親クラスとして継承して、Buttonなどを作っている人もいれば、継承せずに作っている人もいるという不思議。

どうやら、Widgetクラスとは`Button`や`Label`などの総称のことではないかと自分は理解しました。
なので、Widgetクラスを継承しておけば、他を包括しているので、万能!みたいな感じかなと。

## ルートウィジェットとは
ウィジェットのことは少しわかったのですが、次によくわからなかった部分が、`ルートウィジェット`になります。

ズバリ結論から言いますと、`ルートウィジェット`というのは、**全てのウィジェットの1番基盤の部分**になります。

少しわかりにくいと思いますので、具体例を見ながら確認していきましょう。

```python:Application.py
from kivy.app import App
from kivy.uix.widget import Widget

class MyWidget(Widget):
    pass

class MyApp(App):
    def build(self):
        return MyWidget()

if __name__ == '__main__':
    MyApp().run() 
```
こちらの`python`ファイルの方で、**Widgetクラス**を継承して、新しい`MyWidgetクラス`を作成しています。

```python:my.kv
<MyWidget>:
    BoxLayout:
        orientation: 'vertical'

        Label:

        GridLayout:
            rows:1
            cols:4

            Button:
            
            Button:

            Button:

            Button:
```
上の場合ですと、`ルートウィジェット`というのは、`MyWidget`になります。
`ルートウィジェット`というのはアプリケーションのなかに、**必ず1つだけしか存在しない**という特徴があります。

* `MyWidget`という**1番大きなWidget**を定義する
* その中に`BoxLayout`という2番目に大きなWidgetを定義する
* `BoxLayout`の中に、`Label`や`GridLayout`や`Button`などの細かいウィジェットが入っている

このような構造になっております。


`MyWidget`が親だとすると、`BoxLayout`は子ども、`Label`や`GridLayout`
や`Button`は孫みたいな感じになりますね。

## selfとrootの違い
先程のコードのkvファイルのみを用いて説明をしたいと思います。

```python:my.kv
<MyWidget>:
    BoxLayout:
        orientation: 'vertical'

        Label:

        GridLayout:
            rows:1
            cols:4

            Button:
            
            Button:

            Button:

            Button:
```
まず、`self`とは個々のウィジェット自身を指します。
GridLayoutの部分に`self.`を書けば、`GridLayout自身`を指しますし、Button
の部分に書けば、`Button自身`を指します。

次に`root`ですね。
正直、rootに関してはあまり確信を持てていない状態です。
個人的な認識としては、`rootウィジェット`を指していると考えております。

#### 確信を持てていない理由
なぜ、確信を持てていないのかというと、例えば、下で説明するような画面の切り替えを行う場合に、rootは結局1番根っこの`rootウィジェット`を指しているのか、それとも`rootウィジェットに追加されたウィジェット`を指しているのかわからないからです。

# pos_hintとsize_hint
アプリケーションを作るということになった場合、PCとスマホで画面の大きさが違うという問題にぶち当たりますよね。

通常は、以下のように、`ピクセル単位`で数字を指定していくのですが、これだと、PCの時は良いかもしれないが、スマホの時はサイズが合わないというふうになってしまいます。

```python
pos: 20, 50
size: 500, 300
```

そこで、活躍してくれるのが、`pos_hint`と`size_hint`というものになります。

```python
pos_hint: {'x':0, 'top':1}
size_hint: 0.3, 0.4
```
のような形で、`size_hint`は全画面に対してどれくらいの割合の大きさか、`pos_hint`は`画面の左下を(0,0)`として、どれくらいの割合の場所に位置するかというのを指定します。

## pos_hintの変数
`pos_hint`は**x, y, right, top**を変数として指定することができます。
>xはウィジェットの左の部分の位置を指定
yはウィジェットの下部分の位置を指定
rightはウィジェットの右部分の位置を指定
topはウィジェットの上部分の位置を指定

を行うことができます。

# root.sizeとhint_sizeはどう使い分ければ良い?
個人的によくわかりにくかったのが、この2つを使い分けることですね。

* `root.size`は1番大きい外枠の`Widget`の大きさを基準にする
* `hint_size`は今現在の`Widget`の大きさを基準にする

少し文字だとわかりにくいので、具体例を使って説明していきたいと思います。

```python:Application.py
from kivy.app import App
from kivy.uix.widget import Widget

class MyWidget(Widget):
    pass

class MyApp(App):
    def build(self):
        return MyWidget()

if __name__ == '__main__':
    MyApp().run() 
```

```python:my.kv
<MyWidget>:
    BoxLayout:
        orientation: 'vertical'
        size: root.size

        Label:
            id: txt01
            text: root.text
            size_hint: 1, 0.1
```
`BoxLayout`でまず、`root.size`を使用しているのですが、これは、`ルートウィジェット`の大きさまで大きくするという認識で考えております。

`Label`の方で、`size_hint`を使用していますが、こちらは、`Label`より１つ外側のWidget(今回は、BoxLayout)の大きさを基準にして、調整する形になります。

もしも、`Label`で`root.size`を使った場合は、`MyWidget`の大きさの`Label`ができあがります。(間違っていたらすみません。)

# つまづいた点
こちらは、GUIアプリを作っている際に、つまづいて、Teratailにも質問してみた内容です。

```python:Application.py
import japanize_kivy
import pandas_datareader.data as web
import numpy as np
import matplotlib.pyplot as plt
import datetime
import talib
import mplfinance as mpf
import time
import pandas as pd
import tensorflow as tf
import schedule
import traceback
import sys
from sklearn import preprocessing
from StockApp import *
from matplotlib import gridspec
from kivy.app import App
from kivy.config import Config
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import *
from kivy.resources import resource_add_path
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
from kivy.graphics import *
from kivy.factory import Factory

# マルチタッチを無効化する => 右クリックしても赤い点が残らない
Config.set('input', 'mouse', 'mouse, disable_multitouch')
Config.set('modules', 'inspector', '')

Name = ['A', 'B']

# csvファイルを読み込む
Hello01 = pd.read_csv('graph01.csv')
Hello02 = pd.read_csv('graph02.csv')
Hello03 = pd.read_csv('graph03.csv')

Hello01['Date'] = pd.to_datetime(Hello01['Date'])
Hello02['Date'] = pd.to_datetime(Hello02['Date'])
Hello03['Date'] = pd.to_datetime(Hello03['Date'])

# DataFrameの状態では、インデックス番号で指定できないからnp.arrayで配列に変換する
A_Hello = np.array(Hello01).T
B_Hello = np.array(Hello02).T
C_Hello = np.array(Hello03).T

class FirstPage(BoxLayout):
    text = StringProperty() # プロパティの追加
    graph = NumericProperty(0)
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.text = ' '

        # リストに入れる
        self.d_list = []
        self.w_list = []
        self.m_list = []

        self.numbers = 0
        with open('File01.txt', 'r', encoding='utf-8') as day:
            self.day = day.readlines()
        with open('File02.txt', 'r', encoding='utf-8') as week:
            self.week = week.readlines()
        with open('File03.txt', 'r', encoding='utf-8') as month:
            self.month = month.readlines()

    # ActionBarの次へボタンを押したら次の会社に行くようにする処理
    def Next(self, index_num):
        if 0 <= index_num < len(Name) - 1:
            self.numbers = index_num 

        self.text = self.day[self.numbers] 

    # 月のグラフの表示を更新する処理
    def update01(self):
        self.text = self.month[self.numbers]

    # 週のグラフの表示を更新する処理
    def update02(self):
        self.text = self.week[self.numbers]

    # 日の表示を更新する処理
    def update03(self):
        self.text = self.day[self.numbers]


class GraphView(BoxLayout):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # データをrootウィジェットの方に呼び出すようにする => このクラスがインスタンス化された時に1番最初に呼び出される
        self.A_Hello = A_Hello
        self.B_Hello = B_Hello
        self.C_Hello = C_Hello

        self.d_data = Hello01
        self.w_data = Hello02
        self.m_data = Hello03

        self.num = 0


        # figとaxだけで表す必要あり
        self.fig, self.ax = plt.subplots(2, 1, 
                                        gridspec_kw={
                                            'height_ratios':[4,1]
                                        })

        self.Update(self.A_Hello[self.num+1], self.d_data[Name[self.num]], Hello['Date'])

        self.add_widget(FigureCanvasKivyAgg(self.fig))


    def Update(self, data, signal, times):
        # 前にプロットされたグラフを消去する
        self.ax[0].clear()
        self.ax[1].clear()

        period = 30
        upper, middle, lower = talib.BBANDS(signal, timeperiod=period, nbdevup=1, nbdevdn=1, matype=0)    # 引数としてHelloを取得するからselfをつける必要はない

        rsi = talib.RSI(signal, timeperiod=period)

        # 取得したデータを元にプロットする
        self.ax[0].plot(times, data)
        self.ax[0].set_ylabel('price', fontsize=15)
        self.ax[0].plot(times, upper)
        self.ax[0].plot(times, lower)
        self.ax[0].legend([Name[self.num]])
        self.ax[0].tick_params(labelsize=10)

        self.ax[1].plot(times, rsi)
        self.ax[1].set_xlabel('time', fontsize=20)
        self.ax[1].set_ylabel('persentage', fontsize=15)
        self.ax[1].legend(['RSI'])
        self.ax[1].tick_params(labelsize=10)

        # 再描画する
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()
        time.sleep(0.1) # 0.1秒だけ開ける



    # ActionBarの次へボタンを押したら次に行くようにする処理
    def Next(self, index_num):
        if 0 <= index_num < len(Name) - 1:
            self.num = index_num

        A_Hello = self.A_Hello[self.num+1]
        data = self.d_data[Name[self.num]]

        self.Update(A_Hello, data, Hello['Date'])


    # 月のグラフの表示を更新する処理
    def update01(self):
        C_Hello = self.C_Hello[self.num+1]
        m_data =  self.m_data[Name[self.num]]

        self.Update(C_Hello, m_data, Hello03['Date']) # 上で変更された変数を元にしてメソッドが実行される


    # 週のグラフの表示を更新する処理
    def update02(self):
        B_Hello = self.B_Hello[self.num+1]
        w_data = self.w_data[Name[self.num]]

        self.Update(B_Hello, w_data, Hello02['Date'])

    # 日毎の表示を更新する処理
    def update03(self):
        A_Hello = self.A_Hello[self.num+1]
        d_data = self.d_data[Name[self.num]]

        self.Update(A_Hello, d_data, Hello['Date'])

class TextWidget(Widget):
    sm = ScreenManager()
    print(Name[0])
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def change_page(self):
        self.clear_widgets()
        self.add_widget(FirstPage())

    def change_page2(self):
        self.clear_widgets()
        page2 = Factory.SecondPage()
        self.add_widget(page2)

class CopyHelloApp(App):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.title = 'HelloWold' # ウィンドウの名前を変更する

    def build(self):
        return TextWidget()

if __name__ == '__main__':
    CopyHelloApp().run()
```

```python:copyhello.kv
#:kivy 2.0.0

# TextWidget rootウィジェットに指定されている
TextWidget:

<TextWidget>:
    FirstPage:
        id: page1

<page1>:
    orientation: 'vertical'
    size: root.size  # BoxLayoutのサイズ rootウィジェット(TextWidget)の大きさに合わせる

    # メニューバー
    ActionBar:

        ActionView:
            ActionPrevious:
                title: 'ページタイトル1'
                with_previous: False # 戻るボタンを表示する

            ActionButton:
                text: '次のページ'
                on_release: app.root.change_page2()

            ActionGroup:
                text: 'グループ名'
                mode: 'spinner'
                ActionButton:
                    text: 'A'
                    on_release: app.root.ids['graph_view'].Next(0)
                    on_release: app.root.ids['page1'].Next(0)
                    on_release: name.text = 'A'
                ActionButton:
                    text: "B"
                    on_release: app.root.ids['graph_view'].Next(1)
                    on_release: app.root.ids['page1'].Next(1)
                    on_release: name.text = 'B'


        BoxLayout:
            orientation: 'horizontal'
            size_hint_y: 0.1

            Label:
                id: name
                text: 'A'

            Label:
                id: show
                text: app.root.ids['page1'].text


        # グラフをここで表示する
        GraphView:
            size_hint_y: 0.9
            id: graph_view

        GridLayout:
            cols: 3
            rows: 1
            size_hint_y: 0.1
            Button:
                id: button11
                text: '月'
                font_size: 48
                on_release: app.root.ids['graph_view'].update01()
                on_release: app.root.ids['page1'].update01()

            Button:
                id: button12
                text: '週'
                font_size: 48
                on_release: app.root.ids['graph_view'].update02()
                on_release: app.root.ids['page1'].update02()

            Button:
                id: button13
                text: '日'
                font_size: 48
                on_release: app.root.ids['graph_view'].update03()
                on_release: app.root.ids['page1'].update03()

<GraphView>:


<SecondPage@BoxLayout>:
    id: page2
    size: root.size
    orientation: 'horizontal'

    # 画面の切り替えを行う
    ActionBar:

        ActionView:
            ActionPrevious:
                title: 'ページタイトル2'
                with_previous: False # 戻るボタンを表示する

            ActionButton:
                text: '前のページ'
                on_release: app.root.change_page()

    MyLabel:
        id: first_button
        text: 'Hello'

    MyLabel:
        id: second_button
        text: 'World'

<MyLabel@Label>:
    font_size: 60
    size_hint_y: 1
'''
```

このコードですと、なぜか画面が真っ暗になってしまったので、なんでかなと思いつつ、エラーになる前の状態から１つずつ機能を追加していきました。

```python:Application.py
import japanize_kivy
import pandas_datareader.data as web
import numpy as np
import matplotlib.pyplot as plt
import datetime
import talib
import mplfinance as mpf
import time
import pandas as pd
import tensorflow as tf
import schedule
import traceback
import sys
from sklearn import preprocessing
from StockApp import *
from matplotlib import gridspec
from kivy.app import App
from kivy.config import Config
from kivy.uix.widget import Widget
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import *
from kivy.resources import resource_add_path
from kivy.core.text import LabelBase, DEFAULT_FONT
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.garden.matplotlib.backend_kivyagg import FigureCanvasKivyAgg
from kivy.graphics import *
from kivy.factory import Factory

# マルチタッチを無効化する => 右クリックしても赤い点が残らない
Config.set('input', 'mouse', 'mouse, disable_multitouch')
Config.set('modules', 'inspector', '')

Name = ['A', 'B']

# csvファイルを読み込む
Hello01 = pd.read_csv('graph01.csv')
Hello02 = pd.read_csv('graph02.csv')
Hello03 = pd.read_csv('graph03.csv')

Hello01['Date'] = pd.to_datetime(Hello01['Date'])
Hello02['Date'] = pd.to_datetime(Hello02['Date'])
Hello03['Date'] = pd.to_datetime(Hello03['Date'])
 
# DataFrameの状態では、インデックス番号で指定できないからnp.arrayで配列に変換する
A_Hello = np.array(Hello01).T
B_Hello = np.array(Hello02).T
C_Hello = np.array(Hello03).T

class FirstPage(BoxLayout):
    text = StringProperty() # プロパティの追加
    graph = NumericProperty(0)
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.text = ' '

        # リストに入れる
        self.d_list = []
        self.w_list = []
        self.m_list = []

        self.numbers = 0
        with open('File01.txt', 'r', encoding='utf-8') as day:
            self.day = day.readlines()
        with open('File02.txt', 'r', encoding='utf-8') as week:
            self.week = week.readlines()
        with open('File03.txt', 'r', encoding='utf-8') as month:
            self.month = month.readlines()

    # ActionBarの次へボタンを押したら次の会社に行くようにする処理
    def Next(self, index_num):
        if 0 <= index_num < len(Name) - 1:
            self.numbers = index_num 
        
        self.text = self.day[self.numbers] 
    
    # 月のグラフの表示を更新する処理
    def update01(self):
        self.text = self.month[self.numbers]

    # 週のグラフの表示を更新する処理
    def update02(self):
        self.text = self.week[self.numbers]
        
    # 日の表示を更新する処理
    def update03(self):
        self.text = self.day[self.numbers]


class GraphView(BoxLayout):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # データをrootウィジェットの方に呼び出すようにする => このクラスがインスタンス化された時に1番最初に呼び出される
        self.A_Hello = A_Hello
        self.B_Hello = B_Hello
        self.C_Hello = C_Hello

        self.d_data = Hello01
        self.w_data = Hello02
        self.m_data = Hello03

        self.num = 0


        # figとaxだけで表す必要あり
        self.fig, self.ax = plt.subplots(2, 1, 
                                        gridspec_kw={
                                            'height_ratios':[4,1]
                                        })

        self.Update(self.A_Hello[self.num+1], self.d_data[Name[self.num]], Hello['Date']) # グラフを初期化する => ここは日毎のデータにする
        # ax[0].set_title('HelloPredict')
    
        self.add_widget(FigureCanvasKivyAgg(self.fig))


    def Update(self, data, signal, times):
        # 前にプロットされたグラフを消去する
        self.ax[0].clear()
        self.ax[1].clear()
        
        period = 30
        upper, middle, lower = talib.BBANDS(signal, timeperiod=period, nbdevup=1, nbdevdn=1, matype=0)    # 引数としてHelloを取得するからselfをつける必要はない
        
        rsi = talib.RSI(signal, timeperiod=period)

        # 取得したデータを元にプロットする
        self.ax[0].plot(times, data)
        self.ax[0].set_ylabel('price', fontsize=15)
        self.ax[0].plot(times, upper)
        self.ax[0].plot(times, lower)
        self.ax[0].legend([Name[self.num]])
        self.ax[0].tick_params(labelsize=10)

        self.ax[1].plot(times, rsi)
        self.ax[1].set_xlabel('time', fontsize=20)
        self.ax[1].set_ylabel('persentage', fontsize=15)
        self.ax[1].legend(['RSI'])
        self.ax[1].tick_params(labelsize=10)

        # 再描画する
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()
        time.sleep(0.1) # 0.1秒だけ開ける



    # ActionBarの次へボタンを押したら次に行くようにする処理
    def Next(self, index_num):
        if 0 <= index_num < len(Name) - 1:
            self.num = index_num
        
        A_Hello = self.A_Hello[self.num+1]
        data = self.d_data[Name[self.num]]

        self.Update(A_Hello, data, Hello['Date'])

  
    # 月のグラフの表示を更新する処理
    def update01(self):
        C_Hello = self.C_Hello[self.num+1]
        m_data =  self.m_data[Name[self.num]]

        self.Update(C_Hello, m_data, Hello03['Date']) # 上で変更された変数を元にしてメソッドが実行される
        

    # 週のグラフの表示を更新する処理
    def update02(self):
        B_Hello = self.B_Hello[self.num+1]
        w_data = self.w_data[Name[self.num]]

        self.Update(B_Hello, w_data, Hello02['Date'])
        
    # 日毎の表示を更新する処理
    def update03(self):
        A_Hello = self.A_Hello[self.num+1]
        d_data = self.d_data[Name[self.num]]

        self.Update(A_Hello, d_data, Hello['Date'])

class DisplayCompany(BoxLayout):
    pass


class TextWidget(BoxLayout):
    sm = ScreenManager()
    print(Name[0])
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.page1 = Factory.FirstPage()
        self.page2 = Factory.SecondPage()

    def change_page(self):
        self.clear_widgets()
        self.add_widget(self.page1)

    def change_page2(self):
        self.clear_widgets()
        self.add_widget(self.page2)

class CopyHelloApp(App):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.title = 'HelloWold' # ウィンドウの名前を変更する

    def build(self):
        return TextWidget()

if __name__ == '__main__':
    CopyHelloApp().run()
```

```python:copyhello.kv
#:kivy 2.0.0

# TextWidget rootウィジェットに指定されている
TextWidget:

<TextWidget>:
    PredictShow:
        id: page1

<PredictShow>:
    orientation: 'vertical'
    size: root.size  # BoxLayoutのサイズ rootウィジェット(TextWidget)の大きさに合わせる
        
    # メニューバー
    ActionBar:

        ActionView:
            ActionPrevious:
                title: 'ページタイトル1'
                with_previous: False # 戻るボタンを表示する

            ActionButton:
                text: '次のページ'
                on_release: app.root.change_page2()

            ActionGroup:
                text: 'グループ名'
                mode: 'spinner'
                ActionButton:
                    text: 'A'
                    on_release: root.ids.graph_view.Next(0)
                    on_release: app.root.ids['page1'].Next(0)
                    on_release: name.text = 'A'
                ActionButton:
                    text: "B"
                    on_release: root.ids.graph_view.Next(1)
                    on_release: app.root.ids['page1'].Next(1)
                    on_release: name.text = 'B'
                

        BoxLayout:
            orientation: 'horizontal'
            size_hint_y: 0.1
            
            Label:
                id: name
                text: 'A'

            Label:
                id: show
                text: root.text


        # グラフをここで表示する
        GraphView:
            size_hint_y: 0.9
            id: graph_view

        GridLayout:
            cols: 3
            rows: 1
            size_hint_y: 0.1
            Button:
                id: button11
                text: '月'
                font_size: 48
                on_release: root.ids.graph_view.update01()
                on_release: app.root.ids['page1'].update01()
                
            Button:
                id: button12
                text: '週'
                font_size: 48
                on_release: root.ids.graph_view.update02()
                on_release: app.root.ids['page1'].update02()

            Button:
                id: button13
                text: '日'
                font_size: 48
                on_release: root.ids.graph_view.update03()
                on_release: app.root.ids['page1'].update03()

<GraphView>:
    
 
<DisplayCompany>:
    id: page2
    size: root.size
    orientation: 'horizontal'

    # 画面の切り替えを行う
    ActionBar:

        ActionView:
            ActionPrevious:
                title: 'ページタイトル2'
                with_previous: False # 戻るボタンを表示する

            ActionButton:
                text: '前のページ'
                on_release: app.root.change_page()

    MyLabel:
        id: first_button
        text: 'Hello'

    MyLabel:
        id: second_button
        text: 'World'

<MyLabel@Label>:
    font_size: 60
    size_hint_y: 1
```


#### 変更点
・rootウィジェットである、TextWidgetの親クラスをBoxLayoutに変更。
・カスタムウィジェットである<page1>の部分を<FirstPage>に変更。
・Labelのtextを`app.root.ids['page1'].text`から`root.text`に変更
・TextWidgetクラス内で、`__init__`メソッドを追加し、`self.page1=Factory.FirstPage()`と`self.page2=Factory.SecondPage()`を記述。
・rootウィジェット内にidを置いたウィジェット(今回ではFirstPage)のメソッドにアクセスする際に、`app.root.ids['ID名'].メソッド名`を使用する
・rootウィジェットのメソッドにアクセスする際に、`root.メソッド名`を使用する。
・rootウィジェットの下のウィジェットのメソッドにアクセスする際に、`root.ids.ID名.メソッド名`を使用する。


#### わかった点(個人的に解釈しているだけで確証はない)
・画面を切り替えるときは、`Widgetクラス`じゃなくて、`BoxLayoutクラス`を継承する方が上手くいく。
・textやsizeなど**イベントでは無い部分**に対して、`app.root`は使わない。`root.size`や`root.text`という記述をする。
・TextWidgetクラスの初期化部分で、`self.ページ名`を記述しておくと、別のページに移動しても元の画面の情報が失われない。
・rootウィジェットに追加したウィジェットのメソッドにアクセス→**app.root.ids['ID名'].メソッド名**
・rootウィジェットクラスのメソッドにアクセス→**app.root.メソッド名**
・rootウィジェットの下のウィジェットのメソッドにアクセス→**root.ids.ID名.メソッド名**



# まとめ
`Kivy`の記事は少ないとのことで、自分も理解するのが大変でした。
わかりやすい記事があって助かりました。

自分の記事も皆さんのお助けになればと思います。