Integrate the Pollination Viewer
Last updated
Last updated
Pollination App developers can easily integrate the same high performance 3D web viewer that we use throughout our apps in their own work. We offer both a Streamlit component and a React component that can be used by the Pollination community.
The Streamlit component is published on PyPi, and our React component is available on npmjs.
The Streamlit component can be installed by executing the following command:
The first resource you should know about is the sample viewer app:
The code for this app is located here:
This app exposes all of the configurations and features of pollination-streamlit-viewer.
Here's a screenshot of the viewer:
The value returned by viewer
represents the scene that has been loaded into the viewer. This object is represented as a deeply nested dictionary with fields that often follow the structure of vtk.js objects. By default, this state will contain a single value called scene with an array of all the actors in the scene. This default return value will not update as you update the scene using the viewer controls. This is useful for reading default values, and actor[id]
s. Those actor[id]
s are used by some actions to modify actor properties.
If you want to read and respond to values as the viewer state changes, use the subscribe
toggle described below.
You can make use of the Streamlit provided st.file_uploader component to load a .vtkjs file.
Another package, pollination-streamlit-io, provides a component called get_hbjson
that can be used to get an hbjson model either from the web, or one of the Pollination CAD plugins. After getting the hbjson model, you will have to transform it into a vtkjs file that can be rendered by the Pollination viewer. Check out our sample app that demonstrates some of the following code..
There is a "gotcha" here that can cause the pollination-viewer to rerender the model multiple times unecessarily, and can sometimes cause your app to crash. We can fix the problem by using features of Streamlit that will help prevent the app from unecessarily receiving new versions of the same model.
Using @st.cache:
An alternative way to solve this problem is to use the on_change callback that is an optional argument to the get_hbjson component:
If your app generates an hbjson model programatically, you can cache the generated vtk.js models using st.session_state.
The subscribe toggle is a powerful feature that may be easy to misuse. If set, the value returned by st_vtkjs will contain additional fields.
These fields contain information about the renderer, legend, and "widgets" (the compass rose and section planes) and will update as you make changes to the scene. Be warned! This toggle can easily cause your app to go into an infinite rendering loop as the component responds to changes and returns new values. See the last section of this guide Gotchas: Controlling Component Re-rendering.
None of the features in the Sample Viewer App require the subscribe toggle to be set to True, so it shouldn't be necessary in most apps. A scenario in which it might be useful would be if you had a chart elsewhere in the app, and you wanted its min / max values (or color scheme) to stay in sync with the viewer legend's min / max values.
The action stack is an advanced feature that allows you to send actions to the viewer's dispatch function. Our Streamlit component executes each action once as they are added to the action_stack
array. There are a bunch of examples of common things you might want to do in the Sample Viewer App, the full list of available actions will develop over time with the viewer itself. Check the viewer's reducer function for the full list of available actions.
Here's an example of how to set the colorset from streamlit using the action_stack
:
This renders the select box in the right hand sidebar:
Please be aware, that if you've set the subscribe
toggle to True
this will cause the component to rerender. Please see the previous section on the Subscribe Toggle or the next section Gotchas: Controlling Component Re-Rendering for more information.
By default when the value of variables in Streamlit change, often in response to user input, the component will re-render. When a component like st_vtkjs
returns input back to Streamlit, it can easily cause an infinite loop of rendering that will cause the Streamlit app to report "Running..." in the top right corner of the app. This will eventually cause the app to fail. To control this behavior there are two techniques that you should employ.
First, use st.session_state
to prevent a variable from being reinitialized when the app responds to other changing values. st.session_state is a feature of Streamlit, and you can look to their docs to learn more about this feature.
Each Streamlit input component can use it's own session state automatically.
You can also use st.session_state
yourself.
The second strategy: don't set the subscribe
toggle unless you absolutely need to. All of the controls in the Viewer Sample work without this toggle. It is rarely necessary and if you choose to use it, you should know the ins and outs of this problem.
These software are both under active development, please report bugs on the Pollination forum.
Argument | Description |
---|---|
key
A unique string for each instance of the viewer.
content
A .vtkjs
file (see the section below Loading a File)
toolbar
A boolean
that toggles the toolbar visibility. Default is True
.
sidebar
A boolean that toggles the sidebar visiblity. Default is True
.
subscribe
A boolean that toggles subscription to the VTKJS camera and renderer content. Default is False
.
clear
A boolean to clear the current contents from the viewer before loading new content. Default is True
.
action_stack
The action stack is an advanced feature that allows the streamlit component to send actions to the React component's dispatch function. A set of useful actions are demonstrated in the sample app.
style
A dictionary to set the style for the viewer. The key and values can be any CSS style attribute. Default is: {"height" : " 640px", "border": "1px solid #d0d7de", "borderRadius": "2px"}