Skip to content

Commit 0ad9f76

Browse files
feat(bokeh): implement line-realtime (#3129)
## Implementation: `line-realtime` - bokeh Implements the **bokeh** version of `line-realtime`. **File:** `plots/line-realtime/implementations/bokeh.py` **Parent Issue:** #3073 --- :robot: *[impl-generate workflow](https://github.com/MarkusNeusinger/pyplots/actions/runs/20620562054)* --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 0482f2b commit 0ad9f76

2 files changed

Lines changed: 177 additions & 0 deletions

File tree

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
""" pyplots.ai
2+
line-realtime: Real-Time Updating Line Chart
3+
Library: bokeh 3.8.1 | Python 3.13.11
4+
Quality: 91/100 | Created: 2025-12-31
5+
"""
6+
7+
import numpy as np
8+
import pandas as pd
9+
from bokeh.io import export_png, output_file, save
10+
from bokeh.models import Arrow, ColumnDataSource, Label, Range1d, VeeHead
11+
from bokeh.plotting import figure
12+
13+
14+
# Data - Simulated CPU usage over time with sliding window effect
15+
np.random.seed(42)
16+
17+
# Generate 100 points of CPU usage data (we'll show last 60 as "visible window")
18+
n_total = 100
19+
n_visible = 60
20+
21+
# Create timestamps (every 100ms for last 10 seconds)
22+
timestamps = pd.date_range(end=pd.Timestamp.now(), periods=n_total, freq="100ms")
23+
24+
# Generate realistic CPU usage with some patterns
25+
base_usage = 45 # Base CPU usage %
26+
trend = np.linspace(0, 10, n_total) # Slight upward trend
27+
noise = np.cumsum(np.random.randn(n_total) * 2) # Random walk component
28+
spikes = np.zeros(n_total)
29+
spike_indices = [15, 35, 55, 78] # Add some usage spikes
30+
for idx in spike_indices:
31+
if idx < n_total:
32+
spikes[idx : idx + 5] = np.array([15, 25, 20, 10, 5])[: n_total - idx]
33+
34+
cpu_usage = base_usage + trend + noise + spikes
35+
cpu_usage = np.clip(cpu_usage, 5, 95) # Keep between 5-95%
36+
37+
# For static image: show the "visible window" portion
38+
visible_timestamps = timestamps[-n_visible:]
39+
visible_values = cpu_usage[-n_visible:]
40+
41+
# Create color gradient to show recency (older points fade)
42+
alpha_values = np.linspace(0.3, 1.0, n_visible)
43+
point_sizes = np.linspace(15, 30, n_visible)
44+
45+
# Create source for the main line with all data columns
46+
source = ColumnDataSource(
47+
data={"x": visible_timestamps, "y": visible_values, "alpha": alpha_values, "size": point_sizes}
48+
)
49+
50+
# Create figure - 4800x2700 for 16:9
51+
p = figure(
52+
width=4800,
53+
height=2700,
54+
title="CPU Usage Monitor · line-realtime · bokeh · pyplots.ai",
55+
x_axis_label="Time",
56+
y_axis_label="CPU Usage (%)",
57+
x_axis_type="datetime",
58+
tools="",
59+
toolbar_location=None,
60+
)
61+
62+
# Set axis range for Y with some padding
63+
p.y_range = Range1d(0, 100)
64+
65+
# Draw gradient segments to show fading effect on older data
66+
# Use multiple line segments with decreasing alpha
67+
segment_size = 5
68+
for i in range(0, n_visible - segment_size, segment_size):
69+
segment_x = visible_timestamps[i : i + segment_size + 1]
70+
segment_y = visible_values[i : i + segment_size + 1]
71+
segment_alpha = 0.3 + 0.7 * (i / n_visible)
72+
p.line(x=segment_x, y=segment_y, line_width=6, line_color="#306998", line_alpha=segment_alpha)
73+
74+
# Draw the most recent portion with full opacity
75+
p.line(x=visible_timestamps[-15:], y=visible_values[-15:], line_width=8, line_color="#306998", line_alpha=1.0)
76+
77+
# Add scatter points (larger on recent, smaller on older)
78+
p.scatter(
79+
x="x", y="y", source=source, size="size", fill_color="#306998", fill_alpha="alpha", line_color="white", line_width=2
80+
)
81+
82+
# Highlight the current/latest value with a larger marker
83+
latest_x = visible_timestamps[-1]
84+
latest_y = visible_values[-1]
85+
p.scatter(x=[latest_x], y=[latest_y], size=40, fill_color="#FFD43B", line_color="#306998", line_width=4)
86+
87+
# Add label showing current value
88+
current_value_label = Label(
89+
x=latest_x,
90+
y=latest_y + 8,
91+
text=f"Current: {latest_y:.1f}%",
92+
text_font_size="36pt",
93+
text_color="#306998",
94+
text_font_style="bold",
95+
text_align="center",
96+
)
97+
p.add_layout(current_value_label)
98+
99+
# Add arrow indicating scroll direction (data flowing left)
100+
arrow = Arrow(
101+
end=VeeHead(size=35, fill_color="#666666", line_color="#666666"),
102+
x_start=visible_timestamps[10],
103+
y_start=12,
104+
x_end=visible_timestamps[2],
105+
y_end=12,
106+
line_color="#666666",
107+
line_width=4,
108+
)
109+
p.add_layout(arrow)
110+
111+
# Add text label for scroll indicator
112+
scroll_label = Label(
113+
x=visible_timestamps[6],
114+
y=16,
115+
text="Older data scrolls off",
116+
text_font_size="24pt",
117+
text_color="#666666",
118+
text_font_style="italic",
119+
text_align="center",
120+
)
121+
p.add_layout(scroll_label)
122+
123+
# Styling
124+
p.title.text_font_size = "40pt"
125+
p.title.text_color = "#306998"
126+
p.xaxis.axis_label_text_font_size = "28pt"
127+
p.yaxis.axis_label_text_font_size = "28pt"
128+
p.xaxis.major_label_text_font_size = "22pt"
129+
p.yaxis.major_label_text_font_size = "22pt"
130+
131+
# Grid styling
132+
p.xgrid.grid_line_alpha = 0.3
133+
p.ygrid.grid_line_alpha = 0.3
134+
p.xgrid.grid_line_dash = [6, 4]
135+
p.ygrid.grid_line_dash = [6, 4]
136+
137+
# Background
138+
p.background_fill_color = "#fafafa"
139+
p.border_fill_color = "white"
140+
141+
# Axis styling
142+
p.xaxis.axis_line_width = 2
143+
p.yaxis.axis_line_width = 2
144+
p.xaxis.major_tick_line_width = 2
145+
p.yaxis.major_tick_line_width = 2
146+
147+
# Save as PNG and HTML
148+
export_png(p, filename="plot.png")
149+
output_file("plot.html", title="Real-Time Line Chart - Bokeh")
150+
save(p)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
library: bokeh
2+
specification_id: line-realtime
3+
created: '2025-12-31T14:10:00Z'
4+
updated: '2025-12-31T14:20:31Z'
5+
generated_by: claude-opus-4-5-20251101
6+
workflow_run: 20620562054
7+
issue: 3073
8+
python_version: 3.13.11
9+
library_version: 3.8.1
10+
preview_url: https://storage.googleapis.com/pyplots-images/plots/line-realtime/bokeh/plot.png
11+
preview_thumb: https://storage.googleapis.com/pyplots-images/plots/line-realtime/bokeh/plot_thumb.png
12+
preview_html: https://storage.googleapis.com/pyplots-images/plots/line-realtime/bokeh/plot.html
13+
quality_score: 91
14+
review:
15+
strengths:
16+
- Excellent implementation of the fade/gradient effect showing data recency through
17+
both alpha and marker size
18+
- Clear visual indication of scrolling direction with arrow and explanatory text
19+
- Prominent highlighting of current value with contrasting yellow marker and label
20+
- Realistic CPU monitoring scenario with believable spike patterns
21+
- Clean title format following pyplots.ai conventions
22+
- Good use of Bokeh annotations (Arrow, Label, VeeHead)
23+
weaknesses:
24+
- No legend is present (minor issue for single-series plot)
25+
- Data stays mostly in the 25-60% range; showing occasional values near 0% or 90%+
26+
would demonstrate the full scale
27+
- Could utilize Bokeh HoverTool for interactive HTML output to show values on hover

0 commit comments

Comments
 (0)