Vtk Interactive#

import panel as pn
import pyvista as pv

from pyvista import examples

pn.extension('vtk', design='material', sizing_mode='stretch_width', template='material')

pn.state.template.config.raw_css.append("""
#main {
  padding: 0;
}""")

For this example we use the pyvista library to load a dataset and generate easily a VTK scene

m = examples.download_st_helens().warp_by_scalar()

# default camera position
cpos = [
    (567000.9232163235, 5119147.423216323, 6460.423216322832),
    (562835.0, 5114981.5, 2294.5),
    (-0.4082482904638299, -0.40824829046381844, 0.8164965809277649)
]

# pyvista plotter
pl = pv.Plotter(notebook=True);
actor = pl.add_mesh(m, smooth_shading=True, lighting=True)
pl.camera_position = cpos #set camera position

# save initial camera properties
renderer = list(pl.ren_win.GetRenderers())[0]
initial_camera = renderer.GetActiveCamera()
initial_camera_pos = {
    "focalPoint": initial_camera.GetFocalPoint(),
    "position": initial_camera.GetPosition(),
    "viewUp": initial_camera.GetViewUp()
}

# Panel creation using the VTK Scene created by the plotter pyvista
orientation_widget = True
enable_keybindings = True
vtkpan = pn.pane.VTK(
    pl.ren_win, margin=0, sizing_mode='stretch_both', orientation_widget=orientation_widget,
    enable_keybindings=enable_keybindings, min_height=600
)
vtkpan

WidgetBox with colorbars and actor selection

# Creation of a mapping between Custom name and the VTK object reference
actor_ref = ["None", actor.__this__]
actor_names = ["None", 'St Helen']
actor_opts = {k:v for k,v in zip(actor_names, actor_ref)}

options = {}
actor_selection = pn.widgets.Select(value=None, options=actor_opts, margin=0, name="Actor Selection")
actor_selection

WidgetBoxes with general parameters of the vtk Scene (Widgets, Background, Lights,…)

# Scene Layout
color = ''.join(['#'] + ['{:02x}'.format(int(v*255)) for v in pl.background_color[:3]])
bind_and_orient = pn.widgets.CheckBoxGroup(
    value=['Orientation Widget', 'Key Bindings'], options=['Orientation Widget', 'Key Bindings']
)
reset_camera = pn.widgets.Button(name='Reset Camera')
background_color = pn.widgets.ColorPicker(value=color, name='Background Color')
scene_props = pn.WidgetBox(bind_and_orient, reset_camera, background_color, sizing_mode='stretch_width')

# Light properties
light_box_title = pn.widgets.StaticText(value='Light properties')
light_type = pn.widgets.Select(value='HeadLight', options=['HeadLight', 'SceneLight', 'CameraLight'])
light_intensity = pn.widgets.FloatSlider(start=0, end=1, value=1, name="Intensity")
light_props = pn.WidgetBox(light_box_title, light_type, light_intensity, sizing_mode='stretch_width')

pn.Column(scene_props, light_props, max_width=320)

WidgetBox with properties of the Actors

#layout actor props
opacity = pn.widgets.FloatSlider(value=1, start=0, end=1, name='Opacity', disabled=True)
lighting = pn.widgets.Toggle(value=True, name='Lighting', disabled=True)
interpolation = pn.widgets.Select(value='Phong', options=['Flat','Phong'], name='Interpolation', disabled=True)
edges = pn.widgets.Toggle(value=False, name='Show Edges', disabled=True)
edges_color = pn.widgets.ColorPicker(value='#ffffff', name='Edges Color', disabled=True)
representation = pn.widgets.Select(value='Surface', options=['Points','Wireframe','Surface'], name='Representation', disabled=True)
frontface_culling = pn.widgets.Toggle(value=False, name='Frontface Culling', disabled=True)
backface_culling = pn.widgets.Toggle(value=False, name='Backface Culling', disabled=True)
ambient = pn.widgets.FloatSlider(value=0, start=-1, end=1, name='Ambient', disabled=True)
diffuse = pn.widgets.FloatSlider(value=1, start=0, end=2, name='Diffuse', disabled=True)
specular = pn.widgets.FloatSlider(value=0, start=0, end=10, name='Specular', disabled=True)
specular_power = pn.widgets.FloatSlider(value=100, start=0, end=100, name='Specular Power', disabled=True)

actor_props = pn.WidgetBox(
    opacity, lighting, interpolation, edges, edges_color,
    representation, frontface_culling, backface_culling,
    ambient, diffuse, specular, specular_power
)

actor_props

Linking all widgets together using jslinks

#Linking
light_type.jslink(vtkpan, code={'value':"""
const light = target.renderer_el.getRenderer().getLights()[0]
if (source.value == 'HeadLight')
    light.setLightTypeToHeadLight()
else if (source.value == 'CameraLight')
    light.setLightTypeToCameraLight()
else if (source.value == 'SceneLight')
    light.setLightTypeToSceneLight()
target.renderer_el.getRenderWindow().render()
"""})

light_intensity.jslink(vtkpan, code={'value':"""
const light = target.renderer_el.getRenderer().getLights()[0]
light.setIntensity(source.value)
target.renderer_el.getRenderWindow().render()
"""})


bind_and_orient.jslink(vtkpan, code = {'active':"""
target.orientation_widget = source.active.includes(0)
target.enable_keybindings = source.active.includes(1)
"""})

reset_camera.js_on_click(args={'target': vtkpan, 'initial_camera':initial_camera_pos},
                         code="target.camera = initial_camera");

background_color.jslink(vtkpan, code={'value':"""
const hextoarr = (color) => {return [parseInt(color.slice(1,3),16)/255, parseInt(color.slice(3,5),16)/255, parseInt(color.slice(5,7),16)/255]}
target.renderer_el.getRenderer().setBackground(hextoarr(source.color))
target.renderer_el.getRenderWindow().render()
"""});

opacity.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setOpacity(source.value)
    target.renderer_el.getRenderWindow().render()
}
""")

lighting.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setLighting(source.active)
    target.renderer_el.getRenderWindow().render()
}
""")

edges.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setEdgeVisibility(source.active)
    target.renderer_el.getRenderWindow().render()
}
""")

interpolation.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    if(source.value=="Flat"){
        actor.getProperty().setInterpolationToFlat()
    }else{
        actor.getProperty().setInterpolationToPhong()
    }
    target.renderer_el.getRenderWindow().render()
}
""")

edges_color.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const hextoarr = (color) => {return [parseInt(color.slice(1,3),16)/255, parseInt(color.slice(3,5),16)/255, parseInt(color.slice(5,7),16)/255]}
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setEdgeColor(hextoarr(source.color))
    target.renderer_el.getRenderWindow().render()
}
""")

representation.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    if(source.value=="Points"){
        actor.getProperty().setRepresentationToPoints()
    }else if(source.value=="Wireframe"){
        actor.getProperty().setRepresentationToWireframe()
    }else if(source.value=="Surface"){
        actor.getProperty().setRepresentationToSurface()
    }
    target.renderer_el.getRenderWindow().render()
}
""")

frontface_culling.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setFrontfaceCulling(source.active)
    target.renderer_el.getRenderWindow().render()
}
""")

backface_culling.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setBackfaceCulling(source.active)
    target.renderer_el.getRenderWindow().render()
}
""")

ambient.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setAmbient(source.value)
    target.renderer_el.getRenderWindow().render()
}
""")

diffuse.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setDiffuse(source.value)
    target.renderer_el.getRenderWindow().render()
}
""")

specular.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setSpecular(source.value)
    target.renderer_el.getRenderWindow().render()
}
""")

specular_power.jscallback(args={"target":vtkpan, "actor_selection":actor_selection}, value="""
if (actor_selection.value!="None"){
    const actor = target.getActors(actor_selection.value)[0]
    actor.getProperty().setSpecularPower(source.value)
    target.renderer_el.getRenderWindow().render()
}
""")

actor_selection.jslink(target=vtkpan, code = {'value' : """
if (source.value!="None"){
    const actor = target.getActors(source.value)[0]
    target.outline.setInputData(actor.getMapper().getInputData())
    target.renderer_el.getRenderer().addActor(target.outline_actor)
    
    //synchronize actor props and widgets values
    const properties = actor.getProperty()
    opacity.setv({value: properties.getOpacity()}, {silent: true})
    lighting.setv({active: !!properties.getLighting()}, {silent: true})
    edges.active = !!properties.getEdgeVisibility()
    const actor_color = "#" + properties.getEdgeColor().map((c) => ("0" + Math.round(255*c).toString(16,2)).slice(-2)).join('')
    edges_color.setv({color: actor_color}, {silent: true})
    const interp_string = properties.getInterpolationAsString()
    interpolation.setv({value: interp_string[0] + interp_string.slice(1).toLocaleLowerCase()}, {silent: true})
    const repr_string = properties.getRepresentationAsString()
    representation.setv({value: repr_string[0] + repr_string.slice(1).toLocaleLowerCase()}, {silent: true})
    frontface_culling.setv({active: !!properties.getFrontfaceCulling()}, {silent: true})
    backface_culling.setv({active: !!properties.getBackfaceCulling()}, {silent: true})
    ambient.setv({value: properties.getAmbient()}, {silent: true})
    diffuse.setv({value: properties.getDiffuse()}, {silent: true})
    specular.setv({value: properties.getSpecular()}, {silent: true})
    specular_power.setv({value: properties.getSpecularPower()}, {silent: true})
    //enable widgets modifications
    opacity.disabled = false
    lighting.disabled = false
    interpolation.disabled = false
    edges.disabled = false
    edges_color.disabled = false
    representation.disabled = false
    frontface_culling.disabled = false
    backface_culling.disabled = false
    ambient.disabled = false
    diffuse.disabled = false
    specular.disabled = false
    specular_power.disabled = false
} else {
    target.renderer_el.getRenderer().removeActor(target.outline_actor)
    opacity.disabled = true
    lighting.disabled = true
    interpolation.disabled = true
    edges.disabled = true
    edges_color.disabled = true
    representation.disabled = true
    frontface_culling.disabled = true
    backface_culling.disabled = true
    ambient.disabled = true
    diffuse.disabled = true
    specular.disabled = true
    specular_power.disabled = true
}
target.renderer_el.getRenderWindow().render()

"""}, args={"opacity":opacity, "lighting":lighting, "interpolation": interpolation, "edges": edges, "edges_color": edges_color,
            "representation": representation, "frontface_culling": frontface_culling, "backface_culling": backface_culling,
            "ambient": ambient, "diffuse": diffuse, "specular": specular, "specular_power": specular_power});

pn.Column(
    "This example demonstrates the use of **VTK and pyvista** to display a *scene*",
    pn.Row(
        vtkpan.servable(title='VTK - Mt. St Helens'),
        pn.Column(
            actor_selection,
            pn.Tabs(
                ('Scene controller', pn.Column(scene_props, light_props)),
                ('Actor properties',actor_props)
            )
        ).servable(target='sidebar')
    ), min_height=600
)