I’m trying to figure out how to make a clip scan through a volume of an XDMF dataset and do that as a trame application. Using trame/examples/07_paraview/TimeAnimation/app.py at master · Kitware/trame · GitHub as an example - I can seek around to the end or middle or hard code the time I want to use from the XDMF dataset but I can’t figure out how to make the scene animation / timekeeper advance the clip plane like I can in paraview normal / python traces. The XDMF data source will display but when I hide it and display the clip the scene becomes empty. For the clip I’m trying to do a psuedo time of 0-1 interpolated to 0-100 in y for the animation and after I seek to my desired dataset time I set time_keeper.SetSuppressTimeSource(source). Animations seem to work a bit differently in trame.
Do you need animation cue? In trame you can programmatically drive the clip plane location independently from the time and just create an animation of that.
switched to a threshold; tried with an additional python cue - only a bit of luck.
app.py (10.0 KB)
It behaves rather oddly - it seems to pick up some cells at the edge of the threshold boundary. So it’s not drawing results correctly with the animation.
It also seems to be loading the geometry every time even though I marked the base reader datasource as an ignored time source, this makes the animation much slower than it should be since it’s really just trying to sweep through a single time snapshot.
r"""
Installation requirements:
pip install trame trame-vuetify trame-vtk
"""
import paraview.web.venv # Available in PV 5.10-RC2+
import os
import json
import asyncio
import numpy as np
from trame.app import get_server, asynchronous
from trame.widgets import vuetify, paraview
from trame.ui.vuetify import SinglePageLayout
import paraview as pv
from paraview import simple
# -----------------------------------------------------------------------------
# Trame setup
# -----------------------------------------------------------------------------
server = get_server(client_type="vue2")
state, ctrl = server.state, server.controller
# -----------------------------------------------------------------------------
animation_scene = simple.GetAnimationScene()
time_keeper = animation_scene.TimeKeeper
metadata = None
time_values = []
representation = None
time_values = []
representation = None
reader = None
animationScene = None
pipelineEnd = None
pipelineEndDisplay = None
_min = 0
_max = 1
PythonAnimationCue1 = simple.PythonAnimationCue()
PythonAnimationCue1.Script= """
def start_cue(self):
Text1 = Text()
Text1.Text = ''
Show()
def tick(self):
animationScene1 = GetAnimationScene()
animationTime = animationScene1.TimeKeeper.Time
Text1 = FindSource("Text1")
Text1.Text=str(animationTime)
threshold = FindSource('threshold')
threshold.UpperThreshold = animationTime #_min
def end_cue(self): pass
"""
def load_data(**kwargs):
global time_values, representation, reader, animationScene, pipelineEnd, pipelineEndDisplay
global _min, _max
# CLI
args, _ = server.cli.parse_known_args()
full_path = os.path.abspath(args.data)
#base_path = os.path.dirname(full_path)
print(full_path)
files = []
reader_props = {}
case_path = full_path
animationScene = simple.GetAnimationScene()
#code for setting up a reader
reader = simple.XDMFReader(FileNames=case_path)
simple.MergeBlocks()
reader.UpdatePipeline()
animationScene.GoToFirst()
animationScene.GoToLast()
animationScene.Stride = 1
print(list(time_keeper.TimestepValues))
#view = simple.GetActiveViewOrCreate('RenderView')
#display = simple.Show(reader, view)
data_info = reader.GetDataInformation()
cell_fields = reader.CellData
fields = list(reader.CellData.keys())
print(fields)
representation = simple.Show(reader, view)
representation.Representation = 'Surface'
representation.Opacity = 0.1
simple.ColorBy(representation, None)
time_values = list(time_keeper.TimestepValues)
state.fields = fields
reader.UpdatePipeline()
print(animationScene.AnimationTime)
if len(reader.CellData.keys()) == 0:
print("Failed to find any cell fields do you have no fields at time index 0? Also check if case type should be recomposed or decomposed")
else:
print(reader.CellData.keys())
#adjust to suitable field to threshold on
foi = 'foi'
_min, _max = reader.CellData.GetArray(foi).GetRange()
threshold = simple.Threshold(registrationName='threshold', Input=reader)
threshold.UpperThreshold = _min
threshold.Scalars = ['CELLS', foi]
threshold.UseContinuousCellRange = 0
threshold.AllScalars = 0
threshold.ThresholdMethod = 'Above Upper Threshold'
renderView = simple.GetActiveViewOrCreate('RenderView')
representation = simple.Show(threshold, renderView, 'UnstructuredGridRepresentation')
pipelineEnd = threshold
representation.Representation = 'Surface'
pipelineEndDisplay = representation
simple.Hide(reader, renderView)
representation.UpdatePipeline()
renderView.Update()
simple.ColorBy(representation, None)
keyFrame0 = simple.CompositeKeyFrame(KeyTime=_min, KeyValues=[_min], Interpolation='Ramp')
keyFrame1 = simple.CompositeKeyFrame(KeyTime = _max, KeyValues=[_max], Interpolation='Ramp')
cue = simple.GetAnimationTrack('UpperThreshold', index=0, proxy=threshold)
cue.KeyFrames = [keyFrame0, keyFrame1]
animationScene.GoToLast()
simple.Render()
time_keeper.SetSuppressTimeSource(reader, True)
#animationScene.Cues.append(PythonAnimationCue1)
animationScene.StartTime = _min
animationScene.PlayMode = 'Sequence'
#animationScene.Duration=[_max - _min]
animationScene.NumberOfFrames = 100
animationScene.FramesPerTimestep = 1
animationScene.Loop = 1
animationScene.GoToFirst()
state.time_value = _min
state.times = [float(x) for x in np.arange(_min, _max, animationScene.NumberOfFrames).tolist()]
state.time_min = state.times[0]
state.time_max = state.times[-1]
#simple.HideScalarBarIfNotNeeded(pLUT, renderView1)
#animationScene.Play()
update_color_by(None, fields, "remote")
simple.ResetCamera()
view.CenterOfRotation = view.CameraFocalPoint
update_view("local")
ctrl.on_server_ready.add(load_data)
# -----------------------------------------------------------------------------
# ParaView pipeline
# -----------------------------------------------------------------------------
# Rendering setup
view = simple.GetRenderView()
view.UseColorPaletteForBackground = 0
view.Background = [0.8, 0.8, 0.8]
view.OrientationAxesVisibility = 0
view = simple.Render()
# -----------------------------------------------------------------------------
# Callbacks
# -----------------------------------------------------------------------------
@state.change("active_array")
def update_color_by(active_array, fields, viewMode="remote", **kwargs):
print('update_color_by')
#calculate?
if len(fields) == 0:
return
displayProperties = pipelineEndDisplay
displayProperties.UpdatePipeline()
fieldName = active_array #fields[active_array]
displayProperties.ColorArrayName = ['CELLS', fieldName]
simple.UpdateScalarBars(view)
displayProperties.RescaleTransferFunctionToDataRange()
name = fieldName
if name is not None:
lut = simple.GetColorTransferFunction(name)
pwf = simple.GetOpacityTransferFunction(name)
try:
_min, _max = reader.CellData.GetArray(fieldName).GetRange()
pwf.RescaleTransferFunction(_min, _max)
except AttributeError as e:
print(f"error accessing on {fieldName}")
pass
update_view(viewMode)
@state.change("time")
def update_time(time, viewMode, **kwargs):
print(f'update_time time {time} {viewMode}')
if len(time_values) == 0:
return
if time >= time_values[-1] or len(time_values) == 0:
time = 0
state.time = time
time_value = time
time_keeper.Time = time_value
state.time_value = time_value
threshold = simple.FindSource('threshold')
threshold.UpperThreshold = time
print(f"update time post update threshold Upper: {threshold.UpperThreshold}")
update_view(viewMode)
@state.change("play")
@asynchronous.task
async def update_play(**kwargs):
print(f'update_play')
while state.play:
with state:
print(f'update_play time= {state.time}')
#dt...
state.time += 1
update_time(state.time, state.viewMode)
#await asyncio.sleep(0.1)
await asyncio.sleep(3)
@state.change("viewMode")
def update_view(viewMode, **kwargs):
print(f'update_view')
simple.Render()
ctrl.view_update()
#ctrl.view_update_image()
#we don't need to update the geometry if it's colormap only mods..
#todo: test if we can make this conditional?
#ctrl.view_update_geometry()
#if viewMode == "local":
# ctrl.view_update_geometry()
# -----------------------------------------------------------------------------
# GUI
# -----------------------------------------------------------------------------
state.trame__title = "ParaView"
with SinglePageLayout(server) as layout:
layout.title.set_text("Time")
layout.icon.click = ctrl.view_reset_camera
with layout.toolbar:
vuetify.VSpacer()
vuetify.VSelect(
v_model=("active_array", 0),
items=("fields", []),
style="max-width: 200px",
hide_details=True,
dense=True,
)
vuetify.VTextField(
v_model=("time_value", 0),
disabled=True,
hide_details=True,
dense=True,
style="max-width: 200px",
classes="mx-2",
)
vuetify.VSlider(
v_model=("time", 0),
min=("time_min", 0),
max=("time_max", 0),
hide_details=True,
dense=True,
style="max-width: 600px",
)
vuetify.VCheckbox(
v_model=("play", False),
off_icon="mdi-play",
on_icon="mdi-stop",
hide_details=True,
dense=True,
classes="mx-2",
)
with vuetify.VBtn(icon=True, click=ctrl.view_reset_camera):
vuetify.VIcon("mdi-crop-free")
vuetify.VCheckbox(
v_model=("viewMode", "remote"),
true_value="remote",
false_value="local",
off_icon="mdi-rotate-3d",
on_icon="mdi-video-image",
hide_details=True,
dense=True,
classes="mx-2",
)
vuetify.VProgressLinear(
indeterminate=True,
absolute=True,
bottom=True,
active=("trame__busy",),
)
with layout.content:
with vuetify.VContainer(fluid=True, classes="pa-0 fill-height"):
html_view = paraview.VtkRemoteLocalView(view, namespace="view")
ctrl.view_update = html_view.update
ctrl.view_update_geometry = html_view.update_geometry
ctrl.view_update_image = html_view.update_image
ctrl.view_reset_camera = html_view.reset_camera
# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
if __name__ == "__main__":
server.cli.add_argument("--data", help="Path to state file", dest="data")
server.start()
Why do you insist on using the time animation infrastructure when your code could be so much simpler without it?
Why do you use VtkRemoteLocalView
instead of VtkRemoteView
?
If you need help and dedicated guidance, Kitware offer some easy to use support packages.
what’s here is based on the example in trame trame/examples/07_paraview/TimeAnimation/app.py at master · Kitware/trame · GitHub , definitely for the VtkRemotelocalView
Sorry about the animation cue; so you’re saying just do a async time loop and manipulate the planes / thresholds directly?
Yes, that’s what I was saying all along. That example was just using the ParaView Python API to make the reader pick a new time (since that was what we wanted to animate). But in your case, time is not relevant, just the animation part with the async loop.