Skip to content

Commit d029ad4

Browse files
feat(pygal): implement lollipop-basic (#5447)
## Implementation: `lollipop-basic` - python/pygal Implements the **python/pygal** version of `lollipop-basic`. **File:** `plots/lollipop-basic/implementations/python/pygal.py` **Parent Issue:** #934 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/24957187995)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent d85b84d commit d029ad4

2 files changed

Lines changed: 202 additions & 154 deletions

File tree

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,84 @@
1-
""" pyplots.ai
1+
""" anyplot.ai
22
lollipop-basic: Basic Lollipop Chart
3-
Library: pygal 3.1.0 | Python 3.13.11
4-
Quality: 91/100 | Created: 2025-12-23
3+
Library: pygal 3.1.0 | Python 3.14.4
4+
Quality: 86/100 | Updated: 2026-04-26
55
"""
66

7-
import pygal
8-
from pygal.style import Style
7+
import os
8+
import sys
9+
from pathlib import Path
910

1011

11-
# Data - Product sales by category (sorted by value for better readability)
12+
# Remove script directory from path to avoid name collision with pygal package
13+
_script_dir = str(Path(__file__).parent)
14+
sys.path = [p for p in sys.path if p != _script_dir]
15+
16+
import pygal # noqa: E402
17+
from pygal.style import Style # noqa: E402
18+
19+
20+
# Theme tokens
21+
THEME = os.getenv("ANYPLOT_THEME", "light")
22+
PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17"
23+
INK = "#1A1A17" if THEME == "light" else "#F0EFE8"
24+
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
25+
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
26+
27+
BRAND = "#009E73" # Okabe-Ito position 1
28+
OKABE_ITO = (BRAND, "#D55E00", "#0072B2", "#CC79A7", "#E69F00", "#56B4E9", "#F0E442")
29+
30+
# Data — Product sales by category (sorted descending)
1231
categories = ["Smartphones", "Laptops", "Tablets", "Headphones", "Smartwatches", "Cameras", "Speakers", "Gaming"]
1332
values = [892, 654, 478, 312, 287, 198, 156, 134]
1433

15-
# Sort by value descending (already sorted in this example)
1634
sorted_data = sorted(zip(categories, values, strict=True), key=lambda x: x[1], reverse=True)
1735
categories = [item[0] for item in sorted_data]
1836
values = [item[1] for item in sorted_data]
1937

20-
# Custom style for 4800x2700 canvas with visible stroke
38+
# Style — theme-adaptive chrome, brand-green data
2139
custom_style = Style(
22-
background="white",
23-
plot_background="white",
24-
foreground="#333333",
25-
foreground_strong="#333333",
26-
foreground_subtle="#666666",
27-
colors=("#306998",) * len(categories), # Python Blue for all series
28-
title_font_size=72,
29-
label_font_size=48,
30-
major_label_font_size=42,
31-
legend_font_size=42,
40+
background=PAGE_BG,
41+
plot_background=PAGE_BG,
42+
foreground=INK,
43+
foreground_strong=INK,
44+
foreground_subtle=INK_MUTED,
45+
colors=(BRAND,) * len(categories),
46+
title_font_size=64,
47+
label_font_size=44,
48+
major_label_font_size=40,
49+
legend_font_size=40,
3250
value_font_size=36,
3351
value_label_font_size=36,
34-
stroke_width=8, # Line width in style
52+
stroke_width=8,
53+
opacity=1,
54+
opacity_hover=0.9,
3555
)
3656

37-
# Create horizontal lollipop chart using XY
38-
# Categories on y-axis, values on x-axis (horizontal orientation)
57+
# Plot — horizontal lollipop via XY: stem from x=0 to value, baseline node hidden
3958
n = len(categories)
4059
chart = pygal.XY(
4160
width=4800,
4261
height=2700,
43-
title="lollipop-basic · pygal · pyplots.ai",
62+
title="lollipop-basic · pygal · anyplot.ai",
4463
x_title="Sales (units)",
64+
y_title="Product Category",
4565
style=custom_style,
4666
show_legend=False,
47-
dots_size=24,
48-
stroke=True, # Enable stroke/lines
49-
show_y_guides=True,
67+
dots_size=28,
68+
stroke=True,
5069
show_x_guides=True,
51-
margin=100,
70+
show_y_guides=False,
71+
margin=120,
5272
xrange=(0, max(values) * 1.1),
5373
range=(0.5, n + 0.5),
5474
y_labels=[{"label": cat, "value": n - i} for i, cat in enumerate(categories)],
5575
)
5676

57-
# Add lollipop data - each is a line from x=0 to value
5877
for i, (cat, val) in enumerate(zip(categories, values, strict=True)):
5978
y_pos = n - i
60-
# Hide dot at baseline (r=0), show large dot at value end
6179
chart.add(cat, [{"value": (0, y_pos), "node": {"r": 0}}, {"value": (val, y_pos)}])
6280

63-
# Save outputs
64-
chart.render_to_png("plot.png")
65-
chart.render_to_file("plot.html")
81+
# Save
82+
chart.render_to_png(f"plot-{THEME}.png")
83+
with open(f"plot-{THEME}.html", "wb") as f:
84+
f.write(chart.render())

0 commit comments

Comments
 (0)