|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | 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 |
5 | 5 | """ |
6 | 6 |
|
7 | | -import pygal |
8 | | -from pygal.style import Style |
| 7 | +import os |
| 8 | +import sys |
| 9 | +from pathlib import Path |
9 | 10 |
|
10 | 11 |
|
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) |
12 | 31 | categories = ["Smartphones", "Laptops", "Tablets", "Headphones", "Smartwatches", "Cameras", "Speakers", "Gaming"] |
13 | 32 | values = [892, 654, 478, 312, 287, 198, 156, 134] |
14 | 33 |
|
15 | | -# Sort by value descending (already sorted in this example) |
16 | 34 | sorted_data = sorted(zip(categories, values, strict=True), key=lambda x: x[1], reverse=True) |
17 | 35 | categories = [item[0] for item in sorted_data] |
18 | 36 | values = [item[1] for item in sorted_data] |
19 | 37 |
|
20 | | -# Custom style for 4800x2700 canvas with visible stroke |
| 38 | +# Style — theme-adaptive chrome, brand-green data |
21 | 39 | 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, |
32 | 50 | value_font_size=36, |
33 | 51 | 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, |
35 | 55 | ) |
36 | 56 |
|
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 |
39 | 58 | n = len(categories) |
40 | 59 | chart = pygal.XY( |
41 | 60 | width=4800, |
42 | 61 | height=2700, |
43 | | - title="lollipop-basic · pygal · pyplots.ai", |
| 62 | + title="lollipop-basic · pygal · anyplot.ai", |
44 | 63 | x_title="Sales (units)", |
| 64 | + y_title="Product Category", |
45 | 65 | style=custom_style, |
46 | 66 | 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, |
50 | 69 | show_x_guides=True, |
51 | | - margin=100, |
| 70 | + show_y_guides=False, |
| 71 | + margin=120, |
52 | 72 | xrange=(0, max(values) * 1.1), |
53 | 73 | range=(0.5, n + 0.5), |
54 | 74 | y_labels=[{"label": cat, "value": n - i} for i, cat in enumerate(categories)], |
55 | 75 | ) |
56 | 76 |
|
57 | | -# Add lollipop data - each is a line from x=0 to value |
58 | 77 | for i, (cat, val) in enumerate(zip(categories, values, strict=True)): |
59 | 78 | y_pos = n - i |
60 | | - # Hide dot at baseline (r=0), show large dot at value end |
61 | 79 | chart.add(cat, [{"value": (0, y_pos), "node": {"r": 0}}, {"value": (val, y_pos)}]) |
62 | 80 |
|
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