In commit 9bba65199e,
the QRCodeWidget was put inside a BoxLayout as a workaround to avoid the "copy to clipboard" and
"save as file" functionality grabbing extra whitespace/stretch/padding and putting it into the
exported image.
However, in turn that commit introduced a bug, where making the dialog larger does not make the
QRCodeWidget larger (which worked prior).
This commit tries to fix the regression and also the original bug.
182 lines
5.1 KiB
Python
182 lines
5.1 KiB
Python
from typing import Optional
|
|
|
|
import qrcode
|
|
|
|
from PyQt5.QtGui import QColor, QPen
|
|
import PyQt5.QtGui as QtGui
|
|
from PyQt5.QtCore import Qt, QRect
|
|
from PyQt5.QtWidgets import (
|
|
QApplication, QVBoxLayout, QTextEdit, QHBoxLayout, QPushButton, QWidget,
|
|
QFileDialog,
|
|
)
|
|
|
|
from electrum.i18n import _
|
|
from electrum.simple_config import SimpleConfig
|
|
|
|
from .util import WindowModalDialog, WWLabel, getSaveFileName
|
|
|
|
|
|
class QRCodeWidget(QWidget):
|
|
|
|
def __init__(self, data = None):
|
|
QWidget.__init__(self)
|
|
self.data = None
|
|
self.qr = None
|
|
self._framesize = None # type: Optional[int]
|
|
self.setData(data)
|
|
|
|
|
|
def setData(self, data):
|
|
if self.data != data:
|
|
self.data = data
|
|
if self.data:
|
|
self.qr = qrcode.QRCode(
|
|
error_correction=qrcode.constants.ERROR_CORRECT_L,
|
|
box_size=10,
|
|
border=0,
|
|
)
|
|
self.qr.add_data(self.data)
|
|
else:
|
|
self.qr = None
|
|
|
|
self.update()
|
|
|
|
|
|
def paintEvent(self, e):
|
|
if not self.data:
|
|
return
|
|
|
|
black = QColor(0, 0, 0, 255)
|
|
white = QColor(255, 255, 255, 255)
|
|
black_pen = QPen(black)
|
|
black_pen.setJoinStyle(Qt.MiterJoin)
|
|
|
|
if not self.qr:
|
|
qp = QtGui.QPainter()
|
|
qp.begin(self)
|
|
qp.setBrush(white)
|
|
qp.setPen(white)
|
|
r = qp.viewport()
|
|
qp.drawRect(0, 0, r.width(), r.height())
|
|
qp.end()
|
|
return
|
|
|
|
matrix = self.qr.get_matrix()
|
|
k = len(matrix)
|
|
qp = QtGui.QPainter()
|
|
qp.begin(self)
|
|
r = qp.viewport()
|
|
framesize = min(r.width(), r.height())
|
|
self._framesize = framesize
|
|
boxsize = int(framesize/(k + 2))
|
|
if boxsize < 2:
|
|
qp.drawText(0, 20, 'Cannot draw QR code:')
|
|
qp.drawText(0, 40, 'Boxsize too small')
|
|
qp.end()
|
|
return
|
|
size = k*boxsize
|
|
left = (framesize - size)/2
|
|
top = (framesize - size)/2
|
|
# Draw white background with margin
|
|
qp.setBrush(white)
|
|
qp.setPen(white)
|
|
qp.drawRect(0, 0, framesize, framesize)
|
|
# Draw qr code
|
|
qp.setBrush(black)
|
|
qp.setPen(black_pen)
|
|
for r in range(k):
|
|
for c in range(k):
|
|
if matrix[r][c]:
|
|
qp.drawRect(int(left+c*boxsize), int(top+r*boxsize),
|
|
boxsize - 1, boxsize - 1)
|
|
qp.end()
|
|
|
|
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)
|
|
qrw.setMinimumSize(250, 250)
|
|
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())
|