1
0
Files
electrum/electrum/gui/qt/qrcodewidget.py

153 lines
4.4 KiB
Python

from typing import Optional
import qrcode
import qrcode.exceptions
import PyQt6.QtGui as QtGui
from PyQt6.QtCore import QRect
from PyQt6.QtWidgets import QApplication, QVBoxLayout, QHBoxLayout, QPushButton, QWidget
from electrum.i18n import _
from electrum.simple_config import SimpleConfig
from electrum.gui.common_qt.util import draw_qr
from .util import WindowModalDialog, WWLabel, getSaveFileName
class QrCodeDataOverflow(qrcode.exceptions.DataOverflowError):
pass
class QRCodeWidget(QWidget):
MIN_BOXSIZE = 2 # min size in pixels of single black/white unit box of the qr code
def __init__(self, data=None, *, manual_size: bool = False):
QWidget.__init__(self)
self.data = None
self.qr = None
self._framesize = None # type: Optional[int]
self._manual_size = manual_size
self.setData(data)
def setData(self, data):
if data:
qr = qrcode.QRCode(
error_correction=qrcode.constants.ERROR_CORRECT_L,
border=1,
)
try:
qr.add_data(data)
qr_matrix = qr.get_matrix() # test that data fits in QR code
except (ValueError, qrcode.exceptions.DataOverflowError) as e:
raise QrCodeDataOverflow() from e
self.qr = qr
self.data = data
if not self._manual_size:
k = len(qr_matrix)
size = min(k * 5, 150 + k * self.MIN_BOXSIZE)
self.setMinimumSize(size, size)
else:
self.qr = None
self.data = None
self.update()
def paintEvent(self, e):
if not self.data:
return
draw_qr(
qr=self.qr,
paint_device=self,
is_enabled=self.isEnabled(),
min_boxsize=self.MIN_BOXSIZE,
)
def grab(self) -> QtGui.QPixmap:
"""Overrides QWidget.grab to only include the QR code itself,
excluding horizontal/vertical stretch.
"""
fsize = self._framesize
if fsize is None:
fsize = -1
rect = QRect(0, 0, fsize, fsize)
return QWidget.grab(self, rect)
class QRDialog(WindowModalDialog):
def __init__(
self,
*,
data,
parent=None,
title="",
show_text=False,
help_text=None,
show_copy_text_btn=False,
config: SimpleConfig,
):
WindowModalDialog.__init__(self, parent, title)
self.config = config
vbox = QVBoxLayout()
qrw = QRCodeWidget(data, manual_size=False)
vbox.addWidget(qrw, 1)
help_text = data if show_text else help_text
if help_text:
text_label = WWLabel()
text_label.setText(help_text)
vbox.addWidget(text_label)
hbox = QHBoxLayout()
hbox.addStretch(1)
def print_qr():
filename = getSaveFileName(
parent=self,
title=_("Select where to save file"),
filename="qrcode.png",
config=self.config,
)
if not filename:
return
p = qrw.grab()
p.save(filename, 'png')
self.show_message(_("QR code saved to file") + " " + filename)
def copy_image_to_clipboard():
p = qrw.grab()
QApplication.clipboard().setPixmap(p)
self.show_message(_("QR code copied to clipboard"))
def copy_text_to_clipboard():
QApplication.clipboard().setText(data)
self.show_message(_("Text copied to clipboard"))
b = QPushButton(_("Copy Image"))
hbox.addWidget(b)
b.clicked.connect(copy_image_to_clipboard)
if show_copy_text_btn:
b = QPushButton(_("Copy Text"))
hbox.addWidget(b)
b.clicked.connect(copy_text_to_clipboard)
b = QPushButton(_("Save"))
hbox.addWidget(b)
b.clicked.connect(print_qr)
b = QPushButton(_("Close"))
hbox.addWidget(b)
b.clicked.connect(self.accept)
b.setDefault(True)
vbox.addLayout(hbox)
self.setLayout(vbox)
# note: the word-wrap on the text_label is causing layout sizing issues.
# see https://stackoverflow.com/a/25661985 and https://bugreports.qt.io/browse/QTBUG-37673
# workaround:
self.setMinimumSize(self.sizeHint())