7
11

More than 3 years have passed since last update.

kivyに関する備忘録

Posted at

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 レイアウト
Python: Kivy と Matplotlib でデータセットの確認ツールを書いてみる
Kv Languageを使ったKivyの動かし方
Kv言語の基本
Python: Kivy で Matplotlib のグラフをプロットする
Python3入門〜Kivy による GUI アプリケーション開発,サウンド入出力,ウェブスクレイピング〜

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 を作った

Widgetとは

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

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

ルートウィジェットとは

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

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

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

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クラスを作成しています。

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の中に、LabelGridLayoutButtonなどの細かいウィジェットが入っている

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

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

selfとrootの違い

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

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の時は良いかもしれないが、スマホの時はサイズが合わないというふうになってしまいます。

pos: 20, 50
size: 500, 300

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

pos_hint: {'x':0, 'top':1}
size_hint: 0.3, 0.4

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

pos_hintの変数

pos_hintx, y, right, topを変数として指定することができます。

xはウィジェットの左の部分の位置を指定
yはウィジェットの下部分の位置を指定
rightはウィジェットの右部分の位置を指定
topはウィジェットの上部分の位置を指定

を行うことができます。

root.sizeとhint_sizeはどう使い分ければ良い?

個人的によくわかりにくかったのが、この2つを使い分けることですね。

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

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

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() 
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より1つ外側のWidget(今回は、BoxLayout)の大きさを基準にして、調整する形になります。

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

つまづいた点

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

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()
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
'''

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

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()
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に変更。
・カスタムウィジェットであるの部分をに変更。
・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.sizeroot.textという記述をする。
・TextWidgetクラスの初期化部分で、self.ページ名を記述しておくと、別のページに移動しても元の画面の情報が失われない。
・rootウィジェットに追加したウィジェットのメソッドにアクセス→app.root.ids['ID名'].メソッド名
・rootウィジェットクラスのメソッドにアクセス→app.root.メソッド名
・rootウィジェットの下のウィジェットのメソッドにアクセス→root.ids.ID名.メソッド名

まとめ

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

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

7
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
11