Skip to content

Commit

Permalink
Add use_container_width option
Browse files Browse the repository at this point in the history
  • Loading branch information
edsaac committed Nov 17, 2023
1 parent a5a292e commit 6c86f96
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 64 deletions.
13 changes: 9 additions & 4 deletions stpyvista/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
# Changelog

## [v 0.0.10] - 2023-11-16
- Add `use_container_width` option to `stpyvista`. Defaults to True
- Changed bokeh.resources to import CDN instead of INLINE to html generation.
- Drop ipython from the dependencies.

## [v 0.0.9] - 2023-09-05

- Use hatchling for building package. Remove setup.py support.
- Remove `panel` as the jupyter_notebook backend. Not necessary if reading from panel.
- Move changelog from README.md to CHANGELOG.md

# [v 0.0.8]
## [v 0.0.8]

- Remove excessive whitespace below the rendered component.
- Can pass additional kwargs to panel.pane.vtk, e.g. setting an orientation_widget. Check panel-vtk for details on valid kwargs.

# [v 0.0.6]
## [v 0.0.6]

- Replaced `pythreejs` backend for `panel` backend. This is a temporary solution as pyvista will remove panel support in favor of trame.

# [v 0.0.5]
## [v 0.0.5]

- Support transparent backgrounds to blend with streamlit's web app theme.
- Add a control to spin along a certain axis the first mesh passed to the plotter.

# [v 0.0.4]
## [v 0.0.4]

- Pass a key to the stpyvista component to avoid re-rendering at every streamlit interaction
- Using ipywidgets `embed_minimal_html` directly instead of pyvista `export_html`.
Expand Down
9 changes: 4 additions & 5 deletions stpyvista/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ build-backend = "hatchling.build"

[project]
name = "stpyvista"
version = "0.0.9"
version = "0.0.10"
authors = [
{ name="Edwin S", email="[email protected]" },
{ name="Edwin Saavedra C.", email="[email protected]" },
]
description = "Streamlit component to render pyvista 3D visualizations"
readme = "README.md"
requires-python = ">=3.8"
requires-python = ">=3.9"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
Expand All @@ -21,8 +21,7 @@ dependencies = [
"streamlit",
"pyvista",
"bokeh",
"panel",
"ipython"
"panel"
]

[project.urls]
Expand Down
1 change: 0 additions & 1 deletion stpyvista/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
streamlit
pyvista
ipython
bokeh
panel
95 changes: 51 additions & 44 deletions stpyvista/src/stpyvista/__init__.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,55 @@
# __init__.py

from io import BytesIO
from pathlib import Path
from typing import Optional, Union

import streamlit as st
from typing import Optional, Literal
import streamlit.components.v1 as components

import pyvista as pv
pv.set_jupyter_backend('static')

#from tempfile import NamedTemporaryFile
from io import BytesIO

import panel as pn
pn.extension('vtk')
from bokeh.resources import INLINE
from bokeh.resources import CDN

pv.set_jupyter_backend("static")
pn.extension("vtk", sizing_mode="stretch_width")

# Tell streamlit that there is a component called stpyvista,
# and that the code to display that component is in the "frontend" folder
frontend_dir = (Path(__file__).parent / "frontend").absolute()

_component_func = components.declare_component(
"stpyvista",
path=str(frontend_dir)
)
_component_func = components.declare_component("stpyvista", path=str(frontend_dir))


class stpyvistaTypeError(TypeError):
""" Unsupported format for input"""
"""Unsupported format for input"""

pass


# Create the python function that will be called from the front end
def stpyvista(
plotter : pv.Plotter,
horizontal_align : str = "center",
panel_kwargs = None,
key: Optional[str] = None
) -> None:
HA_MODES = Literal["left", "center", "right"]


def stpyvista(
plotter: pv.Plotter,
use_container_width: bool = True,
horizontal_align: HA_MODES = "center",
panel_kwargs=None,
key: Optional[str] = None,
) -> None:
"""
Renders a HTML_stpyvista as a threejs model.
Renders an interactive pyvisya Plotter in streamlit.
Parameters
----------
input: Union[pv.Plotter, HTML_stpyvista]
Plotter to render
use_container_width : bool = True
If True, set the dataframe width to the width of the parent container. \
This takes precedence over the `horizontal_align` argument. \
Defaults to True
horizontal_align: str = "center"
Either "center", "left" or "right"
Either "center", "left" or "right". Defaults to "center".
panel_kwargs: dict | None
Optional keyword parameters to pass to pn.panel() Check:
Expand All @@ -56,7 +59,6 @@ def stpyvista(
orientation_widget: bool
Show the xyz axis indicator
key: str|None
An optional key that uniquely identifies this component. If this is
None, and the component's arguments are changed, the component will
Expand All @@ -67,39 +69,44 @@ def stpyvista(
None
"""

if isinstance(plotter, pv.Plotter):

if isinstance(plotter, pv.Plotter):
if panel_kwargs is None:
panel_kwargs = dict()

width, height = plotter.window_size
geo_pan_pv = pn.panel(
plotter.ren_win,
width = width,
height = height,
**panel_kwargs)


if use_container_width:
geo_pan_pv = pn.panel(plotter.ren_win, height=height, **panel_kwargs)
else:
geo_pan_pv = pn.panel(
plotter.ren_win, height=height, width=width, **panel_kwargs
)

# Create HTML file
model_bytes = BytesIO()
geo_pan_pv.save(model_bytes, resources=INLINE)
panel_html = model_bytes.getvalue().decode('utf-8')
geo_pan_pv.save(model_bytes, resources=CDN)
panel_html = model_bytes.getvalue().decode("utf-8")
model_bytes.close()

component_value = _component_func(
panel_html = panel_html,
width = width,
height = height,
horizontal_align = horizontal_align,
key = key,
default = 0)
panel_html=panel_html,
height=height,
width=width,
horizontal_align=horizontal_align,
use_container_width=1 if use_container_width else 0,
key=key,
default=0,
)

return component_value

else:
raise(stpyvistaTypeError)
else:
raise (stpyvistaTypeError)


def main():
pass


if __name__ == "__main__":
main()
4 changes: 3 additions & 1 deletion stpyvista/src/stpyvista/__main__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# __main__.py


def main():
pass


if __name__ == "__main__":
main()
main()
29 changes: 22 additions & 7 deletions stpyvista/src/stpyvista/frontend/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,37 @@ function onRender(event) {
if (!window.rendered) {

// You most likely want to get the data passed in like this
const {panel_html, width, height, horizontal_align, key} = event.detail.args;
const {panel_html, height, width, horizontal_align, use_container_width, key} = event.detail.args;

const stpyvistadiv = document.getElementById("stpyvistadiv");
const stpyvistaframe = document.getElementById("stpyvistaframe");

// Style the wrapping div for the iframe
stpyvistadiv.style.textAlign = horizontal_align;

// Overwrite default iframe dimensions with the container width
if (Boolean(use_container_width)){

stpyvistaframe.width = document.body.offsetWidth;

// Listen to resize changes. If any, panel takes care of resizing
function updateFrameWidth() {
stpyvistaframe.width = document.body.offsetWidth;
}

window.onresize = function(event) {
updateFrameWidth();
}
} else {
stpyvistaframe.width = width + 24;
}

// Overwrite default iframe dimensions and put model in the iframe
// just CSS styling does not apply to the iframe
stpyvistaframe.srcdoc = panel_html;
stpyvistaframe.width = width + 24;
stpyvistaframe.height = height + 20;
stpyvistaframe.scrolling = "yes";
// console.log("WIDTH", width)

// Style the wrapping div for the iframe

// stpyvistadiv.style.width = stpyvistaframe.width + 10;
stpyvistadiv.style.textAlign = horizontal_align;

// console.log("HEIGHT", height)
Streamlit.setFrameHeight(height + 50);
Expand Down
18 changes: 16 additions & 2 deletions stpyvista/test/cube.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,22 @@
plotter.view_isometric()
plotter.background_color = 'lightgray'

chk = st.sidebar.checkbox("use_container_width", False)
align = st.sidebar.select_slider("align", ["left", "center", "right"])

## Pass a key to avoid re-rendering at each time something changes in the page
stpyvista(plotter, panel_kwargs=dict(orientation_widget = True))
stpyvista(
plotter,
use_container_width=chk,
panel_kwargs=dict(orientation_widget = True),
horizontal_align=align
)

## Add something else below
st.text("Jello there"*50)
st.markdown("Jello there"*50)

cols = st.columns([1,2,1], gap="small")

for col in cols:
with col:
stpyvista(plotter, use_container_width=chk)

0 comments on commit 6c86f96

Please sign in to comment.