ギャラリーから選択する
Kivyを使ってAndroidアプリを作成していて画像を選択する機能をいれたのですが、実装に苦労をしたので方法をメモ。
とりあえず結論としてギャラリーを呼びだしてそこから選択する形で実装しました。方法のサンプルは以下のような感じです。
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy import platform
from kivy.clock import Clock
from kivy.core.window import Window
currentActivity = None
CLS_Activity = None
CLS_Intent = None
ImagesMedia = None
REQUEST_GALLERY = 1
MediaStore_Images_Media_DATA = '_data'
class FileSelect(BoxLayout):
def open_ImageSelect(self, *args):
if platform == 'android':
self.android_select_image()
def select_path(self, filepath):
if filepath != '':
self.image1.source = filepath
self.image1.reload()
def android_select_image(self):
if platform == 'android':
from android import activity
# イメージギャラリー
intent = CLS_Intent(CLS_Intent.ACTION_PICK,ImagesMedia.EXTERNAL_CONTENT_URI)
currentActivity.startActivityForResult(intent, REQUEST_GALLERY)
activity.bind(on_activity_result=self.on_activity_result)
def on_activity_result(self, request_code, result_code, intent):
try:
if request_code == REQUEST_GALLERY:
if result_code == CLS_Activity.RESULT_CANCELED:
#未選択
Clock.schedule_once(lambda dt: self.select_path(''), 0)
return
if result_code != CLS_Activity.RESULT_OK:
raise NotImplementedError('Unknown result_code "{}"'.format(result_code))
selectedImage = intent.getData() # Uri
filePathColumn = [MediaStore_Images_Media_DATA] # String[]
cursor = currentActivity.getContentResolver().query(selectedImage, filePathColumn, None, None, None) # Cursor
cursor.moveToFirst()
columnIndex = cursor.getColumnIndex(filePathColumn[0]) # int
selectedPicturePath = cursor.getString(columnIndex) # String
Clock.schedule_once(lambda dt: self.select_path(selectedPicturePath), 0)
cursor.close()
finally:
from android import activity
activity.unbind(on_activity_result=self.on_activity_result)
class TestFileSelectApp(App):
def build(self):
if platform == 'android':
Window.bind(on_keyboard=self.key_input)
from jnius import autoclass, cast
global currentActivity
global CLS_Activity
global CLS_Intent
global ImagesMedia
PythonActivity = autoclass('org.kivy.android.PythonActivity')
currentActivity = cast('android.app.Activity', PythonActivity.mActivity)
CLS_Activity = autoclass('android.app.Activity')
CLS_Intent = autoclass('android.content.Intent')
ImagesMedia = autoclass('android.provider.MediaStore$Images$Media')
from android.permissions import request_permissions, Permission
request_permissions([Permission.READ_EXTERNAL_STORAGE, Permission.MANAGE_DOCUMENTS])
return FileSelect()
def key_input(self, window, key, scancode, codepoint, modifier):
if key == 27:
return True
else:
return False
if __name__ == '__main__':
TestFileSelectApp().run()
確認用のKVファイル
<FileSelect>:
image1:image1
BoxLayout:
orientation: 'vertical'
Image:
id: image1
source: ''
Button:
text: 'image select'
size_hint_y: 0.1
on_press: root.open_ImageSelect()
簡単にサンプルの説明をすると、実装のメインとなる関数は下記2つ。
・android_select_image:ギャラリーを呼び出しています。
・on_activity_result:ギャラリーから戻るときの処理。パスを取得しています。
REQUEST_GALLERYは定数にしていますが数値は任意です。どこから戻ってきたのかを判別するもので、呼び出すときに渡した値と同じ値で返ってきます。
あと必要なのが、Android API にアクセスするため Pyjnius を使っています。サンプルでは build内で分岐させていますが頭のグローバル変数宣言時に書いてもいいと思います。
from jnius import autoclass, cast
PythonActivity = autoclass('org.kivy.android.PythonActivity')
currentActivity = cast('android.app.Activity', PythonActivity.mActivity)
Activity = autoclass('android.app.Activity')
Intent = autoclass('android.content.Intent')
ImagesMedia = autoclass('android.provider.MediaStore$Images$Media')
※サンプルで変数名についてる”CLS_”は、ラップしたJavaクラスだと分かりやすくするために適当につけただけで深い意味はないです。
あとは適当で、open_ImageSelect は呼び出しているだけの関数です。
select_path の関数で選択した画像のパスを使っています。サンプルでは表示しているだけですが、選択画像のパスを使ってやりたいことを実装します。呼び出し元と別クラスにしてファイル選択処理を実装する場合は、コールバック関数として渡す形になるかと思います。
以上、私のたどり着いたAndroidアプリでの画像選択方法でした。
(参考)
試した方法履歴
上記の方法に至るまでに紆余曲折、いろいろなファイル選択方法を試したのでそれも残しておきたいと思います。
①tkinterのfiledialog
基本的にPC(Windows)をメインで使って開発しています。KivyというよりPythonを触りはじめてからそのまま使っている方法。
サンプルコード(省略版)
from tkinter import filedialog
~
def open_ImageSelect(self, *args):
filepath = filedialog.askopenfilename(
title = "画像ファイルを開く",
filetypes = [("Image file", ".bmp .png .jpg .tif") ], # ファイルフィルタ
initialdir = "./" # 自分自身のディレクトリ
)
PCでのデバッグまでは問題なかったのですが、いざAndroidで実機確認をしようとしたとき、tkinterがAndroidに対応していないことを知りました・・・
②KivyのFileChooser
Android や iOS など各OSで動作することを謳っているKivy 。Kivy自体が持っているのファイル選択を使えば Android にも対応しているはず。
ということで次に試したものがこちら。
サンプルコード(省略版)
from kivy.uix.boxlayout import BoxLayout
from kivy.properties import StringProperty, ObjectProperty
from kivy.uix.popup import Popup
~
def open_FileChooser(self):
content = FileChoosePopup(load=self.load, cancel=self.popup_close, image_path="./")
self.popup = Popup(title="Load file", content=content,size_hint=(0.9, 0.9))
self.popup.open()
def load(self, path, filename):
filepath = os.path.join(path, filename[0])
~
self.popup_close()
def popup_close(self):
self.popup.dismiss()
class FileChoosePopup(BoxLayout):
load = ObjectProperty(None)
cancel = ObjectProperty(None)
image_path = StringProperty(None)
KVファイル
<FileChoosePopup>:
BoxLayout:
orientation: "vertical"
FileChooser:
id: fc
path: root.image_path
filters: ['*.jpg','*.jpeg','*.png']
FileChooserIconLayout
BoxLayout:
size_hint: (1, 0.1)
pos_hint: {'center_x': 0.5, 'center_y': 0.5}
spacing: 20
Button:
text: "Cancel"
on_release: root.cancel()
Button:
text: "Load"
on_release: root.load(fc.path, fc.selection)
id: ldbtn
disabled: True if fc.selection==[] else False
Android でも動いたのですが、アイコン表示でもフォルダとファイルの区別くらいしかできず、実現したい画像ファイルを選択する機能としては再検討が必要でした。
(以下、つづきは後日追記予定)
コメント