Guía 31 – Lector de códigos QR

Llevamos unos años que los códigos QR se han puesto de moda.

Se usan frecuentemente para facilitar dirección URL, pero también para facilitar un bloque de datos o como verificación de un documento electrónico trasladado a papel.

Dentro de mis guías, en concreto en la guía 3, expliqué cómo hacer imágenes con código de este tipo para ponerla en documentos. También existe un plugin descargable, que permite hacer código QR en JavaScript y presentarlo en la pantalla.

Lo que faltaba, al menos en mi caso, es que pudiéramos disponer de una aplicación PHPRunner que pudiera incorporar la lectura de los QR en nuestras aplicaciones y justo eso, es de lo que trata este artículo.

Objetivo

Poder leer códigos QR dentro de una aplicación, utilizando las cámaras del dispositivo donde se esté utilizando la aplicación.

DEMO: https://fhumanes.com/scannerQR/scannerqr_add.php

Solución Técnica

Desde el inicio de la búsqueda de información vi que desde JavaScript, con bastante antigüedad, se indicaba que era posible hacer esta función de lectura de QR. He revisado muchos ejemplos, normalmente incompletos, hasta encontrar una solución que me gustaba y resultaba fácil integrarla en PHPRunner.

https://github.com/schmich/instascan

Con este producto conseguí completar un ejemplo que funcionara:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>demo 2</title>
    <!-- <script type="text/javascript" src="js/instascan.js"></script> -->
    <script type="text/javascript" src="https://rawgit.com/schmich/instascan-builds/master/instascan.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>

    <!-- Latest compiled and minified CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css"
        integrity="sha384-HSMxcRTRxnN+Bdg0JdbxYKrThecOKuH5zCYotlSAcp1+c8xmyTe9GYg1l9a69psu" crossorigin="anonymous">
    <!-- Latest compiled and minified JavaScript -->
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"
        integrity="sha384-aJ21OjlMXNL5UyIl/XNwTMqvzeRMZH2w8c5cRVpzpU8Y5bApTppSuUkhZXN0VxHd" crossorigin="anonymous">
    </script>
</head>

<body>
    <style>
        #preview {
            width: 500px;
            height: 500px;
            margin: 0px auto;
        }
    </style>
    <video id="preview"></video>
    <div class="btn-group btn-group-toggle mb-5" data-toggle="buttons">
        <label class="btn btn-primary active">
            <input type="radio" name="options" value="1" autocomplete="off" checked> Front Camera
        </label>
        <label class="btn btn-secondary">
            <input type="radio" name="options" value="2" autocomplete="off"> Back Camera 1
        </label>
    <label class="btn btn-secondary">
            <input type="radio" name="options" value="3" autocomplete="off"> Back Camera 2
        </label>
    </div>
    <script type="text/javascript">
        var scanner = new Instascan.Scanner({
            video: document.getElementById('preview'),
            scanPeriod: 5,
            mirror: false
        });
        scanner.addListener('scan', function (content) {
            alert(content);
            //window.location.href=content;
        });
        Instascan.Camera.getCameras().then(function (cameras) {
            if (cameras.length > 0) {
        alert('Camaras: '+ cameras.length);
                scanner.start(cameras[0]);
                $('[name="options"]').on('change', function () {
                    if ($(this).val() == 1) {
                        if (cameras[0] != "") {
                            scanner.start(cameras[0]);
                        } else {
                            alert('No Front camera found!');
                        }
                    } else if ($(this).val() == 2) {
                        if (cameras[1] != "") {
                            scanner.start(cameras[1]);
                        } else {
                            alert('No Back camera 1 found!');
                        }
                    } else if ($(this).val() == 3) {
                        if (cameras[2] != "") {
                            scanner.start(cameras[2]);
                        } else {
                            alert('No Back camera 2 found!');
                        }
                    }
                });
            } else {
                console.error('No cameras found.');
                alert('No cameras found.');
            }
        }).catch(function (e) {
            console.error(e);
            alert(e);
        });
    </script>
</body>
</html>

Para integrar la solución he divido el código en 2 partes.

    • El HTML lo he puesto en un código SNIPPET “scannerqr_snippet”
      $html = <<<EOT
      <script type="text/javascript" src="https://rawgit.com/schmich/instascan-builds/master/instascan.min.js"></script>
      <style>
        #preview {
          width: 300px;
          height: 300px;
          margin: 0px auto;
        }
      </style>
      <div>
      <video id="preview"></video>
      </div>
      <div align="left"  class="btn-group btn-group-toggle mb-5" data-toggle="buttons">
          <label class="btn btn-primary active">
              <input type="radio" name="options" value="1" autocomplete="off" checked> Front Camera
          </label>
          <label class="btn btn-success">
              <input type="radio" name="options" value="2" autocomplete="off"> Back Camera 1
          </label>
          <label class="btn btn-info">
              <input type="radio" name="options" value="3" autocomplete="off"> Back Camera 2
          </label>
      </div>
      EOT;
      echo $html;
    • El JavaScript lo he puesto en un código en el evento “JavaScript OnLoad Event”
      var ctrlQR = Runner.getControl(pageid, 'text');
      ctrlQR.makeReadonly();
      
      var scanner = new Instascan.Scanner({
        video: document.getElementById('preview'),
        scanPeriod: 5,
        mirror: false
      });
      scanner.addListener('scan', function (content) {
        // alert(content);
        ctrlQR.setValue(content);
        var audio = new Audio('MyCode/beep.mp3');
        audio.play();
      
      });
      Instascan.Camera.getCameras().then(function (cameras) {
        if (cameras.length > 0) {
          // alert('Camaras: '+ cameras.length);
           scanner.start(cameras[0]);
           $('[name="options"]').on('change', function () {
              if ($(this).val() == 1) {
                if (cameras[0] != "") {
                   scanner.start(cameras[0]);
                } else {
                   alert('No Front camera found!');
                }
              } else if ($(this).val() == 2) {
                if (cameras[1] != "") {
                   scanner.start(cameras[1]);
                } else {
                   alert('No Back camera 1 found!');
                }
              } else if ($(this).val() == 3) {
                if (cameras[2] != "") {
                   scanner.start(cameras[2]);
                } else {
                   alert('No Back camera 2 found!');
                }
              }
           });
        } else {
           console.error('No cameras found.');
           alert('No cameras found.');
        }
      }).catch(function (e) {
        console.error(e);
        alert(e);
      });

      Y así de sencillo ha quedado la integración.

Como siempre, para cualquier duda o necesidad, contactar conmigo a través del email [email protected]

También, os dejo los fuentes para que lo podáis instalar en vuestros Windows y podáis hacer pruebas.

Veréis que he dejado el enlace  en  un CDN para descargar la librería de JavaScript. Si queréis disponer de ella en local no la podéis incluir en el proyecto de PHPRunner porque éste hace algo que la modifica y da error. Dejarla fuera del proyecto.

Adjuntos

Archivo Tamaño de archivo Descargas
zip PHPRunner 10.4 y Backup de Base de Datos 38 KB 578

Blog personal para facilitar soporte gratuito a usuarios de PHPRunner