From 3c70c51e6d487113cd3ac86227c20befa0c66371 Mon Sep 17 00:00:00 2001 From: Ed Saa Date: Fri, 17 May 2024 20:07:52 -0500 Subject: [PATCH] Implement dataview for HTML representations --- stpyvista/CHANGELOG.md | 3 ++ stpyvista/pyproject.toml | 2 +- stpyvista/src/stpyvista/__init__.py | 80 ++++++++++++++++++++++++++++- stpyvista/test/dataview.py | 58 +++++++++++++++++++++ 4 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 stpyvista/test/dataview.py diff --git a/stpyvista/CHANGELOG.md b/stpyvista/CHANGELOG.md index 2576386..102e2b9 100644 --- a/stpyvista/CHANGELOG.md +++ b/stpyvista/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## [v 0.0.18] - 2024-05-17 +- Introduce `dataview` to display HTML representation of pyvista data + ## [v 0.0.17] - 2024-04-11 - Rewrite buffer using context manager - Introduce and experimental viewer based on trame and vanilla vtk-js diff --git a/stpyvista/pyproject.toml b/stpyvista/pyproject.toml index f86a00d..bb9771a 100644 --- a/stpyvista/pyproject.toml +++ b/stpyvista/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "stpyvista" -version = "0.0.17" +version = "0.0.18" authors = [ { name="Edwin Saavedra C.", email="esaavedrac@u.northwestern.edu" }, ] diff --git a/stpyvista/src/stpyvista/__init__.py b/stpyvista/src/stpyvista/__init__.py index 46bdcac..b2b206f 100644 --- a/stpyvista/src/stpyvista/__init__.py +++ b/stpyvista/src/stpyvista/__init__.py @@ -4,12 +4,15 @@ from pathlib import Path from typing import Optional, Literal import base64 +import xml.dom.minidom +import streamlit as st import streamlit.components.v1 as components + from pyvista.plotting import Plotter +from pyvista import DataSet import panel as pn - from bokeh.resources import CDN, INLINE pn.extension("vtk", sizing_mode="stretch_both") @@ -32,7 +35,9 @@ class stpyvistaValueError(ValueError): ) -def experimental_vtkjs(vtksz_data: bytes, height: int = 400, key: Optional[str] = None) -> dict: +def experimental_vtkjs( + vtksz_data: bytes, height: int = 400, key: Optional[str] = None +) -> dict: """ Renders an interactive Pyvista Plotter in streamlit. @@ -149,6 +154,77 @@ def stpyvista( raise stpyvistaTypeError(f"{plotter} is not a `pyvista.Plotter` instance. ") +def dataview(obj: DataSet): + """ + Renders the HTML representation of a Pyvista/VTK dataset. + + Parameters + ---------- + element: pv.DataSet + Pyvista element with some data to show. + + Returns + ------- + None + """ + + def assemble_details(title: str, table: str) -> str: + return f"
{title}{table}
" + + try: + # Look up an HTML representation and arange in details tags + + dom = xml.dom.minidom.parseString(obj._repr_html_()) + tables = dom.getElementsByTagName("table") + + css = """ + + """ + + html = StringIO("""
""") + + if len(tables) == 1: + html.write(assemble_details("Header", tables[0].toprettyxml())) + + else: + headers = ( + dom.getElementsByTagName("table")[0] + .getElementsByTagName("tr")[0] + .getElementsByTagName("th") + ) + + for title, table in zip(headers, tables[1:]): + html.write( + assemble_details(title.firstChild.nodeValue, table.toprettyxml()) + ) + + html.write(css + "
") + + # Render in streamlit + st.html(html.getvalue()) + + except AttributeError: + # Defaults to streamlit's write function + st.write(obj) + + def main(): pass diff --git a/stpyvista/test/dataview.py b/stpyvista/test/dataview.py new file mode 100644 index 0000000..df102b5 --- /dev/null +++ b/stpyvista/test/dataview.py @@ -0,0 +1,58 @@ +import streamlit as st +import pyvista as pv +import numpy as np +from stpyvista import stpyvista, dataview + +def put_in_plotter(actor: pv.DataSet): + plotter = pv.Plotter() + plotter.window_size = (300, 300) + plotter.add_mesh(actor, color='purple', line_width=10) + plotter.view_isometric() + return plotter + +def sphere(): + return pv.Sphere(radius=1.0, center=(0, 0, 0)) + +def spline(): + theta = np.linspace(-1 * np.pi, 1 * np.pi, 100) + z = np.linspace(2, -2, 100) + r = z**2 + 1 + x = r * np.sin(theta) + y = r * np.cos(theta) + points = np.column_stack((x, y, z)) + return pv.Spline(points, 1000) + +def surface(): + x = np.arange(-10, 10, 0.5) + y = np.arange(-10, 10, 0.5) + x, y = np.meshgrid(x, y) + z = np.sin(np.sqrt(x**2 + y**2)) + surf = pv.StructuredGrid(x, y, z) + x, y, z = surf.cell_centers().points.T + surf["my_scalar"] = x * y * z + + return surf + + +def main(): + + st.title("Testing `dataview`") + + datasets = [ + sphere(), + spline(), + surface() + ] + + for obj in datasets: + cols = st.columns([1, 1.5]) + + with cols[0]: + stpyvista(put_in_plotter(obj)) + with cols[1]: + dataview(obj) + + dataview(pv.MultiBlock(datasets)) + +if __name__ == "__main__": + main() \ No newline at end of file