tkivy: use camera - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 644cb871f585b433f0737dc1a9d590e90cf1db91 DIR parent 3c1d6fab694c3c0d823b00d52163bf9877d812d7 HTML Author: ThomasV <thomasv@electrum.org> Date: Wed, 16 Mar 2016 20:39:59 +0100 kivy: use camera Diffstat: M gui/kivy/Readme.txt | 3 +++ M gui/kivy/main_window.py | 24 +++++++++++++++++++----- M gui/kivy/qr_scanner/scanner_androi… | 51 +++++++++++++++++++++++++------ M gui/kivy/uix/dialogs/qr_scanner.py | 12 ++++++------ M gui/kivy/uix/ui_screens/send.kv | 2 +- 5 files changed, 70 insertions(+), 22 deletions(-) --- DIR diff --git a/gui/kivy/Readme.txt b/gui/kivy/Readme.txt t@@ -16,3 +16,6 @@ then you need to rebuild the distribution. To do so: Note: python-for-android must be patched with: git pull git@github.com:denys-duchier/python-for-android.git fix-recursive-delete + + +export P4A_pyjnius_DIR=local_clone DIR diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py t@@ -35,7 +35,7 @@ Factory.register('InstallWizard', Factory.register('InfoBubble', module='electrum_gui.kivy.uix.dialogs') Factory.register('OutputList', module='electrum_gui.kivy.uix.dialogs') Factory.register('OutputItem', module='electrum_gui.kivy.uix.dialogs') - +Factory.register('QrScannerDialog', module='electrum_gui.kivy.uix.dialogs.qr_scanner') #from kivy.core.window import Window t@@ -241,12 +241,15 @@ class ElectrumWindow(App): self.show_error("invoice error:" + pr.error) self.send_screen.do_clear() - def on_qr(self, data): + def on_qr(self, d, data): + from electrum.bitcoin import base_decode, is_address + if is_address(data): + self.set_URI(data) + return if data.startswith('bitcoin:'): self.set_URI(data) return # try to decode transaction - from electrum.bitcoin import base_decode from electrum.transaction import Transaction try: text = base_decode(data, None, base=43).encode('hex') t@@ -307,6 +310,17 @@ class ElectrumWindow(App): popup.open() def scan_qr(self, on_complete): + self.scan_qr_android(on_complete) + + def scan_qr_android(self, on_complete): + dlg = Cache.get('electrum_widgets', 'QrScannerDialog') + if not dlg: + dlg = Factory.QrScannerDialog() + Cache.append('electrum_widgets', 'QrScannerDialog', dlg) + dlg.bind(on_complete=on_complete) + dlg.open() + + def scan_qr_zxing(self, on_complete): if platform != 'android': return from jnius import autoclass t@@ -555,8 +569,8 @@ class ElectrumWindow(App): @profiler def update_wallet(self, *dt): self._trigger_update_status() - #if self.wallet.up_to_date or not self.network or not self.network.is_connected(): - self.update_tabs() + if self.wallet.up_to_date or not self.network or not self.network.is_connected(): + self.update_tabs() @profiler def notify_transactions(self, *dt): DIR diff --git a/gui/kivy/qr_scanner/scanner_android.py b/gui/kivy/qr_scanner/scanner_android.py t@@ -244,6 +244,19 @@ class AndroidCamera(Widget): self._holder.pos = pos +from electrum.util import profiler + +use_camera = True +if use_camera: + from kivy.uix.camera import Camera + from kivy.clock import Clock + from PIL import Image as PILImage + class MyCamera(Camera): + def start(self): + self.play = True + def stop(self): + self.play = False + class ScannerAndroid(ScannerBase): '''Widget that use the AndroidCamera and zbar to detect qrcode. When found, the `symbols` will be updated t@@ -251,10 +264,15 @@ class ScannerAndroid(ScannerBase): def __init__(self, **kwargs): super(ScannerAndroid, self).__init__(**kwargs) - self._camera = AndroidCamera( + if use_camera: + self._camera = MyCamera(resolution=self.camera_size) + Clock.schedule_interval(self._detect_qrcode_frame2, 1) + else: + self._camera = AndroidCamera( size=self.camera_size, size_hint=(None, None)) - self._camera.bind(on_preview_frame=self._detect_qrcode_frame) + self._camera.bind(on_preview_frame=self._detect_qrcode_frame) + self.add_widget(self._camera) # create a scanner used for detecting qrcode t@@ -264,6 +282,7 @@ class ScannerAndroid(ScannerBase): self._scanner.setConfig(0, Config.X_DENSITY, 3) self._scanner.setConfig(0, Config.Y_DENSITY, 3) + def start(self): self._camera.start() t@@ -271,24 +290,36 @@ class ScannerAndroid(ScannerBase): self._camera.stop() def _detect_qrcode_frame(self, instance, camera, data): - # the image we got by default from a camera is using the NV21 format - # zbar only allow Y800/GREY image, so we first need to convert, - # then start the detection on the image if not self.get_root_window(): self.stop() return parameters = camera.getParameters() size = parameters.getPreviewSize() - barcode = Image(size.width, size.height, 'NV21') + self.check_image(size.width, size.height, data) + + def _detect_qrcode_frame2(self, *args): + if not self._camera.play: + return + tex = self._camera.texture + if not tex: + return + im = PILImage.fromstring('RGBA', tex.size, tex.pixels) + im = im.convert('L') + self.check_image(tex.size[0], tex.size[1], im.tostring()) + + @profiler + def check_image(self, width, height, data): + print "zzz", width, height, len(data) + # the image we got by default from a camera is using the rgba format + # zbar only allow Y800/GREY image, so we first need to convert, + # then start the detection on the image + barcode = Image(width, height, 'NV21') barcode.setData(data) barcode = barcode.convert('Y800') - result = self._scanner.scanImage(barcode) - if result == 0: self.symbols = [] return - # we detected qrcode! extract and dispatch them symbols = [] it = barcode.getSymbols().iterator() t@@ -301,9 +332,9 @@ class ScannerAndroid(ScannerBase): count=symbol.getCount(), bounds=symbol.getBounds()) symbols.append(qrcode) - self.symbols = symbols + ''' # can't work, due to the overlay. def on_symbols(self, instance, value): DIR diff --git a/gui/kivy/uix/dialogs/qr_scanner.py b/gui/kivy/uix/dialogs/qr_scanner.py t@@ -11,8 +11,8 @@ class QrScannerDialog(Factory.AnimatedPopup): def on_symbols(self, instance, value): instance.stop() self.dismiss() - uri = App.get_running_app().decode_uri(value[0].data) - self.dispatch('on_complete', uri) + data = value[0].data + self.dispatch('on_complete', data) def on_complete(self, x): ''' Default Handler for on_complete event. t@@ -30,10 +30,10 @@ Builder.load_string(''' size_hint: None, None size: '340dp', '290dp' pos_hint: {'center_y': .53} - separator_color: .89, .89, .89, 1 - separator_height: '1.2dp' - title_color: .437, .437, .437, 1 - background: 'atlas://gui/kivy/theming/light/dialog' + #separator_color: .89, .89, .89, 1 + #separator_height: '1.2dp' + #title_color: .437, .437, .437, 1 + #background: 'atlas://gui/kivy/theming/light/dialog' on_activate: qrscr.start() qrscr.size = self.size DIR diff --git a/gui/kivy/uix/ui_screens/send.kv b/gui/kivy/uix/ui_screens/send.kv t@@ -77,7 +77,7 @@ SendScreen: IconButton: id: qr size_hint: 0.6, 1 - on_release: app.scan_qr(on_complete=app.on_qr) + on_release: Clock.schedule_once(lambda dt: app.scan_qr(on_complete=app.on_qr)) icon: 'atlas://gui/kivy/theming/light/camera' Button: text: _('Paste')