"""Genera un icono provisional para jlauncher (PNG 1024x1024). Dibuja un «squircle» con degradado y un triángulo de «play» (es un lanzador de juegos). Se ejecuta sin pantalla con la plataforma offscreen de Qt: QT_QPA_PLATFORM=offscreen .venv/bin/python assets/make_icon.py Salida: assets/icon.png. El .icns lo construye build.sh con iconutil. """ from __future__ import annotations import os from pathlib import Path os.environ.setdefault("QT_QPA_PLATFORM", "offscreen") from PySide6.QtCore import QPointF, QRectF, Qt # noqa: E402 from PySide6.QtGui import ( # noqa: E402 QBrush, QColor, QGuiApplication, QImage, QLinearGradient, QPainter, QPainterPath, ) SIZE = 1024 def render() -> QImage: img = QImage(SIZE, SIZE, QImage.Format_ARGB32) img.fill(Qt.transparent) p = QPainter(img) p.setRenderHint(QPainter.Antialiasing, True) # «Squircle»: rectángulo con esquinas redondeadas estilo macOS (~22% del lado). margin = SIZE * 0.06 rect = QRectF(margin, margin, SIZE - 2 * margin, SIZE - 2 * margin) radius = rect.width() * 0.2237 body = QPainterPath() body.addRoundedRect(rect, radius, radius) grad = QLinearGradient(rect.topLeft(), rect.bottomRight()) grad.setColorAt(0.0, QColor("#4b3bd6")) grad.setColorAt(1.0, QColor("#7b2ff7")) p.fillPath(body, QBrush(grad)) # Barras verticales tenues: guiño a «jail». p.save() p.setClipPath(body) p.setPen(Qt.NoPen) p.setBrush(QColor(255, 255, 255, 26)) bars = 5 bar_w = rect.width() * 0.052 gap = (rect.width() - bars * bar_w) / (bars + 1) x = rect.left() + gap for _ in range(bars): p.drawRoundedRect(QRectF(x, rect.top(), bar_w, rect.height()), bar_w / 2, bar_w / 2) x += bar_w + gap p.restore() # Triángulo de «play» centrado, blanco con esquinas redondeadas. cx, cy = rect.center().x(), rect.center().y() r = rect.width() * 0.26 tri = QPainterPath() tri.moveTo(QPointF(cx - r * 0.55, cy - r * 0.95)) tri.lineTo(QPointF(cx - r * 0.55, cy + r * 0.95)) tri.lineTo(QPointF(cx + r * 1.0, cy)) tri.closeSubpath() pen_brush = QColor("#ffffff") stroker_pen = p.pen() stroker_pen.setColor(pen_brush) stroker_pen.setWidthF(r * 0.28) stroker_pen.setJoinStyle(Qt.RoundJoin) stroker_pen.setCapStyle(Qt.RoundCap) p.strokePath(tri, stroker_pen) p.fillPath(tri, pen_brush) p.end() return img def main() -> int: QGuiApplication([]) out = Path(__file__).resolve().parent / "icon.png" img = render() if not img.save(str(out), "PNG"): print(f"[icon] no se pudo guardar {out}") return 1 print(f"[icon] generado {out} ({SIZE}x{SIZE})") return 0 if __name__ == "__main__": raise SystemExit(main())