Skip to content

Commit 36258fa

Browse files
feat(bokeh): implement errorbar-basic (#9519)
## Implementation: `errorbar-basic` - python/bokeh Implements the **python/bokeh** version of `errorbar-basic`. **File:** `plots/errorbar-basic/implementations/python/bokeh.py` **Parent Issue:** #973 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/anyplot/actions/runs/28473166319)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Markus Neusinger <2921697+MarkusNeusinger@users.noreply.github.com>
1 parent 9f78c6a commit 36258fa

2 files changed

Lines changed: 138 additions & 95 deletions

File tree

plots/errorbar-basic/implementations/python/bokeh.py

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,25 @@
11
""" anyplot.ai
22
errorbar-basic: Basic Error Bar Plot
3-
Library: bokeh 3.9.0 | Python 3.14.4
4-
Quality: 88/100 | Updated: 2026-04-25
3+
Library: bokeh 3.9.1 | Python 3.13.14
4+
Quality: 92/100 | Updated: 2026-06-30
55
"""
66

77
import os
8+
import sys
9+
import time
10+
from pathlib import Path
11+
12+
13+
# Remove script dir from sys.path so 'bokeh.py' doesn't shadow the installed bokeh package
14+
_here = os.path.dirname(os.path.abspath(__file__))
15+
sys.path = [p for p in sys.path if p != _here]
816

917
import numpy as np
10-
from bokeh.io import export_png, output_file, save
18+
from bokeh.io import output_file, save
1119
from bokeh.models import ColumnDataSource, Label, TeeHead, Whisker
1220
from bokeh.plotting import figure
21+
from selenium import webdriver
22+
from selenium.webdriver.chrome.options import Options
1323

1424

1525
# Theme tokens
@@ -20,7 +30,7 @@
2030
INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0"
2131
INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F"
2232

23-
# Okabe-Ito categorical palette (positions 1-6)
33+
# Imprint categorical palette (positions 1-6)
2434
IMPRINT = ["#009E73", "#C475FD", "#4467A3", "#BD8233", "#AE3030", "#2ABCCD"]
2535

2636
# Data — experimental measurements with associated uncertainties
@@ -40,16 +50,22 @@
4050
data={"categories": categories, "means": means, "upper": upper, "lower": lower, "colors": colors}
4151
)
4252

43-
# Plot
53+
# Canvas: 3200×1800 (landscape) — hard rule, no deviation
54+
W, H = 3200, 1800
55+
4456
p = figure(
45-
width=4800,
46-
height=2700,
57+
width=W,
58+
height=H,
4759
x_range=categories,
48-
title="errorbar-basic · bokeh · anyplot.ai",
60+
title="errorbar-basic · python · bokeh · anyplot.ai",
4961
x_axis_label="Experimental Group",
5062
y_axis_label="Response Value (units)",
5163
toolbar_location=None,
5264
tools="",
65+
min_border_bottom=160,
66+
min_border_left=180,
67+
min_border_top=110,
68+
min_border_right=50,
5369
)
5470

5571
# Error bars (Whisker with TeeHead caps) — one whisker per group so each can take its own color
@@ -93,23 +109,23 @@
93109

94110
# Title
95111
p.title.text_color = INK
96-
p.title.text_font_size = "36pt"
112+
p.title.text_font_size = "50pt"
97113
p.title.text_font_style = "normal"
98114
p.title.align = "left"
99115

100116
# Axis labels
101117
p.xaxis.axis_label_text_color = INK
102118
p.yaxis.axis_label_text_color = INK
103-
p.xaxis.axis_label_text_font_size = "32pt"
104-
p.yaxis.axis_label_text_font_size = "32pt"
119+
p.xaxis.axis_label_text_font_size = "42pt"
120+
p.yaxis.axis_label_text_font_size = "42pt"
105121
p.xaxis.axis_label_text_font_style = "normal"
106122
p.yaxis.axis_label_text_font_style = "normal"
107123

108124
# Tick labels
109125
p.xaxis.major_label_text_color = INK_SOFT
110126
p.yaxis.major_label_text_color = INK_SOFT
111-
p.xaxis.major_label_text_font_size = "24pt"
112-
p.yaxis.major_label_text_font_size = "24pt"
127+
p.xaxis.major_label_text_font_size = "34pt"
128+
p.yaxis.major_label_text_font_size = "34pt"
113129

114130
# Axis lines and ticks
115131
p.xaxis.axis_line_color = INK_SOFT
@@ -121,7 +137,7 @@
121137

122138
# Subtle y-grid only
123139
p.ygrid.grid_line_color = INK
124-
p.ygrid.grid_line_alpha = 0.10
140+
p.ygrid.grid_line_alpha = 0.15
125141
p.xgrid.grid_line_color = None
126142

127143
# Y-range trimmed to data — eliminates dead space below
@@ -131,7 +147,27 @@
131147
p.y_range.start = max(0.0, y_min - y_pad)
132148
p.y_range.end = y_max + y_pad
133149

134-
# Save
135-
export_png(p, filename=f"plot-{THEME}.png")
136-
output_file(f"plot-{THEME}.html", title="errorbar-basic · bokeh · anyplot.ai")
150+
# Save HTML (required interactive artifact)
151+
output_file(f"plot-{THEME}.html", title="errorbar-basic · python · bokeh · anyplot.ai")
137152
save(p)
153+
154+
# Screenshot with headless Chrome via Selenium — export_png uses chromedriver snap shim which fails
155+
opts = Options()
156+
for arg in (
157+
"--headless=new",
158+
"--no-sandbox",
159+
"--disable-dev-shm-usage",
160+
"--disable-gpu",
161+
f"--window-size={W},{H}",
162+
"--hide-scrollbars",
163+
):
164+
opts.add_argument(arg)
165+
driver = webdriver.Chrome(options=opts)
166+
# Use CDP to set exact viewport dimensions (--window-size alone can be 100-150px short due to browser chrome)
167+
driver.execute_cdp_cmd(
168+
"Emulation.setDeviceMetricsOverride", {"width": W, "height": H, "deviceScaleFactor": 1, "mobile": False}
169+
)
170+
driver.get(f"file://{Path(f'plot-{THEME}.html').resolve()}")
171+
time.sleep(3)
172+
driver.save_screenshot(f"plot-{THEME}.png")
173+
driver.quit()

0 commit comments

Comments
 (0)