Esempio n. 1
0
class Templates(BoxLayout):
    """Only going to copy new templates out to the remarkable"""
    def __init__(self, **kwargs):
        """Initialize the class"""
        super(Templates, self).__init__(**kwargs)
        self.orientation = 'vertical'
        self.file_chooser = FileChooserListView()
        self.file_chooser.rootpath = TEMPLATE_DIR
        self.file_chooser.path = TEMPLATE_DIR
        self.file_chooser.multiselect = True
        self.add_widget(self.file_chooser)

    def on_dropfile(self, *args):
        """Copy the file to the local directory when a file is dropped here"""
        copy2(args[2].decode('UTF-8'), TEMPLATE_DIR)
        self.file_chooser._update_files()
Esempio n. 2
0
class Splash(BoxLayout):
    """You can write over your splash screens"""
    def __init__(self, **kwargs):
        """Initialize the class"""
        super(Splash, self).__init__(**kwargs)
        self.orientation = 'vertical'
        self.file_chooser = FileChooserListView()
        self.file_chooser.rootpath = SPLASH_DIR
        self.file_chooser.path = SPLASH_DIR
        self.file_chooser.multiselect = True
        self.add_widget(self.file_chooser)

    def on_dropfile(self, *args):
        """Copy the file to the local directory when a file is dropped here"""
        copy2(args[2].decode('UTF-8'), SPLASH_DIR)
        self.file_chooser._update_files()
Esempio n. 3
0
class TXQRApp(App):

    def build_config(self, config):
        config.setdefaults('settings', {
            'blocksize': 512,
            'borderwidth': 10,
            'duration': 300,
            'error': '33%',
            'base64': 1,
            'imagery': 'QRCODE',
            'multicolor': 0,
            'coding': 'TXQR-Android',
            'extra': 10,
            'detection': 'QRCODE'
        })
        # todo: organize config and settings entries to be in the same order, for clarity
        
    def build_settings(self, settings):
        settings.add_json_panel('Coding Settings', self.config, data = 
            # output
            """[
            {
                "type": "title",
                "title": "General Output"
            },{
                "type": "bool",
                "title": "Base64",
                "desc": "Whether to base64 encode the data as TXQR does",
                "section": "settings",
                "key": "base64"
            },{
                "type": "title",
                "title": "Barcode Output"
            },{
                "type": "numeric",
                "title": "Chunk Size",
                "desc": "Size of chunks data is broken into",
                "section": "settings",
                "key": "blocksize"
            },{
                "type": "numeric",
                "title": "Border Width",
                "desc": "Number of cells used for the border",
                "section": "settings",
                "key": "borderwidth"
            },{
                "type": "numeric",
                "title": "Duration",
                "desc": "The duration of each individual frame",
                "section": "settings",
                "key": "duration"
            },{
                "type": "options",
                "title": "Error",
                "desc": "The degree of additional error correction",
                "section": "settings",
                "key": "error",
                "options": ["0%", "33%", "66%", "100%"]
            },{
                "type": "bool",
                "title": "Multicolor",
                "desc": "Whether to display 3-channel colored images (no decoding support)",
                "section": "settings",
                "key": "multicolor"
            },{
                "type": "options",
                "title": "Imagery",
                "desc": "What kind of 2D barcodes to display (AZTEC decoding not supported)",
                "section": "settings",
                "key": "imagery",
                "options": ["QRCODE", "AZTEC"]
            },{
                "type": "options",
                "title": "Fountain Coding",
                "desc": "How to encode data (TXQR-Android decoding not supported)",
                "section": "settings",
                "key": "coding",
                "options": ["QRStreamRaw", "TXQR-Android", "LT-code"]
            },{
                "type": "numeric",
                "title": "TXQR Extra",
                "desc": "The number of extra QR codes to generate for TXQR-Android",
                "section": "settings",
                "key": "extra"
            },{
                "type": "title",
                "title": "Input"
            },{
                "type": "options",
                "title": "Detection",
                "desc": "What kind of data to detect",
                "section": "settings",
                "key": "detection",
                "options": ["QRCODE", "OCR"]
            }]""")
        self.settingsheader.content = settings

    def build(self):

        tabbedpanel = TabbedPanel(do_default_tab = False)
        tabbedpanel.bind(current_tab = self.on_current_tab)
        self.curtab = None

        self.filechooser = FileChooser(path = os.path.abspath('.'), on_submit = self.on_file_submit)
        self.filechooserheader = TabbedPanelHeader(text = 'Select file', content = self.filechooser)
        
        tabbedpanel.add_widget(self.filechooserheader)

        self.qrwidget = QRCode()
        self.qrwidget.borderwidth = self.config.getint('settings', 'borderwidth')
        self.on_config_change(self.config, 'settings', 'error', self.config.get('settings', 'error'))
        self.qrwidget.imagery = self.config.get('settings', 'imagery')
        self.qrwidgetheader = TabbedPanelHeader(text = 'Sending', content = self.qrwidget)
        tabbedpanel.add_widget(self.qrwidgetheader)

        self.readiter = None
        self.writefile = None

        self.cameraheaders = (
            TabbedPanelHeader(text = 'Camera 1'),#, content = Camera(index = 0, play = False)),
            TabbedPanelHeader(text = 'Camera 2')#, content = Camera(index = 1, play = False))
        )
        for cameraheader in self.cameraheaders:
            tabbedpanel.add_widget(cameraheader)
        #self.camera = Camera(index=1)
        #self.camera._camera.bind(on_texture = self.on_camtexture)
        #self.camera._camera.widget = self.camera
        #pagelayout.add_widget(self.camera)

        self.settingsheader = TabbedPanelHeader(text = 'Settings', content = self._app_settings)
        tabbedpanel.add_widget(self.settingsheader)

        self.tabbedpanel = tabbedpanel

        self.settings_cls = SettingsWithNoMenu
        # this seems clearer than doing the hack to make dynamic creation work in tabbedpanel
        self._app_settings = self.create_settings()

        return tabbedpanel

    def display_settings(self, settings):
        self.tabbedpanel.switch_to(self.settingsheader)
        return True

    def close_settings(self, *args):
        self.tabbedpanel.switch_to(self.lasttab)

    def on_file_submit(self, chooser, selection, touch):
        if len(selection) == 0:
            return

        blocksize = self.config.getint('settings', 'blocksize')
        file = open(selection[0], 'rb')

        coding = self.config.get('settings', 'coding')
        if coding == 'QRStreamRaw':
            data = file.read()
            data = [data[i:i+blocksize] for i in range(0, len(data), blocksize)]
        elif coding == 'TXQR-Android':
            data, score, compressed, compressed_data = fountaincoding.encode_and_compress(file, blocksize, extra = self.config.getint('settings', 'extra'))
        elif coding == 'LT-code':
            data = lt.encode.encoder(file, blocksize )
        else:
            self.config.set('settings', 'coding', 'QRStreamRaw') # to recover after error

        self.readdata = data
        self.readiter = iter(self.readdata)
        self.tabbedpanel.switch_to(self.qrwidgetheader)

    def on_config_change(self, config, section, key, value):
        if key == 'borderwidth':
            self.qrwidget.borderwidth = int(value)
        elif key == 'error':
            self.qrwidget.error = float(value[:value.find('%')])/100
        elif key == 'imagery':
            self.qrwidget.imagery = value
        elif key == 'coding':
            self.on_file_submit(self.filechooser, self.filechooser.selection, None)

    def on_current_tab(self, panel, header):
        self.lasttab = self.curtab
        self.curtab = header

        if self.lasttab is self.qrwidgetheader:
            self.on_leave_sending(self.lasttab)
        elif self.lasttab in self.cameraheaders:
            self.on_leave_camera(self.lasttab)

        if self.curtab is self.qrwidgetheader:
            self.on_enter_sending(self.curtab)
        elif self.curtab in self.cameraheaders:
            self.on_enter_camera(self.curtab)

    def on_enter_sending(self, qrcodeheader):
        self.interval = Clock.schedule_interval(self.on_update_sending, float(self.config.get('settings', 'duration')) / 1000)
    def on_leave_sending(self, qrcodeheader):
        self.interval.cancel()
    def on_update_sending(self, clock):
        if self.readiter is None:
            return
        count = 3 if self.config.getint('settings', 'multicolor') else 1
        datas = []
        for index in range(count):
            try:
                data = next(self.readiter)
            except StopIteration:
                self.readiter = iter(self.readdata)
                data = next(self.readiter)
            if self.config.getint('settings', 'base64'):
                data = base64.b64encode(data)
            datas.append(data)
        self.qrwidget.data = datas

    def on_enter_camera(self, cameraheader):
        if cameraheader.content is None:
            index = int(cameraheader.text[-1]) - 1

            # the camera is not created until used because it can
            # begin reading from the user's hardware in the constructor,
            # and they might not be planning to use it

            if not self.ensure_permission('CAMERA'):
                return self.tabbedpanel.switch_to(self.settingsheader)

            # TODO: android only wants one camera object, whereas desktop seems to prefer separate ones.
            #       probably prioritise android, as desktop usually has only 1 camera
            cameraheader.content = Camera(index = index, resolution = (960, 720), play = True)

            # this hack works around a control flow issue with adding
            # a widget inside the on_content event handler, where it
            # is replaced with the previous content
            # see https://github.com/kivy/kivy/issues/7221
            # this rest of this if block should likely be removed if that issue is closed
            self.tabbedpanel.clear_widgets()
            self.tabbedpanel.add_widget(cameraheader.content)
            correct_clear_widgets = self.tabbedpanel.clear_widgets
            def restore_clear_widgets():
                self.tabbedpanel.clear_widgets = correct_clear_widgets
            self.tabbedpanel.clear_widgets = restore_clear_widgets
        else:
            cameraheader.content.play = True
        cameraheader.content._camera.bind(on_texture = self.on_update_camera)
        detection = self.config.get('settings', 'detection')
        if detection == 'OCR':
            self.ocr = OCR()
    def on_leave_camera(self, cameraheader):
        detection = self.config.get('settings', 'detection')
        if detection == 'OCR':
            self.ocr.stop()
        camera = cameraheader.content
        if camera is not None:
            camera._camera.unbind(on_texture = self.on_update_camera)
            camera.play = False
        if self.writefile is not None:
            if self.ltdecoder is not None:
                # todo: store partial state
                self.ltdecoder.stream_dump(self.writefile)
            self.writefile.close()
            self.writefile = None
            print('Closed', self.writename)

    def on_update_camera(self, _camera):
        detection = self.config.get('settings', 'detection')
        if detection == 'QRCODE':
            image = PIL.Image.frombytes('RGBA', _camera.texture.size, _camera.texture.pixels)
            codes = zbarlight.scan_codes(['qrcode'], image)
        elif detection == 'OCR':
            self.ocr.provide(_camera.texture)
            codes = self.ocr.release()

        if codes is None:
            return

        if self.writefile is None:
            name = 'txqrapp_' + str(datetime.datetime.now()).replace(' ','_')
            if self.config.getint('settings', 'base64'):
                name += '_b64decode'
            name += '.dat'
            name = os.path.join(self.filechooser.path, name)
            self.writename = name
            self.writefile = open(name, 'wb')
            print('Opened', self.writename)
            coding = self.config.get('settings', 'coding')
            if coding == 'LT-code':
                self.ltdecoder = lt.decode.LtDecoder()
            else:
                self.ltdecoder = None

        # todo: display datarate or something in gui
        length = 0
        for data in codes:
            if self.config.getint('settings', 'base64'):
                data = base64.b64decode(data)
            if self.ltdecoder is not None:
                self.ltdecoder.consume_block(lt.decode.block_from_bytes(data))
                if self.ltdecoder.is_done():
                    print('lt done')
                    self.tabbedpanel.switch_to(self.filechooserheader)
                    self.filechooser._update_files()
                    self.filechooser.selection = [self.writename]
                    self.filechooser.layout.ids.scrollview.scroll_y = 0
                    return
                else:
                    print('not done yet')
            else:
                self.writefile.write(data)
            length += len(data)
        print(length)

    def ensure_permission(self, permission):
        if platform != 'android':
            return
        from android.permissions import check_permission, request_permission, Permission
        permission = getattr(Permission, permission)
        if check_permission(permission):
            return True
        return request_permission(permission)