Merge pull request #9782 from f321x/fix_qr_input_from_file
qt: add qr reading from file to ScanQRTextEdit and SendTab
This commit is contained in:
@@ -161,6 +161,13 @@ class PayToEdit(QWidget, Logger, GenericInputHandler):
|
||||
show_error=self.send_tab.show_error,
|
||||
setText=self.try_payment_identifier,
|
||||
)
|
||||
self.on_qr_from_file_input_btn = partial(
|
||||
self.input_qr_from_file,
|
||||
allow_multi=False,
|
||||
config=self.config,
|
||||
show_error=self.send_tab.show_error,
|
||||
setText=self.try_payment_identifier,
|
||||
)
|
||||
self.on_input_file = partial(
|
||||
self.input_file,
|
||||
config=self.config,
|
||||
|
||||
@@ -25,7 +25,8 @@ import sys
|
||||
from typing import Callable, Optional, TYPE_CHECKING, Mapping, Sequence
|
||||
|
||||
from PyQt6.QtWidgets import QMessageBox, QWidget
|
||||
from PyQt6.QtGui import QImage
|
||||
from PyQt6.QtGui import QImage, QPainter, QColor
|
||||
from PyQt6.QtCore import QRect
|
||||
|
||||
from electrum.i18n import _
|
||||
from electrum.util import UserFacingException
|
||||
@@ -59,16 +60,34 @@ def scan_qrcode(
|
||||
def scan_qr_from_image(image: QImage) -> Sequence[QrCodeResult]:
|
||||
"""Might raise exception: MissingQrDetectionLib."""
|
||||
qr_reader = get_qr_reader()
|
||||
image_y800 = image.convertToFormat(QImage.Format.Format_Grayscale8)
|
||||
res = qr_reader.read_qr_code(
|
||||
image_y800.constBits().__int__(),
|
||||
image_y800.sizeInBytes(),
|
||||
image_y800.bytesPerLine(),
|
||||
image_y800.width(),
|
||||
image_y800.height(),
|
||||
)
|
||||
|
||||
for attempt in range(4):
|
||||
image_y800 = image.convertToFormat(QImage.Format.Format_Grayscale8)
|
||||
res = qr_reader.read_qr_code(
|
||||
image_y800.constBits().__int__(),
|
||||
image_y800.sizeInBytes(),
|
||||
image_y800.bytesPerLine(),
|
||||
image_y800.width(),
|
||||
image_y800.height(),
|
||||
)
|
||||
if res:
|
||||
break
|
||||
# zbar doesn't like qr codes that are too large in relation to the whole image
|
||||
image = _reduce_qr_code_density(image)
|
||||
return res
|
||||
|
||||
def _reduce_qr_code_density(image: QImage) -> QImage:
|
||||
""" Reduces the size of the qr code relative to the whole image. """
|
||||
new_image = QImage(image.width(), image.height(), QImage.Format.Format_RGB32)
|
||||
new_image.fill(QColor(255, 255, 255)) # Fill white
|
||||
|
||||
painter = QPainter(new_image)
|
||||
source_rect = QRect(0, 0, image.width(), image.height())
|
||||
target_rect = QRect(0, 0, int(image.width() * 0.75), int(image.height() * 0.75))
|
||||
painter.drawImage(target_rect, image, source_rect)
|
||||
painter.end()
|
||||
|
||||
return new_image
|
||||
|
||||
def find_system_cameras() -> Mapping[str, str]:
|
||||
"""Returns a camera_description -> camera_path map."""
|
||||
|
||||
@@ -47,6 +47,13 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin):
|
||||
show_error=self.show_error,
|
||||
setText=setText,
|
||||
)
|
||||
self.on_qr_from_file_input_btn = partial(
|
||||
self.input_qr_from_file,
|
||||
allow_multi=allow_multi,
|
||||
config=config,
|
||||
show_error=self.show_error,
|
||||
setText=setText,
|
||||
)
|
||||
self.on_input_file = partial(
|
||||
self.input_file,
|
||||
config=config,
|
||||
@@ -62,7 +69,8 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin):
|
||||
self.add_menu_button(
|
||||
options=[
|
||||
("picture_in_picture.png", _("Read QR code from screen"), self.on_qr_from_screenshot_input_btn),
|
||||
("file.png", _("Read file"), self.on_input_file),
|
||||
("qr_file.png", _("Read QR code from file"), self.on_qr_from_file_input_btn),
|
||||
("file.png", _("Read text from file"), self.on_input_file),
|
||||
],
|
||||
)
|
||||
self.add_qr_input_from_camera_button(config=config, show_error=self.show_error, allow_multi=allow_multi, setText=setText)
|
||||
@@ -72,7 +80,8 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin):
|
||||
m.addSeparator()
|
||||
m.addAction(get_icon_camera(), _("Read QR code with camera"), self.on_qr_from_camera_input_btn)
|
||||
m.addAction(read_QIcon("picture_in_picture.png"), _("Read QR code from screen"), self.on_qr_from_screenshot_input_btn)
|
||||
m.addAction(read_QIcon("file.png"), _("Read file"), self.on_input_file)
|
||||
m.addAction(read_QIcon("qr_file.png"), _("Read QR code from file"), self.on_qr_from_file_input_btn)
|
||||
m.addAction(read_QIcon("file.png"), _("Read text from file"), self.on_input_file)
|
||||
m.exec(e.globalPos())
|
||||
|
||||
|
||||
|
||||
@@ -180,6 +180,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
|
||||
|
||||
menu.addAction(get_icon_camera(), _("Read QR code with camera"), self.payto_e.on_qr_from_camera_input_btn)
|
||||
menu.addAction(read_QIcon("picture_in_picture.png"), _("Read QR code from screen"), self.payto_e.on_qr_from_screenshot_input_btn)
|
||||
menu.addAction(read_QIcon("qr_file.png"), _("Read QR code from file"), self.payto_e.on_qr_from_file_input_btn)
|
||||
menu.addAction(read_QIcon("file.png"), _("Read invoice from file"), self.payto_e.on_input_file)
|
||||
self.paytomany_menu = menu.addToggle(_("&Pay to many"), self.toggle_paytomany)
|
||||
menu.addSeparator()
|
||||
|
||||
@@ -839,6 +839,8 @@ class GenericInputHandler:
|
||||
fileName = getOpenFileName(
|
||||
parent=None,
|
||||
title='select file',
|
||||
# trying to open non-text things like pdfs makes electrum freeze
|
||||
filter="Text files (*.txt *.csv);;All files (*)",
|
||||
config=config,
|
||||
)
|
||||
if not fileName:
|
||||
@@ -859,6 +861,52 @@ class GenericInputHandler:
|
||||
except Exception as e:
|
||||
show_error(_('Invalid payment identifier in file') + ':\n' + repr(e))
|
||||
|
||||
def input_qr_from_file(
|
||||
self,
|
||||
*,
|
||||
allow_multi: bool = False,
|
||||
config: 'SimpleConfig',
|
||||
show_error: Callable[[str], None],
|
||||
setText: Callable[[str], None] = None,
|
||||
):
|
||||
from .qrreader import scan_qr_from_image
|
||||
if setText is None:
|
||||
setText = self.setText
|
||||
|
||||
file_name = getOpenFileName(
|
||||
parent=None,
|
||||
title=_("Select image file"),
|
||||
config=config,
|
||||
filter="Image files (*.png *.jpg *.jpeg *.bmp);;",
|
||||
)
|
||||
if not file_name:
|
||||
return
|
||||
image = QImage(file_name)
|
||||
if image.isNull():
|
||||
show_error(_("Failed to open image file."))
|
||||
return
|
||||
try:
|
||||
scan_result: Sequence[QrCodeResult] = scan_qr_from_image(image)
|
||||
except MissingQrDetectionLib as e:
|
||||
show_error(_("Unable to scan image.") + "\n" + repr(e))
|
||||
return
|
||||
if len(scan_result) < 1:
|
||||
show_error(_("No QR code was found in the image."))
|
||||
return
|
||||
if len(scan_result) > 1 and not allow_multi:
|
||||
show_error(_("More than one QR code was found in the image."))
|
||||
return
|
||||
|
||||
if len(scan_result) > 1:
|
||||
result_text = "\n".join([r.data for r in scan_result])
|
||||
else:
|
||||
result_text = scan_result[0].data
|
||||
|
||||
try:
|
||||
setText(result_text)
|
||||
except Exception as e:
|
||||
show_error(_("Couldn't set result") + ':\n' + repr(e))
|
||||
|
||||
def input_paste_from_clipboard(
|
||||
self,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user