kivyで画面遷移

kivyで画面遷移する方法について

概要

Pythonのマルチプラットフォーム向けGUIライブラリkivyで 実行時に画面を切り替えて使用する方法について試した時のメモ。
ついでにAndroidアプリ化もしてみた。

ソース

ソースは↓の「開く」をクリックすると表示されます。
ダウンロードしたいときは こちら からどうぞ。
(multi_screenって名前はなんか違う気もするけど、複数画面を制御するってことでヨシとしとこう)

メイン処理/スクリーンマネージャのソース

multi_screen.py

第1画面のソース

screen1.py

第2画面のソース

screen2.py

解説

kivyで画面を切り替えて使用するには、ScreenManagerを継承したクラスを使用し、ここに各画面を登録し、 self.currentに表示する画面の名前(name)を設定することで切り替えるらしい。

準備

今回は日本語を使ってみようと思うので、日本語フォントをインストール。
有名どころではNotoSansCJKとかTAKAOとか。
ubuntuだと以下でインストールできる。
NotoSansCJK は Androidでもインストールされていることが多いのかな?(手元のちょっと古いAndroidには入ってた)

sudo apt install fonts-noto-cjk
# または
sudo apt install fonts-takao

あと、テキスト入力も試してみるので、クリップボード操作のためのライブラリをインストールしておく。

sudo apt install xclip

multi_screen.py

まず、multi_screen.pyの内容について。

今回は日本語を使ってみるので、フォントディレクトリの登録と日本語対応フォントをデフォルトフォントに設定する。

from kivy.core.text import LabelBase, DEFAULT_FONT
LabelBase.get_system_fonts_dir()                                        # フォントディレクトリにシステムフォントディレクトリを登録
LabelBase.register(DEFAULT_FONT, fn_regular='NotoSansCJK-Regular.ttc')  # デフォルトフォントを設定

ScreenManagerを継承したControlScreenManagerクラスを生成。

class ControlScreenManager(ScreenManager):
    def __init__(self, **kwargs):
        super(ControlScreenManager, self).__init__(**kwargs)

        # 画面間で共有する変数
        self.target_string = ""

ネットで検索すると画面間で共有する変数を画面間で直接やりとりする例があったが、 ソースの再利用性などを考えてスクリーンマネージャで管理することにした。
そのための設定/取得メソッド

    # ターゲット文字列の設定
    def set_target_string(self, text) :
        self.target_string = text
    
    # ターゲット文字列の取得
    def get_target_string(self) :
        return self.target_string

画面切り替え処理
これも画面から他の画面に直接遷移している例があるけど、 一旦スクリーンマネージャでうけとってから 遷移した方が分かりやすいと思う。

    # screen1への切り替え
    def switch_to_screen1(self):
        self.transition = SlideTransition(direction='left', duration=1)
        self.current = 'screen1'
        
    # screen2への切り替え
    def switch_to_screen2(self):
        self.transition = FadeTransition(duration=1)
        self.current = 'screen2'

アプリケーションクラス

アプリケーションクラスのbuildメソッドでは上で定義したスクリーンマネージャのインスタンスに 各画面のインスタンスを登録し、そのインスタンスをリターンする。

KV言語で書く方法もあるけど、画面切り替えのときに使用する名前をここで定義できるので ソースの見通しが良くなって好み。

class ScreenManager1(App):
    def build(self):
        # スクリーンマネージャの生成
        sm = ControlScreenManager()
        # 画面1を追加
        sm.add_widget(Screen1(name='screen1'))
        # 画面2を追加
        sm.add_widget(Screen2(name='screen2'))
        
        return sm

screen1.py

レイアウトの定義

kvファイルにレイアウトを書く例が多いけど、こう書くとレイアウトと処理をまとめて書けるので好み。

[!NOTE] 最近知ったけど、色指定は(rr, gg, bb, aa)(各値は0~1)と書かれている例が多いけど、 色名(“black”とか”red”とか。指定できる色名は kivyインストールディレクトリのutil.pyで定義されているhex_colormapを参照) の他、”#RRGGBBAA”でも指定可能(AAは省略可能)。 どちらも文字列指定なのでダブルクォーテーションまたはシングルクォーテーションで囲む必要あり。

また、サイズ類はdp(36)みたいな書き方もできるけど、文字列で"36dp"と書くこともできる。 サイズ類は数値で指定すると単位はpxになる。

Builder.load_string(
"""
<Screen1>:
    ・・・・
            bg_color_normal : "#585858ff"       # "coloe_name" or "#RRGGBB" or "#RRGGBAA" or (rr, gg, bb, aa) で指定
    ・・・・
            item_height          : '36dp'   # 数値で指定したときの単位はpx
    ・・・・

[!NOTE] Androidアプリ化する場合、テキスト入力が下の方にあるとソフトキーボードが出てきたときに見えなくなるので 上の方に配置しておく方が無難。
なんかうまくやる方法があるのかもしれんけど、現状分かってない。

Screen派生クラスの定義

class Screen1(Screen):

あとはScreen1クラス内で動作を定義していけば良い。
画面切り替え処理はkv言語で直接スクリーンマネージャの処理をコールすると訳わかめになるので 一旦このクラス内で受け取ってスクリーンマネージャの処理をコールするようにしている。
この辺は好みの問題なので、お好きにどうぞ。

    def switch_to_screen1(self):
        print('switch_to_screen1')
        self.manager.switch_to_screen1()

[!NOTE] スクリーンマネージャはScreen派生クラスのself.managerで取得できる。

screen2.py

こちらも同様にレイアウトを定義しておく。

Builder.load_string(
"""
<Screen2>:
    ・・・・

Screen派生クラスの定義

class Screen2(Screen):

画面が切り替わる前にScreen1で設定した文字列を取得したいので、on_pre_enter()をオーバーライドする。
文字列をScreen1から直接取ると画面構成変えた時に困るので、スクリーンマネージャ経由で取得するようにしている。

    def on_pre_enter(self, *args):
        # 表示用ラベルを書き換え
        text = self.manager.get_target_string()
        if len(text) == 0 :
            text = "NONE"       # 空文字が来たらNONEに書き換え
        self.label.text = f'{text} が選択されました'

実行

python multi_screen.pyで実行。
一番上のドロップダウンリストで項目を選択し、2番目の「Go to Screen 2」ボタンを押すとScreen2に切り替わる。
Screen2では画面下側の領域にScreen1のドロップダウンリストで選択した文字列が表示される。 「Go to Screen 1」ボタンでScreen1に戻る。
Screen1のテキスト入力欄に文字列を入力し、「ADD」ボタンをクリックするとドロップダウンリストに入力した文字列が追加される。
もちろん、その文字列を選択してScreen2に切り替えればその文字列が表示される。

[!NOTE] Lunuxでは日本語入力できないみたい。ただしコピペは可能なので他のところで入力してコピペすればOK。
Androidではそのまま日本語も入力できる。

Androidアプリ化

Buildozerをお試しの手順でAndroidアプリ化もできる。
ここの準備が終わっていれば以下のコマンドでOK。

multi_screen.pyをリネーム

python for androidはエントリーポイントがmain.pyに固定らしいので、リネーム

mv multi_screen.py main.py

buildozer.spec の生成

buildozer init

今回は特に編集の必要なし。 titlepackage.namepackage.domainなんかは必要なら変更してちょ。

build実行

buildozer -v android debug 2>&1 | tee mk.log

adbサーバの起動

Windows側(コマンドプロンプト等)から以下を実行(サーバを起動する)

cd ≪SDK Platform-Tools for Windowsを展開したディレクトリ≫\platform-tools
adb.exe devices -l

実行

で、実行

buildozer -v android deploy run logcat 2>&1 | tee run.log

で、Android上で動作した。メデタシメデタシ。
文字入力もちゃんと動いてるし、改造したボタンやスピナーも動いてる。