GithubHelp home page GithubHelp logo

trame-vtk's Introduction

VTK/ParaView widgets for trame

Test and Release

trame-vtk extend trame widgets with components that can interface with VTK and/or ParaView.

VTK integration in trame allows you to create rich visualization and data processing applications by leveraging the Python wrapping of the VTK library. Several components are available so you can leverage VTK either for its data processing and/or rendering. trame lets you choose if you want to leverage Remote Rendering or if the client should do the rendering by leveraging vtk.js under the hood.

Installing

trame-vtk can be installed with pip:

pip install --upgrade trame-vtk

Usage

The Trame Tutorial is the place to go to learn how to use the library and start building your own application.

The API Reference documentation provides API-level documentation.

License

trame-vtk is made available under the BSD-3-Clause License. For more details, see LICENSE This license has been chosen to match the one use by VTK and ParaView which can be exposed via this library.

Community

Trame | Discussions | Issues | RoadMap | Contact Us

image

Enjoying trame?

Share your experience with a testimonial or with a brand approval.

Development: Grabbing client before push to PyPI

To update the client code, run the following command line while updating the targeted version

bash .fetch_externals.sh

Trame widgets

VtkRemoteView

The VtkRemoteView component relies on the server for rendering by sending images to the client by simply binding your vtkRenderWindow to it. This component gives you controls to the image size reduction and quality to reduce latency while interacting.

How to use it?

The component allows you to directly tap into a vtk.js interactor's events so you can bind your own method from Python to them. The list of available events can be found here.

The component also provides a convenient method for pushing a new image to the client when you're modifying your scene on the Python side.

from trame.widgets import vtk

def end():
    pass

remote_view = vtk.vtkRemoteView(
    view=...,               # Instance of vtkRenderWindow (required)
    ref=...,                # Identifier for this component
    interactive_quality=60, # [0, 100] 0 for fastest render, 100 for best quality
    interactive_ratio=...,  # [0.1, 1] Image size scale factor while interacting
    interactor_events=(     # Enable vtk.js interactor events for method binding
        "events",
        ["EndAnimation"],
    ),
    EndAnimation=end,       # Bind method to the enabled event
)

remote_view.update()  # Force image to be pushed to client

Examples

VtkLocalView

The VtkLocalView component relies on the server for defining the vtkRenderWindow but then only the geometry is exchanged with the client. The server does not need a GPU as no rendering is happening on the server. The vtkRenderWindow is only used to retrieve the scene data and parameters (coloring by, representations, ...). By relying on the same vtkRenderWindow, you can easily switch from a VtkRemoteView to a VtkLocalView or vice-versa. This component gives you controls on how you want to map mouse interaction with the camera. The default setting mimic default VTK interactor style so you will rarely have to override to the interactor_settings.

How to use it?

The component allows you to directly tap into a vtk.js interactor events so you can bind your own method from python to them. The list of available events can be found here.

The component also provides a convenient method to push the scene to the client when you're modifying your scene on the python side.

from trame.widgets import vtk

def end():
    pass

local_view = vtk.VtkLocalView(
    view=...,                # Instance of vtkRenderWindow (required)
    ref=...,                 # Identifier for this component
    context_name=...,        # Namespace for geometry cache
    interactor_settings=..., # Options for camera controls. See below.
    interactor_events=(      # Enable vtk.js interactor events for method binding
        "events",
        ['EndAnimation'],
    ),
    EndAnimation=end,        # Bind method to the enabled event
)

local_view.update()  # Force geometry to be pushed

Interactor Settings

For the interactor_settings we expect a list of mouse event type linked to an action. The example below is what is used as default:

interactor_settings=[
  {
    button: 1,
    action: 'Rotate',
  }, {
    button: 2,
    action: 'Pan',
  }, {
    button: 3,
    action: 'Zoom',
    scrollEnabled: true,
  }, {
    button: 1,
    action: 'Pan',
    shift: true,
  }, {
    button: 1,
    action: 'Zoom',
    alt: true,
  }, {
    button: 1,
    action: 'ZoomToMouse',
    control: true,
  }, {
    button: 1,
    action: 'Roll',
    alt: true,
    shift: true,
  }
]

A mouse event can be identified with the following set of properties:

Attribute Value Description
button 1, 2, 3 Which button should be down
shift true/false Is the Shift key down
alt true/false Is the Alt key down
control true/false Is the Ctrl key down
scrollEnabled true/false Some action could also be triggered by scroll
dragEnabled true/false Mostly used to disable default drag behavior

And the action could be one of the following:

Action Description
Pan Will pan the object on the plane normal to the camera
Zoom Will zoom closer or further from the object based on the drag direction
Roll Will rotate the object around the view direction
ZoomToMouse Will zoom while keeping the location that was initially under the mouse at the same spot

Examples

VtkRemoteLocalView

The VtkRemoteLocalView component is a blend of VtkLocalView and VtkRemoteView where the user can choose dynamically which mode they want to be in. When instantiating a VtkRemoteLocalView several variables and triggers will be created for you to more easily control your view.

How to use it?

from trame.html import vtk

rl_view = vtk.VtkRemoteLocalView(
    view=...,                # Instance of vtkRenderWindow (required)

    # Just VtkRemoteLocalView params
    namespace=...,           # Prefix for variables and triggers. See below. (required)
    mode="local",            # Decide between local or remote. See below.

    # VtkRemoteView params
    **remote_view_params,

    # VtkLocalView params
    **local_view_params,
)

rl_view.update_geometry()  # Force update to geometry
rl_view.update_image()     # Force update to image
rl_view.view()             # Get linked vtkRenderWindow instance

Namespace parameter

Constructing a VtkRemoteLocalView will set several variables, prefixed by a namespace. In the example below we used namespace="view".

Variable Description
viewId str representing the vtkRenderWindow id
viewMode local`or `remote to control which View is displayed to the user

Constructing a VtkRemoteLocalView will also set several trame triggers.

Trigger Description
viewCamera When call with no arguments, the server will push its camera to the client
viewAnimateStart Start the animation loop for constantly rendering
viewAnimateStop Stop the animation loop

The namespace will also be used as ref= unless provided by the user.

Mode parameter

The mode is driven by the variable {namespace}Mode but can be provided when instantiated so the default can be overridden and a JavaScript expression can be used instead of the default variable. This attribute behaves the same way as any trame one except, we won't register the left side as a state entry since we already have one under {namespace}Mode. This means we will evaluate the left side of the expression assuming a tuple is provided and the right side of the tuple is used to set its initial value.

Examples

trame-vtk's People

Contributors

actions-user avatar alesgenova avatar banesullivan avatar christost avatar erikbedard avatar felipecybis avatar jourdain avatar jwindgassen avatar mattthecuber avatar psavery avatar tkoyama010 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

trame-vtk's Issues

UI warning on failed serialization

Could the local vtk view have some sort of dismissable error popup, perhaps in the bottom corner, that lists the types of data that were unable to be serialized?

In PyVista, we suppress the warnings from VTK to avoid lengthy output in Jupyter:

pyvista/pyvista@c67a5ff

I wonder if in VtkLocalView, we could override that VTK logger and capture this error message:

https://github.com/Kitware/VTK/blob/af0878b870528596009fc9a627a8aa9b90917316/Web/Python/vtkmodules/web/render_window_serializer.py#L184

for the a better UI message directly with trame

VTK_LONG_LONG (int64) unsupported

Just realized that numpy's dtype int64 maps to VTK's VTK_LONG_LONG which the web serializer module in VTK is failing to handle. Being that (on my system), the default for numpy.arange is int64 this is a pretty pressing issue we should patch.

I'm not sure what JS type this maps to though...

We definitely need to patch this upstream in VTK, but I'm opening the issue here to patch more quickly

VTK_LONG_LONG and VTK_UNSIGNED_LONG_LONG definitions:

https://github.com/Kitware/VTK/blob/aaa1eb770f0de3b86382fd0bad99d4a5755318bd/Common/Core/vtkType.h#L63-L64

Indeed, 16 and 17 are missing from arrayTypesMapping:

https://github.com/Kitware/VTK/blob/aaa1eb770f0de3b86382fd0bad99d4a5755318bd/Web/Python/vtkmodules/web/__init__.py#L3-L20

And here is at least a simple example with PyVista where this fails:

import numpy as np
import pyvista as pv

mesh = pv.Sphere()
mesh.cell_data['foo'] = np.arange(mesh.n_cells)

plotter = pv.Plotter()
plotter.add_mesh(mesh, scalars='foo')
plotter.show()
File /opt/miniconda3/envs/pyvista-dev/lib/python3.9/site-packages/vtkmodules/web/render_window_serializer.py:347, in getArrayDescription(array, context)
    344 if not array:
    345     return None
--> 347 pMd5 = digest(array)
    348 context.cacheDataArray(
    349     pMd5, {"array": array, "mTime": array.GetMTime(), "ts": time.time()}
    350 )
    352 root = {}

File /opt/miniconda3/envs/pyvista-dev/lib/python3.9/site-packages/vtkmodules/web/render_window_serializer.py:322, in digest(array)
    319 if record and record["mtime"] == array.GetMTime():
    320     return record["sha"]
--> 322 record = {"sha": hashDataArray(array), "mtime": array.GetMTime()}
    324 dataArrayShaMapping[objId] = record
    325 return record["sha"]

File /opt/miniconda3/envs/pyvista-dev/lib/python3.9/site-packages/vtkmodules/web/__init__.py:46, in hashDataArray(dataArray)
     44 def hashDataArray(dataArray):
     45     hashedBit = hashlib.md5(memoryview(dataArray)).hexdigest()
---> 46     typeCode = arrayTypesMapping[dataArray.GetDataType()]
     47     return "%s_%d%s" % (hashedBit, dataArray.GetSize(), typeCode)

IndexError: list index out of range

View not automatically updating when two trame apps running in same jupyter notebook

Hi all,

As suggested by @jourdain, I'm transitioning the discussion here to an issue in the trame-vtk repo. The issue is as follows:

When I run two trame apps within a jupyter notebook, only the last is automatically updated upon changing, e.g., the opacity of an actor. The first is updated only upon clicking within the viewer. In contrast, I'd expect each to update automatically, independent of the number of trame apps running. Here's a simple example that illustrates the issue:

# cell 1
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vuetify
import pyvista as pv

class Cone():
    def __init__(self, name):
        server = get_server(name)
        
        p = pv.Plotter()
        p.add_mesh(pv.Cone(), name="cone")
        self.plotter = p

        with SinglePageLayout(server) as layout:
            with layout.content:
                with vuetify.VContainer(fluid=True, classes="pa-0 fill-height"):
                    pv.trame.PyVistaRemoteView(p)

            self.server = server
            self.ui = layout

    def update_opacity(self, value): 
        self.plotter.actors["cone"].prop.opacity = value
        self.plotter.update()

# cell 2
cone_1 = Cone("1")
await cone_1.ui.ready
cone_1.ui

# cell 3
cone_2 = Cone("2")
await cone_2.ui.ready
cone_2.ui

# cell 4
cone_1.update_opacity(0.5) # cone opacity doesn't change without clicking 

# cell 5
cone_2.update_opacity(0.5) # cone opacity changes automatically

In the previous discussion, @jourdain noted that it was an issue with remote vtk and having more than one server within the same interpreter and thought that using a child_server might provide a quick fix.

Feature request: add TextActor or CornerAnnotations serializer

It would be awsome to be able to serialize pyvista title/text objects. I've looked a little bit in the vtkjs side (https://kitware.github.io/vtk-js/api/Interaction_UI_CornerAnnotation.html) but I must confess it is quite different from other classes I looked up before. Not quite sure what would it need.

I see it has all this methods:

    'annotationContainer',
    'northWestContainer',
    'northContainer',
    'northEastContainer',
    'westContainer',
    'eastContainer',
    'southWestContainer',
    'southContainer',
    'southEastContainer',

that are divs. I assume it would work just passing innerHtml to them? Have you guys already thought about text or annotations serialization?

Would love to help in my free time, but definitely need guidance :)

vtkLookupTable serializer not getting the right NumberOfColors?

Screen Shot 2023-01-26 at 10 56 41 AM

I would think that GetNumberOfColors() is all that's needed. But maybe something in the client-side VTK.js code isn't using this?

Example code:

"""Validate vtkLookupTable serializer with N Colors."""

import pyvista as pv
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import html, vuetify
from trame.widgets.vtk import VtkLocalView, VtkRemoteView

server = get_server()
state, ctrl = server.state, server.controller

state.trame__title = "PyVista Lookup Table N Colors"

# -----------------------------------------------------------------------------
pv.set_plot_theme("document")


mesh = pv.Wavelet()

plotter = pv.Plotter(off_screen=True)
actor = plotter.add_mesh(mesh, n_colors=7)
plotter.set_background("lightgrey")
plotter.view_isometric()

# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------

with SinglePageLayout(server) as layout:
    layout.icon.click = ctrl.view_reset_camera
    layout.title.set_text(state.trame__title)

    with layout.toolbar:
        vuetify.VSpacer()

    with layout.content:
        with vuetify.VContainer(
            fluid=True,
            classes="pa-0 fill-height",
        ):
            with vuetify.VContainer(
                fluid=True, classes="pa-0 fill-height", style="width: 50%;"
            ):
                local = VtkLocalView(
                    plotter.ren_win,
                )
            with vuetify.VContainer(
                fluid=True, classes="pa-0 fill-height", style="width: 50%;"
            ):
                remote = VtkRemoteView(
                    plotter.ren_win,
                )

            def view_update(**kwargs):
                local.update(**kwargs)
                remote.update(**kwargs)

            def view_reset_camera(**kwargs):
                local.reset_camera(**kwargs)
                remote.reset_camera(**kwargs)

            ctrl.view_update = view_update
            ctrl.view_reset_camera = view_reset_camera

            ctrl.on_server_ready.add(view_update)

    # hide footer
    layout.footer.hide()

# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

if __name__ == "__main__":
    server.start()

Forward the PinchEvent with vtkRemoteView for touch screens

I recently tried using an iPad to view a Trame application and noticed that PinchEvents are not forwarded with remote rendering.

I tried following the example in https://github.com/Kitware/trame/blob/master/examples/06_vtk/01_SimpleCone/RemoteRendering.py to forward and trigger the event on the vtkRenderWindowInteractor, but I can get it working just right.

I'm curious if this is something that could be handled by default in trame-vtk?

Leverage caching and partial updates with scene graph

We should improve the caching and synchronization of the scene graph when there are thousands of actors.

For example, toggling edge visibility for 2,500 actors requires a massive scene graph synchronization that should be able to be optimized.

The main culprit I see in the scene graph being synchronized is the vtkColorTransferFunction for every actor/mapper -- quickly blowing up the scene graph.

I managed to resolve my primary issue using composite mappers, but this issue will arise again and we should leverage caching with the scene graph serializers

import vtkmodules.vtkRenderingOpenGL2  # noqa
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import html
from trame.widgets import vtk as vtk_widgets
from trame.widgets import vuetify
from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch  # noqa
from vtkmodules.vtkRenderingCore import (
    vtkActor,
    vtkPolyDataMapper,
    vtkRenderer,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
)

# -----------------------------------------------------------------------------
# Trame initialization
# -----------------------------------------------------------------------------

TITLE = "Local Toggle Edges with Many Actors"

server = get_server()
state, ctrl = server.state, server.controller

state.trame__title = TITLE

# -----------------------------------------------------------------------------
# VTK pipeline
# -----------------------------------------------------------------------------


renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)

renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

actors = []
for i in range(50):
    for j in range(50):
        cone_source = vtkConeSource()
        cone_source.SetCenter((i, j, 0))
        mapper = vtkPolyDataMapper()
        actor = vtkActor()
        mapper.SetInputConnection(cone_source.GetOutputPort())
        actor.SetMapper(mapper)
        renderer.AddActor(actor)
        actors.append(actor)
renderer.ResetCamera()
renderWindow.Render()
renderer.SetBackground(0, 0.5, 0)

camera = renderer.GetActiveCamera()

# -----------------------------------------------------------------------------
# Callbacks
# -----------------------------------------------------------------------------


def toggle_edges():
    for actor in actors:
        actor.GetProperty().SetEdgeVisibility(not actor.GetProperty().GetEdgeVisibility())
    ctrl.view_update()


# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------

with SinglePageLayout(server) as layout:
    layout.icon.click = ctrl.view_reset_camera
    layout.title.set_text(TITLE)

    with layout.toolbar:
        vuetify.VSpacer()
        vuetify.VBtn("Toggle edges", click=toggle_edges)

    with layout.content:
        with vuetify.VContainer(
            fluid=True,
            classes="pa-0 fill-height",
        ):

            local_view = vtk_widgets.VtkLocalView(
                renderWindow,
                ref="view_local",
            )
            ctrl.view_update = local_view.update
            ctrl.view_reset_camera = local_view.reset_camera
            ctrl.view_push_camera = local_view.push_camera


# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

if __name__ == "__main__":
    server.start()

Trying to play animation using paraview.simple

I'm creating a PVD file viewing tool. I'm using paraview.simple to read and display my files, but I can't execute the animation. When I run it, only the last frame is displayed in the browser.
How can I do this? Do you have an example similar to what I'm trying to do?

examples/07_paraview/Picking not working?

Hi Sebastien,

I was playing around with the examples and noted that examples/07_paraview/Picking is (maybe) misbehaving. Box selections look right, but hoverOnPoint/Cell is not displaying any tooltip with info as it happens in pv.

Cheers

Error in WidgetManager, TypeError: unsupported operand type(s) for |: 'type' and '_GenericAlias'

Description:
Raises error when importing WidgetManager.

--> 107 WidgetParam = vtkAbstractWidget | Type[vtkAbstractWidget]
    108 RepresentationParam = vtkWidgetRepresentation | Type[vtkWidgetRepresentation]
    111 class WidgetManager:

TypeError: unsupported operand type(s) for |: 'type' and '_GenericAlias'

I'm not sure do you wanna use or here?
Reproduce the issue:

from trame_vtk.modules.vtk.widget import WidgetManager

view_update might be throttling?

I'm experiencing an issue where calling view_update() for VtkRemoteView does not always work. It seems like the call might be throttled?

It results in a clunky user interface in this slicing example. After moving the plane widget in the scene on the left, the scene on the right should immediately update. However it does not always update, sometimes requireing a user-click in either of the two views for the one on the right to update

import pyvista as pv
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vtk as vtk_widgets
from trame.widgets import vuetify

pv.OFF_SCREEN = True

server = get_server()
state, ctrl = server.state, server.controller

state.trame__title = "Multi Plotters"


mesh = pv.Wavelet()

pl1 = pv.Plotter()
pl1.add_mesh(mesh.contour())
pl1.reset_camera()

pl2 = pv.Plotter()
pl2.add_mesh(mesh.outline(), color='black')
pl2.reset_camera()

def my_callback(normal, origin):
    pl2.add_mesh(mesh.slice(normal, origin), name="slice")
    ctrl.view2_update()  # <-- is this being throttled?

pl1.add_plane_widget(my_callback)


with SinglePageLayout(server) as layout:
    layout.title.set_text("Multi Views")
    layout.icon.click = ctrl.view_reset_camera

    with layout.content:
        with vuetify.VContainer(
            fluid=True,
            classes="pa-0 fill-height",
        ):
            with vuetify.VCol(classes="pa-0 fill-height"):
                view = vtk_widgets.VtkRemoteView(pl1.render_window, ref="view1")
                ctrl.view1_update = view.update
            with vuetify.VCol(classes="pa-0 fill-height"):
                view = vtk_widgets.VtkRemoteView(pl2.render_window, ref="view2")
                ctrl.view2_update = view.update

server.start()
view-update.mov

Add `push_camera` method for local views

Add the ability to serialize a vtkCamera object and push it to the local view to more easily sync a camera between client-server when using a vtkRenderWindow

How to select cells using paraview.simple

I'm using paraview.simple to read and display PVD files. In Paraview, there is a really useful tool called "Plot Selection Over Time", which I would like to use in my trame application. How can I make selections with the mouse in this case?

Another question would be to plot this graph, I thought about using a new VContainer from the trame.vuetify library, but it is located below the original container.

AttributeError module 'asyncio' has no attribute 'get_running_loop'

server.start()
File "d:\Users\me\Anaconda3\lib\site-packages\trame_server\core.py", line 516, in start
"exec_mode": exec_mode,
File "d:\Users\me\Anaconda3\lib\site-packages\trame_server\protocol.py", line 56, in server_start
**kwargs,
File "d:\Users\me\Anaconda3\lib\site-packages\wslink\server.py", line 300, in start_webserver
return exec_modesexec_mode
File "d:\Users\me\Anaconda3\lib\site-packages\wslink\server.py", line 284, in main_exec
loop.run_until_complete(create_coroutine())
File "d:\Users\me\Anaconda3\lib\asyncio\base_events.py", line 468, in run_until_complete
return future.result()
File "d:\Users\me\Anaconda3\lib\site-packages\wslink\backends\aiohttp_init_.py", line 126, in start
port_callback(self.get_port())
File "d:\Users\me\Anaconda3\lib\site-packages\trame_server\protocol.py", line 98, in port_callback
if not self.server.ready.done():
File "d:\Users\me\Anaconda3\lib\site-packages\trame_server\core.py", line 338, in ready
self._running_future = asyncio.get_running_loop().create_future()
AttributeError: module 'asyncio' has no attribute 'get_running_loop'


Python 3.6.5 |Anaconda, Inc.| (default, Mar 29 2018, 13:32:41) [MSC v.1900 64 bit (AMD64)]

How to handle vtkPolyDataSilhouette with local view?

How can we support the usage of filters like vtkPolyDataSilhouette? If I modify the examples/06_vtk/01_SimpleCone/LocalRendering.py example to us it as follows, then the silhouette is not synchronized to the client view as when the camera moves. I'm not sure how to cleanly handle this..

diff --git a/examples/06_vtk/01_SimpleCone/LocalRendering.py b/examples/06_vtk/01_SimpleCone/LocalRendering.py
index 092b81c..12812d5 100644
--- a/examples/06_vtk/01_SimpleCone/LocalRendering.py
+++ b/examples/06_vtk/01_SimpleCone/LocalRendering.py
@@ -46,6 +46,20 @@ actor = vtkActor()
 mapper.SetInputConnection(cone_source.GetOutputPort())
 actor.SetMapper(mapper)
 renderer.AddActor(actor)
+
+
+import vtk
+sil = vtk.vtkPolyDataSilhouette()
+sil.SetInputConnection(cone_source.GetOutputPort())
+sil.SetCamera(renderer.GetActiveCamera())
+silmapper = vtkPolyDataMapper()
+silmapper.SetInputConnection(sil.GetOutputPort())
+silactor = vtkActor()
+silactor.GetProperty().SetColor(1, 0, 0)
+silactor.GetProperty().SetLineWidth(10)
+silactor.SetMapper(silmapper)
+renderer.AddActor(silactor)
+
 renderer.ResetCamera()
 renderWindow.Render()

v2.8.4 breaks compatability with Python 3.8

6e163d6 broke PyVista compatibility with Python 3.8. See https://github.com/pyvista/pyvista/actions/runs/7914881477/job/21605549677?pr=5625

[large traceback shortened]
    File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/trame_vtk/modules/vtk/serializers/data.py", line 29, in polydata_serializer
      points = get_array_description(
    File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/trame_vtk/modules/vtk/serializers/helpers.py", line 76, in get_array_description
      p_md5 = digest(array)
    File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/trame_vtk/modules/vtk/serializers/helpers.py", line 51, in digest
      record = {"sha": hash_data_array(array), "mtime": array.GetMTime()}
    File "/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/trame_vtk/modules/vtk/serializers/utils.py", line 51, in hash_data_array
      hashed_bit = hashlib.md5(memoryview(data_array), usedforsecurity=False).hexdigest()
  TypeError: openssl_md5() takes at most 1 argument (2 given)

Edit: I should be more precise in saying that it breaks PyVista functionality when using trame-vtk.

No tests?

There are no automated tests of this package - what would testing look like with trame?

Mouse wheel event can break PyVistaAxesGrid.py example

When you start the PyVistaAxesGrid.py example, if you begin by moving the mouse wheel rather than a different kind of mouse interaction, the rendering canvas turns completely blank (and can only be restored through refreshing the page).

Client rendering color rescale issue

This is in reference to this discussion on pyvista.

If you run his example script, and click "Load Model", you get this image:

image

If you then click "Load Scalars", the desired output is the following:

image

However, instead, you get this:

image

But, if you then refresh the web page, you then get the correct image.

It appears that the colors are not getting rescaled properly. @jourdain, is this potentially an issue with updating from a delta state?

Note that if we change the viewer mode to "server" instead of "client", it works properly.

VtkLocalView not syncing if actors are removed

I have modified examples/06_vtk/01_SimpleCone/LocalRendering.py to have a delete button that will remove the actor from the render window. However, on view.update, the data being removed is not synced to the client renderer.

This is the added code:

def remove_actor():
    renderer.RemoveActor(actor)
    renderer.Modified()
    ctrl.view_update()

...

        with vuetify.VBtn(icon=True, click=remove_actor):
            vuetify.VIcon("mdi-delete")

and this is the full example:

r"""
Version for trame 1.x - https://github.com/Kitware/trame/blob/release-v1/examples/VTK/SimpleCone/LocalRendering.py
Delta v1..v2          - https://github.com/Kitware/trame/commit/674f72774228bbcab5689417c1c5642230b1eab8
"""

from trame.app import get_server
from trame.widgets import vuetify, vtk
from trame.ui.vuetify import SinglePageLayout

from vtkmodules.vtkFiltersSources import vtkConeSource
from vtkmodules.vtkRenderingCore import (
    vtkRenderer,
    vtkRenderWindow,
    vtkRenderWindowInteractor,
    vtkPolyDataMapper,
    vtkActor,
)
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch  # noqa

# -----------------------------------------------------------------------------
# Trame initialization
# -----------------------------------------------------------------------------

server = get_server()
state, ctrl = server.state, server.controller

state.trame__title = "VTK Remote View - Local Rendering"

# -----------------------------------------------------------------------------
# VTK pipeline
# -----------------------------------------------------------------------------

DEFAULT_RESOLUTION = 6

renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)

renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()

cone_source = vtkConeSource()
mapper = vtkPolyDataMapper()
actor = vtkActor()
mapper.SetInputConnection(cone_source.GetOutputPort())
actor.SetMapper(mapper)
renderer.AddActor(actor)
renderer.ResetCamera()
renderWindow.Render()

# -----------------------------------------------------------------------------
# Callbacks
# -----------------------------------------------------------------------------


@state.change("resolution")
def update_cone(resolution=DEFAULT_RESOLUTION, **kwargs):
    cone_source.SetResolution(resolution)
    ctrl.view_update()


def update_reset_resolution():
    state.resolution = DEFAULT_RESOLUTION


def remove_actor():
    renderer.RemoveActor(actor)
    renderer.Modified()
    ctrl.view_update()


# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------

with SinglePageLayout(server) as layout:
    layout.icon.click = ctrl.view_reset_camera
    layout.title.set_text("Cone Application")

    with layout.toolbar:
        vuetify.VSpacer()
        vuetify.VSlider(
            v_model=("resolution", DEFAULT_RESOLUTION),
            min=3,
            max=60,
            step=1,
            hide_details=True,
            dense=True,
            style="max-width: 300px",
        )
        vuetify.VDivider(vertical=True, classes="mx-2")
        with vuetify.VBtn(icon=True, click=update_reset_resolution):
            vuetify.VIcon("mdi-undo-variant")

        with vuetify.VBtn(icon=True, click=remove_actor):
            vuetify.VIcon("mdi-delete")

    with layout.content:
        with vuetify.VContainer(
            fluid=True,
            classes="pa-0 fill-height",
        ):
            view = vtk.VtkLocalView(renderWindow, ref="view")
            ctrl.view_update = view.update
            ctrl.view_reset_camera = view.reset_camera


# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

if __name__ == "__main__":
    server.start()

BUG: `axes_actor_serializer` raises unexpected `vtkTransform` `AttributeError`

The following line of code

actor.GetUserTransform().GetTranspose(matrix)

unexpectedly raises

AttributeError: 'vtkmodules.vtkCommonTransforms.vtkMatrixToLinearTr' object has no attribute 'GetTranspose

for valid transforms, e.g. vtkMatrixToLinearTransform, which do not implement GetTranspose.

This error is raised, for example, when implicitly setting UserTransform via SetUserMatrix.
E.g. to reproduce the bug, do:

from vtk import vtkAxesActor, vtkMatrix4x4
# Create actor with user matrix
actor = vtkAxesActor()
matrix = vtkMatrix4x4()
actor.SetUserMatrix(matrix)

# Apply transform
user_matrix = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
if actor.GetUserTransform():
    matrix = vtkMatrix4x4()
    actor.GetUserTransform().GetTranspose(matrix)
    for i in range(4):
        for j in range(4):
            idx = i + 4 * j
            user_matrix[idx] = matrix.GetElement(j, i)

This bug showed up recently in PyVista CI testing for PR pyvista#5019. Here's a link to the AttributeError being raised in the test. The cause of the error was traced back to the fact that the axes internally make a call to actor.SetUserMatrix(matrix) exactly as shown above.

A suggested fix is to replace:

if actor.GetUserTransform():
matrix = vtkMatrix4x4()
actor.GetUserTransform().GetTranspose(matrix)

with:

if actor.GetUserMatrix():
    matrix = actor.GetUserMatrix()
    matrix.Transpose()

Client side lighting still isn't quite right?

I'm not sure what exactly is going wrong here, but a handful of things are still off with the lighting when the ambient and specular are set.

I've gone through a bunch of different lighting configurations: with vtkLightKit, all the different LightTypes and all actually looks really really good. But once I play with the ambient/specular. Things start to diverge.

If we add an ambient and specular to a vtkProperty the VTK.js view looks very different than the VTK rendering and it is emphasized for some colors more than others and different depending on the light set up.

For all the plots below, client-side is left and server-side is right

With a vtkLightKit

tan #0000ff white ParaView
Screen Shot 2023-01-26 at 11 17 55 AM Screen Shot 2023-01-26 at 11 17 59 AM Screen Shot 2023-01-26 at 11 18 02 AM Screen Shot 2023-01-26 at 11 14 05 AM

With a single vtkLight.SetLightTypeToHeadlight

This scenario looks quite good!

tan #0000ff white
Screen Shot 2023-01-26 at 11 33 37 AM Screen Shot 2023-01-26 at 11 33 40 AM Screen Shot 2023-01-26 at 11 33 44 AM

With a single vtkLight.SetLightTypeToCameraLight

tan #0000ff white
Screen Shot 2023-01-26 at 11 35 18 AM Screen Shot 2023-01-26 at 11 35 21 AM Screen Shot 2023-01-26 at 11 35 25 AM

With a single vtk.vtkLight.SetLightTypeToSceneLight

tan #0000ff white
Screen Shot 2023-01-26 at 11 36 43 AM Screen Shot 2023-01-26 at 11 36 48 AM Screen Shot 2023-01-26 at 11 36 53 AM

Example code

"""Validate lighting and properties."""

import pyvista as pv
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import html, vuetify
from trame.widgets.vtk import VtkLocalView, VtkRemoteView

server = get_server()
state, ctrl = server.state, server.controller

state.trame__title = "PyVista Lighting"

# -----------------------------------------------------------------------------
pv.set_plot_theme("document")


mesh = pv.Cone()

plotter = pv.Plotter(off_screen=True, lighting="none")
actor = plotter.add_mesh(
    mesh, ambient=0.5, specular=0.5, specular_power=100, color="tan"
)
plotter.set_background("paraview")
plotter.view_isometric()

# light = pv.Light(light_type='headlight')  # GOOD
# light = pv.Light(position=(1, 0, 0), light_type='camera light')  # BAD
light = pv.Light(position=(0, 1, 0), light_type="scene light")  # BAD
plotter.add_light(light)


@state.change("color")
def color(color="tan", **kwargs):
    actor.prop.color = color
    ctrl.view_update()


# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------

with SinglePageLayout(server) as layout:
    layout.icon.click = ctrl.view_reset_camera
    layout.title.set_text(state.trame__title)

    with layout.toolbar:
        vuetify.VSpacer()
        vuetify.VSelect(
            label="Color",
            v_model=("color", "tan"),
            items=("array_list", ["tan", "#0000ff", "white"]),
            hide_details=True,
            dense=True,
            outlined=True,
            classes="pt-1 ml-2",
            style="max-width: 250px",
        )

    with layout.content:
        with vuetify.VContainer(
            fluid=True,
            classes="pa-0 fill-height",
        ):
            with vuetify.VContainer(
                fluid=True, classes="pa-0 fill-height", style="width: 50%;"
            ):
                local = VtkLocalView(
                    plotter.ren_win,
                )
            with vuetify.VContainer(
                fluid=True, classes="pa-0 fill-height", style="width: 50%;"
            ):
                remote = VtkRemoteView(
                    plotter.ren_win,
                )

            def view_update(**kwargs):
                local.update(**kwargs)
                remote.update(**kwargs)

            def view_reset_camera(**kwargs):
                local.reset_camera(**kwargs)
                remote.reset_camera(**kwargs)

            ctrl.view_update = view_update
            ctrl.view_reset_camera = view_reset_camera

            ctrl.on_server_ready.add(view_update)

    # hide footer
    layout.footer.hide()

# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

if __name__ == "__main__":
    server.start()

vtkSmartVolumeMapper serializer fails

import pyvista as pv
from pyvista import examples

await pv.set_jupyter_backend('trame')

vol = pv.Wavelet()

p = pv.Plotter()
p.add_volume(vol)
p.show()

The local view is empty and the serializer is giving the error

ERROR:vtkmodules.web.render_window_serializer:!!!No serializer for vtkSmartVolumeMapper with id 00000002850ad240

No matter which volume mapper I use, it seems volume rendered data aren't well supported

Volume rendering is broken

I can't get volume rendering to work with any client-side views right now

"""Validate volume rendering with VTK.js."""

import pyvista as pv
import numpy as np
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vuetify, html
from trame.widgets.vtk import VtkLocalView, VtkRemoteView

# Just for using this script in testing
from trame_client.utils.testing import enable_testing

server = enable_testing(get_server(), "local_rendering_ready")
state, ctrl = server.state, server.controller

state.trame__title = "Volume Validation"
state.local_rendering_ready = 0

# -----------------------------------------------------------------------------

image = pv.Wavelet()

plotter = pv.Plotter(off_screen=True)
actor = plotter.add_volume(image)
plotter.reset_camera()


# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------

with SinglePageLayout(server) as layout:
    layout.icon.click = ctrl.view_reset_camera
    layout.title.set_text(state.trame__title)

    with layout.toolbar:
        vuetify.VSpacer()
        html.Div("{{ local_rendering_ready }}", classes="readyCount")

    with layout.content:
        with vuetify.VContainer(
            fluid=True,
            classes="pa-0 fill-height",
        ):
            with vuetify.VCol(classes="fill-height"):
                view = VtkLocalView(
                    plotter.ren_win,
                    on_ready="local_rendering_ready++",
                )
                ctrl.view_update = view.update
                ctrl.view_reset_camera = view.reset_camera
            with vuetify.VCol(classes="fill-height"):
                VtkRemoteView(plotter.ren_win)

    # hide footer
    layout.footer.hide()

# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

if __name__ == "__main__":
    server.start()

The error for VTK not having the web module enabled is not user friendly

$ python trame-test.py
> VTK is not available inside your Python environment
Traceback (most recent call last):
  File "trame-test.py", line 144, in <module>
    view = vtk.VtkRemoteView(
  File "/home/local/KHQ/bane.sullivan/Software/viz-stuff/env-egl-vtk/lib/python3.8/site-packages/trame_vtk/widgets/vtk/common.py", line 531, in __init__
    self.server.state[self.__view_key_id] = MODULE.id(view)
  File "/home/local/KHQ/bane.sullivan/Software/viz-stuff/env-egl-vtk/lib/python3.8/site-packages/trame_vtk/modules/vtk/__init__.py", line 175, in id
    return HELPER.id(vtk_obj)
AttributeError: 'NoneType' object has no attribute 'id'

The check and failure in

try:
from vtkmodules.vtkWebCore import vtkWebApplication
from vtkmodules.web.utils import mesh as vtk_mesh
has_vtk = True
except ImportError:
print("> VTK is not available inside your Python environment")
has_vtk = False

Is not descriptive enough for new users and the actual error that happens is not insightful for what is going wrong.

Considering the the VTK on conda-forge still does not have the needed module enabled, this error could turn away a lot of new users from the software.

I suggest a few things:

  1. Change the print statement to an actual warning
  2. On __init__ for any of the VTK widget classes in trame, check has_vtk and raise a meaningful error to point users in the right direction
  3. Change the message given to something about needing the web module rather than a blanket "VTK is not available" as VTK can be installed perfectly well but miss the module trame requires. The fact that VTK has different modules that can be enabled or not is an advanced topic the majority of Python users aren't going to dabble into

On a related note, if trame is a core platform from Kitware then we should consider merging the web module in VTK into the Core of VTK to avoid these issues altogether.

Client side lighting can get too bright

For the AddRemoveActors.py example, the client side lighting starts off correct, but if you change to the remote view and then back to the local view, the lighting is too bright.

Maybe something is causing extra lights to be added?

RemoteView `still_ratio` is not used initially

When using a still_ratio, the initial view uses a default of 1 and requires some sort of interaction event before the still_ratio is used. Take the following example:

import pyvista as pv
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vuetify
from trame.widgets.vtk import VtkRemoteView

server = get_server()
state, ctrl = server.state, server.controller

state.trame__title = "PyVista Remote View Ratios"

# -----------------------------------------------------------------------------
pv.set_plot_theme("document")


mesh = pv.Wavelet()

plotter = pv.Plotter(off_screen=True)
actor = plotter.add_mesh(mesh)
plotter.set_background("lightgrey")
plotter.show_grid()
plotter.view_isometric()


# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------

with SinglePageLayout(server) as layout:
    layout.icon.click = ctrl.view_reset_camera
    layout.title.set_text(state.trame__title)

    with layout.toolbar:
        vuetify.VSpacer()

    with layout.content:
        with vuetify.VContainer(
            fluid=True,
            classes="pa-0 fill-height",
        ):
            view = VtkRemoteView(plotter.ren_win, interactive_ratio=2, still_ratio=2)
            ctrl.view_update = view.update
            ctrl.view_reset_camera = view.reset_camera

# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------

if __name__ == "__main__":
    server.start()
Screen.Recording.2023-02-08.at.8.49.26.PM.mov

How can we get the view to use the set still_ratio on initialization

See also brendanjmeade/kale#10

reading pvd files with pyvista, flashing red and blue.

I've been working in a project to integrate a scientific program with trame in web. The output of our program are .vtu and .pvd files. To visualize it in trame, I tried to use pyvista to read the pvd and iterate foreach vtu file. The problem occurs while updating from one file to the next one: the whole mesh flashes red or blue. How could I fix my problem? I should use another way other than Pyvista?

All vtu files have the same mesh, only the scalars are changing. I think the problem occurs updating the colormap. What do you think?

Broken with aiohttp 3.9.0

I'm seeing the following error in the Jupyter logs when using aiohttp 3.9.0 and the trame-vtk remote view is broken/not loading any images. Downgrading to aiohttp==3.8.6 fixes the issue

[E 2023-11-24 07:02:08.454 ServerApp] Exception in callback functools.partial(<bound method IOLoop._discard_future_result of <tornado.platform.asyncio.AsyncIOMainLoop object at 0x7f8af5895910>>, <Task finished name='Task-241' coro=<WebSocketProtocol13._receive_frame_loop() done, defined at /opt/conda/lib/python3.11/site-packages/tornado/websocket.py:1098> exception=error('Error -3 while decompressing data: invalid stored block lengths')>)
    Traceback (most recent call last):
      File "/opt/conda/lib/python3.11/site-packages/tornado/ioloop.py", line 738, in _run_callback
        ret = callback()
              ^^^^^^^^^^
      File "/opt/conda/lib/python3.11/site-packages/tornado/ioloop.py", line 762, in _discard_future_result
        future.result()
      File "/opt/conda/lib/python3.11/site-packages/tornado/websocket.py", line 1101, in _receive_frame_loop
        await self._receive_frame()
      File "/opt/conda/lib/python3.11/site-packages/tornado/websocket.py", line 1190, in _receive_frame
        handled_future = self._handle_message(opcode, data)
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/lib/python3.11/site-packages/tornado/websocket.py", line 1202, in _handle_message
        data = self._decompressor.decompress(data)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/opt/conda/lib/python3.11/site-packages/tornado/websocket.py", line 781, in decompress
        result = decompressor.decompress(
                 ^^^^^^^^^^^^^^^^^^^^^^^^
    zlib.error: Error -3 while decompressing data: invalid stored block lengths

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.