135 lines
3.8 KiB
Python
135 lines
3.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Generate a simple icon for Path Juggler.
|
|
Creates an .icns file for use with the macOS app bundle.
|
|
|
|
Requires: pip install Pillow
|
|
"""
|
|
|
|
import subprocess
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
try:
|
|
from PIL import Image, ImageDraw
|
|
except ImportError:
|
|
print("Pillow not installed. Install with: pip3 install Pillow")
|
|
print("Or skip icon generation - the app will use a default icon.")
|
|
exit(1)
|
|
|
|
|
|
def create_icon_image(size: int) -> Image.Image:
|
|
"""Create a single icon image at the given size."""
|
|
img = Image.new('RGBA', (size, size), (0, 0, 0, 0))
|
|
draw = ImageDraw.Draw(img)
|
|
|
|
# Background - purple/blue gradient-like color (juggler theme)
|
|
margin = int(size * 0.08)
|
|
radius = int(size * 0.18)
|
|
|
|
# Draw rounded rectangle background
|
|
draw.rounded_rectangle(
|
|
[margin, margin, size - margin, size - margin],
|
|
radius=radius,
|
|
fill=(88, 86, 214, 255) # Purple-blue
|
|
)
|
|
|
|
center_x = size // 2
|
|
center_y = size // 2
|
|
|
|
# Draw three "juggling balls" in an arc
|
|
ball_radius = int(size * 0.09)
|
|
arc_radius = int(size * 0.22)
|
|
|
|
# Ball positions (arc above center)
|
|
balls = [
|
|
(center_x - arc_radius, center_y - int(size * 0.08)), # Left
|
|
(center_x, center_y - int(size * 0.18)), # Top center
|
|
(center_x + arc_radius, center_y - int(size * 0.08)), # Right
|
|
]
|
|
|
|
ball_colors = [
|
|
(255, 107, 107, 255), # Red/coral
|
|
(78, 205, 196, 255), # Teal
|
|
(255, 230, 109, 255), # Yellow
|
|
]
|
|
|
|
for (bx, by), color in zip(balls, ball_colors):
|
|
draw.ellipse(
|
|
[bx - ball_radius, by - ball_radius, bx + ball_radius, by + ball_radius],
|
|
fill=color
|
|
)
|
|
|
|
# Draw path arrows (two crossing paths)
|
|
arrow_color = (255, 255, 255, 200)
|
|
line_width = max(2, int(size * 0.025))
|
|
|
|
# Left-to-right arrow
|
|
arrow_y = center_y + int(size * 0.15)
|
|
arrow_start = margin + int(size * 0.1)
|
|
arrow_end = size - margin - int(size * 0.1)
|
|
|
|
draw.line(
|
|
[(arrow_start, arrow_y), (arrow_end - int(size * 0.08), arrow_y)],
|
|
fill=arrow_color,
|
|
width=line_width
|
|
)
|
|
# Arrow head
|
|
head_size = int(size * 0.06)
|
|
draw.polygon([
|
|
(arrow_end, arrow_y),
|
|
(arrow_end - head_size, arrow_y - head_size // 2),
|
|
(arrow_end - head_size, arrow_y + head_size // 2),
|
|
], fill=arrow_color)
|
|
|
|
return img
|
|
|
|
|
|
def create_icns(output_path: Path):
|
|
"""Create an .icns file with all required sizes."""
|
|
sizes = [16, 32, 64, 128, 256, 512, 1024]
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
iconset_path = Path(tmpdir) / "icon.iconset"
|
|
iconset_path.mkdir()
|
|
|
|
for size in sizes:
|
|
img = create_icon_image(size)
|
|
img.save(iconset_path / f"icon_{size}x{size}.png")
|
|
|
|
if size <= 512:
|
|
img_2x = create_icon_image(size * 2)
|
|
img_2x.save(iconset_path / f"icon_{size}x{size}@2x.png")
|
|
|
|
result = subprocess.run(
|
|
["iconutil", "-c", "icns", str(iconset_path), "-o", str(output_path)],
|
|
capture_output=True,
|
|
text=True
|
|
)
|
|
|
|
if result.returncode != 0:
|
|
print(f"iconutil failed: {result.stderr}")
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
def main():
|
|
script_dir = Path(__file__).parent
|
|
output_path = script_dir / "AppIcon.icns"
|
|
|
|
print("Generating Path Juggler icon...")
|
|
|
|
if create_icns(output_path):
|
|
print(f"Icon created: {output_path}")
|
|
print("\nTo use this icon in the build:")
|
|
print("1. Edit build_app.sh")
|
|
print("2. Find the line: icon=None,")
|
|
print("3. Change it to: icon='AppIcon.icns',")
|
|
else:
|
|
print("Failed to create icon")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|