我在使用trame实现网页上传vtu文件并进行渲染时遇到了问题。使用内置路径第一次渲染时没有问题,但我上传另外一个vtu文件进行渲染时,前端还是之前第一个文件。这个问题困扰我好久了,十分感谢!
第一文件运行截图
第二个文件上传后出现问题
import os
from trame.decorators import TrameApp
from trame.app import get_server
from trame.ui.vuetify import SinglePageWithDrawerLayout
from trame.widgets import vtk, vuetify, trame
from pathlib import Path
from trame_vtk.modules.vtk.serializers import configure_serializer
from vtkmodules.vtkCommonDataModel import vtkDataObject
from vtkmodules.vtkFiltersCore import vtkContourFilter
from vtkmodules.vtkIOXML import vtkXMLUnstructuredGridReader
from vtkmodules.vtkRenderingAnnotation import vtkCubeAxesActor
from trame.widgets import vuetify
from trame.app.file_upload import ClientFile
from trame.widgets import html
from vtkmodules.vtkRenderingCore import (
vtkActor,
vtkDataSetMapper,
vtkRenderer,
vtkRenderWindow,
vtkRenderWindowInteractor,
)
# Required for interactor initialization
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch # noqa
# Required for rendering initialization, not necessary for
# local rendering, but doesn't hurt to include it
import vtkmodules.vtkRenderingOpenGL2 # noqa
import rendering
CURRENT_DIRECTORY = os.path.abspath(os.path.dirname(__file__))
# Configure scene encoder
configure_serializer(encode_lut=True, skip_light=True)
# -----------------------------------------------------------------------------
# Constants
# -----------------------------------------------------------------------------
class Representation:
Points = 0
Wireframe = 1
Surface = 2
SurfaceWithEdges = 3
class LookupTable:
Rainbow = 0
Inverted_Rainbow = 1
Greyscale = 2
Inverted_Greyscale = 3
# -----------------------------------------------------------------------------
# VTK pipeline
# -----------------------------------------------------------------------------
reader = vtkXMLUnstructuredGridReader()
mesh_mapper = vtkDataSetMapper()
contour_mapper = vtkDataSetMapper()
mesh_actor = vtkActor()
contour_actor = vtkActor()
contour = vtkContourFilter()
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
def create_vtk_pipeline(file_name):
renderer = vtkRenderer()
renderWindow = vtkRenderWindow()
renderWindow.AddRenderer(renderer)
renderWindowInteractor = vtkRenderWindowInteractor()
renderWindowInteractor.SetRenderWindow(renderWindow)
renderWindowInteractor.GetInteractorStyle().SetCurrentStyleToTrackballCamera()
# Read Data
reader = vtkXMLUnstructuredGridReader()
reader.SetFileName(file_name)
reader.Update()
data = reader.GetOutput()
# Extract Array/Field information
dataset_arrays = []
fields = [
(reader.GetOutput().GetPointData(), vtkDataObject.FIELD_ASSOCIATION_POINTS),
(reader.GetOutput().GetCellData(), vtkDataObject.FIELD_ASSOCIATION_CELLS),
]
for field in fields:
field_arrays, association = field
for i in range(field_arrays.GetNumberOfArrays()):
array = field_arrays.GetArray(i)
array_range = array.GetRange()
dataset_arrays.append(
{
"text": array.GetName(),
"value": i,
"range": list(array_range),
"type": association,
}
)
default_array = dataset_arrays[0]
default_min, default_max = default_array.get("range")
# Mesh
mesh_mapper = vtkDataSetMapper()
mesh_mapper.SetInputConnection(reader.GetOutputPort())
mesh_actor = vtkActor()
mesh_actor.SetMapper(mesh_mapper)
renderer.AddActor(mesh_actor)
# Mesh: Setup default representation to surface
mesh_actor.GetProperty().SetRepresentationToSurface()
mesh_actor.GetProperty().SetPointSize(1)
mesh_actor.GetProperty().EdgeVisibilityOff()
# Mesh: Apply rainbow color map
mesh_lut = mesh_mapper.GetLookupTable()
mesh_lut.SetHueRange(0.666, 0.0)
mesh_lut.SetSaturationRange(1.0, 1.0)
mesh_lut.SetValueRange(1.0, 1.0)
mesh_lut.Build()
# Mesh: Color by default array
mesh_mapper.SelectColorArray(default_array.get("text"))
mesh_mapper.GetLookupTable().SetRange(default_min, default_max)
if default_array.get("type") == vtkDataObject.FIELD_ASSOCIATION_POINTS:
mesh_mapper.SetScalarModeToUsePointFieldData()
else:
mesh_mapper.SetScalarModeToUseCellFieldData()
mesh_mapper.SetScalarVisibility(True)
mesh_mapper.SetUseLookupTableScalarRange(True)
mesh_mapper.Update() ###
# Contour
contour = vtkContourFilter()
contour.SetInputConnection(reader.GetOutputPort())
contour_mapper = vtkDataSetMapper()
contour_mapper.SetInputConnection(contour.GetOutputPort())
contour_actor = vtkActor()
contour_actor.SetMapper(contour_mapper)
renderer.AddActor(contour_actor)
# Contour: ContourBy default array
contour_value = 0.5 * (default_max + default_min)
contour.SetInputArrayToProcess(
0, 0, 0, default_array.get("type"), default_array.get("text")
)
contour.SetValue(0, contour_value)
# Contour: Setup default representation to surface
contour_actor.GetProperty().SetRepresentationToSurface()
contour_actor.GetProperty().SetPointSize(1)
contour_actor.GetProperty().EdgeVisibilityOff()
# Contour: Apply rainbow color map
contour_lut = contour_mapper.GetLookupTable()
contour_lut.SetHueRange(0.666, 0.0)
contour_lut.SetSaturationRange(1.0, 1.0)
contour_lut.SetValueRange(1.0, 1.0)
contour_lut.Build()
# Contour: Color by default array
contour_mapper.SelectColorArray(default_array.get("text"))
contour_mapper.GetLookupTable().SetRange(default_min, default_max)
if default_array.get("type") == vtkDataObject.FIELD_ASSOCIATION_POINTS:
contour_mapper.SetScalarModeToUsePointFieldData()
else:
contour_mapper.SetScalarModeToUseCellFieldData()
contour_mapper.SetScalarVisibility(True)
contour_mapper.SetUseLookupTableScalarRange(True)
# Cube Axes
cube_axes = vtkCubeAxesActor()
renderer.AddActor(cube_axes)
# Cube Axes: Boundaries, camera, and styling
cube_axes.SetBounds(mesh_actor.GetBounds())
cube_axes.SetCamera(renderer.GetActiveCamera())
cube_axes.SetXLabelFormat("%6.1f")
cube_axes.SetYLabelFormat("%6.1f")
cube_axes.SetZLabelFormat("%6.1f")
cube_axes.SetFlyModeToOuterEdges()
renderer.ResetCamera()
renderer.ResetCameraClippingRange()
renderWindow.Render()
return renderWindow, cube_axes, mesh_actor, contour_actor, mesh_mapper, dataset_arrays, contour, contour_value, default_min, default_max, renderer
@TrameApp()
class MyApp:
def __init__(self, server=None):
self.server = get_server(client_type="vue2")
self.state = self.server.state
self.ctrl = self.server.controller
self.state.setdefault("active_ui", None)
file_name = "../data/disk_out_ref.vtu"
self.renderWindow, self.cube_axes, self.mesh_actor, self.contour_actor, self.mesh_mapper, self.dataset_arrays, self.contour, self.contour_value, self.default_min, self.default_max, self.renderer= create_vtk_pipeline(
file_name)
self.server.state.update(dict(mem_blob=0, mem_vtk=0))
self.html_view = None
self.ui =self._ui()
# print(self.ui)
self.state.change("cube_axes_visibility")(self.update_cube_axes_visibility)
self.state.change("mesh_representation")(self.update_mesh_representation)
self.state.change("contour_representation")(self.update_contour_representation)
self.state.change("mesh_color_array_idx")(self.update_mesh_color_by_name)
self.state.change("contour_color_array_idx")(self.update_contour_color_by_name)
self.state.change("mesh_color_preset")(self.update_mesh_color_preset)
self.state.change("contour_color_preset")(self.update_contour_color_preset)####
self.state.change("mesh_opacity")(self.update_mesh_opacity)
self.state.change("contour_opacity")(self.update_contour_opacity)
self.state.change("contour_by_array_idx")(self.update_contour_by)
self.state.change("contour_value")(self.update_contour_value)
def update_cube_axes_visibility(self, cube_axes_visibility, **kwargs):
self.cube_axes.SetVisibility(cube_axes_visibility)
self.ctrl.view_update()
# Selection Change
def actives_change(self, ids):
_id = ids[0]
if _id == "1": # Mesh
self.state.active_ui = "mesh"
elif _id == "2": # Contour
self.state.active_ui = "contour"
else:
self.state.active_ui = "nothing"
# Visibility Change
def visibility_change(self, event):
_id = event["id"]
_visibility = event["visible"]
if _id == "1": # Mesh
self.mesh_actor.SetVisibility(_visibility)
elif _id == "2": # Contour
self.contour_actor.SetVisibility(_visibility)
self.ctrl.view_update()
# Representation Callbacks
def update_representation(self, actor, mode):
property = actor.GetProperty()
if mode == Representation.Points:
property.SetRepresentationToPoints()
property.SetPointSize(5)
property.EdgeVisibilityOff()
elif mode == Representation.Wireframe:
property.SetRepresentationToWireframe()
property.SetPointSize(1)
property.EdgeVisibilityOff()
elif mode == Representation.Surface:
property.SetRepresentationToSurface()
property.SetPointSize(1)
property.EdgeVisibilityOff()
elif mode == Representation.SurfaceWithEdges:
property.SetRepresentationToSurface()
property.SetPointSize(1)
property.EdgeVisibilityOn()
def update_mesh_representation(self, mesh_representation, **kwargs):
self.update_representation(self.mesh_actor, mesh_representation)
self.ctrl.view_update()
def update_contour_representation(self, contour_representation, **kwargs):
self.update_representation(self.contour_actor, contour_representation)
self.ctrl.view_update()
# Color By Callbacks
def color_by_array(self, actor, array):
_min, _max = array.get("range")
mapper = actor.GetMapper()
mapper.SelectColorArray(array.get("text"))
mapper.GetLookupTable().SetRange(_min, _max)
if array.get("type") == vtkDataObject.FIELD_ASSOCIATION_POINTS:
self.mesh_mapper.SetScalarModeToUsePointFieldData()
else:
self.mesh_mapper.SetScalarModeToUseCellFieldData()
mapper.SetScalarVisibility(True)
mapper.SetUseLookupTableScalarRange(True)
def update_mesh_color_by_name(self, mesh_color_array_idx, **kwargs):
array = self.dataset_arrays[mesh_color_array_idx]
self.color_by_array(self.mesh_actor, array)
self.ctrl.view_update()
def update_contour_color_by_name(self, contour_color_array_idx, **kwargs):
array = self.dataset_arrays[contour_color_array_idx]
self.color_by_array(self.contour_actor, array)
self.ctrl.view_update()
# Color Map Callbacks
def use_preset(self, actor, preset):
lut = actor.GetMapper().GetLookupTable()
if preset == LookupTable.Rainbow:
lut.SetHueRange(0.666, 0.0)
lut.SetSaturationRange(1.0, 1.0)
lut.SetValueRange(1.0, 1.0)
elif preset == LookupTable.Inverted_Rainbow:
lut.SetHueRange(0.0, 0.666)
lut.SetSaturationRange(1.0, 1.0)
lut.SetValueRange(1.0, 1.0)
elif preset == LookupTable.Greyscale:
lut.SetHueRange(0.0, 0.0)
lut.SetSaturationRange(0.0, 0.0)
lut.SetValueRange(0.0, 1.0)
elif preset == LookupTable.Inverted_Greyscale:
lut.SetHueRange(0.0, 0.666)
lut.SetSaturationRange(0.0, 0.0)
lut.SetValueRange(1.0, 0.0)
lut.Build()
def update_mesh_color_preset(self, mesh_color_preset, **kwargs):
self.use_preset(self.mesh_actor, mesh_color_preset)
self.ctrl.view_update()
def update_contour_color_preset(self, contour_color_preset, **kwargs):
self.use_preset(self.contour_actor, contour_color_preset)
self.ctrl.view_update()
# Opacity Callbacks
def update_mesh_opacity(self, mesh_opacity, **kwargs):
self.mesh_actor.GetProperty().SetOpacity(mesh_opacity)
self.ctrl.view_update()
def update_contour_opacity(self, contour_opacity, **kwargs):
self.contour_actor.GetProperty().SetOpacity(contour_opacity)
self.ctrl.view_update()
# Contour Callbacks
def update_contour_by(self, contour_by_array_idx, **kwargs):
array = self.dataset_arrays[contour_by_array_idx]
contour_min, contour_max = array.get("range")
contour_step = 0.01 * (contour_max - contour_min)
contour_value = 0.5 * (contour_max + contour_min)
self.contour.SetInputArrayToProcess(0, 0, 0, array.get("type"), array.get("text"))
self.contour.SetValue(0, contour_value)
# Update UI
self.state.contour_min = contour_min
self.state.contour_max = contour_max
self.state.contour_value = contour_value
self.state.contour_step = contour_step
# Update View
self.ctrl.view_update()
def update_contour_value(self, contour_value, **kwargs):
self.contour.SetValue(0, float(contour_value))
self.ctrl.view_update()
def standard_buttons(self):
vuetify.VCheckbox(
v_model=("cube_axes_visibility", True),
on_icon="mdi-cube-outline",
off_icon="mdi-cube-off-outline",
classes="mx-1",
hide_details=True,
dense=True,
)
vuetify.VCheckbox(
v_model="$vuetify.theme.dark",
on_icon="mdi-lightbulb-off-outline",
off_icon="mdi-lightbulb-outline",
classes="mx-1",
hide_details=True,
dense=True,
)
vuetify.VCheckbox(
v_model=("viewMode", "local"),
on_icon="mdi-lan-disconnect",
off_icon="mdi-lan-connect",
true_value="local",
false_value="remote",
classes="mx-1",
hide_details=True,
dense=True,
)
with vuetify.VBtn(icon=True, click="$refs.view.resetCamera()"):
vuetify.VIcon("mdi-crop-free")
def pipeline_widget(self):
trame.GitTree(
sources=(
"pipeline",
[
{"id": "1", "parent": "0", "visible": 1, "name": "Mesh"},
{"id": "2", "parent": "1", "visible": 1, "name": "Contour"},
],
),
actives_change=(self.actives_change, "[$event]"),
visibility_change=(self.visibility_change, "[$event]"),
)
def ui_card(self, title, ui_name):
with vuetify.VCard(v_show=f"active_ui == '{ui_name}'"):
vuetify.VCardTitle(
title,
classes="grey lighten-1 py-1 grey--text text--darken-3",
style="user-select: none; cursor: pointer",
hide_details=True,
dense=True,
)
content = vuetify.VCardText(classes="py-2")
return content
def mesh_card(self):
with self.ui_card(title="Mesh", ui_name="mesh"):
vuetify.VSelect(
# Representation
v_model=("mesh_representation", Representation.Surface),
items=(
"representations",
[
{"text": "Points", "value": 0},
{"text": "Wireframe", "value": 1},
{"text": "Surface", "value": 2},
{"text": "SurfaceWithEdges", "value": 3},
],
),
label="Representation",
hide_details=True,
dense=True,
outlined=True,
classes="pt-1",
)
with vuetify.VRow(classes="pt-2", dense=True):
with vuetify.VCol(cols="6"):
vuetify.VSelect(
# Color By
label="Color by",
v_model=("mesh_color_array_idx", 0),
items=("array_list", self.dataset_arrays),
hide_details=True,
dense=True,
outlined=True,
classes="pt-1",
)
with vuetify.VCol(cols="6"):
vuetify.VSelect(
# Color Map
label="Colormap",
v_model=("mesh_color_preset", LookupTable.Rainbow),
items=(
"colormaps",
[
{"text": "Rainbow", "value": 0},
{"text": "Inv Rainbow", "value": 1},
{"text": "Greyscale", "value": 2},
{"text": "Inv Greyscale", "value": 3},
],
),
hide_details=True,
dense=True,
outlined=True,
classes="pt-1",
)
vuetify.VSlider(
# Opacity
v_model=("mesh_opacity", 1.0),
min=0,
max=1,
step=0.1,
label="Opacity",
classes="mt-1",
hide_details=True,
dense=True,
)
def contour_card(self):
with self.ui_card(title="Contour", ui_name="contour"):
vuetify.VSelect(
# Contour By
label="Contour by",
v_model=("contour_by_array_idx", 0),
items=("array_list", self.dataset_arrays),
hide_details=True,
dense=True,
outlined=True,
classes="pt-1",
)
vuetify.VSlider(
# Contour Value
v_model=("contour_value", self.contour_value),
min=("contour_min", self.default_min),
max=("contour_max", self.default_max),
step=("contour_step", 0.01 * (self.default_max - self.default_min)),
label="Value",
classes="my-1",
hide_details=True,
dense=True,
)
vuetify.VSelect(
# Representation
v_model=("contour_representation", Representation.Surface),
items=(
"representations",
[
{"text": "Points", "value": 0},
{"text": "Wireframe", "value": 1},
{"text": "Surface", "value": 2},
{"text": "SurfaceWithEdges", "value": 3},
],
),
label="Representation",
hide_details=True,
dense=True,
outlined=True,
classes="pt-1",
)
with vuetify.VRow(classes="pt-2", dense=True):
with vuetify.VCol(cols="6"):
vuetify.VSelect(
# Color By
label="Color by",
v_model=("contour_color_array_idx", 0),
items=("array_list", self.dataset_arrays),
hide_details=True,
dense=True,
outlined=True,
classes="pt-1",
)
with vuetify.VCol(cols="6"):
vuetify.VSelect(
# Color Map
label="Colormap",
v_model=("contour_color_preset", LookupTable.Rainbow),
items=(
"colormaps",
[
{"text": "Rainbow", "value": 0},
{"text": "Inv Rainbow", "value": 1},
{"text": "Greyscale", "value": 2},
{"text": "Inv Greyscale", "value": 3},
],
),
hide_details=True,
dense=True,
outlined=True,
classes="pt-1",
)
vuetify.VSlider(
# Opacity
v_model=("contour_opacity", 1.0),
min=0,
max=1,
step=0.1,
label="Opacity",
classes="mt-1",
hide_details=True,
dense=True,
)
def upload(self, files):
for file in files:
file_helper = ClientFile(file)
file_name = file.get("name")
file_content = file.get("content")
print(file_helper.info)
with open(f"../06/{file_name}", "wb") as f:
f.write(file_content)
print(f"成功上传 {len(files)} 个文件")
# path = os.path.join("../06", file_name)
path = Path("../06") / file_name
print(path)
# self.ctrl.view_update()
self.renderWindow, self.cube_axes, self.mesh_actor, self.contour_actor, self.mesh_mapper, self.dataset_arrays, self.contour, self.contour_value, self.default_min, self.default_max, self.renderer = create_vtk_pipeline(
file_name)
self.ctrl.view_update = self.view.update
self.ctrl.view_reset_camera = self.view.reset_camera
self.ctrl.view_update()
def _ui(self):
with SinglePageWithDrawerLayout(self.server) as layout:
layout.title.set_text("Viewer")
with layout.toolbar:
# toolbar components
vuetify.VSpacer()
vuetify.VDivider(vertical=True, classes="mx-2")
self.standard_buttons()
html.Input(
type="file",
multiple=True,
change=(self.upload, "[$event.target.files]"),
__events=["change"],
style="max-width: 200px; height: 30px;",
)
with layout.drawer as drawer:
# drawer components
drawer.width = 325
self.pipeline_widget()
vuetify.VDivider(classes="mb-2")
self.mesh_card()
self.contour_card()
with layout.content:
# content components
with vuetify.VContainer(
fluid=True,
classes="pa-0 fill-height",
):
# view = vtk.VtkRemoteView(renderWindow, interactive_ratio=1)
# view = vtk.VtkLocalView(renderWindow)
self.view = vtk.VtkRemoteLocalView(
self.renderWindow, namespace="view", mode="local", interactive_ratio=1
)
self.ctrl.view_update = self.view.update
self.ctrl.view_reset_camera = self.view.reset_camera
return layout
# -----------------------------------------------------------------------------
# Main
# -----------------------------------------------------------------------------
if __name__ == "__main__":
app = MyApp()
app.server.start()
似乎是后端渲染内容没有在前端更新
只能有一个renderWindow