QRコード作成 - Kivy Advent Calendar 2013
Python for Androidの中の「recipes」フォルダには、ビルドして組み込むためのモジュールが多数収められていますが、なぜかこの中にPythonで書かれているのでビルドの必要がないモジュールも含まれています。
pyqrcodeはQRコードを作るためのモジュールで、Pythonで書かれているので特にビルドする必要はなく、pyqrcode.pyをコピーするだけでQRコードを作成できるようになります。
これはPILに依存するので、プラットフォームによってはPILをインストールする必要があります。ただAndroidであればKivyLauncherにPILが内蔵されているので動作します。
(/sdcard/kivy/qrcode/)
android.txt | Androidの場合必要 |
main.py | 下記参照 |
pyqrcode.py | 次のファイルをダウンロードして置いてください https://github.com/kivy/python-for-android/raw/master/recipes/pyqrcode/src/pyqrcode.py ※PyPI公開しているもの (https://pypi.python.org/pypi/PyQRCode) とは別物です |
(android.txt)
title=qrcode author=cheeseshop orientation=portrait
(main.py)
from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.textinput import TextInput from kivy.graphics.texture import Texture from pyqrcode import MakeQRImage from kivy.lang import Builder Builder.load_string(''': qr: _qr url: _url msg: _msg canvas: Color: rgb: 0.8, 0.8, 0.7 Rectangle: size: self.size pos: self.pos BoxLayout: orientation: 'vertical' Image: id: _qr size_hint: 1, 1 opacity: 0 TextInput: id: _url multiline: False size_hint: 0.9, 0.1 pos_hint: {'center_x':0.5,'center_y':0.2} on_text_validate: root.makeimage() Label: id: _msg text: '' size_hint: 1, 1 ''') class QRCode(BoxLayout): def makeimage(self): try: im = MakeQRImage(self.url.text) self.msg.text = '' except: self.msg.text = 'cannot make QR Image' self.qr.opacity = 0 return im = im.convert('RGB') tx = Texture.create( size=im.size, colorfmt='rgb', bufferfmt='ubyte') tx.blit_buffer(im.tostring()) tx.flip_vertical() self.qr.texture = tx self.qr.opacity = 1 class QRCodeApp(App): def build(self): return QRCode() if __name__ == '__main__': QRCodeApp().run()
テキスト欄にURLを入力してEnterキーを押すと、そのURLのQRコードを表示します。携帯どうしでURLを送りたいとき便利かもしれません。
解説
今回のポイントは「pyqrcodeが返すイメージオブジェクトをどのようにしてKivyで扱うか」です。QRコードはPILのImageオブジェクトとして作成されますが、KivyはOpenGLを元にした画像オブジェクト型で処理をしているからです。
可能であれば一時的な画像ファイルに書き出さずメモリ上ですませたいですのですが、KivyのTextureオブジェクトはソースを見る限りファイルからの取り込みしかできないようです。でも調べてみるとblit_buffer()というバイト列から取り込む方法がありました。
PILはtostring()を使えば画像情報をバイト列として吐き出せるので、フォーマットを合わせればメモリ上で画像のやり取りができそうです。いろいろ試してみたところ、PILからは「RGB」で吐き出し、Textureオブジェクトは「colorfmt='rgb', bufferfmt='ubyte'」で受け取ったら、表示はできるようになりました。
しかし携帯のQRコードリーダでテストしてみると、なぜか読み込んでくれない...
よくよく見ると、QRコードでおなじみ「回」みたいな形をしたマーカの位置が違っています。実はPILのバイト列は、Kivyが使っているOpenGLテクスチャとスキャン方向が逆になっているので、上下逆の画像として表示されてしまうのでした(ついでにQRコードの鏡像は等価ではないというのも初めて知りました)。
結局テクスチャの方でflip_vertical()とすることで対応しました。PILで上下反転するとビットマップ書き換えが発生してしまいますが、flip_vertical()は単にtex_coords (テクスチャ座標系) を反転するだけで効率がよいためです。