qrcodewidget.py (3689B)
1 ''' Kivy Widget that accepts data and displays qrcode 2 ''' 3 4 from threading import Thread 5 from functools import partial 6 7 import qrcode 8 from qrcode import exceptions 9 10 from kivy.uix.floatlayout import FloatLayout 11 from kivy.graphics.texture import Texture 12 from kivy.properties import StringProperty 13 from kivy.properties import ObjectProperty, StringProperty, ListProperty,\ 14 BooleanProperty 15 from kivy.lang import Builder 16 from kivy.clock import Clock 17 18 19 20 Builder.load_string(''' 21 <QRCodeWidget> 22 canvas.before: 23 # Draw white Rectangle 24 Color: 25 rgba: root.background_color 26 Rectangle: 27 size: self.size 28 pos: self.pos 29 canvas.after: 30 Color: 31 rgba: root.foreground_color 32 Rectangle: 33 size: self.size 34 pos: self.pos 35 Image 36 id: qrimage 37 pos_hint: {'center_x': .5, 'center_y': .5} 38 allow_stretch: True 39 size_hint: None, None 40 size: root.width * .9, root.height * .9 41 ''') 42 43 class QRCodeWidget(FloatLayout): 44 45 data = StringProperty(None, allow_none=True) 46 background_color = ListProperty((1, 1, 1, 1)) 47 foreground_color = ListProperty((0, 0, 0, 0)) 48 49 def __init__(self, **kwargs): 50 super(QRCodeWidget, self).__init__(**kwargs) 51 self.data = None 52 self.qr = None 53 self._qrtexture = None 54 self.failure_cb = None 55 56 def on_data(self, instance, value): 57 if not (self.canvas or value): 58 return 59 try: 60 self.update_qr() 61 except qrcode.exceptions.DataOverflowError: 62 if self.failure_cb: 63 self.failure_cb() 64 else: 65 raise 66 67 def set_data(self, data, failure_cb=None): 68 if self.data == data: 69 return 70 self.failure_cb = failure_cb 71 MinSize = 210 if len(data) < 128 else 500 72 self.setMinimumSize((MinSize, MinSize)) 73 self.data = data 74 self.qr = None 75 76 def update_qr(self): 77 if not self.data and self.qr: 78 return 79 L = qrcode.constants.ERROR_CORRECT_L 80 data = self.data 81 self.qr = qr = qrcode.QRCode( 82 version=None, 83 error_correction=L, 84 box_size=10, 85 border=0, 86 ) 87 qr.add_data(data) 88 qr.make(fit=True) 89 self.update_texture() 90 91 def setMinimumSize(self, size): 92 # currently unused, do we need this? 93 self._texture_size = size 94 95 def _create_texture(self, k): 96 self._qrtexture = texture = Texture.create(size=(k,k), colorfmt='rgb') 97 # don't interpolate texture 98 texture.min_filter = 'nearest' 99 texture.mag_filter = 'nearest' 100 101 def update_texture(self): 102 if not self.qr: 103 return 104 matrix = self.qr.get_matrix() 105 k = len(matrix) 106 # create the texture 107 self._create_texture(k) 108 buff = [] 109 bext = buff.extend 110 cr, cg, cb, ca = self.background_color[:] 111 cr, cg, cb = cr*255, cg*255, cb*255 112 for r in range(k): 113 for c in range(k): 114 bext([0, 0, 0] if matrix[k-1-r][c] else [cr, cg, cb]) 115 # then blit the buffer 116 buff = bytes(buff) 117 # update texture 118 self._upd_texture(buff) 119 120 def _upd_texture(self, buff): 121 texture = self._qrtexture 122 texture.blit_buffer(buff, colorfmt='rgb', bufferfmt='ubyte') 123 img = self.ids.qrimage 124 img.anim_delay = -1 125 img.texture = texture 126 img.canvas.ask_update() 127 128 if __name__ == '__main__': 129 from kivy.app import runTouchApp 130 import sys 131 data = str(sys.argv[1:]) 132 runTouchApp(QRCodeWidget(data=data)) 133 134