atlasを使って画像表示 / ドラッグで移動可能なウィジェット - Kivy Advent Calendar 2013
何となくカードゲームを作ってみたかったので、カードの画像が1枚に収まってる素材を探してきて、それをatlasで表示してみます。
カードはドラッグで移動させたいので、Scatterをあわせてみました。
ランダムにばらまいてみました。まだ全然ゲームにはなってません... いや神経衰弱はできるかな?
(/sdcard/kivy/cards/)
trump.gif | カードの素材 (http://sozai.7gates.net/docs/trump/) |
cards.atlas | 素材の座標位置を示すファイル |
main.py |
(cards.atlas)
{ "trump.gif":{ "SA":[ 0,540,60,90], "HA":[ 60,540,60,90], "DA":[120,540,60,90], "CA":[180,540,60,90], "S2":[ 0,450,60,90], "H2":[ 60,450,60,90], "D2":[120,450,60,90], "C2":[180,450,60,90], "S3":[ 0,360,60,90], "H3":[ 60,360,60,90], "D3":[120,360,60,90], "C3":[180,360,60,90], "S4":[ 0,270,60,90], "H4":[ 60,270,60,90], "D4":[120,270,60,90], "C4":[180,270,60,90], "S5":[ 0,180,60,90], "H5":[ 60,180,60,90], "D5":[120,180,60,90], "C5":[180,180,60,90], "S6":[ 0, 90,60,90], "H6":[ 60, 90,60,90], "D6":[120, 90,60,90], "C6":[180, 90,60,90], "S7":[ 0, 0,60,90], "H7":[ 60, 0,60,90], "D7":[120, 0,60,90], "C7":[180, 0,60,90], "S8":[240,540,60,90], "H8":[300,540,60,90], "D8":[360,540,60,90], "C8":[420,540,60,90], "S9":[240,450,60,90], "H9":[300,450,60,90], "D9":[360,450,60,90], "C9":[420,450,60,90], "ST":[240,360,60,90], "HT":[300,360,60,90], "DT":[360,360,60,90], "CT":[420,360,60,90], "SJ":[240,270,60,90], "HJ":[300,270,60,90], "DJ":[360,270,60,90], "CJ":[420,270,60,90], "SQ":[240,180,60,90], "HQ":[300,180,60,90], "DQ":[360,180,60,90], "CQ":[420,180,60,90], "SK":[240, 90,60,90], "HK":[300, 90,60,90], "DK":[360, 90,60,90], "CK":[420, 90,60,90], "J1":[240, 0,60,90], "J2":[300, 0,60,90], "N": [360, 0,60,90] } }
(main.py)
from kivy.app import App from kivy.uix.relativelayout import RelativeLayout from kivy.uix.scatter import Scatter from random import randint from kivy.lang import Builder Builder.load_string('''\: canvas.before: Color: rgb: 0, 0.3, 0.1 Rectangle: pos: self.pos size: self.size : suit: 'N' rank: '' face: _face do_rotation: True do_scale: False auto_bring_to_front: True do_collide_after_children: True size: (120, 150) size_hint: (None, None) ToggleButton: id: _face size: (60, 90) size_hint: (None, None) background_normal: 'atlas://cards/N' background_down: 'atlas://cards/%s%s' % (self.parent.suit, self.parent.rank) ''') class CardTable(RelativeLayout): pass class Card(Scatter): def on_touch_up(self, touch): if self.collide_point(*touch.pos): self.face.state = 'down' if self.face.state == 'normal' else 'normal' class CardTableApp(App): def build(self): self.table = CardTable() for suit in list('SHDC'): for rank in list('A23456789TJQK'): card = Card() card.suit = suit card.rank = rank card.pos = (randint(50, 1150), randint(50, 1870)) self.table.add_widget(card) return self.table if __name__ == '__main__': CardTableApp().run()
追記
- いろいろ調整してはいますが、さすがに小サイズのウィジェットに「カードへのタッチで開く/閉じるをトグルにする」と「ドラッグして動かす」の両方を持たせるのは無理がありました。本当にゲームを作るときは別のUIに出来ないか検討してみます...
- 画面サイズを決めうちにしているところがあります。kivy.core.windowで取れるはずなので、後で修正すると思います...
別のアプリを使ってから復帰する - Kivy Advent Calendar 2013
KivyLauncherでテキスト入力だけのアプリを作り、何か入力してから別のアプリに切り替えてみます。しかしその後KivyLauncherに戻ってもランチャ画面になってしまい、入力した内容は失われてしまいます。
(main.py)
from kivy.app import App from kivy.uix.textinput import TextInput class Memo(TextInput): pass class MemoApp(App): def build(self): self.memo = Memo() return self.memo if __name__ == '__main__': MemoApp().run()
モバイルOSのアプリ切り替えは必ずしもメモリ上に残ることを保証していないため、アプリが自主的に状態を保存する必要があります。Kivyアプリが状態保存に対応していない場合は、(menuボタン等による)アプリ切り替えも(home/backボタンによる)終了と同様に終了処理が呼び出されます。KivyLauncher自体はメモリに残っていたとしても、すでにKivyアプリは終了しているため復帰が行われてもランチャ画面に戻ってしまいます。
Kivyアプリに復帰できるようにするにはon_pause、on_resumeメソッドを用意します。
on_pauseが最後にTrueを返すと、別のアプリに切り替える際に終了処理を呼び出さなくなるので、KivyLauncherがメモリ上に残っていればそのKivyアプリにすぐ復帰します。またOSがKivyLauncherのメモリを解放していた場合でも、ファイルに保存した状態をon_resumeでメモリに呼び戻して同じように復帰できます。
(main.py)
from kivy.app import App from kivy.uix.textinput import TextInput class Memo(TextInput): pass class MemoApp(App): def on_pause(self): with open('memo.txt', 'w') as f: f.write(self.memo.text) return True def on_resume(self): with open('memo.txt') as f: self.memo.text = f.read() def build(self): self.memo = Memo() return self.memo if __name__ == '__main__': MemoApp().run()
追記
さすがにbackボタンで終了した場合は内容はクリアされます (たぶんon_stopにフックをかければ保存対応可能かと思いますが...)
画面を転換する - Kivy Advent Calendar 2013
5日目にCarouselを使いましたが、これはユーザのスライド操作で複数のウィジェットを見せていくことができます。これに対しScreenManagerは、それ自体はスライド操作を受け付けませんが、フェードイン、ワイプなどのさまざまな効果を持たせた画面転換ができます。
(/sdcard/kivy/screen/)
android.txt |
main.py |
(android.txt)
title=screen author=cheeseshop orientation=portrait
(main.py)
from kivy.app import App from kivy.properties import StringProperty from kivy.uix.screenmanager import ScreenManager, Screen from kivy.uix.screenmanager import ShaderTransition, SlideTransition, FadeTransition, WipeTransition FADEINOUT_TRANSITION_FS = '''$HEADER$ uniform float t; uniform sampler2D tex_in; uniform sampler2D tex_out; void main(void) { float t1 = 0.45; float t2 = 0.55; vec4 cin = vec4(texture2D(tex_in, tex_coord0.st)); vec4 cout = vec4(texture2D(tex_out, tex_coord0.st)); vec4 frag_col = t < t1 ? vec4*1 on_text: app.settr(self.text) Button: pos_hint: {'center_x':0.5, 'top':0.2} size_hint: (None, None) size: (200, 50) text: 'Back to home' on_press: root.manager.transition = app.tr1; root.manager.current = 'home' ''') class FadeInOutTransition(ShaderTransition): fs = StringProperty(FADEINOUT_TRANSITION_FS) class HomeScreen(Screen): pass class SettingScreen(Screen): pass class ScreenApp(App): trs = { 'Slide_H': (SlideTransition(direction='right'), SlideTransition(direction='left')), 'Slide_V': (SlideTransition(direction='up'), SlideTransition(direction='down')), 'Swap': SwapTransition(), 'Fade': FadeTransition(), 'Wipe': WipeTransition(), 'FadeInOut': FadeInOutTransition(), } def settr(self, tr): trs = self.trs[tr] if isinstance(trs, tuple): self.tr0 = trs[0] self.tr1 = trs[1] else: self.tr0 = trs self.tr1 = trs def build(self): self.sm = ScreenManager() self.sm.add_widget(HomeScreen(name='home')) self.sm.add_widget(SettingScreen(name='setting')) self.settr('Fade') return self.sm if __name__ == '__main__': ScreenApp().run()
解説
このサンプルでは、「home」と「setting」の2つの画面間の移動をボタンで行います。setting画面には、6種類の画面効果を切り替えるオプションを用意しています。
まずScreenManagerを作り、そこに複数の画面を追加(add_widget)しておきます。transitionプロパティで効果を指定してから、currentプロパティに画面のnameを指定することで転換が行われます。
6種類のうち5つは組み込みで持っている画面の転換方法です。
transition | arg | |
---|---|---|
SlideTransition | 'right' 'left' | 水平方向にスライドして切り替える |
SlideTransition | 'up' 'down' | 垂直方向にスライドして切り替える |
SwapTransition | 前後を入れ替えるようにして切り替える | |
FadeTransition | じわじわと切り替える | |
WipeTransition | ワイプして切り替える |
最後の1つ「FadeInOut」はこのスクリプトの中で作成したオリジナルの画面効果です。
FadeInOutTransition | フェードアウトしてからフェードイン |
Pythonスクリプトなのに「void main(vold) {...}」とかC言語っぽい文字列がありますが、これがシェーダ効果を記述する言語(GLSL)です。ShaderTransitionのプロパティにセットするとOpenGL APIを通じて自動的にコンパイルされ、画面効果として使えるようになります。
追記
このサンプルでは使っていませんが、Kivy 1.8からは次の効果も使えるようです。
FallOutTransition | 中心へ古い画面が落ち込んでいく |
RiseInTransition | 中心から新しい画面が広がってくる |
*1:1.0 - t / t1) * cout) :
t > t2 ? vec4((t - t2) / (1.0 - t2) * cin) : vec4(0.0);
gl_FragColor = frag_col;
}
'''
from kivy.lang import Builder
Builder.load_string('''
ポップアップ - Kivy Advent Calendar 2013
(※ビルドの話をしましたが、再びKivyLauncherで出来る範囲での話に戻ります)
5日目の「スライドっぽい...」でも使ったので、今回はポップアップについて解説します。
Kivyの中で、ポップアップに当たるものは次のようなものがあります。
Popup | タイトルバーあり / 背景が暗くなる |
ModalView | タイトルバーなし / 背景が暗くなる |
Bubble | タイトルバーなし / ふきだし (背景は暗くならない) |
元のウィンドウの上にオーバラップして表示され、ヘルプ・エラー・進捗を示すメッセージウィンドウとして、あるいはさまざまな選択を要求するメニューやダイアログ画面として使われます。いずれもイベント処理を追加しなければドラッグで動いたりはせず、ただ表示するだけです。
PopupとModalViewの違いは、タイトルバーがあるかないかだけです。これらは単体でもopen/dismissで表示・非表示ができます。attach_toで親ウィンドウを示すこともできますが、デフォルトで元のウィンドウが暗くなってしまうので、設定画面やウィザードのように、大きく画面を占める使い方が主で、ポップアップメニューのような使い方には適さないと思います(background_colorをアルファ値0にすれば背景を暗くさせないことも可能ですが)。
一方Bubbleは下のウィジェットを暗くすることはせず、またふきだしの向きをつけられるので「ウィジェットに対するアクション選択肢」を示すポップアップメニューとして適したものです。その代わり下のウィジェットが隠れてしまうほど大きく画面を占める使い方には向きません。
Bubbleの代表的なものが、TextInputでテキストがあるところを長押しして表示される「Cut-Copy-Paste」の3ボタンのポップアップです。Bubble自体に表示や非表示にするメソッドはなく、元となるウィジェットへadd_widget、remove_widgetして表示、非表示としていて、ウィジェットへのタップをきっかけに選択肢を開くといった形になります。
以下はBubbleのサンプルで、タッチした座標を出しています。
(/sdcard/kivy/bubbles/)
android.txt |
main.py |
(main.py)
from kivy.app import App from kivy.uix.floatlayout import FloatLayout from kivy.uix.bubble import Bubble from kivy.lang import Builder Builder.load_string(''': canvas.before: Color: rgb: 0.0, 0.2, 0.2, 1 Rectangle: pos: self.pos size: self.size : label: _label arrow_pos: 'bottom_left' size_hint: (None, None) size: (200, 50) Label: id: _label ''') class Result(Bubble): def __init__(self, **kwargs): super(Result, self).__init__(**kwargs) self.label.text = kwargs['text'] class Bubbles(FloatLayout): def on_touch_up(self, touch): result = Result(text='(%d, %d)'%touch.pos, pos=touch.pos) self.add_widget(result) class BubblesApp(App): def build(self): return Bubbles() if __name__ == '__main__': BubblesApp().run()
Python for Androidのビルド (2) - Kivy Advent Calendar 2013
VMも動いたのでいよいよビルド作業に入りますが、その前にやっておく作業があります。
ビルド環境の修正
基本的にこのVMイメージは初期状態でPython for Androidのビルドに必要なコマンドはすべてインストールされているため、apt-getやyumで環境構築する必要はありません。
ただ、現在(2013年11月)配布されているイメージは次の問題があり対応が必要になります。
(1) python-for-androidの再取得
ディスクイメージ内に配置してある /home/kivy/android/python-for-android/ は、パス名の問題でPython-2.7.2のコンパイルに失敗します。これはあらためてgithubから最新版を取得・展開すれば解決します。
$ cd python-for-android $ git pull
(2) cythonのアップグレード
Cythonの処理中に「構文が違う」というエラーを返すことになりますので、pipを使って最新版にしておきます。
$ sudo pip install --upgrade cython
Python for Androidのビルド
/home/kivy/android/python-for-android/ に移動して distribute.sh を実行します。
$ ./distribute.sh -m "sqlite3 openssl pyopenssl lxml audiostream cymunk ffmpeg pil pyjnius twisted kivy"
必要なレシピがあればここで追加しておきます。上のコマンドラインはKivyLauncher 1.7.2と同じもので、最終的なAPKのサイズは12〜13MBくらいになります。
レシピの中には現在ビルドできないものもあるようです。この場合 dist/default/ フォルダがの中にファイルが1つもない状態で終了します。問題のレシピを取り除いて再度distribute.shを実行するときは dist/default/ フォルダを一旦削除する必要があります。
distribute.shによってPythonと各種ライブラリがAndroid向け実行ファイルとしてビルドされれば、次はAPKパッケージを作る準備段階になります。念のため次のファイルが作成されていることを確認してください。
(src/ 以下)
- default.properties
- local.properties
- objs/local/armeabi/*.a
- objs/local/armeabi/*.so
- objs/local/armeabi/objs/**
- libs/armeabi/*.so
(dist/default/ 以下)
- build.py
APKパッケージの作成
dist/default/ に移動して build.py を実行します。
$ ./build.py \ --name "Kivy Launcher" \ --package org.kivy.pygame \ --version 1.7.2.1 \ --launcher \ --icon templates/launcher-icon.png \ --presplash templates/launcher-presplash.jpg \ --permission INTERNET \ --permission BLUETOOTH \ release
dist/default/bin/ の下に次のようなファイル名でAPKファイルが作成されます。
KivyLauncher-1.7.2.1-release-unsigned.apk
これをAndroid端末のDownloadsなどのフォルダに送って、ファイラなどで実行するとインストールが始まります。
(あらかじめ「提供元不明のアプリのインストール」を許可しておいてください)
Python for Androidのビルド (1) - Kivy Advent Calendar 2013
(※Python for Androidは後半の記事ですが、時間切れのため先行して公開します)
KivyLauncherではopensslやPILなどのモジュールが導入されていて、せいぜいWebサーバと連携して何かやる程度の軽いアプリならあまり困らないのですが、それでも自分でモジュールをインストールしたいなということはあります。
でもAndroidの保護機構のため、Google Playからインストールしたリリース版のKivyLauncherにはライブラリインストールはできません。また /sdcard/kivy/(プロジェクトフォルダ)/ の下に置けばimportで取り込む場合もPythonで記述されたライブラリに限られます。
「Python for Android」というのは文字通りAndroid上で動くPythonをコンパイルするためのキットで、ライブラリの組み込みやAndroidのインストーラパッケージ (APK) を作るツールも含まれています。このビルドオプションに複数アプリを切り替えるランチャーを組み込むものがあり、それをセットしてビルドしたものがKivyLauncherとして公開されているわけです。
KivyLauncherをビルドし直せば次のことが可能になります。
Kivyサイトでは、Android SDK/NDKやビルドに必要なツールをインストールしたLinuxベースの開発環境イメージを配布しています。既存のLinux環境に構築することもできますが、ここではこの開発環境イメージを使って動かすことにします。
Oracle VM VirtualBoxのインストール
Linux、Windows、Mac OS X、Solarisなど、VirtualBoxが動くマシンだったら、環境のセットアップを飛ばして手っ取り早くビルドできるのでオススメです。
まずはVirtualBoxのインストールから。プラットフォームにあったパッケージをダウンロードしてインストールします。
https://www.virtualbox.org/wiki/Downloads
VMの作成
次にVMのイメージを入手します。
http://kivy.org/#download
KivyサイトのVirtual Machinesの項目にある「Kivy Python for android VM」(Googleドライブで公開)をダウンロードします。7-zipという日本ではややマイナーな圧縮形式ですが、展開ツールはすぐ見つかると思います。
ユーザディレクトリ下など適当な場所に展開したら、Oracle VM VirtualBoxを実行して
でセットアップは完了です。
Ubuntuの操作
VMの起動を行うとUbuntuのKDEデスクトップが立ち上がります。
ここから次のようにテキスト端末を開いて、kivyユーザとしてログインします。このVMでのkivyユーザの初期パスワードは「kivy123」で設定されています。
- Ctrl+Alt+F1を押せば全画面のCUIコンソールになり、ログイン待ちになる
- 別のコンソールが必要なら、Ctrl+Alt+F2 〜 Ctrl+Alt+F6 で立ち上がる
- Ctrl+Alt+F7でKDEのデスクトップに戻る
ビルド作業はCUIかターミナルのどちらかで行うことになります。まあ各々好みがあるので問いませんが、速度的にいえばCUIの方がお薦めです。
共有フォルダの設定
ソースファイルや作成されたAPKパッケージなど、VMとホスト機の間でファイルのやり取りができるよう共有フォルダを作っておくといいでしょう。
まずホスト機の適当な場所に共有フォルダshareを作り、VirtualBoxマネージャの「フォルダ共有」でそのフォルダを設定します。
次にゲストOSでmount操作を行えば、shareフォルダを通じてファイルのやり取りができるようになります。
$ mkdir share $ sudo mount -t vboxsf share share
【修正】スライドっぽいものを作る - Kivy Advent Calendar 2013
Carouselというウィジェットは、ドラッグしてウィジェットを次々と表示できます。すでに2日目 (Tofu issue) で何の解説もなく使ってますが...
これとRstDocumentを組み合わせればスライドっぽいものができますね。
ということで作ってみました。横方向にスライドすれば次の画面が出てきます。
(一部白い斑点がありますが、これはAndroidの開発者オプションでタップ位置を表示しているものです)
(/sdcard/kivy/slides/)
docutils.zip | docutils-*.tar.gzの中の「docutils」ディレクトリをZIP圧縮したもの |
android.txt | Androidの場合必要 |
fonts_ja.py | 2日目「Tofu issue」のfonts_ja.pyと同じ |
main.py | |
slides.kv | |
title.txt | スライド表紙 |
page01.rst | 1ページ目 |
page02.rst | 2ページ目 |
page03.rst | 3ページ目 |
(android.txt)
title=slides author=cheeseshop orientation=landscape
(main.py)
# -*- coding: utf-8 -*- import os, sys sys.path.insert(0, os.path.join(os.getcwd(), 'docutils.zip')) from kivy.app import App from kivy.uix.carousel import Carousel from kivy.uix.label import Label from kivy.uix.rst import RstDocument from kivy.uix.textinput import TextInput from kivy.uix.popup import Popup import codecs import fonts_ja class TLabel(Label): def __init__(self, **kwargs): if kwargs.has_key('source'): kwargs['text'] = codecs.open(kwargs['source'], 'r', 'utf-8').read() del kwargs['source'] super(TLabel, self).__init__(**kwargs) class Rst(RstDocument): source_encoding = 'utf-8' def on_touch_up(self, touch): if not self.parent.current_slide is self: return if touch.is_double_tap: popup = Popup( title='Source', content=TextInput(text=self.text), auto_dismiss=True, size_hint=(0.8, 0.8)) popup.open() def __init__(self, **kwargs): if kwargs.has_key('source'): kwargs['text'] = codecs.open(kwargs['source'], 'r', 'utf-8').read() del kwargs['source'] super(Rst, self).__init__(**kwargs) class SlidesApp(App): def build(self): carousel = Carousel(direction='right') carousel.add_widget(TLabel(source='title.txt')) carousel.add_widget(Rst(source='page01.rst')) carousel.add_widget(Rst(source='page02.rst')) carousel.add_widget(Rst(source='page03.rst')) return carousel if __name__ == '__main__': SlidesApp().run()
(slides.kv)
: font_size: '20pt' halign: 'center' valign: 'middle' color: 0.2, 0.2, 0.0, 1 canvas.before: Color: rgb: 0.9, 0.9, 0.8 Rectangle: pos: self.pos size: self.size : font_size: '28pt' halign: 'center' valign: 'middle' color: 0.2, 0.2, 0.0, 1 : cols: 1 content: content size_hint_y: None height: content.texture_size[1] + 30 canvas: Color: rgb: parse_color('#cccccc') Rectangle: pos: self.x - 1, self.y - 1 size: self.width + 2, self.height + 2 Color: rgb: parse_color('#eeeeee') Rectangle: pos: self.pos size: self.size Label: id: content markup: True text_size: self.width - 10, None font_name: '/system/fonts/DroidSansFallback.ttf' color: (0, 0, 0, 1)
(title.txt)
Kivy / Python for Androidでプレゼンツール 2013.12.05 http://d.hatena.ne.jp/cheeseshop/
(page01.rst)
================================================ CarouselとScreenManagerの違い ================================================ Carousel
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
(page02.rst)
================================================ このプレゼンツールのポイント ================================================ Tofu issue + 等幅フォントのところだけ化けるのに対応
(page03.rst)
================================================ まとめ ================================================ .. image:: .kivy/icon/kivy-icon-128.png
解説
docutils.zip
2日目でRstDocument使ったときは「docutilsをそのままプロジェクトにつっこんで」とか書きましたが、1MB以上あるんですね。Androidへの転送は面倒だし。
今回はライブラリ本体だけZIP圧縮しておいています。Pythonはこの手が使えるので便利ですね。
ただこれでも480KBくらいありますので、他のプロジェクトと共通で使うのなら、KivyLauncherをビルドしてdocutilsを最初から入れていた方がいいかもしれません。
source引数
TLabelとRstでsourceにファイル名を入れていますが、なぜかその場でファイルを読んでtextに入れ直しています。
これはRstDocumentウィジェットは「source_encoding='utf-8'」としているのに、全然設定が渡されておらず、docutilsは「input_encoding='ascii'」でrstファイルを読もうとしてUnicodeエラーに。
いろいろ試行錯誤しましたが時間切れのため、codecs.openで読み込んでtextにUnicode文字列をつっこむ方法を取っています(Unicode文字列を渡せば正しくレンダリングするのですが)。
ひょっとしたらSphinxの日本語パッチか何かで、スマートな解決方法をやっているのかもしれません。
ダブルタップ
ページをダブルタップをするとTextInputが開き、reSTソースが表示されます(グレーアウトした背景をタップすれば閉じます)。
本当は「プレゼン中にその場で編集→即時更新」なんてかっこいいこと考えたのですが、これも時間切れ。今はただ表示するだけです。
ちなみに最後のreturn Trueを忘れると挙動がおかしくなるようです。ダブルタップのイベントが他のページにも渡されてしまうのかもしれません。
(追記 2013-12-13) ごめんなさい。イベント処理についての知識が足りてなかったです。この件について後で解説します。
(追記 2013-12-21) http://d.hatena.ne.jp/cheeseshop/20131221 で解説を書きました
(追記 2013-12-21) 上のmain.pyを修正しました。今はスライド自身のreSTソースが表示されているはずです。
追記
あーやっと理解しました。
masterブランチでは直っていますが、KivyLaucher 1.7.2はdocutilsにエンコーディングを渡してないです(preloadメソッドにencoding引数がない!)
この問題も1.8.0リリース待ちですかね...
https://twitter.com/mathieuvirbel/status/407950627046948864
昨日、作者さんが「うまくいけば今週中にKivy 1.8出すよ、しかもPython 3コンパチだ」とか呟いてるし。
期待していいんだね? Python for Androidだけ遅れるとかないよね?