Files
pallectrum/electrum/gui/common_qt/util.py
2025-04-10 10:22:29 +02:00

119 lines
3.3 KiB
Python

from typing import Optional
import os.path
from PyQt6 import QtGui
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QColor, QPen, QPaintDevice, QFontDatabase, QImage
import qrcode
from electrum.i18n import _
_cached_font_ids: dict[str, int] = {}
def get_font_id(filename: str) -> int:
font_id = _cached_font_ids.get(filename)
if font_id is not None:
return font_id
# font_id will be negative on error
font_id = QFontDatabase.addApplicationFont(
os.path.join(os.path.dirname(__file__), '..', 'fonts', filename)
)
_cached_font_ids[filename] = font_id
return font_id
def draw_qr(
*,
qr: Optional[qrcode.main.QRCode],
paint_device: QPaintDevice, # target to paint on
is_enabled: bool = True,
min_boxsize: int = 2, # min size in pixels of single black/white unit box of the qr code
) -> None:
"""Draw 'qr' onto 'paint_device'.
- qr.box_size is ignored. We will calculate our own boxsize to fill the whole size of paint_device.
- qr.border is respected.
"""
black = QColor(0, 0, 0, 255)
grey = QColor(196, 196, 196, 255)
white = QColor(255, 255, 255, 255)
black_pen = QPen(black) if is_enabled else QPen(grey)
black_pen.setJoinStyle(Qt.PenJoinStyle.MiterJoin)
if not qr:
qp = QtGui.QPainter()
qp.begin(paint_device)
qp.setBrush(white)
qp.setPen(white)
r = qp.viewport()
qp.drawRect(0, 0, r.width(), r.height())
qp.end()
return
# note: next line can raise qrcode.exceptions.DataOverflowError (or ValueError)
matrix = qr.get_matrix() # includes qr.border
k = len(matrix)
qp = QtGui.QPainter()
qp.begin(paint_device)
r = qp.viewport()
framesize = min(r.width(), r.height())
boxsize = int(framesize / k)
if boxsize < min_boxsize:
# The amount of data is still within what can fit into a QR code,
# however we don't have enough pixels to draw it.
qp.setBrush(white)
qp.setPen(white)
qp.drawRect(0, 0, r.width(), r.height())
qp.setBrush(black)
qp.setPen(black)
qp.drawText(0, 20, _("Cannot draw QR code") + ":")
qp.drawText(0, 40, _("Not enough space available."))
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 if is_enabled else grey)
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 paintQR(data) -> Optional[QImage]:
if not data:
return None
# Create QR code
qr = qrcode.QRCode()
qr.add_data(data)
# Create a QImage to draw on
matrix = qr.get_matrix()
k = len(matrix)
boxsize = 5
size = k * boxsize
# Create the image with appropriate size
base_img = QImage(size, size, QImage.Format.Format_ARGB32)
# Use draw_qr to paint on the image
draw_qr(
qr=qr,
paint_device=base_img,
is_enabled=True,
min_boxsize=boxsize
)
return base_img