Guidance Requested for JS to Py

Hi, all!

What I’m trying to do:

I have tested two QR/Barcode scanner examples discussed in the forum. Both work on the method of using the camera to take a picture and then upload it to the server to be decoded. I prefer a quicker way of decoding, and have found a QR scanner by JS code:

Access Camera and Read QR code using JavaScript - Bing video

Following the example in the video I created a custom HTML page and added the following:

.result{ background-color: green; color:#fff; padding:20px; } .row{ display:flex; }

SCAN RESULT

Result Here

And it works! The camera captures the QR code and returns the code by the JS alert.

What I’ve tried and what’s not working:

  1. I have two questions:

See the line in the html snippet above? It is required for the JS function to use. Now in the UI design, how do I add it so that it can appear as an element in the translated html?

  1. I would like to have the JS function call rewritten in Py, so that I can have a method return the scanned content to use in subsequent code. I have looked at the Quill example a few times but still have not understood it. hope someone can point the direction to me.

Code Sample:

# this is a formatted code snippet.
# paste your code between ``` 

Clone link:
share a copy of your app

Clone of QR scanner | Anvil

In the app the page “QrScannerHtml5” is the custom HTML.

Thank you in advance for the guidance.

P. Redjo

What you’re describing can be done, but that link is a link to your account, not a clone link, so nobody else can use that link to see the app code.

If you’re using the classic editor, see here for instructions for a clone link: Anvil Docs | Cloning Apps For the beta editor, see here: Anvil Docs | Cloning Apps

1 Like

Sorry. How about this?

Sorry it should be this:

Two times wrong. Sorry. Here it is:

https://anvil.works/build#clone:UHI2LEKNQY6RWBTV=LA3QUZ5RFMWRZ447AMHO2QS2

That one works, thanks!

It looks like you’re using this library: https://github.com/schmich/instascan

That one shows sample code like this:

<body>
    <video id="preview"></video>
    <script type="text/javascript">
      let scanner = new Instascan.Scanner({ video: document.getElementById('preview') });
      scanner.addListener('scan', function (content) {
        console.log(content);
      });
      Instascan.Camera.getCameras().then(function (cameras) {
        if (cameras.length > 0) {
          scanner.start(cameras[0]);
        } else {
          console.error('No cameras found.');
        }
      }).catch(function (e) {
        console.error(e);
      });
    </script>
  </body>

When you’re converting to use the Anvil Python bridge, you’ll need to pass a Javascript element to the Instascan object. That’s the same element you get from js.get_dom_node

    element = anvil.js.get_dom_node(self.content_panel)
    self.scanner = Instascan.Scanner({"video": element});

After that, anytime you’re asked for a Javascript callback, use a Python function instead. You’re already doing that, so you should be good to keep testing.

None of the sample HTML for your first question showed. You probably pasted HTML into the post without quoting it. But, in general, you should be able to do everything through Python via the Javascript bridge.

2 Likes

Thank you very much! I have figured out how to do it. I just need to consolidate what I have learned so far. Thanks again!

1 Like

@jshaffstall,

I have to ask how to use a python function as js callback. I now I have the following js code:

> <script type="text/javascript">
>   let scanner = new Instascan.Scanner({ video: document.getElementById('preview') });
>   scanner.addListener('scan', onScanned);
> 
>   function onScanned(content) {
>     //alert(content);
>     return(content);
>   }
> 
>   Instascan.Camera.getCameras().then(function (cameras) {
>     if (cameras.length > 0) {
>  	  var selectedCam = cameras.length - 1;
>       scanner.start(cameras[selectedCam]);
>     } else {
>       console.error('No cameras found.');
>     }
>   }).catch(function (e) {
>     console.error(e);
>   });
> </script>

How do I replace the function “onScanned” used in scanner.addListener with a python function? I have created a python function on the “code” side in the same form:

def onScanned(self, content):
    self.scanResult = content
    alert(self.scanResult)
    return content

Please advise. Thank you!

Redjo

Don’t work in Javascript, work in Python. For most Javascript libraries, you shouldn’t need to write any Javascript functions.

You’ve already got the structure in your QrScannerHtml5 form, where you use anvil.js to access Instascan.Scanner and add a Python function as a listener to it.

The only thing I see with your QrScannerHtml5 form is that you probably want to move some of the code from your __init__ to a form_show event handler. The form and its HTML elements are not necessarily hooked into the DOM in the __init__, so these lines should go into form_show instead:

    element = anvil.js.get_dom_node(self)
    self.scanner = Instascan.Scanner('preview', element);
    self.scanner.addListener('scan', self.onScanSuccessful)
    self.doScan()
1 Like

@jshaffstall thanks for reading my post and advice. I have not succeeded because I still have no luck with the async code. I will leave it for a while. But thanks anyway.

@jshaffstall Hi! I have reorganized my code a bit in the process of understanding the JS code. The original JS snippet is revised to the following:

<script type="text/javascript">
let scanner;
function startScan(cameras) {
  if (cameras.length > 0) {
    var selectedCam = cameras[cameras.length-1];
    scanner.start(selectedCam);
  } else {
    alert('No cameras found.');
  }    
}
function getCamErr(e) {
    //console.error(e);
  alert(e);
}
function onScanSuccess_js(content) {
  scanner.stop();
  //alert('js ' + content);
  //anvil.call(this, 'onScanSuccess_py', content).then(function (r) {alert(r);})
}
function scanQrCode() {
  scanner = new Instascan.Scanner({ video: document.getElementById('preview') });
  Instascan.Camera.getCameras().then(startScan).catch(getCamErr);
  scanner.addListener('scan', onScanSuccess_js);
}
</script>

Primarily I separated the callback/event handler functions out for myself. The action is packed into function scanQrCode. On the Py side, I have

  def form_show(self, **event_args):
    """This method is called when the HTML panel is shown on the screen"""
    self.call_js('scanQrCode')

The code runs as intended. The problem I have now is how to make the scan result available to the Py side as well as other pages. After reading through the docs, I can see two possibilities:

  1. Use a Py function as callback in scanner.addListener. When it uses the Js function it is this:

scanner.addListener('scan', onScanSuccess_js);

Now I would like to use onScanSuccess_py to replace onScanSuccess_js:

  def onScanSuccess_py(self, result):
    global qrScanResult
    global scanCount
    Globals.qrScanResult = result
    Globals.scanCount += 1
    alert(Globals.qrScanResult)

But I have not seen any example how to do it.

  1. Alternatively, in onScanSuccess_js, just call onScanSuccess_py following example that I can find in the forum, this should look like

anvil.call(this, 'onScanSuccess_py', content).then(function (r) {alert(r);});

I have not been successful with either approach and need your advice. The code is in the following:

https://anvil.works/build#clone:NG6SOC2YQDBFESMS=HZFSGIGHQOGKXVR64KLPHFXW

see the QrScanner_JsPy page.

Thanks in advance.

Redjo

1 Like

My apologies! I found the cause why the Py callback didn’t work! It was because I had added the library

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://rawgit.com/schmich/instascan-builds/master/instascan.min.js"></script>

also in the Js snippet in the custom html. Earlier I had added the same in native library which I found necessary for mapping the Js code to Py, but did not (I really didn’t understand it) remove the same in the custom html. After removing it the callback works.

Thanks for the encouragement!

1 Like

This is to wrap up the topic. I have summarized my experience in the following app (see “ExplanatoryNote”):

https://anvil.works/build#clone:T7PCB5U4I5SGLNNH=JXX4QYWZLLFODU73BYBNZWOS