1
0

Merge pull request #9955 from SomberNight/202506_qt_mac_camera_permission

qt gui: qrreader: macos: add runtime requesting of camera permission
This commit is contained in:
ghost43
2025-06-23 14:21:52 +00:00
committed by GitHub
3 changed files with 44 additions and 11 deletions

View File

@@ -97,7 +97,7 @@ from .update_checker import UpdateCheck, UpdateCheckThread
from .channels_list import ChannelsList
from .confirm_tx_dialog import ConfirmTxDialog
from .rbf_dialog import BumpFeeDialog, DSCancelDialog
from .qrreader import scan_qrcode
from .qrreader import scan_qrcode_from_camera
from .swap_dialog import SwapDialog, InvalidSwapParameters
from .balance_dialog import (BalanceToolButton, COLOR_FROZEN, COLOR_UNMATURED, COLOR_UNCONFIRMED, COLOR_CONFIRMED,
COLOR_LIGHTNING, COLOR_FROZEN_LIGHTNING)
@@ -2322,7 +2322,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
return
self.show_transaction(tx)
scan_qrcode(parent=self.top_level_window(), config=self.config, callback=cb)
scan_qrcode_from_camera(parent=self.top_level_window(), config=self.config, callback=cb)
def read_tx_from_file(self) -> Optional[Transaction]:
fileName = getOpenFileName(

View File

@@ -26,7 +26,8 @@ from typing import Callable, Optional, TYPE_CHECKING, Mapping, Sequence
from PyQt6.QtWidgets import QMessageBox, QWidget
from PyQt6.QtGui import QImage, QPainter, QColor
from PyQt6.QtCore import QRect
from PyQt6.QtCore import QRect, QCoreApplication
from PyQt6 import QtCore
from electrum.i18n import _
from electrum.util import UserFacingException
@@ -43,18 +44,24 @@ if TYPE_CHECKING:
_logger = get_logger(__name__)
def scan_qrcode(
def scan_qrcode_from_camera(
*,
parent: Optional[QWidget],
config: 'SimpleConfig',
callback: Callable[[bool, str, Optional[str]], None],
) -> None:
"""Scans QR code using camera."""
"""Scans QR code using camera. It handles requesting camera access permission from the OS if needed."""
assert parent is None or isinstance(parent, QWidget), f"parent should be a QWidget, not {parent!r}"
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 do_scan():
_scan_qrcode_from_camera(parent=parent, config=config, callback=callback)
if _has_camera_permission():
do_scan()
else:
# Request permission now. This is only a thing on macOS atm.
# Note: this assumes we are running on the main thread. Permissions can only be requested from the main thread.
app = QCoreApplication.instance()
app.requestPermission(QtCore.QCameraPermission(), lambda _x: do_scan())
def scan_qr_from_image(image: QImage) -> Sequence[QrCodeResult]:
@@ -181,3 +188,29 @@ def _scan_qrcode_using_qtmultimedia(
_qr_dialog = None
callback(False, repr(e), None)
def _scan_qrcode_from_camera(
*,
parent: Optional[QWidget],
config: 'SimpleConfig',
callback: Callable[[bool, str, Optional[str]], None],
) -> None:
"""Scans QR code using camera."""
assert parent is None or isinstance(parent, QWidget), f"parent should be a QWidget, not {parent!r}"
if not _has_camera_permission():
callback(False, _("Missing camera permission."), None)
return
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 _has_camera_permission() -> bool:
if not hasattr(QtCore, "QCameraPermission"): # requires Qt 6.5+
_logger.info(f"QtCore does not support QCameraPermission. This requires Qt 6.5+")
return True # hope for the best
app = QCoreApplication.instance()
permission_status = app.checkPermission(QtCore.QCameraPermission())
return permission_status == QtCore.Qt.PermissionStatus.Granted

View File

@@ -792,10 +792,10 @@ class GenericInputHandler:
except Exception as e:
show_error(_('Invalid payment identifier in QR') + ':\n' + repr(e))
from .qrreader import scan_qrcode
from .qrreader import scan_qrcode_from_camera
if parent is None:
parent = self if isinstance(self, QWidget) else None
scan_qrcode(parent=parent, config=config, callback=cb)
scan_qrcode_from_camera(parent=parent, config=config, callback=cb)
def input_qr_from_screenshot(
self,