|
1 | | -""" pyplots.ai |
| 1 | +""" anyplot.ai |
2 | 2 | line-filled: Filled Line Plot |
3 | | -Library: bokeh 3.8.1 | Python 3.13.11 |
4 | | -Quality: 91/100 | Created: 2025-12-30 |
| 3 | +Library: bokeh 3.9.0 | Python 3.13.13 |
| 4 | +Quality: 91/100 | Updated: 2026-05-12 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import os |
| 8 | +import time |
| 9 | +from pathlib import Path |
| 10 | + |
7 | 11 | import numpy as np |
8 | | -from bokeh.io import export_png, save |
| 12 | +from bokeh.io import output_file, save |
9 | 13 | from bokeh.models import ColumnDataSource, HoverTool |
10 | 14 | from bokeh.plotting import figure |
11 | | -from bokeh.resources import CDN |
| 15 | +from selenium import webdriver |
| 16 | +from selenium.webdriver.chrome.options import Options |
| 17 | + |
12 | 18 |
|
| 19 | +# Theme tokens |
| 20 | +THEME = os.getenv("ANYPLOT_THEME", "light") |
| 21 | +PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" |
| 22 | +ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" |
| 23 | +INK = "#1A1A17" if THEME == "light" else "#F0EFE8" |
| 24 | +INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" |
| 25 | +BRAND = "#009E73" |
13 | 26 |
|
14 | 27 | # Data - Monthly website traffic over a year |
15 | 28 | np.random.seed(42) |
|
29 | 42 | p = figure( |
30 | 43 | width=4800, |
31 | 44 | height=2700, |
32 | | - title="line-filled · bokeh · pyplots.ai", |
| 45 | + title="line-filled · bokeh · anyplot.ai", |
33 | 46 | x_axis_label="Month", |
34 | | - y_axis_label="Website Visitors", |
| 47 | + y_axis_label="Website Visitors (count)", |
35 | 48 | ) |
36 | 49 |
|
37 | 50 | # Filled area using varea |
38 | | -p.varea(x="month", y1="traffic_zero", y2="traffic", source=source, fill_color="#306998", fill_alpha=0.4) |
| 51 | +p.varea(x="month", y1="traffic_zero", y2="traffic", source=source, fill_color=BRAND, fill_alpha=0.35) |
39 | 52 |
|
40 | 53 | # Line on top of the fill |
41 | | -p.line(x="month", y="traffic", source=source, line_color="#306998", line_width=4) |
| 54 | +p.line(x="month", y="traffic", source=source, line_color=BRAND, line_width=4) |
42 | 55 |
|
43 | 56 | # Add points for visual emphasis |
44 | | -p.scatter(x="month", y="traffic", source=source, size=12, color="#306998", fill_alpha=0.8) |
| 57 | +p.scatter(x="month", y="traffic", source=source, size=12, color=BRAND, fill_alpha=0.8) |
45 | 58 |
|
46 | 59 | # Add hover tool |
47 | 60 | hover = HoverTool(tooltips=[("Month", "@month"), ("Visitors", "@traffic{0,0}")]) |
|
54 | 67 | p.xaxis.major_label_text_font_size = "18pt" |
55 | 68 | p.yaxis.major_label_text_font_size = "18pt" |
56 | 69 |
|
| 70 | +# Theme-adaptive text colors |
| 71 | +p.title.text_color = INK |
| 72 | +p.xaxis.axis_label_text_color = INK |
| 73 | +p.yaxis.axis_label_text_color = INK |
| 74 | +p.xaxis.major_label_text_color = INK_SOFT |
| 75 | +p.yaxis.major_label_text_color = INK_SOFT |
| 76 | + |
57 | 77 | # Grid styling |
58 | | -p.xgrid.grid_line_alpha = 0.3 |
59 | | -p.ygrid.grid_line_alpha = 0.3 |
60 | | -p.xgrid.grid_line_dash = [6, 4] |
61 | | -p.ygrid.grid_line_dash = [6, 4] |
| 78 | +p.xgrid.grid_line_alpha = 0.10 |
| 79 | +p.ygrid.grid_line_alpha = 0.10 |
| 80 | +p.xgrid.grid_line_color = INK |
| 81 | +p.ygrid.grid_line_color = INK |
| 82 | + |
| 83 | +# Axis styling |
| 84 | +p.xaxis.axis_line_color = INK_SOFT |
| 85 | +p.yaxis.axis_line_color = INK_SOFT |
| 86 | +p.xaxis.major_tick_line_color = INK_SOFT |
| 87 | +p.yaxis.major_tick_line_color = INK_SOFT |
62 | 88 |
|
63 | 89 | # Background |
64 | | -p.background_fill_color = "#fafafa" |
65 | | -p.border_fill_color = "#ffffff" |
| 90 | +p.background_fill_color = PAGE_BG |
| 91 | +p.border_fill_color = PAGE_BG |
| 92 | +p.outline_line_color = INK_SOFT |
66 | 93 |
|
67 | 94 | # X-axis ticks for each month |
68 | 95 | p.xaxis.ticker = list(range(1, 13)) |
69 | 96 |
|
70 | | -# Save as PNG and HTML |
71 | | -export_png(p, filename="plot.png") |
| 97 | +# Save as interactive HTML |
| 98 | +output_file(f"plot-{THEME}.html") |
| 99 | +save(p) |
| 100 | + |
| 101 | +# Screenshot with headless Chrome |
| 102 | +W, H = 4800, 2700 |
| 103 | +opts = Options() |
| 104 | +for arg in ( |
| 105 | + "--headless=new", |
| 106 | + "--no-sandbox", |
| 107 | + "--disable-dev-shm-usage", |
| 108 | + "--disable-gpu", |
| 109 | + f"--window-size={W},{H}", |
| 110 | + "--hide-scrollbars", |
| 111 | +): |
| 112 | + opts.add_argument(arg) |
72 | 113 |
|
73 | | -# Also save interactive HTML |
74 | | -save(p, filename="plot.html", resources=CDN, title="line-filled · bokeh · pyplots.ai") |
| 114 | +driver = webdriver.Chrome(options=opts) |
| 115 | +driver.set_window_size(W, H) |
| 116 | +driver.get(f"file://{Path(f'plot-{THEME}.html').resolve()}") |
| 117 | +time.sleep(3) # Let bokeh's JS render the canvas |
| 118 | +driver.save_screenshot(f"plot-{THEME}.png") |
| 119 | +driver.quit() |
0 commit comments