Qt PayToEdit: add option to scan QR code from screen(shot)
this ports the following commits:448376e4416053f6f696
This commit is contained in:
committed by
SomberNight
parent
9d125118da
commit
b7b53e56bc
@@ -22,14 +22,15 @@
|
|||||||
# Note: this module is safe to import on all platforms.
|
# Note: this module is safe to import on all platforms.
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from typing import Callable, Optional, TYPE_CHECKING, Mapping
|
from typing import Callable, Optional, TYPE_CHECKING, Mapping, Sequence
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QMessageBox, QWidget
|
from PyQt5.QtWidgets import QMessageBox, QWidget
|
||||||
|
from PyQt5.QtGui import QImage
|
||||||
|
|
||||||
from electrum.i18n import _
|
from electrum.i18n import _
|
||||||
from electrum.util import UserFacingException
|
from electrum.util import UserFacingException
|
||||||
from electrum.logging import get_logger
|
from electrum.logging import get_logger
|
||||||
from electrum.qrreader import MissingQrDetectionLib
|
from electrum.qrreader import get_qr_reader, QrCodeResult, MissingQrDetectionLib
|
||||||
|
|
||||||
from electrum.gui.qt.util import MessageBoxMixin, custom_message_box
|
from electrum.gui.qt.util import MessageBoxMixin, custom_message_box
|
||||||
|
|
||||||
@@ -47,12 +48,26 @@ def scan_qrcode(
|
|||||||
config: 'SimpleConfig',
|
config: 'SimpleConfig',
|
||||||
callback: Callable[[bool, str, Optional[str]], None],
|
callback: Callable[[bool, str, Optional[str]], None],
|
||||||
) -> None:
|
) -> None:
|
||||||
|
"""Scans QR code using camera."""
|
||||||
if sys.platform == 'darwin' or sys.platform in ('windows', 'win32'):
|
if sys.platform == 'darwin' or sys.platform in ('windows', 'win32'):
|
||||||
_scan_qrcode_using_qtmultimedia(parent=parent, config=config, callback=callback)
|
_scan_qrcode_using_qtmultimedia(parent=parent, config=config, callback=callback)
|
||||||
else: # desktop Linux and similar
|
else: # desktop Linux and similar
|
||||||
_scan_qrcode_using_zbar(parent=parent, config=config, callback=callback)
|
_scan_qrcode_using_zbar(parent=parent, config=config, callback=callback)
|
||||||
|
|
||||||
|
|
||||||
|
def scan_qr_from_image(image: QImage) -> Sequence[QrCodeResult]:
|
||||||
|
"""Might raise exception: MissingQrDetectionLib."""
|
||||||
|
qr_reader = get_qr_reader()
|
||||||
|
image_y800 = image.convertToFormat(QImage.Format_Grayscale8)
|
||||||
|
res = qr_reader.read_qr_code(
|
||||||
|
image_y800.constBits().__int__(), image_y800.byteCount(),
|
||||||
|
image_y800.bytesPerLine(),
|
||||||
|
image_y800.width(),
|
||||||
|
image_y800.height()
|
||||||
|
)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
def find_system_cameras() -> Mapping[str, str]:
|
def find_system_cameras() -> Mapping[str, str]:
|
||||||
"""Returns a camera_description -> camera_path map."""
|
"""Returns a camera_description -> camera_path map."""
|
||||||
if sys.platform == 'darwin' or sys.platform in ('windows', 'win32'):
|
if sys.platform == 'darwin' or sys.platform in ('windows', 'win32'):
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ class ScanQRTextEdit(ButtonsTextEdit, MessageBoxMixin):
|
|||||||
|
|
||||||
def contextMenuEvent(self, e):
|
def contextMenuEvent(self, e):
|
||||||
m = self.createStandardContextMenu()
|
m = self.createStandardContextMenu()
|
||||||
m.addAction(_("Read QR code"), self.on_qr_input_btn)
|
m.addSeparator()
|
||||||
|
m.addAction(_("Read QR code from camera"), self.on_qr_from_camera_input_btn)
|
||||||
|
m.addAction(_("Read QR code from screen"), self.on_qr_from_screenshot_input_btn)
|
||||||
m.exec_(e.globalPos())
|
m.exec_(e.globalPos())
|
||||||
|
|
||||||
|
|
||||||
@@ -46,6 +48,8 @@ class ScanShowQRTextEdit(ButtonsTextEdit, MessageBoxMixin):
|
|||||||
|
|
||||||
def contextMenuEvent(self, e):
|
def contextMenuEvent(self, e):
|
||||||
m = self.createStandardContextMenu()
|
m = self.createStandardContextMenu()
|
||||||
m.addAction(_("Read QR code"), self.on_qr_input_btn)
|
m.addSeparator()
|
||||||
|
m.addAction(_("Read QR code from camera"), self.on_qr_from_camera_input_btn)
|
||||||
|
m.addAction(_("Read QR code from screen"), self.on_qr_from_screenshot_input_btn)
|
||||||
m.addAction(_("Show as QR code"), self.on_qr_show_btn)
|
m.addAction(_("Show as QR code"), self.on_qr_show_btn)
|
||||||
m.exec_(e.globalPos())
|
m.exec_(e.globalPos())
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from electrum.i18n import _, languages
|
|||||||
from electrum.util import FileImportFailed, FileExportFailed, make_aiohttp_session, resource_path
|
from electrum.util import FileImportFailed, FileExportFailed, make_aiohttp_session, resource_path
|
||||||
from electrum.invoices import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED
|
from electrum.invoices import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED
|
||||||
from electrum.logging import Logger
|
from electrum.logging import Logger
|
||||||
|
from electrum.qrreader import MissingQrDetectionLib
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .main_window import ElectrumWindow
|
from .main_window import ElectrumWindow
|
||||||
@@ -879,7 +880,7 @@ class OverlayControlMixin:
|
|||||||
# The old code positioned the items the other way around, so we just insert at position 0 instead
|
# The old code positioned the items the other way around, so we just insert at position 0 instead
|
||||||
self.overlay_layout.insertWidget(0, widget)
|
self.overlay_layout.insertWidget(0, widget)
|
||||||
|
|
||||||
def addButton(self, icon_name: str, on_click, tooltip: str) -> QAbstractButton:
|
def addButton(self, icon_name: str, on_click, tooltip: str) -> QPushButton:
|
||||||
button = QPushButton(self.overlay_widget)
|
button = QPushButton(self.overlay_widget)
|
||||||
button.setToolTip(tooltip)
|
button.setToolTip(tooltip)
|
||||||
button.setIcon(read_QIcon(icon_name))
|
button.setIcon(read_QIcon(icon_name))
|
||||||
@@ -943,7 +944,7 @@ class OverlayControlMixin:
|
|||||||
):
|
):
|
||||||
if setText is None:
|
if setText is None:
|
||||||
setText = self.setText
|
setText = self.setText
|
||||||
def qr_input():
|
def qr_from_camera_input() -> None:
|
||||||
def cb(success: bool, error: str, data):
|
def cb(success: bool, error: str, data):
|
||||||
if not success:
|
if not success:
|
||||||
if error:
|
if error:
|
||||||
@@ -960,10 +961,39 @@ class OverlayControlMixin:
|
|||||||
from .qrreader import scan_qrcode
|
from .qrreader import scan_qrcode
|
||||||
scan_qrcode(parent=self, config=config, callback=cb)
|
scan_qrcode(parent=self, config=config, callback=cb)
|
||||||
|
|
||||||
|
def qr_from_screenshot_input() -> None:
|
||||||
|
from .qrreader import scan_qr_from_image
|
||||||
|
scanned_qr = None
|
||||||
|
for screen in QApplication.instance().screens():
|
||||||
|
try:
|
||||||
|
scan_result = scan_qr_from_image(screen.grabWindow(0).toImage())
|
||||||
|
except MissingQrDetectionLib as e:
|
||||||
|
show_error(_("Unable to scan image.") + "\n" + repr(e))
|
||||||
|
return
|
||||||
|
if len(scan_result) > 0:
|
||||||
|
if (scanned_qr is not None) or len(scan_result) > 1:
|
||||||
|
show_error(_("More than one QR code was found on the screen."))
|
||||||
|
return
|
||||||
|
scanned_qr = scan_result
|
||||||
|
if scanned_qr is None:
|
||||||
|
show_error(_("No QR code was found on the screen."))
|
||||||
|
return
|
||||||
|
data = scanned_qr[0].data
|
||||||
|
if allow_multi:
|
||||||
|
new_text = self.text() + data + '\n'
|
||||||
|
else:
|
||||||
|
new_text = data
|
||||||
|
setText(new_text)
|
||||||
|
|
||||||
icon = "camera_white.png" if ColorScheme.dark_scheme else "camera_dark.png"
|
icon = "camera_white.png" if ColorScheme.dark_scheme else "camera_dark.png"
|
||||||
self.addButton(icon, qr_input, _("Read QR code"))
|
btn = self.addButton(icon, lambda: None, _("Read QR code"))
|
||||||
# side-effect: we export this method:
|
menu = QMenu()
|
||||||
self.on_qr_input_btn = qr_input
|
menu.addAction(_("Read QR code from camera"), qr_from_camera_input)
|
||||||
|
menu.addAction(_("Read QR code from screen"), qr_from_screenshot_input)
|
||||||
|
btn.setMenu(menu)
|
||||||
|
# side-effect: we export these methods:
|
||||||
|
self.on_qr_from_camera_input_btn = qr_from_camera_input
|
||||||
|
self.on_qr_from_screenshot_input_btn = qr_from_screenshot_input
|
||||||
|
|
||||||
def add_file_input_button(
|
def add_file_input_button(
|
||||||
self,
|
self,
|
||||||
|
|||||||
Reference in New Issue
Block a user