trame
trame copied to clipboard
SetInteractorStyle fails to do anything
Describe the bug
Trying to use SetInteractorStyle
always seems to fail, I always get vtkInteractorStyleTrackballCamera
. Specifically I can't use a custom subclass.
To Reproduce
The script below was taken from https://examples.vtk.org/site/Python/Picking/HighlightWithSilhouette/ with minimal adaptation for trame.
When running, the custom code in MouseInteractorHighLightActor.onLeftButtonDown
never executes, so the functionality does not work and there are no debug prints.
Code
#!/usr/bin/env python
# noinspection PyUnresolvedReferences
import vtkmodules.vtkRenderingOpenGL2
from vtkmodules.vtkCommonColor import vtkNamedColors
from vtkmodules.vtkCommonCore import vtkMinimalStandardRandomSequence
from vtkmodules.vtkFiltersHybrid import vtkPolyDataSilhouette
from vtkmodules.vtkFiltersSources import vtkSphereSource
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleTrackballCamera
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkPolyDataMapper,
vtkPropPicker,
vtkRenderWindow,
vtkRenderWindowInteractor,
vtkRenderer
)
from trame.app import get_server
from trame.ui.vuetify import SinglePageLayout
from trame.widgets import vtk, vuetify
def get_program_parameters():
import argparse
description = 'Highlighting a selected object with a silhouette.'
epilogue = '''
Click on the object to highlight it.
The selected object is highlighted with a silhouette.
'''
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('numberOfSpheres', nargs='?', type=int, default=10,
help='The number of spheres, default is 10.')
args = parser.parse_args()
return args.numberOfSpheres
class MouseInteractorHighLightActor(vtkInteractorStyleTrackballCamera):
def __init__(self, silhouette=None, silhouetteActor=None):
self.AddObserver("LeftButtonPressEvent", self.onLeftButtonDown)
self.LastPickedActor = None
self.Silhouette = silhouette
self.SilhouetteActor = silhouetteActor
def onLeftButtonDown(self, obj, event):
print("In onLeftButtonDown")
clickPos = self.GetInteractor().GetEventPosition()
# Pick from this location.
picker = vtkPropPicker()
picker.Pick(clickPos[0], clickPos[1], 0, self.GetDefaultRenderer())
self.LastPickedActor = picker.GetActor()
# If we picked something before, remove the silhouette actor and
# generate a new one.
if self.LastPickedActor:
print(f"You picked: {self.LastPickedActor}")
self.GetDefaultRenderer().RemoveActor(self.SilhouetteActor)
# Highlight the picked actor by generating a silhouette
self.Silhouette.SetInputData(self.LastPickedActor.GetMapper().GetInput())
self.GetDefaultRenderer().AddActor(self.SilhouetteActor)
else:
print("Nothing picked")
# Forward events
self.OnLeftButtonDown()
return
def SetSilhouette(self, silhouette):
self.Silhouette = silhouette
def SetSilhouetteActor(self, silhouetteActor):
self.SilhouetteActor = silhouetteActor
def main():
numberOfSpheres = get_program_parameters()
colors = vtkNamedColors()
# A renderer and render window
renderer = vtkRenderer()
renderer.SetBackground(colors.GetColor3d('SteelBlue'))
renderWindow = vtkRenderWindow()
renderWindow.SetSize(640, 480)
renderWindow.AddRenderer(renderer)
# An interactor
interactor = vtkRenderWindowInteractor()
interactor.SetRenderWindow(renderWindow)
randomSequence = vtkMinimalStandardRandomSequence()
# randomSequence.SetSeed(1043618065)
# randomSequence.SetSeed(5170)
randomSequence.SetSeed(8775070)
# Add spheres to play with
for i in range(numberOfSpheres):
source = vtkSphereSource()
# random position and radius
x = randomSequence.GetRangeValue(-5.0, 5.0)
randomSequence.Next()
y = randomSequence.GetRangeValue(-5.0, 5.0)
randomSequence.Next()
z = randomSequence.GetRangeValue(-5.0, 5.0)
randomSequence.Next()
radius = randomSequence.GetRangeValue(0.5, 1.0)
randomSequence.Next()
source.SetRadius(radius)
source.SetCenter(x, y, z)
source.SetPhiResolution(11)
source.SetThetaResolution(21)
mapper = vtkPolyDataMapper()
mapper.SetInputConnection(source.GetOutputPort())
actor = vtkActor()
actor.SetMapper(mapper)
r = randomSequence.GetRangeValue(0.4, 1.0)
randomSequence.Next()
g = randomSequence.GetRangeValue(0.4, 1.0)
randomSequence.Next()
b = randomSequence.GetRangeValue(0.4, 1.0)
randomSequence.Next()
actor.GetProperty().SetDiffuseColor(r, g, b)
actor.GetProperty().SetDiffuse(0.8)
actor.GetProperty().SetSpecular(0.5)
actor.GetProperty().SetSpecularColor(colors.GetColor3d('White'))
actor.GetProperty().SetSpecularPower(30.0)
renderer.AddActor(actor)
renderer.ResetCamera()
# Create the silhouette pipeline, the input data will be set in the
# interactor
silhouette = vtkPolyDataSilhouette()
silhouette.SetCamera(renderer.GetActiveCamera())
# Create mapper and actor for silhouette
silhouetteMapper = vtkPolyDataMapper()
silhouetteMapper.SetInputConnection(silhouette.GetOutputPort())
silhouetteActor = vtkActor()
silhouetteActor.SetMapper(silhouetteMapper)
silhouetteActor.GetProperty().SetColor(colors.GetColor3d("Tomato"))
silhouetteActor.GetProperty().SetLineWidth(5)
# Set the custom type to use for interaction.
style = MouseInteractorHighLightActor(silhouette, silhouetteActor)
style.SetDefaultRenderer(renderer)
interactor.SetInteractorStyle(style)
server = get_server(client_type="vue2")
ctrl = server.controller
with SinglePageLayout(server) as layout:
layout.title.set_text("Hello trame")
with layout.content:
with vuetify.VContainer(
fluid=True,
classes="pa-0 fill-height",
):
view = vtk.VtkLocalView(renderWindow)
server.start()
if __name__ == "__main__":
main()
Expected behavior
The MouseInteractorHighLightActor
sublcass should work like it does in the VTK example linked.
Platform:
I've checked only the following, but I imagine the bug is on all platforms.
Device:
- [X] Desktop
- [ ] Mobile
OS:
- [ ] Windows
- [ ] MacOS
- [X] Linux
- [ ] Android
- [ ] iOS
Browsers Affected:
- [X] Chrome
- [X] Firefox
- [ ] Microsoft Edge
- [ ] Safari
- [ ] Opera
- [ ] Brave
- [ ] IE 11
VtkLocalView
has its own set of interactors that don't match the server. You can see how to configure it here. Also even the VtkRemoteView don't match all the clients events. Some efforts have been made in trame-rca (remote-controlled-area) to properly propagate to the server those events so custom interactors could be used.