44import PIL
55from kivy .clock import Clock
66from kivy .lang import Builder
7+ from kivy .logger import Logger
78from kivy .properties import ListProperty
89from kivy .uix .anchorlayout import AnchorLayout
9- from pyzbar import pyzbar
1010
1111from .utils import fix_android_image
1212
1313MODULE_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+
16112class 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