HTML Visualizations in Documentation#
This page describes the complete procedure for generating interactive
plot_data HTML files and embedding them in the Sphinx documentation.
All HTML files live in doc/source/_static/html/plot_data_view_examples/
and are embedded in .rst pages via <iframe> directives.
Complete Procedure#
Step 1: Build a PrimitiveGroup in Python#
from dessia_drawing.core import Drawing
from drawing_tools.config.default_language_configs import DEFAULT_FRENCH_CONFIG, DEFAULT_ENGLISH_CONFIG
from drawing_tools.sheet.featured_sheet import FeaturedSheet
from drawing_tools.view.featured_view import FeaturedView
drawing = Drawing.from_json("data/json/my_drawing.json")
fs = FeaturedSheet(drawing.sheets[0], language_configs=[DEFAULT_FRENCH_CONFIG, DEFAULT_ENGLISH_CONFIG])
# Any @plot_data_view method:
primitive_group = fs.plot_data_balloons()
# or view-level:
# primitive_group = fs.views[2].plot_data_balloons()
Step 2: Serialize to JSON and write into the HTML template#
import json
from pathlib import Path
data_str = json.dumps(primitive_group.to_dict())
output = Path("doc/source/_static/html/plot_data_view_examples/my_visualization.html")
TEMPLATE = '''<!DOCTYPE html>
<html lang="en">
<style>
#buttons {
display: flex;
column-gap: 3rem;
row-gap: 0.5em;
flex-wrap: wrap;
margin-bottom: 0.5rem;
margin-top: 1rem;
margin: 0 auto;
}
</style>
<head>
<script src="https://documentation.dessia.io/plot_data/_static/htmls/plot-data.js"></script>
</head>
<div id="buttons"></div>
<div id="app">
<canvas id="canvas" style="border: 1px solid black; margin: auto;"></canvas>
<script type="text/javascript">
const [width, height] = PlotData.computeCanvasSize("#buttons");
const data = DATA_PLACEHOLDER;
const canvas = document.getElementById("canvas");
const plot_data = new PlotData.Draw(data, width, height, 0, 0, canvas.id);
plot_data.setCanvas(canvas.id);
</script>
</div>
</html>'''
output.write_text(TEMPLATE.replace("DATA_PLACEHOLDER", data_str))
Step 3: Embed in the .rst page#
.. raw:: html
<iframe src="../_static/html/plot_data_view_examples/my_visualization.html"
width="100%" height="600px" frameborder="0"></iframe>
The src path is relative to the .rst file. For files in doc/source/guides/,
use ../_static/html/....
Step 4: Verify in a browser#
Open the HTML file directly in a browser and check:
The visualization renders (not blank)
There are no toolbar buttons (Zoom Box, Cluster, Reset, Merge, etc.)
View page source:
<script src=...>points tohttps://documentation.dessia.io/...
Critical Rules#
The HTML template above has three properties that must not be changed:
CDN script URL — The
<script src>must behttps://documentation.dessia.io/plot_data/_static/htmls/plot-data.js. Never use a local.venv/path — it works on your machine but breaks in deployed docs.Empty ``<div id=”buttons”></div>`` — This div must exist in the HTML even though it is empty. The JavaScript call
PlotData.computeCanvasSize("#buttons")uses this element to compute canvas dimensions. Without it, the canvas has zero dimensions and the visualization is a blank white page.No toolbar buttons — The generated HTML must not contain
<button>elements,<input type="range" class="slider">,<div id="sub_button">, or<hr>separators. These are toolbar controls thatplot_dataadds when using.plot(filepath=...), but they clutter the embedded iframe and are not needed for documentation.
Why Not Use .plot(filepath=...) Directly#
The PrimitiveGroup.plot(filepath=...) method generates a complete HTML file,
but it has two problems for documentation:
Local script path — It writes
<script src=/home/.../plot-data.js>pointing to your virtualenv. This breaks in deployed docs.Toolbar buttons — It includes Zoom Box, Cluster slider, Reset view, Merge points, Show points, Change Disposition, Log Scale, Resize, and Apply on rubberband buttons. These clutter the iframe.
If you must use .plot(filepath=...), you need to clean the output afterwards.
Here is the exact procedure to fix an HTML file generated by .plot(filepath=...):
Fixing an HTML File Generated by .plot(filepath=...)#
The following Python code fixes a single HTML file. It applies all necessary regex substitutions to remove toolbar elements and fix the CDN path. Run it on any file or directory:
import re
from pathlib import Path
CDN_SCRIPT = '<script src="https://documentation.dessia.io/plot_data/_static/htmls/plot-data.js"></script>'
def fix_html_for_docs(filepath: Path) -> None:
content = filepath.read_text(encoding="utf-8")
original = content
# 1. Replace local script path with CDN (handles both quoted and unquoted src attributes)
content = re.sub(
r"<script src=[\"']?[^\"'>]*plot-data\.js[^>]*></script>",
CDN_SCRIPT,
content,
)
# 2. Remove all <button> elements (Zoom, Reset, Merge, etc.)
content = re.sub(r"\s*<button[^>]*>.*?</button>\s*", "", content, flags=re.DOTALL)
# 3. Remove slider and styled inputs
content = re.sub(r"\s*Cluster:\s*<input[^>]*class=\"slider\"[^>]*/>\s*", "", content)
content = re.sub(r"\s*<input[^>]*class=\"styled\"[^>]*/>\s*", "", content)
content = re.sub(r"\s*<input[^>]*type=\"button\"[^>]*/>\s*", "", content)
# 4. Remove <div id="buttons">...</div> wrapper (may be empty after button removal)
content = re.sub(r'\s*<div id="buttons">[\s]*</div>', "", content, flags=re.DOTALL)
content = re.sub(r'\s*<div id="buttons">.*?</div>\s*(?:</div>\s*)*', "\n", content, flags=re.DOTALL)
# 5. Remove orphaned <div id="sub_button"> wrappers
content = re.sub(r'\s*<div id="sub_button">\s*</div>', "", content)
content = re.sub(r'\s*<div id="sub_button">[\s\S]*?</div>', "", content)
# 6. Remove <hr> separator between toolbar and canvas
content = re.sub(r"\s*<hr[^>]*/?>", "", content)
# 7. Remove CSS rules for slider and sub_button (keep only #buttons rule)
content = re.sub(r"\s*\.slider\s*\{[^}]*\}\s*", "\n", content)
content = re.sub(r"\s*\.slider:hover\s*\{[^}]*\}\s*", "\n", content)
content = re.sub(r"\s*\.slider::-moz-range-thumb\s*\{[^}]*\}\s*", "\n", content)
content = re.sub(r"\s*#sub_button\s*\{[^}]*\}\s*", "\n", content)
# 8. Clean up excessive blank lines
content = re.sub(r"\n{3,}", "\n\n", content)
if content != original:
filepath.write_text(content, encoding="utf-8")
# Fix a single file:
fix_html_for_docs(Path("doc/source/_static/html/plot_data_view_examples/my_file.html"))
# Or fix all HTML files in the directory:
for html_file in Path("doc/source/_static/html/plot_data_view_examples").rglob("*.html"):
fix_html_for_docs(html_file)
What the Toolbar Looks Like (to Recognize It)#
When .plot(filepath=...) generates an HTML file, it includes a toolbar at the
top of the page with these elements that must be removed:
<button>Switch Point Merge</button><button>Zoom Box</button>,<button>Zoom+</button>,<button>Zoom-</button>Cluster: <input type="range" class="slider" .../><button>Reset clusters</button><button>Reset view</button>,<button>Show points</button><button>Change Disposition</button>,<button>Show / Hide Axes</button><button>Log Scale</button>,<button>Resize</button><input class="styled" type="button" value="Apply on rubberband" .../><hr style="border-top: 2px;"/>(separator between toolbar and canvas)
These are wrapped in <div id="buttons"><div id="sub_button">...</div>...</div>.
The CSS rules .slider, .slider:hover, .slider::-moz-range-thumb, and
#sub_button are also added by .plot() and should be removed.
The #buttons CSS rule must be kept — it is needed by computeCanvasSize.
Auditing Existing HTML Files#
To check if any HTML file in the doc still has unwanted toolbar buttons:
for f in doc/source/_static/html/plot_data_view_examples/*.html; do
n=$(grep -c "<button" "$f" 2>/dev/null)
[ "$n" -gt 0 ] && echo "BAD ($n buttons): $(basename $f)"
done
If any files show up as BAD, apply the fix procedure above.