Skip to content

Commit 68d4d11

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 e0899af commit 68d4d11

File tree

1 file changed

+99
-8
lines changed

1 file changed

+99
-8
lines changed

src/kivy_garden/zbarcam/zbarcam.py

Lines changed: 99 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,111 @@
44
import PIL
55
from kivy.clock import Clock
66
from kivy.lang import Builder
7+
from kivy.logger import Logger
78
from kivy.properties import ListProperty
89
from kivy.uix.anchorlayout import AnchorLayout
9-
from pyzbar import pyzbar
1010

1111
from .utils import fix_android_image
1212

1313
MODULE_DIRECTORY = os.path.dirname(os.path.realpath(__file__))
1414

1515

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

28124
def __init__(self, **kwargs):
29125
# lazy loading the kv file rather than loading at module level,
@@ -74,12 +170,7 @@ def _detect_qrcode_frame(cls, texture, code_types):
74170
pil_image = PIL.Image.frombytes(mode='RGBA', size=size,
75171
data=image_data)
76172
pil_image = fix_android_image(pil_image)
77-
symbols = []
78-
codes = pyzbar.decode(pil_image, symbols=code_types)
79-
for code in codes:
80-
symbol = ZBarCam.Symbol(type=code.type, data=code.data)
81-
symbols.append(symbol)
82-
return symbols
173+
return zbar_decoder.decode(pil_image, code_types)
83174

84175
@property
85176
def xcamera(self):

0 commit comments

Comments
 (0)