Skip to content

Commit c1a262d

Browse files
update(qrcode-basic): matplotlib — scannable QR codes (#5220)
## Summary Updated **matplotlib** implementation for **qrcode-basic**. **Changes:** Use `qrcode` library for real scannable QR code generation instead of manual matrix construction (fixes #3413) ### Changes - Replaced manual QR matrix with `qrcode` library for proper encoding - QR code now encodes "https://pyplots.ai" and is scannable by standard readers - Maintained clean visual style with proper quiet zone and high contrast - Updated spec to require scannable output and primary `qrcode` library recommendation ## Test Plan - [x] Preview images uploaded to GCS staging - [x] Implementation file passes ruff format/check - [x] Metadata YAML updated with current versions - [ ] Automated review triggered --- Generated with [Claude Code](https://claude.com/claude-code) `/update` command --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 15dd9f8 commit c1a262d

2 files changed

Lines changed: 230 additions & 203 deletions

File tree

Lines changed: 92 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,106 @@
11
""" pyplots.ai
22
qrcode-basic: Basic QR Code Generator
3-
Library: matplotlib 3.10.8 | Python 3.13.11
4-
Quality: 91/100 | Created: 2026-01-07
3+
Library: matplotlib 3.10.8 | Python 3.14.3
4+
Quality: 90/100 | Updated: 2026-04-07
55
"""
66

7+
import matplotlib.patches as mpatches
8+
import matplotlib.patheffects as pe
79
import matplotlib.pyplot as plt
810
import numpy as np
11+
import qrcode
12+
from matplotlib.colors import ListedColormap
13+
14+
15+
# Data - Generate a real, scannable QR code encoding "https://pyplots.ai"
16+
content = "https://pyplots.ai"
17+
18+
qr = qrcode.QRCode(version=1, error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=1, border=4)
19+
qr.add_data(content)
20+
qr.make(fit=True)
21+
22+
# Convert QR code to numpy matrix
23+
qr_matrix = np.array(qr.get_matrix(), dtype=int)
24+
rows, cols = qr_matrix.shape
25+
26+
# Plot
27+
fig, ax = plt.subplots(figsize=(12, 12), facecolor="white")
28+
29+
# Custom two-color colormap for crisp rendering
30+
qr_cmap = ListedColormap(["#FFFFFF", "#1A1A2E"])
31+
ax.imshow(qr_matrix, cmap=qr_cmap, interpolation="nearest", vmin=0, vmax=1, aspect="equal")
32+
33+
# Completely remove axes and all frame artifacts
34+
ax.set_axis_off()
35+
for spine in ax.spines.values():
36+
spine.set_visible(False)
37+
ax.set_frame_on(False)
38+
39+
# Decorative rounded-corner frame around the QR code using FancyBboxPatch
40+
padding = 0.8
41+
frame = mpatches.FancyBboxPatch(
42+
(-padding, -padding),
43+
cols - 1 + 2 * padding,
44+
rows - 1 + 2 * padding,
45+
boxstyle=mpatches.BoxStyle.Round(pad=0.6, rounding_size=1.5),
46+
facecolor="none",
47+
edgecolor="#306998",
48+
linewidth=2.5,
49+
zorder=5,
50+
)
51+
ax.add_patch(frame)
52+
53+
# Subtle shadow frame behind the main frame for depth
54+
shadow = mpatches.FancyBboxPatch(
55+
(-padding + 0.15, -padding + 0.15),
56+
cols - 1 + 2 * padding,
57+
rows - 1 + 2 * padding,
58+
boxstyle=mpatches.BoxStyle.Round(pad=0.6, rounding_size=1.5),
59+
facecolor="none",
60+
edgecolor="#306998",
61+
linewidth=2.5,
62+
alpha=0.15,
63+
zorder=4,
64+
)
65+
ax.add_patch(shadow)
966

67+
# Extend view to accommodate frame and annotations
68+
ax.set_xlim(-2.5, cols + 1.5)
69+
ax.set_ylim(rows + 1.5, -2.5)
1070

11-
# Data - Create a QR code-like pattern encoding "pyplots.ai"
12-
# Note: This is a simplified visual representation of a QR code structure
13-
np.random.seed(42)
14-
15-
# QR code dimensions (Version 1: 21x21 modules)
16-
size = 21
17-
18-
# Initialize the QR code matrix (0 = white, 1 = black)
19-
qr_matrix = np.zeros((size, size), dtype=int)
71+
fig.subplots_adjust(left=0.06, right=0.94, top=0.87, bottom=0.14)
2072

21-
# Position Detection Patterns (finder patterns) - 7x7 in three corners
22-
finder_pattern = np.array(
23-
[
24-
[1, 1, 1, 1, 1, 1, 1],
25-
[1, 0, 0, 0, 0, 0, 1],
26-
[1, 0, 1, 1, 1, 0, 1],
27-
[1, 0, 1, 1, 1, 0, 1],
28-
[1, 0, 1, 1, 1, 0, 1],
29-
[1, 0, 0, 0, 0, 0, 1],
30-
[1, 1, 1, 1, 1, 1, 1],
31-
]
73+
# Title with patheffects for subtle glow
74+
title = ax.set_title(
75+
"qrcode-basic \u00b7 matplotlib \u00b7 pyplots.ai", fontsize=28, fontweight="bold", pad=30, color="#306998"
76+
)
77+
title.set_path_effects([pe.withStroke(linewidth=3, foreground="white"), pe.Normal()])
78+
79+
# Accent rule line using axhline in figure coordinates via fig.add_axes
80+
accent_ax = fig.add_axes([0.25, 0.105, 0.50, 0.002])
81+
accent_ax.set_xlim(0, 1)
82+
accent_ax.set_ylim(0, 1)
83+
gradient = np.linspace(0, 1, 256).reshape(1, -1)
84+
accent_ax.imshow(gradient, aspect="auto", cmap=ListedColormap(["#306998", "#FFD43B"]), extent=[0, 1, 0, 1])
85+
accent_ax.set_axis_off()
86+
87+
# Metadata text with patheffects
88+
encoded_text = fig.text(
89+
0.5,
90+
0.085,
91+
f"Encoded: {content}",
92+
ha="center",
93+
fontsize=20,
94+
color="#444444",
95+
family="monospace",
96+
fontweight="medium",
3297
)
98+
encoded_text.set_path_effects([pe.withStroke(linewidth=2, foreground="white"), pe.Normal()])
3399

34-
# Place finder patterns in three corners
35-
qr_matrix[0:7, 0:7] = finder_pattern # Top-left
36-
qr_matrix[0:7, size - 7 : size] = finder_pattern # Top-right
37-
qr_matrix[size - 7 : size, 0:7] = finder_pattern # Bottom-left
38-
39-
# Timing patterns (alternating black/white lines)
40-
for i in range(8, size - 8):
41-
qr_matrix[6, i] = i % 2 # Horizontal
42-
qr_matrix[i, 6] = i % 2 # Vertical
43-
44-
# Alignment pattern (5x5) for Version 2+ QR codes - placing one at center area
45-
alignment = np.array([[1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 1, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1]])
46-
47-
# Place alignment pattern (avoiding finder patterns)
48-
qr_matrix[size - 9 : size - 4, size - 9 : size - 4] = alignment
49-
50-
# Separators (white border around finder patterns) - already 0 in initialization
51-
# Format information areas (fixed patterns near finder patterns)
52-
qr_matrix[8, 0:6] = [1, 0, 1, 0, 1, 0]
53-
qr_matrix[8, 7:9] = [1, 1]
54-
qr_matrix[0:6, 8] = [1, 0, 1, 0, 1, 0]
55-
qr_matrix[7:9, 8] = [1, 1]
56-
57-
# Data encoding area - fill with a pattern representing "pyplots.ai"
58-
# Using deterministic pattern based on the URL characters
59-
data_string = "https://pyplots.ai"
60-
data_values = [ord(c) for c in data_string]
61-
62-
# Fill data area with encoded pattern
63-
data_idx = 0
64-
for row in range(8, size - 1):
65-
for col in range(9, size - 1):
66-
if qr_matrix[row, col] == 0: # Only fill empty cells
67-
if row != 6 and col != 6: # Avoid timing patterns
68-
# Create pattern from data
69-
qr_matrix[row, col] = data_values[data_idx % len(data_values)] % 2
70-
data_idx += 1
71-
72-
# Add quiet zone (white border) by padding
73-
quiet_zone = 4
74-
padded_size = size + 2 * quiet_zone
75-
qr_with_border = np.zeros((padded_size, padded_size), dtype=int)
76-
qr_with_border[quiet_zone : quiet_zone + size, quiet_zone : quiet_zone + size] = qr_matrix
77-
78-
# Create figure (square format for QR code)
79-
fig, ax = plt.subplots(figsize=(12, 12))
80-
81-
# Display QR code with high contrast black on white
82-
ax.imshow(qr_with_border, cmap="gray_r", interpolation="nearest", vmin=0, vmax=1)
83-
84-
# Remove axes for clean QR code appearance
85-
ax.axis("off")
86-
87-
# Add title
88-
ax.set_title("qrcode-basic · matplotlib · pyplots.ai", fontsize=28, fontweight="bold", pad=30, color="#306998")
89-
90-
# Add the encoded content as annotation below
91-
fig.text(0.5, 0.06, f"Encoded: {data_string}", ha="center", fontsize=20, color="#555555", family="monospace")
92-
93-
# Add note about structure
94-
fig.text(
95-
0.5, 0.02, "QR Code Version 1 (21×21) with Error Correction Level M", ha="center", fontsize=14, color="#888888"
100+
version_info = (
101+
f"QR Code Version {qr.version} ({qr_matrix.shape[0]}\u00d7{qr_matrix.shape[1]}) \u00b7 Error Correction Level M"
96102
)
103+
meta_text = fig.text(0.5, 0.04, version_info, ha="center", fontsize=16, color="#777777", style="italic")
104+
meta_text.set_path_effects([pe.withStroke(linewidth=1.5, foreground="white"), pe.Normal()])
97105

98-
plt.tight_layout()
99106
plt.savefig("plot.png", dpi=300, bbox_inches="tight", facecolor="white")

0 commit comments

Comments
 (0)