Kivyとbuildozerでクロスプラットフォームアプリをつくってみた。
この記事は SLP KBIT Advent Calendar 2019 12日目の記事です。
Windows、Linux、Androidなどで動く簡単なアプリを作りました。ボタンを押すと、入力した文字が反映される程度のものです。
お品書き
Kivy、buildozerとは
Kivy とは、PythonでクロスプラットフォームGUIアプリケーションを作ることができるオープンソースライブラリ。画面の大きさが変わってもレイアウトが崩れない所がすごい!
buildozer とは、Kivyで作ったアプリをAndroidなどで動くアプリに変換するツール。
Windowsで作ったアプリをLinux、さらにはAndroidでも動かすことができるなんて素晴らしいですね。
[注]: MacやiOSについては今回取り上げませんが、同様にできるかと思います。
実行環境
Kivyでの開発はWindowsでもLinuxでもできますが、buildozerはLinuxでしか使えないので、Androidアプリまでやりたい方はLinuxで構築することをおすすめします。
実行環境は以下の通り。
Windows10 └ Ubuntu (VM) └ Python3.6.9 (└ venv) ├ Kivy 1.11.1 └ buildozer 0.40.dev0
buildozerは仮想環境ではうまく動かないので、その時だけは仮想なしで実行します。しかし、Kivyの依存関係がややこしいので、buildozerを使う方も使わない方も、まずは仮想環境上で構築しましょう。
Kivyを使う
環境構築
Windows
公式ページに沿って行いました。
Windowsはそれで動かせると思います。
kivyについて調べていると、pygameとcythonを入れている場合が見られたのですがなぜでしょう…依存関係がちょうどいいんですかね。それはともかく、公式にのっとってインストールでOK。
Linux
Linuxは依存関係を揃えるため先に依存関係云々について色々入れます。
VMで新しくマシンを動かしたのですが、最終的にストレージが25GB近くまで膨れ上がったので、buildozerを使うならデフォルトの20GBから増やしておきましょう。
まずは翻訳記事を参考に以下のようにインストール。
$ sudo apt update $ sudo apt upgrade $ sudo apt install -y \ build-essential \ git \ python3 \ python3-dev \ python3-venv \ ffmpeg \ libsdl2-dev \ libsdl2-image-dev \ libsdl2-mixer-dev \ libsdl2-ttf-dev \ libportmidi-dev \ libswscale-dev \ libavformat-dev \ libavcodec-dev \ zlib1g-dev
このpython3
やpython3-dev
などについては、python3.7-venv
のようにマイナーバージョンまで指定ができます。ただし、python3.8以上とpython3.4(もしくは3.5)以下にも未対応だったかと思います。
python-pip
については、venv上でpipが使えるようになるのと、pipをupgradeするとエラーが起きる問題があるので入れません。
最後に
$ pip install cython kivy
これでKivyのインストールは終了です。
実行
今回作ったのはこちら。
ボタン・画面遷移・入力・アクティブバーからなります。
main.py
from kivy.app import App from kivy.uix.widget import Widget from kivy.uix.boxlayout import BoxLayout from kivy.properties import StringProperty, ListProperty, ObjectProperty from kivy.uix.label import Label from kivy.uix.floatlayout import FloatLayout from kivy.uix.textinput import TextInput from kivy.uix.screenmanager import ScreenManager, Screen, FadeTransition from kivy.lang import Builder from kivy.config import Config Config.set('graphics', 'multisamples', '0') # Config.set('graphics', 'width', '640') # Config.set('graphics', 'height', '480') class RootWidget(ScreenManager): pass class Testwidget(Screen): text = StringProperty() label1 = ObjectProperty() def __init__(self, **kwargs): super(Testwidget, self).__init__(**kwargs) self.text = 'start' def buttonClicked(self): self.text = 'U' def buttonClicked2(self): self.text = 'got' def buttonClicked3(self): pass class SecSc(Screen): l0 = ObjectProperty(None) t0 = ObjectProperty(None) def txtmodify(self): self.ids.l0.text = self.ids.t0.text kv = Builder.load_file("test.kv") class TestApp(App): def __init__(self, **kwargs): super(TestApp, self).__init__(**kwargs) self.title = "Testing..." def build(self): return kv if __name__ == "__main__": TestApp().run()
test.kv
#:kivy 1.11.1 #: import FadeTransition kivy.uix.screenmanager.FadeTransition RootWidget: transition: FadeTransition() Testwidget: SecSc: <Testwidget@BoxLayout>: name: "tstw" label1: label1 BoxLayout: orientation: 'vertical' size: root.size ActionBar: ActionView: ActionPrevious: title: 'kivyapp' with_previous: False ActionButton: text: 'I am just a Button.' Label: id: label1 size_hint_y: 1 font_size: 35 text: root.text Button: id: button1 font_size: 35 text: "01" on_press: root.buttonClicked() Button: id: button2 font_size: 35 text: "02" on_press: root.buttonClicked2() Button: id: button3 font_size: 35 text: "03" on_press: root.text = 'that' root.manager.get_screen("inputscr").l0.text = root.label1.text app.root.current = "inputscr" <SecSc>: name: "inputscr" l0: l0 t0: t0 BoxLayout: orientation: 'vertical' size: root.size ActionBar: size_hint_y: 0.1 ActionView: ActionPrevious: title: 'return' with_previous: True on_release: app.root.current = "tstw" ActionButton: text: 'No response. Looks dead.' BoxLayout: TextInput: id: t0 multiline: True text: "I like you" Label: id: l0 text: '' Button: id: b0 text: "modify!" on_release: root.txtmodify()
main.py と kvファイルを同じディレクトリに入れて
python main.py
で起動できます。
解説
Linuxではこのように
from kivy.config import Config Config.set('graphics', 'multisamples', '0')
としなければエラーでVM自体が落ちました。
WindowsはなくてもOKです。コメントアウトしているようにサイズ指定しても問題なく起動できます。
これで土台を作ります。
class RootWidget(ScreenManager): pass
このようにスクリーンを追加します。こうすると画面遷移が簡単にできます。
class Testwidget(Screen): # 省略 class SecSc(Screen): # 省略
この部分ですが、def build(self)
の中で1つだけreturn
しなければなりません。
kv = Builder.load_file("test.kv") class TestApp(App): def __init__(self, **kwargs): super(TestApp, self).__init__(**kwargs) self.title = "Testing..." def build(self): return kv
返す内容はreturn Testwidget()
のように[class名]()
でもいいですが、画面遷移を使うとどうしてもクラスが2つ以上になってしまいます。そこで、Builder.load_file("test.kv")
のように読み込んだkvファイルを返すとうまくいきました。
test.kvについて
main.pyでのクラス名をもとに書いていきます。
土台となるRootWidget
の下に<Testwidget>
などを配置しています。
RootWidget: transition: FadeTransition() Testwidget: SecSc: <Testwidget@BoxLayout>: name: "tstw" label1: label1 BoxLayout: orientation: 'vertical'
BoxLayoutは、画面をウィジェットの数だけ分割します。
デフォルトでは横で、縦分割にするにはorientation: 'vertical'
と書きます。
このmain.pyですが、ObjectProperty
によってkvファイルでもl0
やt0
を使えるようにしています。
class SecSc(Screen): l0 = ObjectProperty(None) t0 = ObjectProperty(None) def txtmodify(self): self.ids.l0.text = self.ids.t0.text # python側では self.ids.[id] で指定idの操作ができる
合わせてkvファイルにはこのように、id: id
と書くことでうまくいきました。
<SecSc>: name: "inputscr" l0: l0 t0: t0
その他諸々kv言語については、以下のような詳しい記事を見てくださればわかりやすいと思います。
buildozerを使う
作ったアプリをAndroidでも動かしてみます。
主な流れはこの記事を参考にさせていただきました。ありがとうございます。
環境構築
VM上のUbuntuにて。
以下の記事のように、python
のリンクを標準のpython2からpython3へ張り直します。
https://toc.tocu.co.jp/blog/tips/item/2129
$ cd /usr/bin/ $ cp python python-bk # バックアップをとる $ unlink ./python $ ln -s /usr/bin/python3.X ./python # 任意のバージョンにリンクを貼り直す $ python -V # 確認してOKならバックアップを消す
次に、aptでpipを入れてpip install -U pip
するとエラーが出るので
$ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py $ sudo python get-pip.py
で大丈夫かと思います。 次に、必要なライブラリを入れます。
$ sudo pip install -U pip $ sudo pip install -U setuptools $ sudo pip install cython kivy
$ sudo dpkg --add-architecture i386 $ sudo apt update $ sudo apt install -y git zip unzip openjdk-8-jdk autoconf libtool pkg-config libncurses5-dev libncursesw5-dev libtinfo5 cmake ccache python2.7 python2.7-dev libncurses5:i386 libstdc++6:i386 zlib1g:i386 ant automake libltdl-dev aidl lld libffi-dev
buildozer(開発版)のインストール。
$ sudo pip install git+http://github.com/kivy/buildozer
CrystaX NDKのインストール。
https://www.crystax.net/en/downloadここからcrystax-ndk-10.3.2-linux-x86_64.tar.xzをDL、解凍します。
Javaの設定を変更してあげます。
$ sudo update-alternatives --config java
で、Java 8 を選択。
また、以下でJAVA_HOMEとPATHを設定します。
$ vi ~/bashrc # 以下を追記する export PATH=$PATH:~/.local/bin/ export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
以上で事前準備は終了です。
実行
まずは設定ファイルを書き換えましょう。
main.pyのあるディレクトリが作業ディレクトリになります。
$ cd /path/to/directory/ $ buildozer init
これで、buildozer.spec
が生成されますので、編集します。
- package.name は 作業ディレクトリ名。
- package.domain は デフォルト以外に変更したほうがよさそう。
- requirements は python3, kivy, 他importしてるやつ。pythonのマイナーバージョンは指定できないのでそのままにしておきます。
android.sdk = 24
にします。android.ndk_path = /path/to/crystax-ndk-10.3.2/
のようにCrystaxの解凍先を指定しておきます。- android.arch は 使うandroidのアーキテクチャを指定します。Xperia XZなら、Snapdragon 820 MSM8996 なので、armeabi-v7aだと確認できます。
さて、設定ファイルを編集したら、コピーして別の場所に保存しておきましょう。作成が失敗したときに書き直すのは面倒なので。
いよいよ実行です。
USBデバッグをオンにしたandroidをPCに接続します。
接続が確認できたら、次のコマンドを打ちapkファイルを作成・実行しましょう!
$ buildozer android debug deploy run
私の環境(Core i5 第5世代・70Mbps前後)では15分ぐらいかかりました。コーヒーでも飲んで待ちましょー。
作成中は、ずらーっとログが出てくるのですが、途中で
[WARNING]: Doing some hacky stuff to link properly
と出たり、3つぐらいエラーが出ますが、処理が止まらない限りは大丈夫です。
うまく生成できなかったら、ログを読んで適宜修正します。
感想
Kivyは奥深いですね。まだ全然理解できていないので精進しようと思います。
C#やJava、Swiftなどを学ばなくても、pythonのみで複数OS上で動くアプリを作れる手軽さは本当にすごい。
ただ、日本語の情報は多いとは言えないので、エラーにぶつかったときは英語のwebページから参考にしないといけないかもしれません。翻訳サイトにはくそお世話になりました。
参考サイト
- Installation on Linux
- invasivecat.hatenablog.com/entry/2019/01/06/221322
- https://buildozer.readthedocs.io/en/latest/installation.html
- https://python-for-android.readthedocs.io/en/latest/quickstart/
- https://qiita.com/t2hk/items/3b1b18d51db6274fd864
- https://qiita.com/t2hk/items/3b1b18d51db6274fd864
…And more!