You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

157 lines
5.9 KiB

#!/usr/bin/env python3
#
# Electron Cash - lightweight Bitcoin client
# Copyright (C) 2019 Axel Gembe <derago@gmail.com>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
from typing import List
from PyQt5.QtWidgets import QWidget
from PyQt5.QtGui import QPainter, QPaintEvent, QPen, QPainterPath, QColor, QTransform
from PyQt5.QtCore import QPoint, QSize, QRect, QRectF, Qt
from electrum.qrreader import QrCodeResult
from .validator import QrReaderValidatorResult
class QrReaderVideoOverlay(QWidget):
"""
Overlays the QR scanner results over the video
"""
BG_RECT_PADDING = 10
BG_RECT_CORNER_RADIUS = 10.0
BG_RECT_OPACITY = 0.75
def __init__(self, parent: QWidget = None):
super().__init__(parent)
self.results = []
self.flip_x = False
self.validator_results = None
self.crop = None
self.resolution = None
self.qr_outline_pen = QPen()
self.qr_outline_pen.setColor(Qt.red)
self.qr_outline_pen.setWidth(3)
self.qr_outline_pen.setStyle(Qt.DotLine)
self.text_pen = QPen()
self.text_pen.setColor(Qt.black)
self.bg_rect_pen = QPen()
self.bg_rect_pen.setColor(Qt.black)
self.bg_rect_pen.setStyle(Qt.DotLine)
self.bg_rect_fill = QColor(255, 255, 255, 255 * self.BG_RECT_OPACITY)
def set_results(self, results: List[QrCodeResult], flip_x: bool,
validator_results: QrReaderValidatorResult):
self.results = results
self.flip_x = flip_x
self.validator_results = validator_results
self.update()
def set_crop(self, crop: QRect):
self.crop = crop
def set_resolution(self, resolution: QSize):
self.resolution = resolution
def paintEvent(self, _event: QPaintEvent):
if not self.crop or not self.resolution:
return
painter = QPainter(self)
# Keep a backup of the transform and create a new one
transform = painter.worldTransform()
# Set scaling transform
transform = transform.scale(self.width() / self.resolution.width(),
self.height() / self.resolution.height())
# Compute the transform to flip the coordinate system on the x axis
transform_flip = QTransform()
if self.flip_x:
transform_flip = transform_flip.translate(self.resolution.width(), 0.0)
transform_flip = transform_flip.scale(-1.0, 1.0)
# Small helper for tuple to QPoint
def toqp(point):
return QPoint(point[0], point[1])
# Starting from here we care about AA
painter.setRenderHint(QPainter.Antialiasing)
# Draw all the QR code results
for res in self.results:
painter.setWorldTransform(transform_flip * transform, False)
# Draw lines between all of the QR code points
pen = QPen(self.qr_outline_pen)
if res in self.validator_results.result_colors:
pen.setColor(self.validator_results.result_colors[res])
painter.setPen(pen)
num_points = len(res.points)
for i in range(0, num_points):
i_n = i + 1
line_from = toqp(res.points[i])
line_from += self.crop.topLeft()
line_to = toqp(res.points[i_n] if i_n < num_points else res.points[0])
line_to += self.crop.topLeft()
painter.drawLine(line_from, line_to)
# Draw the QR code data
# Note that we reset the world transform to only the scaled transform
# because otherwise the text could be flipped. We only use transform_flip
# to map the center point of the result.
painter.setWorldTransform(transform, False)
font_metrics = painter.fontMetrics()
data_metrics = QSize(font_metrics.horizontalAdvance(res.data), font_metrics.capHeight())
center_pos = toqp(res.center)
center_pos += self.crop.topLeft()
center_pos = transform_flip.map(center_pos)
text_offset = QPoint(data_metrics.width(), data_metrics.height())
text_offset = text_offset / 2
text_offset.setX(-text_offset.x())
center_pos += text_offset
padding = self.BG_RECT_PADDING
bg_rect_pos = center_pos - QPoint(padding, data_metrics.height() + padding)
bg_rect_size = data_metrics + (QSize(padding, padding) * 2)
bg_rect = QRect(bg_rect_pos, bg_rect_size)
bg_rect_path = QPainterPath()
radius = self.BG_RECT_CORNER_RADIUS
bg_rect_path.addRoundedRect(QRectF(bg_rect), radius, radius, Qt.AbsoluteSize)
painter.setPen(self.bg_rect_pen)
painter.fillPath(bg_rect_path, self.bg_rect_fill)
painter.drawPath(bg_rect_path)
painter.setPen(self.text_pen)
painter.drawText(center_pos, res.data)