text: from typing import Optional
  
  
  from pyzbar import pyzbar
  import asyncio
  import cv2
  
  
  from hermit.errors import InvalidSignatureRequest
  from .format import decode_qr_code_data
  from .utils import window_is_open
  
  
  def read_qr_code() -> Optional[str]:
      task = _capture_qr_code_async()
      return asyncio.get_event_loop().run_until_complete(task)
  
  
  async def _capture_qr_code_async() -> Optional[str]:
      capture = _start_camera()
      preview_dimensions = (640, 480)
      decoded_data = None
      encoded_data = None
      window_name = "Signature Request QR Code Scanner"
      cv2.namedWindow(window_name)
  
      while window_is_open(window_name):
          ret, frame = capture.read()
          frame = cv2.resize(frame, preview_dimensions)
  
          for qrcode in pyzbar.decode(frame):
              # Extract the position & dimensions of box bounding the QR
              # code
              (x, y, w, h) = qrcode.rect
              # Draw this bounding box on the image
              cv2.rectangle(
                  frame,
                  (x, y),
                  (x + w, y + h),
                  (0, 0, 255),
                  2)
  
              # Decode the QR code data
              encoded_data = qrcode.data
              try:
                  decoded_data = decode_qr_code_data(encoded_data)
              except InvalidSignatureRequest as e:
                  print("Invalid signature request: {}".format(str(e)))
  
              # Use the first QR code found
              if decoded_data is not None:
                  break
  
          # Preview the (reversed) frame
          mirror = cv2.flip(frame, 1)
          cv2.imshow(window_name, mirror)
  
          # Break out of the loop if we found a valid QR code
          if decoded_data:
              break
  
          await asyncio.sleep(0.01)
  
      # Clean up windows before exiting.
      capture.release()
      cv2.destroyWindow(window_name)
  
      return decoded_data
  
  
  def _start_camera() -> cv2.VideoCapture:
      capture = cv2.VideoCapture(0)
      if not capture.isOpened():
          raise IOError("Cannot open webcam")
      return capture
  