Skip to content

Commit 3fd7463

Browse files
committed
Use zbarlight as alternative to pyzbar when it's not available
TODO: Fix setup.py install/extra_requires accordingly, as well as requirements in buildozer.spec for demo app
1 parent 5669e5a commit 3fd7463

File tree

1 file changed

+98
-10
lines changed

1 file changed

+98
-10
lines changed

src/kivy_garden/zbarcam/zbarcam.py

Lines changed: 98 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,110 @@
66
import PIL
77
from kivy.clock import Clock
88
from kivy.lang import Builder
9+
from kivy.logger import Logger
910
from kivy.properties import ListProperty
1011
from kivy.uix.anchorlayout import AnchorLayout
11-
from pyzbar import pyzbar
1212

1313
from .utils import fix_android_image
1414

1515
MODULE_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
1616

1717

18+
class ZBarDecoder:
19+
def validate_code_types(self, code_types):
20+
available_code_types = self.get_available_code_types()
21+
22+
if not all(
23+
code_type in available_code_types
24+
for code_type in code_types
25+
):
26+
raise ValueError(
27+
f'Invalid code types: {code_types}. '
28+
f'Available code types: {available_code_types}'
29+
)
30+
31+
32+
class PyZBarDecoder(ZBarDecoder):
33+
@classmethod
34+
def is_usable(cls):
35+
try:
36+
from pyzbar import pyzbar
37+
cls.pyzbar = pyzbar
38+
return True
39+
40+
except ImportError:
41+
return False
42+
43+
def get_available_code_types(self):
44+
return set(
45+
code_type.lower()
46+
for code_type in self.pyzbar.ZBarSymbol.__members__.keys()
47+
)
48+
49+
def decode(self, image, code_types):
50+
self.validate_code_types(code_types)
51+
pyzbar_code_types = set(
52+
getattr(self.pyzbar.ZBarSymbol, code_type.upper())
53+
for code_type in code_types
54+
)
55+
return [
56+
ZBarCam.Symbol(type=code.type.lower(), data=code.data)
57+
for code in self.pyzbar.decode(
58+
image,
59+
symbols=pyzbar_code_types,
60+
)
61+
]
62+
63+
64+
class ZBarLightDecoder(ZBarDecoder):
65+
@classmethod
66+
def is_usable(cls):
67+
try:
68+
import zbarlight
69+
cls.zbarlight = zbarlight
70+
return True
71+
72+
except ImportError:
73+
return False
74+
75+
def get_available_code_types(self):
76+
return set(
77+
code_type.lower()
78+
for code_type in self.zbarlight.Symbologies.keys()
79+
)
80+
81+
def decode(self, image, code_types):
82+
self.validate_code_types(code_types)
83+
codes = self.zbarlight.scan_codes(code_types, image)
84+
85+
# zbarlight.scan_codes() returns None instead of []
86+
if not codes:
87+
return []
88+
89+
return [
90+
ZBarCam.Symbol(type=None, data=code)
91+
for code in codes
92+
]
93+
94+
95+
available_implementations = {
96+
'pyzbar': PyZBarDecoder,
97+
'zbarlight': ZBarLightDecoder,
98+
}
99+
100+
101+
for name, implementation in available_implementations.items():
102+
if implementation.is_usable():
103+
zbar_decoder = implementation()
104+
Logger.info('ZBarCam: Using implementation %s', name)
105+
break
106+
else:
107+
raise ImportError(
108+
'No zbar implementation available '
109+
f'(tried {", ".join(available_implementations.keys())})'
110+
)
111+
112+
18113
class ZBarCam(AnchorLayout):
19114
"""
20115
Widget that use the Camera and zbar to detect qrcode.
@@ -25,7 +120,7 @@ class ZBarCam(AnchorLayout):
25120
symbols = ListProperty([])
26121
Symbol = collections.namedtuple('Symbol', ['type', 'data'])
27122
# checking all possible types by default
28-
code_types = ListProperty(set(pyzbar.ZBarSymbol))
123+
code_types = ListProperty(zbar_decoder.get_available_code_types())
29124

30125
def __init__(self, **kwargs):
31126
# lazy loading the kv file rather than loading at module level,
@@ -100,14 +195,7 @@ def _detect_qrcode_frame(self, texture, code_types, pixels=None):
100195
pil_image = PIL.Image.frombytes(mode='RGBA', size=size,
101196
data=image_data)
102197
pil_image = fix_android_image(pil_image)
103-
symbols = []
104-
codes = pyzbar.decode(pil_image, symbols=code_types)
105-
106-
for code in codes:
107-
symbol = ZBarCam.Symbol(type=code.type, data=code.data)
108-
symbols.append(symbol)
109-
110-
return symbols
198+
return zbar_decoder.decode(pil_image, code_types)
111199

112200
@property
113201
def xcamera(self):

0 commit comments

Comments
 (0)