Created
March 24, 2026 18:10
-
-
Save lassoan/2fa7c7a02054e6b217ebfd3b33688145 to your computer and use it in GitHub Desktop.
Example of a serverless trame-slicer application
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| Standalone MRML 3D view using only the slicer package (no trame). | |
| Sets up: | |
| - vtkMRMLScene + vtkSlicerApplicationLogic | |
| - vtkMRMLViewNode via vtkMRMLViewLogic | |
| - vtkMRMLDisplayableManagerGroup with all standard 3D displayable managers | |
| - Loads MRHead.nrrd and enables GPU ray-cast volume rendering | |
| Usage: | |
| python mrml_3d_view.py | |
| """ | |
| from pathlib import Path | |
| from slicer import ( | |
| vtkMRMLCameraDisplayableManager, | |
| vtkMRMLColorLegendDisplayableManager, | |
| vtkMRMLCrosshairDisplayableManager3D, | |
| vtkMRMLCrosshairNode, | |
| vtkMRMLDisplayableManagerGroup, | |
| vtkMRMLLayerDisplayableManager, | |
| vtkMRMLLinearTransformsDisplayableManager, | |
| vtkMRMLMarkupsDisplayableManager, | |
| vtkMRMLModelDisplayableManager, | |
| vtkMRMLOrientationMarkerDisplayableManager, | |
| vtkMRMLRulerDisplayableManager, | |
| vtkMRMLScene, | |
| vtkMRMLSegmentationsDisplayableManager3D, | |
| vtkMRMLThreeDReformatDisplayableManager, | |
| vtkMRMLThreeDViewDisplayableManagerFactory, | |
| vtkMRMLThreeDViewInteractorStyle, | |
| vtkMRMLTransformsDisplayableManager3D, | |
| vtkMRMLViewDisplayableManager, | |
| vtkMRMLViewLogic, | |
| vtkMRMLVolumeRenderingDisplayableManager, | |
| vtkSlicerApplicationLogic, | |
| vtkSlicerVolumeRenderingLogic, | |
| vtkSlicerVolumesLogic, | |
| ) | |
| from vtkmodules.vtkCommonCore import vtkCollection, vtkCommand, vtkOutputWindow | |
| from vtkmodules.vtkRenderingCore import ( | |
| vtkInteractorStyle3D, | |
| vtkRenderer, | |
| vtkRenderWindow, | |
| vtkRenderWindowInteractor, | |
| ) | |
| MRHEAD_PATH = Path(__file__).parent / "MRHead.nrrd" | |
| # Maximum render rate driven by displayable-manager update requests (frames/sec). | |
| MAX_UPDATE_RATE_FPS = 60 | |
| THREED_DISPLAYABLE_MANAGERS = [ | |
| vtkMRMLVolumeRenderingDisplayableManager, | |
| vtkMRMLCameraDisplayableManager, | |
| vtkMRMLViewDisplayableManager, | |
| vtkMRMLModelDisplayableManager, | |
| vtkMRMLThreeDReformatDisplayableManager, | |
| vtkMRMLCrosshairDisplayableManager3D, | |
| vtkMRMLOrientationMarkerDisplayableManager, | |
| vtkMRMLRulerDisplayableManager, | |
| vtkMRMLSegmentationsDisplayableManager3D, | |
| vtkMRMLMarkupsDisplayableManager, | |
| vtkMRMLTransformsDisplayableManager3D, | |
| vtkMRMLLayerDisplayableManager, | |
| vtkMRMLLinearTransformsDisplayableManager, | |
| vtkMRMLColorLegendDisplayableManager, | |
| ] | |
| def setup_scene(): | |
| """Create and configure the MRML scene and application logic.""" | |
| # Route VTK warnings to stderr | |
| vtk_out = vtkOutputWindow() | |
| vtk_out.SetDisplayModeToAlwaysStdErr() | |
| vtkOutputWindow.SetInstance(vtk_out) | |
| scene = vtkMRMLScene() | |
| # Crosshair node is expected by several displayable managers | |
| crosshair = vtkMRMLCrosshairNode() | |
| crosshair.SetCrosshairName("default") | |
| scene.AddNode(crosshair) | |
| app_logic = vtkSlicerApplicationLogic() | |
| app_logic.SetMRMLScene(scene) | |
| app_logic.SetViewLogics(vtkCollection()) | |
| # Use the color logic that vtkSlicerApplicationLogic already owns internally, | |
| # set it up, and register it under "Colors" so other module logics can find it | |
| color_logic = app_logic.GetColorLogic() | |
| color_logic.SetMRMLScene(scene) | |
| color_logic.AddDefaultColorNodes() | |
| app_logic.SetModuleLogic("Colors", color_logic) | |
| # Connect the factory to the application logic | |
| factory = vtkMRMLThreeDViewDisplayableManagerFactory.GetInstance() | |
| factory.SetMRMLApplicationLogic(app_logic) | |
| return scene, app_logic | |
| def create_3d_view(scene, app_logic): | |
| """Create a VTK render window and wire it to a vtkMRMLViewNode.""" | |
| renderer = vtkRenderer() | |
| renderer.SetUseDepthPeeling(True) | |
| renderer.SetUseDepthPeelingForVolumes(True) | |
| renderer.SetBackground(0.1, 0.1, 0.15) | |
| renderer.SetBackground2(0.2, 0.2, 0.35) | |
| renderer.SetGradientBackground(True) | |
| render_window = vtkRenderWindow() | |
| render_window.SetWindowName("MRML 3D View") | |
| render_window.SetSize(800, 600) | |
| render_window.AddRenderer(renderer) | |
| interactor = vtkRenderWindowInteractor() | |
| interactor.SetRenderWindow(render_window) | |
| interactor.Initialize() | |
| # Build displayable manager group | |
| factory = vtkMRMLThreeDViewDisplayableManagerFactory.GetInstance() | |
| dm_group = vtkMRMLDisplayableManagerGroup() | |
| dm_group.SetRenderer(renderer) | |
| for dm_type in THREED_DISPLAYABLE_MANAGERS: | |
| name = dm_type.__name__ | |
| if not factory.IsDisplayableManagerRegistered(name): | |
| factory.RegisterDisplayableManager(name) | |
| dm_group.Initialize(factory, renderer) | |
| # Trigger renders when the displayable managers request them. | |
| # Guard against re-entrancy: Render() causes managers to fire UpdateEvent, | |
| # which would call Render() again, creating an infinite recursion. | |
| _rendering = [False] | |
| def _on_update(*_): | |
| if not _rendering[0]: | |
| _rendering[0] = True | |
| try: | |
| render_window.Render() | |
| finally: | |
| _rendering[0] = False | |
| dm_group.AddObserver(vtkCommand.UpdateEvent, _on_update) | |
| # Create the MRML view node and attach it to the group | |
| view_logic = vtkMRMLViewLogic() | |
| view_logic.SetMRMLApplicationLogic(app_logic) | |
| app_logic.GetViewLogics().AddItem(view_logic) | |
| view_logic.SetMRMLScene(scene) | |
| view_node = view_logic.AddViewNode("View1") | |
| view_node.SetMappedInLayout(True) | |
| dm_group.SetMRMLDisplayableNode(view_node) | |
| # vtkMRMLThreeDViewInteractorStyle is an observer, not the interactor style itself | |
| style = vtkMRMLThreeDViewInteractorStyle() | |
| style.SetDisplayableManagers(dm_group) | |
| style.SetInteractor(interactor) | |
| interactor.SetInteractorStyle(vtkInteractorStyle3D()) | |
| # Keep dm_group, view_logic, and style alive for the lifetime of the window. | |
| # They are not referenced after this point but must not be garbage-collected. | |
| render_window._mrml_refs = (dm_group, view_logic, style) | |
| return render_window, interactor, view_node | |
| def load_volume(scene, app_logic): | |
| """Load MRHead.nrrd using vtkSlicerVolumesLogic.""" | |
| volumes_logic = vtkSlicerVolumesLogic() | |
| volumes_logic.SetMRMLScene(scene) | |
| volumes_logic.SetMRMLApplicationLogic(app_logic) | |
| app_logic.SetModuleLogic("Volumes", volumes_logic) | |
| volume_node = volumes_logic.AddArchetypeVolume(str(MRHEAD_PATH), "MRHead", 0) | |
| if not volume_node: | |
| raise RuntimeError(f"Failed to load volume: {MRHEAD_PATH}") | |
| print(f"Loaded: {volume_node.GetName()}") | |
| return volume_node | |
| def enable_volume_rendering(scene, app_logic, volume_node): | |
| """Create a GPU ray-cast volume rendering display node for the volume.""" | |
| from trame_slicer.resources import resources_path | |
| vr_logic = vtkSlicerVolumeRenderingLogic() | |
| vr_logic.SetModuleShareDirectory(resources_path().as_posix()) | |
| vr_logic.SetMRMLScene(scene) | |
| vr_logic.SetMRMLApplicationLogic(app_logic) | |
| app_logic.SetModuleLogic("VolumeRendering", vr_logic) | |
| vr_logic.ChangeVolumeRenderingMethod("vtkMRMLGPURayCastVolumeRenderingDisplayNode") | |
| display_node = vr_logic.CreateDefaultVolumeRenderingNodes(volume_node) | |
| display_node.SetVisibility(True) | |
| return display_node | |
| def main(): | |
| scene, app_logic = setup_scene() | |
| render_window, interactor, view_node = create_3d_view(scene, app_logic) | |
| volume_node = load_volume(scene, app_logic) | |
| enable_volume_rendering(scene, app_logic, volume_node) | |
| render_window.Render() | |
| interactor.Start() | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment