kivy: remove qr_scanner
This commit is contained in:
parent
1f1dbaf523
commit
167088e22a
|
@ -35,7 +35,6 @@ Factory.register('InstallWizard',
|
||||||
Factory.register('InfoBubble', module='electrum_gui.kivy.uix.dialogs')
|
Factory.register('InfoBubble', module='electrum_gui.kivy.uix.dialogs')
|
||||||
Factory.register('OutputList', 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('OutputItem', module='electrum_gui.kivy.uix.dialogs')
|
||||||
Factory.register('QrScannerDialog', module='electrum_gui.kivy.uix.dialogs.qr_scanner')
|
|
||||||
|
|
||||||
|
|
||||||
#from kivy.core.window import Window
|
#from kivy.core.window import Window
|
||||||
|
@ -191,7 +190,6 @@ class ElectrumWindow(App):
|
||||||
# initialize variables
|
# initialize variables
|
||||||
self._clipboard = Clipboard
|
self._clipboard = Clipboard
|
||||||
self.info_bubble = None
|
self.info_bubble = None
|
||||||
self.qrscanner = None
|
|
||||||
self.nfcscanner = None
|
self.nfcscanner = None
|
||||||
self.tabs = None
|
self.tabs = None
|
||||||
self.is_exit = False
|
self.is_exit = False
|
||||||
|
@ -610,15 +608,11 @@ class ElectrumWindow(App):
|
||||||
|
|
||||||
def on_pause(self):
|
def on_pause(self):
|
||||||
# pause nfc
|
# pause nfc
|
||||||
if self.qrscanner:
|
|
||||||
self.qrscanner.stop()
|
|
||||||
if self.nfcscanner:
|
if self.nfcscanner:
|
||||||
self.nfcscanner.nfc_disable()
|
self.nfcscanner.nfc_disable()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_resume(self):
|
def on_resume(self):
|
||||||
if self.qrscanner and qrscanner.get_parent_window():
|
|
||||||
self.qrscanner.start()
|
|
||||||
if self.nfcscanner:
|
if self.nfcscanner:
|
||||||
self.nfcscanner.nfc_enable()
|
self.nfcscanner.nfc_enable()
|
||||||
|
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
'''QrScanner Base Abstract implementation
|
|
||||||
'''
|
|
||||||
|
|
||||||
__all__ = ('ScannerBase', 'QRScanner')
|
|
||||||
|
|
||||||
from collections import namedtuple
|
|
||||||
|
|
||||||
from kivy.uix.anchorlayout import AnchorLayout
|
|
||||||
from kivy.core import core_select_lib
|
|
||||||
from kivy.metrics import dp
|
|
||||||
from kivy.properties import ListProperty, BooleanProperty
|
|
||||||
from kivy.factory import Factory
|
|
||||||
|
|
||||||
|
|
||||||
class ScannerBase(AnchorLayout):
|
|
||||||
''' Base implementation for camera based scanner
|
|
||||||
'''
|
|
||||||
camera_size = ListProperty([320, 240] if dp(1) < 2 else [640, 480])
|
|
||||||
|
|
||||||
symbols = ListProperty([])
|
|
||||||
|
|
||||||
# XXX can't work now, due to overlay.
|
|
||||||
show_bounds = BooleanProperty(False)
|
|
||||||
|
|
||||||
running = BooleanProperty(False)
|
|
||||||
|
|
||||||
Qrcode = namedtuple('Qrcode',
|
|
||||||
['type', 'data', 'bounds', 'quality', 'count'])
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_symbols(self, instance, value):
|
|
||||||
#if self.show_bounds:
|
|
||||||
# self.update_bounds()
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_bounds(self):
|
|
||||||
self.canvas.after.remove_group('bounds')
|
|
||||||
if not self.symbols:
|
|
||||||
return
|
|
||||||
with self.canvas.after:
|
|
||||||
Color(1, 0, 0, group='bounds')
|
|
||||||
for symbol in self.symbols:
|
|
||||||
x, y, w, h = symbol.bounds
|
|
||||||
x = self._camera.right - x - w
|
|
||||||
y = self._camera.top - y - h
|
|
||||||
Line(rectangle=[x, y, w, h], group='bounds')
|
|
||||||
|
|
||||||
|
|
||||||
# load QRCodeDetector implementation
|
|
||||||
|
|
||||||
QRScanner = core_select_lib('qr_scanner', (
|
|
||||||
('android', 'scanner_android', 'ScannerAndroid'),
|
|
||||||
('camera', 'scanner_camera', 'ScannerCamera')), False, 'electrum_gui.kivy')
|
|
||||||
|
|
||||||
Factory.register('QRScanner', cls=QRScanner)
|
|
|
@ -1,390 +0,0 @@
|
||||||
'''
|
|
||||||
Qrcode example application
|
|
||||||
==========================
|
|
||||||
|
|
||||||
Author: Mathieu Virbel <mat@meltingrocks.com>
|
|
||||||
|
|
||||||
License:
|
|
||||||
Copyright (c) 2013 Mathieu Virbel <mat@meltingrocks.com>
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
|
|
||||||
Featuring:
|
|
||||||
|
|
||||||
- Android camera initialization
|
|
||||||
- Show the android camera into a Android surface that act as an overlay
|
|
||||||
- New AndroidWidgetHolder that control any android view as an overlay
|
|
||||||
- New ZbarQrcodeDetector that use AndroidCamera / PreviewFrame + zbar to
|
|
||||||
detect Qrcode.
|
|
||||||
|
|
||||||
'''
|
|
||||||
|
|
||||||
__all__ = ('ScannerAndroid', )
|
|
||||||
|
|
||||||
from kivy.utils import platform
|
|
||||||
if platform != 'android':
|
|
||||||
raise ImportError
|
|
||||||
|
|
||||||
from electrum_gui.kivy.qr_scanner import ScannerBase
|
|
||||||
from kivy.properties import ObjectProperty, NumericProperty
|
|
||||||
from kivy.uix.widget import Widget
|
|
||||||
from kivy.uix.anchorlayout import AnchorLayout
|
|
||||||
from kivy.graphics import Color, Line
|
|
||||||
from jnius import autoclass, PythonJavaClass, java_method, cast
|
|
||||||
from android.runnable import run_on_ui_thread
|
|
||||||
|
|
||||||
# preload java classes
|
|
||||||
System = autoclass('java.lang.System')
|
|
||||||
System.loadLibrary('iconv')
|
|
||||||
PythonActivity = autoclass('org.renpy.android.PythonActivity')
|
|
||||||
Camera = autoclass('android.hardware.Camera')
|
|
||||||
ImageScanner = autoclass('net.sourceforge.zbar.ImageScanner')
|
|
||||||
Image = autoclass('net.sourceforge.zbar.Image')
|
|
||||||
Symbol = autoclass('net.sourceforge.zbar.Symbol')
|
|
||||||
Config = autoclass('net.sourceforge.zbar.Config')
|
|
||||||
SurfaceView = autoclass('android.view.SurfaceView')
|
|
||||||
LayoutParams = autoclass('android.view.ViewGroup$LayoutParams')
|
|
||||||
ImageFormat = autoclass('android.graphics.ImageFormat')
|
|
||||||
LinearLayout = autoclass('android.widget.LinearLayout')
|
|
||||||
|
|
||||||
|
|
||||||
class PreviewCallback(PythonJavaClass):
|
|
||||||
'''Interface used to get back the preview frame of the Android Camera
|
|
||||||
'''
|
|
||||||
__javainterfaces__ = ('android.hardware.Camera$PreviewCallback', )
|
|
||||||
|
|
||||||
def __init__(self, callback):
|
|
||||||
super(PreviewCallback, self).__init__()
|
|
||||||
self.callback = callback
|
|
||||||
|
|
||||||
@java_method('([BLandroid/hardware/Camera;)V')
|
|
||||||
def onPreviewFrame(self, data, camera):
|
|
||||||
self.callback(camera, data)
|
|
||||||
|
|
||||||
|
|
||||||
class SurfaceHolderCallback(PythonJavaClass):
|
|
||||||
'''Interface used to know exactly when the Surface used for the Android
|
|
||||||
Camera will be created and changed.
|
|
||||||
'''
|
|
||||||
|
|
||||||
__javainterfaces__ = ('android.view.SurfaceHolder$Callback', )
|
|
||||||
|
|
||||||
def __init__(self, callback):
|
|
||||||
super(SurfaceHolderCallback, self).__init__()
|
|
||||||
self.callback = callback
|
|
||||||
|
|
||||||
@java_method('(Landroid/view/SurfaceHolder;III)V')
|
|
||||||
def surfaceChanged(self, surface, fmt, width, height):
|
|
||||||
self.callback(fmt, width, height)
|
|
||||||
|
|
||||||
@java_method('(Landroid/view/SurfaceHolder;)V')
|
|
||||||
def surfaceCreated(self, surface):
|
|
||||||
pass
|
|
||||||
|
|
||||||
@java_method('(Landroid/view/SurfaceHolder;)V')
|
|
||||||
def surfaceDestroyed(self, surface):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AndroidWidgetHolder(Widget):
|
|
||||||
'''Act as a placeholder for an Android widget.
|
|
||||||
It will automatically add / remove the android view depending if the widget
|
|
||||||
view is set or not. The android view will act as an overlay, so any graphics
|
|
||||||
instruction in this area will be covered by the overlay.
|
|
||||||
'''
|
|
||||||
|
|
||||||
view = ObjectProperty(allownone=True)
|
|
||||||
'''Must be an Android View
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self._old_view = None
|
|
||||||
from kivy.core.window import Window
|
|
||||||
self._window = Window
|
|
||||||
kwargs['size_hint'] = (None, None)
|
|
||||||
super(AndroidWidgetHolder, self).__init__(**kwargs)
|
|
||||||
|
|
||||||
def on_view(self, instance, view):
|
|
||||||
if self._old_view is not None:
|
|
||||||
layout = cast(LinearLayout, self._old_view.getParent())
|
|
||||||
layout.removeView(self._old_view)
|
|
||||||
self._old_view = None
|
|
||||||
|
|
||||||
if view is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
activity = PythonActivity.mActivity
|
|
||||||
activity.addContentView(view, LayoutParams(*self.size))
|
|
||||||
view.setZOrderOnTop(True)
|
|
||||||
view.setX(self.x)
|
|
||||||
view.setY(self._window.height - self.y - self.height)
|
|
||||||
self._old_view = view
|
|
||||||
|
|
||||||
def on_size(self, instance, size):
|
|
||||||
if self.view:
|
|
||||||
params = self.view.getLayoutParams()
|
|
||||||
params.width = self.width
|
|
||||||
params.height = self.height
|
|
||||||
self.view.setLayoutParams(params)
|
|
||||||
self.view.setY(self._window.height - self.y - self.height)
|
|
||||||
|
|
||||||
def on_x(self, instance, x):
|
|
||||||
if self.view:
|
|
||||||
self.view.setX(x)
|
|
||||||
|
|
||||||
def on_y(self, instance, y):
|
|
||||||
if self.view:
|
|
||||||
self.view.setY(self._window.height - self.y - self.height)
|
|
||||||
|
|
||||||
|
|
||||||
class AndroidCamera(Widget):
|
|
||||||
'''Widget for controling an Android Camera.
|
|
||||||
'''
|
|
||||||
|
|
||||||
index = NumericProperty(0)
|
|
||||||
|
|
||||||
__events__ = ('on_preview_frame', )
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
self._holder = None
|
|
||||||
self._android_camera = None
|
|
||||||
super(AndroidCamera, self).__init__(**kwargs)
|
|
||||||
self._holder = AndroidWidgetHolder(size=self.size, pos=self.pos)
|
|
||||||
self.add_widget(self._holder)
|
|
||||||
|
|
||||||
@run_on_ui_thread
|
|
||||||
def stop(self):
|
|
||||||
self.running = False
|
|
||||||
if self._android_camera is None:
|
|
||||||
return
|
|
||||||
self._android_camera.setPreviewCallback(None)
|
|
||||||
self._android_camera.release()
|
|
||||||
self._android_camera = None
|
|
||||||
self._holder.view = None
|
|
||||||
|
|
||||||
@run_on_ui_thread
|
|
||||||
def start(self):
|
|
||||||
self.running = True
|
|
||||||
if self._android_camera is not None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self._android_camera = Camera.open(self.index)
|
|
||||||
|
|
||||||
# create a fake surfaceview to get the previewCallback working.
|
|
||||||
self._android_surface = SurfaceView(PythonActivity.mActivity)
|
|
||||||
surface_holder = self._android_surface.getHolder()
|
|
||||||
|
|
||||||
# create our own surface holder to correctly call the next method when
|
|
||||||
# the surface is ready
|
|
||||||
self._android_surface_cb = SurfaceHolderCallback(self._on_surface_changed)
|
|
||||||
surface_holder.addCallback(self._android_surface_cb)
|
|
||||||
|
|
||||||
# attach the android surfaceview to our android widget holder
|
|
||||||
self._holder.view = self._android_surface
|
|
||||||
|
|
||||||
# set orientation
|
|
||||||
self._android_camera.setDisplayOrientation(90)
|
|
||||||
|
|
||||||
def _on_surface_changed(self, fmt, width, height):
|
|
||||||
# internal, called when the android SurfaceView is ready
|
|
||||||
# FIXME if the size is not handled by the camera, it will failed.
|
|
||||||
params = self._android_camera.getParameters()
|
|
||||||
params.setPreviewSize(width, height)
|
|
||||||
self._android_camera.setParameters(params)
|
|
||||||
|
|
||||||
# now that we know the camera size, we'll create 2 buffers for faster
|
|
||||||
# result (using Callback buffer approach, as described in Camera android
|
|
||||||
# documentation)
|
|
||||||
# it also reduce the GC collection
|
|
||||||
bpp = ImageFormat.getBitsPerPixel(params.getPreviewFormat()) / 8.
|
|
||||||
buf = '\x00' * int(width * height * bpp)
|
|
||||||
self._android_camera.addCallbackBuffer(buf)
|
|
||||||
self._android_camera.addCallbackBuffer(buf)
|
|
||||||
|
|
||||||
# create a PreviewCallback to get back the onPreviewFrame into python
|
|
||||||
self._previewCallback = PreviewCallback(self._on_preview_frame)
|
|
||||||
|
|
||||||
# connect everything and start the preview
|
|
||||||
self._android_camera.setPreviewCallbackWithBuffer(self._previewCallback);
|
|
||||||
self._android_camera.setPreviewDisplay(self._android_surface.getHolder())
|
|
||||||
self._android_camera.startPreview();
|
|
||||||
|
|
||||||
def _on_preview_frame(self, camera, data):
|
|
||||||
# internal, called by the PreviewCallback when onPreviewFrame is
|
|
||||||
# received
|
|
||||||
self.dispatch('on_preview_frame', camera, data)
|
|
||||||
# reintroduce the data buffer into the queue
|
|
||||||
self._android_camera.addCallbackBuffer(data)
|
|
||||||
|
|
||||||
def on_preview_frame(self, camera, data):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def on_size(self, instance, size):
|
|
||||||
if self._holder:
|
|
||||||
self._holder.size = size
|
|
||||||
|
|
||||||
def on_pos(self, instance, pos):
|
|
||||||
if self._holder:
|
|
||||||
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
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(ScannerAndroid, self).__init__(**kwargs)
|
|
||||||
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.add_widget(self._camera)
|
|
||||||
|
|
||||||
# create a scanner used for detecting qrcode
|
|
||||||
self._scanner = ImageScanner()
|
|
||||||
self._scanner.setConfig(0, Config.ENABLE, 0)
|
|
||||||
self._scanner.setConfig(Symbol.QRCODE, Config.ENABLE, 1)
|
|
||||||
self._scanner.setConfig(0, Config.X_DENSITY, 3)
|
|
||||||
self._scanner.setConfig(0, Config.Y_DENSITY, 3)
|
|
||||||
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self._camera.start()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self._camera.stop()
|
|
||||||
|
|
||||||
def _detect_qrcode_frame(self, instance, camera, data):
|
|
||||||
if not self.get_root_window():
|
|
||||||
self.stop()
|
|
||||||
return
|
|
||||||
parameters = camera.getParameters()
|
|
||||||
size = parameters.getPreviewSize()
|
|
||||||
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()
|
|
||||||
while it.hasNext():
|
|
||||||
symbol = it.next()
|
|
||||||
qrcode = ScannerAndroid.Qrcode(
|
|
||||||
type=symbol.getType(),
|
|
||||||
data=symbol.getData(),
|
|
||||||
quality=symbol.getQuality(),
|
|
||||||
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):
|
|
||||||
if self.show_bounds:
|
|
||||||
self.update_bounds()
|
|
||||||
|
|
||||||
def update_bounds(self):
|
|
||||||
self.canvas.after.remove_group('bounds')
|
|
||||||
if not self.symbols:
|
|
||||||
return
|
|
||||||
with self.canvas.after:
|
|
||||||
Color(1, 0, 0, group='bounds')
|
|
||||||
for symbol in self.symbols:
|
|
||||||
x, y, w, h = symbol.bounds
|
|
||||||
x = self._camera.right - x - w
|
|
||||||
y = self._camera.top - y - h
|
|
||||||
Line(rectangle=[x, y, w, h], group='bounds')
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
from kivy.lang import Builder
|
|
||||||
from kivy.app import App
|
|
||||||
|
|
||||||
qrcode_kv = '''
|
|
||||||
BoxLayout:
|
|
||||||
orientation: 'vertical'
|
|
||||||
|
|
||||||
ZbarQrcodeDetector:
|
|
||||||
id: detector
|
|
||||||
|
|
||||||
Label:
|
|
||||||
text: '\\n'.join(map(repr, detector.symbols))
|
|
||||||
size_hint_y: None
|
|
||||||
height: '100dp'
|
|
||||||
|
|
||||||
BoxLayout:
|
|
||||||
size_hint_y: None
|
|
||||||
height: '48dp'
|
|
||||||
|
|
||||||
Button:
|
|
||||||
text: 'Scan a qrcode'
|
|
||||||
on_release: detector.start()
|
|
||||||
Button:
|
|
||||||
text: 'Stop detection'
|
|
||||||
on_release: detector.stop()
|
|
||||||
'''
|
|
||||||
|
|
||||||
class QrcodeExample(App):
|
|
||||||
def build(self):
|
|
||||||
return Builder.load_string(qrcode_kv)
|
|
||||||
|
|
||||||
QrcodeExample().run()
|
|
|
@ -1,96 +0,0 @@
|
||||||
from kivy.uix.camera import Camera
|
|
||||||
from kivy.clock import Clock
|
|
||||||
from kivy.utils import platform
|
|
||||||
|
|
||||||
from electrum_gui.kivy.qr_scanner import ScannerBase
|
|
||||||
|
|
||||||
import iconv
|
|
||||||
|
|
||||||
try:
|
|
||||||
from zbar import ImageScanner, Config, Image, Symbol
|
|
||||||
except ImportError:
|
|
||||||
raise SystemError('unable to import zbar please make sure you have'
|
|
||||||
' it installed.\nFor mac osx: `brew install zbar then\n`'
|
|
||||||
'`pip install https://github.com/npinchot/zbar/archive/d3c1611ad2411fbdc3e79eb96ca704a63d30ae69.zip`')
|
|
||||||
try:
|
|
||||||
from PIL import Image as PILImage
|
|
||||||
except ImportError:
|
|
||||||
raise SystemError('unable to import Pil/pillow'
|
|
||||||
' please install one of the two.')
|
|
||||||
|
|
||||||
__all__ = ('ScannerCamera', )
|
|
||||||
|
|
||||||
class ScannerCamera(ScannerBase):
|
|
||||||
'''Widget that use the kivy.uix.camera.Camera and zbar to detect
|
|
||||||
qrcode. When found, the `symbols` will be updated
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
super(ScannerCamera, self).__init__(**kwargs)
|
|
||||||
self._camera = None
|
|
||||||
# create a scanner used for detecting qrcode
|
|
||||||
self._scanner = ImageScanner()
|
|
||||||
self._scanner.parse_config('enable')
|
|
||||||
#self._scanner.setConfig(Symbol.QRCODE, Config.ENABLE, 1)
|
|
||||||
#self._scanner.setConfig(0, Config.X_DENSITY, 3)
|
|
||||||
#self._scanner.setConfig(0, Config.Y_DENSITY, 3)
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
if not self._camera:
|
|
||||||
self._camera = Camera(
|
|
||||||
resolution=self.camera_size,
|
|
||||||
size_hint=(None, None))
|
|
||||||
self.add_widget(self._camera)
|
|
||||||
self.bind(size=self._camera.setter('size'))
|
|
||||||
self.bind(pos=self._camera.setter('pos'))
|
|
||||||
else:
|
|
||||||
self._camera._camera.init_camera()
|
|
||||||
self._camera.play = True
|
|
||||||
Clock.schedule_interval(self._detect_qrcode_frame, 1/15)
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
if not self._camera:
|
|
||||||
return
|
|
||||||
self._camera.play = False
|
|
||||||
Clock.unschedule(self._detect_qrcode_frame)
|
|
||||||
# TODO: testing for various platforms(windows, mac)
|
|
||||||
if platform == 'linux':
|
|
||||||
self._camera._camera._pipeline.set_state(1)
|
|
||||||
#self._camera = None
|
|
||||||
|
|
||||||
def _detect_qrcode_frame(self, *args):
|
|
||||||
# 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
|
|
||||||
if not self.get_root_window():
|
|
||||||
self.stop()
|
|
||||||
return
|
|
||||||
cam = self._camera
|
|
||||||
tex = cam.texture
|
|
||||||
if not tex:
|
|
||||||
return
|
|
||||||
im = PILImage.fromstring('RGBA', tex.size, tex.pixels)
|
|
||||||
im = im.convert('L')
|
|
||||||
barcode = Image(tex.size[0],
|
|
||||||
tex.size[1], 'Y800', im.tostring())
|
|
||||||
|
|
||||||
result = self._scanner.scan(barcode)
|
|
||||||
|
|
||||||
if result == 0:
|
|
||||||
self.symbols = []
|
|
||||||
del(barcode)
|
|
||||||
return
|
|
||||||
|
|
||||||
# we detected qrcode! extract and dispatch them
|
|
||||||
symbols = []
|
|
||||||
for symbol in barcode.symbols:
|
|
||||||
qrcode = ScannerCamera.Qrcode(
|
|
||||||
type=symbol.type,
|
|
||||||
data=symbol.data,
|
|
||||||
quality=symbol.quality,
|
|
||||||
count=symbol.count,
|
|
||||||
bounds=symbol.location)
|
|
||||||
symbols.append(qrcode)
|
|
||||||
|
|
||||||
self.symbols = symbols
|
|
||||||
del(barcode)
|
|
Loading…
Reference in New Issue