SomberNight
4 years ago
17 changed files with 251 additions and 184 deletions
@ -1,30 +1,141 @@ |
|||||
#!/usr/bin/env python3 |
# Copyright (C) 2021 The Electrum developers |
||||
|
# Distributed under the MIT software license, see the accompanying |
||||
|
# file LICENCE or http://www.opensource.org/licenses/mit-license.php |
||||
# |
# |
||||
# Electron Cash - lightweight Bitcoin client |
# We have two toolchains to scan qr codes: |
||||
# Copyright (C) 2019 Axel Gembe <derago@gmail.com> |
# 1. access camera via QtMultimedia, take picture, feed picture to zbar |
||||
|
# 2. let zbar handle whole flow (including accessing the camera) |
||||
# |
# |
||||
# Permission is hereby granted, free of charge, to any person |
# notes: |
||||
# obtaining a copy of this software and associated documentation files |
# - zbar needs to be compiled with platform-dependent extra config options to be able |
||||
# (the "Software"), to deal in the Software without restriction, |
# to access the camera |
||||
# including without limitation the rights to use, copy, modify, merge, |
# - zbar fails to access the camera on macOS |
||||
# publish, distribute, sublicense, and/or sell copies of the Software, |
# - qtmultimedia seems to support more cameras on Windows than zbar |
||||
# and to permit persons to whom the Software is furnished to do so, |
# - qtmultimedia is often not packaged with PyQt5 |
||||
# subject to the following conditions: |
# in particular, on debian, you need both "python3-pyqt5" and "python3-pyqt5.qtmultimedia" |
||||
|
# - older versions of qtmultimedia don't seem to work reliably |
||||
# |
# |
||||
# The above copyright notice and this permission notice shall be |
# Considering the above, we use QtMultimedia for Windows and macOS, as there |
||||
# included in all copies or substantial portions of the Software. |
# most users run our binaries where we can make sure the packaged versions work well. |
||||
|
# On Linux where many people run from source, we use zbar. |
||||
# |
# |
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
# Note: this module is safe to import on all platforms. |
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
import sys |
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
from typing import Callable, Optional, TYPE_CHECKING, Mapping |
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
|
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
from PyQt5.QtWidgets import QMessageBox, QWidget |
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
||||
# SOFTWARE. |
from electrum.i18n import _ |
||||
|
from electrum.util import UserFacingException |
||||
from .camera_dialog import (QrReaderCameraDialog, CameraError, NoCamerasFound, |
from electrum.logging import get_logger |
||||
NoCameraResolutionsFound, MissingQrDetectionLib) |
|
||||
from .validator import (QrReaderValidatorResult, AbstractQrReaderValidator, |
from electrum.gui.qt.util import MessageBoxMixin, custom_message_box |
||||
QrReaderValidatorCounting, QrReaderValidatorColorizing, |
|
||||
QrReaderValidatorStrong, QrReaderValidatorCounted) |
|
||||
|
if TYPE_CHECKING: |
||||
|
from electrum.simple_config import SimpleConfig |
||||
|
|
||||
|
|
||||
|
_logger = get_logger(__name__) |
||||
|
|
||||
|
|
||||
|
def scan_qrcode( |
||||
|
*, |
||||
|
parent: Optional[QWidget], |
||||
|
config: 'SimpleConfig', |
||||
|
callback: Callable[[bool, str, Optional[str]], None], |
||||
|
) -> None: |
||||
|
if sys.platform == 'darwin' or sys.platform in ('windows', 'win32'): |
||||
|
_scan_qrcode_using_qtmultimedia(parent=parent, config=config, callback=callback) |
||||
|
else: # desktop Linux and similar |
||||
|
_scan_qrcode_using_zbar(parent=parent, config=config, callback=callback) |
||||
|
|
||||
|
|
||||
|
def find_system_cameras() -> Mapping[str, str]: |
||||
|
"""Returns a camera_description -> camera_path map.""" |
||||
|
if sys.platform == 'darwin' or sys.platform in ('windows', 'win32'): |
||||
|
try: |
||||
|
from .qtmultimedia import find_system_cameras |
||||
|
except ImportError as e: |
||||
|
return {} |
||||
|
else: |
||||
|
return find_system_cameras() |
||||
|
else: # desktop Linux and similar |
||||
|
from electrum import qrscanner |
||||
|
return qrscanner.find_system_cameras() |
||||
|
|
||||
|
|
||||
|
# --- Internals below (not part of external API) |
||||
|
|
||||
|
def _scan_qrcode_using_zbar( |
||||
|
*, |
||||
|
parent: Optional[QWidget], |
||||
|
config: 'SimpleConfig', |
||||
|
callback: Callable[[bool, str, Optional[str]], None], |
||||
|
) -> None: |
||||
|
from electrum import qrscanner |
||||
|
data = None |
||||
|
try: |
||||
|
data = qrscanner.scan_barcode(config.get_video_device()) |
||||
|
except UserFacingException as e: |
||||
|
success = False |
||||
|
error = str(e) |
||||
|
except BaseException as e: |
||||
|
_logger.exception('camera error') |
||||
|
success = False |
||||
|
error = repr(e) |
||||
|
else: |
||||
|
success = True |
||||
|
error = "" |
||||
|
callback(success, error, data) |
||||
|
|
||||
|
|
||||
|
# Use a global to prevent multiple QR dialogs created simultaneously |
||||
|
_qr_dialog = None |
||||
|
|
||||
|
|
||||
|
def _scan_qrcode_using_qtmultimedia( |
||||
|
*, |
||||
|
parent: Optional[QWidget], |
||||
|
config: 'SimpleConfig', |
||||
|
callback: Callable[[bool, str, Optional[str]], None], |
||||
|
) -> None: |
||||
|
try: |
||||
|
from .qtmultimedia import QrReaderCameraDialog, CameraError, MissingQrDetectionLib |
||||
|
except ImportError as e: |
||||
|
icon = QMessageBox.Warning |
||||
|
title = _("QR Reader Error") |
||||
|
message = _("QR reader failed to load. This may happen if " |
||||
|
"you are using an older version of PyQt5.") + "\n\n" + str(e) |
||||
|
if isinstance(parent, MessageBoxMixin): |
||||
|
parent.msg_box(title=title, text=message, icon=icon, parent=None) |
||||
|
else: |
||||
|
custom_message_box(title=title, text=message, icon=icon, parent=parent) |
||||
|
return |
||||
|
|
||||
|
global _qr_dialog |
||||
|
if _qr_dialog: |
||||
|
_logger.warning("QR dialog is already presented, ignoring.") |
||||
|
return |
||||
|
_qr_dialog = None |
||||
|
try: |
||||
|
_qr_dialog = QrReaderCameraDialog(parent=parent, config=config) |
||||
|
|
||||
|
def _on_qr_reader_finished(success: bool, error: str, data): |
||||
|
global _qr_dialog |
||||
|
if _qr_dialog: |
||||
|
_qr_dialog.deleteLater() |
||||
|
_qr_dialog = None |
||||
|
callback(success, error, data) |
||||
|
|
||||
|
_qr_dialog.qr_finished.connect(_on_qr_reader_finished) |
||||
|
_qr_dialog.start_scan(config.get_video_device()) |
||||
|
except (MissingQrDetectionLib, CameraError) as e: |
||||
|
_qr_dialog = None |
||||
|
callback(False, str(e), None) |
||||
|
except Exception as e: |
||||
|
_logger.exception('camera error') |
||||
|
_qr_dialog = None |
||||
|
callback(False, repr(e), None) |
||||
|
|
||||
|
@ -0,0 +1,39 @@ |
|||||
|
#!/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 Mapping |
||||
|
|
||||
|
from .camera_dialog import (QrReaderCameraDialog, CameraError, NoCamerasFound, |
||||
|
NoCameraResolutionsFound, MissingQrDetectionLib) |
||||
|
from .validator import (QrReaderValidatorResult, AbstractQrReaderValidator, |
||||
|
QrReaderValidatorCounting, QrReaderValidatorColorizing, |
||||
|
QrReaderValidatorStrong, QrReaderValidatorCounted) |
||||
|
|
||||
|
|
||||
|
def find_system_cameras() -> Mapping[str, str]: |
||||
|
"""Returns a camera_description -> camera_path map.""" |
||||
|
from PyQt5.QtMultimedia import QCameraInfo |
||||
|
system_cameras = QCameraInfo.availableCameras() |
||||
|
return {cam.description(): cam.deviceName() for cam in system_cameras} |
Loading…
Reference in new issue