66import PIL
77from kivy .clock import Clock
88from kivy .lang import Builder
9+ from kivy .logger import Logger
910from kivy .properties import ListProperty
1011from kivy .uix .anchorlayout import AnchorLayout
11- from pyzbar import pyzbar
1212
1313from .utils import fix_android_image
1414
1515MODULE_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+
18113class 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