class ShowManager(object): """Class interface between the scene, the window and the interactor.""" def __init__(self, scene=None, title='FURY', size=(300, 300), png_magnify=1, reset_camera=True, order_transparent=False, interactor_style='custom', stereo='off', multi_samples=8, max_peels=4, occlusion_ratio=0.0): """Manage the visualization pipeline. Parameters ---------- scene : Scene() or vtkRenderer() The scene that holds all the actors. title : string A string for the window title bar. size : (int, int) ``(width, height)`` of the window. Default is (300, 300). png_magnify : int Number of times to magnify the screenshot. This can be used to save high resolution screenshots when pressing 's' inside the window. reset_camera : bool Default is True. You can change this option to False if you want to keep the camera as set before calling this function. order_transparent : bool True is useful when you want to order transparent actors according to their relative position to the camera. The default option which is False will order the actors according to the order of their addition to the Scene(). interactor_style : str or vtkInteractorStyle If str then if 'trackball' then vtkInteractorStyleTrackballCamera() is used, if 'image' then vtkInteractorStyleImage() is used (no rotation) or if 'custom' then CustomInteractorStyle is used. Otherwise you can input your own interactor style. stereo: string Set the stereo type. Default is 'off'. Other types include: * 'opengl': OpenGL frame-sequential stereo. Referred to as 'CrystalEyes' by VTK. * 'anaglyph': For use with red/blue glasses. See VTK docs to use different colors. * 'interlaced': Line interlaced. * 'checkerboard': Checkerboard interlaced. * 'left': Left eye only. * 'right': Right eye only. * 'horizontal': Side-by-side. multi_samples : int Number of samples for anti-aliazing (Default 8). For no anti-aliasing use 0. max_peels : int Maximum number of peels for depth peeling (Default 4). occlusion_ratio : float Occlusion ration for depth peeling (Default 0 - exact image). Attributes ---------- scene : Scene() or vtkRenderer() iren : vtkRenderWindowInteractor() style : vtkInteractorStyle() window : vtkRenderWindow() Methods ------- initialize() render() start() add_window_callback() Examples -------- >>> from fury import actor, window >>> scene = window.Scene() >>> scene.add(actor.axes()) >>> showm = window.ShowManager(scene) >>> # showm.initialize() >>> # showm.render() >>> # showm.start() """ if scene is None: scene = Scene() self.scene = scene self.title = title self.size = size self.png_magnify = png_magnify self.reset_camera = reset_camera self.order_transparent = order_transparent self.interactor_style = interactor_style self.stereo = stereo self.timers = [] if self.reset_camera: self.scene.ResetCamera() self.window = RenderWindow() if self.stereo.lower() != 'off': enable_stereo(self.window, self.stereo) self.window.AddRenderer(scene) self.window.SetSize(size[0], size[1]) if self.order_transparent: occlusion_ratio = occlusion_ratio or 0.1 antialiasing(self.scene, self.window, multi_samples=0, max_peels=max_peels, occlusion_ratio=occlusion_ratio) if self.interactor_style == 'image': self.style = InteractorStyleImage() elif self.interactor_style == 'trackball': self.style = InteractorStyleTrackballCamera() elif self.interactor_style == 'custom': self.style = CustomInteractorStyle() else: self.style = interactor_style self.iren = RenderWindowInteractor() self.style.SetCurrentRenderer(self.scene) # Hack: below, we explicitly call the Python version of SetInteractor. self.style.SetInteractor(self.iren) self.iren.SetInteractorStyle(self.style) self.iren.SetRenderWindow(self.window) def initialize(self): """Initialize interaction.""" self.iren.Initialize() def render(self): """Render only once.""" self.window.Render() def start(self): """Start interaction.""" try: self.render() if self.title.upper() == "FURY": self.window.SetWindowName(self.title + " " + fury_version) else: self.window.SetWindowName(self.title) self.iren.Start() except AttributeError: self.__init__(self.scene, self.title, size=self.size, png_magnify=self.png_magnify, reset_camera=self.reset_camera, order_transparent=self.order_transparent, interactor_style=self.interactor_style) self.initialize() self.render() if self.title.upper() == "FURY": self.window.SetWindowName(self.title + " " + fury_version) else: self.window.SetWindowName(self.title) self.iren.Start() self.window.RemoveRenderer(self.scene) self.scene.SetRenderWindow(None) self.window.Finalize() del self.iren del self.window def record_events(self): """Record events during the interaction. The recording is represented as a list of VTK events that happened during the interaction. The recorded events are then returned. Returns ------- events : str Recorded events (one per line). Notes ----- Since VTK only allows recording events to a file, we use a temporary file from which we then read the events. """ with InTemporaryDirectory(): filename = "recorded_events.log" recorder = InteractorEventRecorder() recorder.SetInteractor(self.iren) recorder.SetFileName(filename) def _stop_recording_and_close(_obj, _evt): if recorder: recorder.Stop() self.iren.TerminateApp() self.iren.AddObserver("ExitEvent", _stop_recording_and_close) recorder.EnabledOn() recorder.Record() self.initialize() self.render() self.iren.Start() # Deleting this object is the unique way # to close the file. recorder = None # Retrieved recorded events. with open(filename, 'r') as f: events = f.read() return events def record_events_to_file(self, filename="record.log"): """Record events during the interaction. The recording is represented as a list of VTK events that happened during the interaction. The recording is going to be saved into `filename`. Parameters ---------- filename : str Name of the file that will contain the recording (.log|.log.gz). """ events = self.record_events() # Compress file if needed if filename.endswith(".gz"): with gzip.open(filename, 'wb') as fgz: fgz.write(asbytes(events)) else: with open(filename, 'w') as f: f.write(events) def play_events(self, events): """Play recorded events of a past interaction. The VTK events that happened during the recorded interaction will be played back. Parameters ---------- events : str Recorded events (one per line). """ recorder = InteractorEventRecorder() recorder.SetInteractor(self.iren) recorder.SetInputString(events) recorder.ReadFromInputStringOn() self.initialize() # self.render() recorder.Play() # self.window.RemoveRenderer(self.scene) # self.scene.SetRenderWindow(None) # Finalize seems very important otherwise # the recording window will not close. self.window.Finalize() self.exit() # print('After Finalize and Exit') # del self.iren # del self.window def play_events_from_file(self, filename): """Play recorded events of a past interaction. The VTK events that happened during the recorded interaction will be played back from `filename`. Parameters ---------- filename : str Name of the file containing the recorded events (.log|.log.gz). """ # Uncompress file if needed. if filename.endswith(".gz"): with gzip.open(filename, 'r') as f: events = f.read() else: with open(filename) as f: events = f.read() self.play_events(events) def add_window_callback(self, win_callback, event=Command.ModifiedEvent): """Add window callbacks.""" self.window.AddObserver(event, win_callback) self.window.Render() def add_timer_callback(self, repeat, duration, timer_callback): self.iren.AddObserver("TimerEvent", timer_callback) if repeat: timer_id = self.iren.CreateRepeatingTimer(duration) else: timer_id = self.iren.CreateOneShotTimer(duration) self.timers.append(timer_id) def add_iren_callback(self, iren_callback, event="MouseMoveEvent"): self.iren.AddObserver(event, iren_callback) def destroy_timer(self, timer_id): self.iren.DestroyTimer(timer_id) del self.timers[self.timers.index(timer_id)] def destroy_timers(self): for timer_id in self.timers: self.destroy_timer(timer_id) def exit(self): """Close window and terminate interactor.""" # if is_osx and self.timers: # OSX seems to not destroy correctly timers # segfault 11 appears sometimes if we do not do it manually. # self.iren.GetRenderWindow().Finalize() self.iren.TerminateApp() self.destroy_timers() self.timers.clear() def save_screenshot(self, fname, magnification=1, size=None, stereo=None): """Save a screenshot of the current window in the specified filename. Parameters ---------- fname : str or None File name where to save the screenshot. magnification : int, optional Applies a magnification factor to the scene before taking the screenshot which improves the quality. A value greater than 1 increases the quality of the image. However, the output size will be larger. For example, 200x200 image with magnification of 2 will result in a 400x400 image. Default is 1. size : tuple of 2 ints, optional Size of the output image in pixels. If None, the size of the scene will be used. If magnification > 1, then the size will be determined by the magnification factor. Default is None. stereo : str, optional Set the type of stereo for the screenshot. Supported values are: * 'opengl': OpenGL frame-sequential stereo. Referred to as 'CrystalEyes' by VTK. * 'anaglyph': For use with red/blue glasses. See VTK docs to use different colors. * 'interlaced': Line interlaced. * 'checkerboard': Checkerboard interlaced. * 'left': Left eye only. * 'right': Right eye only. * 'horizontal': Side-by-side. """ if size is None: size = self.size if stereo is None: stereo = self.stereo.lower() record(scene=self.scene, out_path=fname, magnification=magnification, size=size, stereo=stereo)
class ShowManager(object): """Class interface between the scene, the window and the interactor.""" def __init__(self, scene=None, title='FURY', size=(300, 300), png_magnify=1, reset_camera=True, order_transparent=False, interactor_style='custom'): """Manage the visualization pipeline. Parameters ---------- scene : Scene() or vtkRenderer() The scene that holds all the actors. title : string A string for the window title bar. size : (int, int) ``(width, height)`` of the window. Default is (300, 300). png_magnify : int Number of times to magnify the screenshot. This can be used to save high resolution screenshots when pressing 's' inside the window. reset_camera : bool Default is True. You can change this option to False if you want to keep the camera as set before calling this function. order_transparent : bool True is useful when you want to order transparent actors according to their relative position to the camera. The default option which is False will order the actors according to the order of their addition to the Scene(). interactor_style : str or vtkInteractorStyle If str then if 'trackball' then vtkInteractorStyleTrackballCamera() is used, if 'image' then vtkInteractorStyleImage() is used (no rotation) or if 'custom' then CustomInteractorStyle is used. Otherwise you can input your own interactor style. Attributes ---------- scene : Scene() or vtkRenderer() iren : vtkRenderWindowInteractor() style : vtkInteractorStyle() window : vtkRenderWindow() Methods ------- initialize() render() start() add_window_callback() Notes ----- Default interaction keys for * 3d navigation are with left, middle and right mouse dragging * resetting the camera press 'r' * saving a screenshot press 's' * for quiting press 'q' Examples -------- >>> from fury import actor, window >>> scene = window.Scene() >>> scene.add(actor.axes()) >>> showm = window.ShowManager(scene) >>> # showm.initialize() >>> # showm.render() >>> # showm.start() """ if scene is None: scene = Scene() self.scene = scene self.title = title self.size = size self.png_magnify = png_magnify self.reset_camera = reset_camera self.order_transparent = order_transparent self.interactor_style = interactor_style self.timers = [] if self.reset_camera: self.scene.ResetCamera() self.window = vtk.vtkRenderWindow() self.window.AddRenderer(scene) if self.title == 'FURY': self.window.SetWindowName(title + ' ' + fury_version) else: self.window.SetWindowName(title) self.window.SetSize(size[0], size[1]) if self.order_transparent: # Use a render window with alpha bits # as default is 0 (false)) self.window.SetAlphaBitPlanes(True) # Force to not pick a framebuffer with a multisample buffer # (default is 8) self.window.SetMultiSamples(0) # Choose to use depth peeling (if supported) # (default is 0 (false)): self.scene.UseDepthPeelingOn() # Set depth peeling parameters # Set the maximum number of rendering passes (default is 4) scene.SetMaximumNumberOfPeels(4) # Set the occlusion ratio (initial value is 0.0, exact image): scene.SetOcclusionRatio(0.0) if self.interactor_style == 'image': self.style = vtk.vtkInteractorStyleImage() elif self.interactor_style == 'trackball': self.style = vtk.vtkInteractorStyleTrackballCamera() elif self.interactor_style == 'custom': self.style = CustomInteractorStyle() else: self.style = interactor_style self.iren = vtk.vtkRenderWindowInteractor() self.style.SetCurrentRenderer(self.scene) # Hack: below, we explicitly call the Python version of SetInteractor. self.style.SetInteractor(self.iren) self.iren.SetInteractorStyle(self.style) self.iren.SetRenderWindow(self.window) def initialize(self): """Initialize interaction.""" self.iren.Initialize() def render(self): """Render only once.""" self.window.Render() def start(self): """Start interaction.""" try: self.iren.Start() except AttributeError: self.__init__(self.scene, self.title, size=self.size, png_magnify=self.png_magnify, reset_camera=self.reset_camera, order_transparent=self.order_transparent, interactor_style=self.interactor_style) self.initialize() self.render() self.iren.Start() self.window.RemoveRenderer(self.scene) self.scene.SetRenderWindow(None) del self.iren del self.window def record_events(self): """Record events during the interaction. The recording is represented as a list of VTK events that happened during the interaction. The recorded events are then returned. Returns ------- events : str Recorded events (one per line). Notes ----- Since VTK only allows recording events to a file, we use a temporary file from which we then read the events. """ with InTemporaryDirectory(): filename = "recorded_events.log" recorder = vtk.vtkInteractorEventRecorder() recorder.SetInteractor(self.iren) recorder.SetFileName(filename) def _stop_recording_and_close(obj, evt): if recorder: recorder.Stop() self.iren.TerminateApp() self.iren.AddObserver("ExitEvent", _stop_recording_and_close) recorder.EnabledOn() recorder.Record() self.initialize() self.render() self.iren.Start() # Deleting this object is the unique way # to close the file. recorder = None # Retrieved recorded events. with open(filename, 'r') as f: events = f.read() return events def record_events_to_file(self, filename="record.log"): """Record events during the interaction. The recording is represented as a list of VTK events that happened during the interaction. The recording is going to be saved into `filename`. Parameters ---------- filename : str Name of the file that will contain the recording (.log|.log.gz). """ events = self.record_events() # Compress file if needed if filename.endswith(".gz"): with gzip.open(filename, 'wb') as fgz: fgz.write(asbytes(events)) else: with open(filename, 'w') as f: f.write(events) def play_events(self, events): """Play recorded events of a past interaction. The VTK events that happened during the recorded interaction will be played back. Parameters ---------- events : str Recorded events (one per line). """ recorder = vtk.vtkInteractorEventRecorder() recorder.SetInteractor(self.iren) recorder.SetInputString(events) recorder.ReadFromInputStringOn() self.initialize() self.render() recorder.Play() def play_events_from_file(self, filename): """Play recorded events of a past interaction. The VTK events that happened during the recorded interaction will be played back from `filename`. Parameters ---------- filename : str Name of the file containing the recorded events (.log|.log.gz). """ # Uncompress file if needed. if filename.endswith(".gz"): with gzip.open(filename, 'r') as f: events = f.read() else: with open(filename) as f: events = f.read() self.play_events(events) def add_window_callback(self, win_callback): """Add window callbacks.""" self.window.AddObserver(vtk.vtkCommand.ModifiedEvent, win_callback) self.window.Render() def add_timer_callback(self, repeat, duration, timer_callback): self.iren.AddObserver("TimerEvent", timer_callback) if repeat: timer_id = self.iren.CreateRepeatingTimer(duration) else: timer_id = self.iren.CreateOneShotTimer(duration) self.timers.append(timer_id) def destroy_timer(self, timer_id): self.iren.DestroyTimer(timer_id) del self.timers[self.timers.index(timer_id)] def destroy_timers(self): for timer_id in self.timers: self.destroy_timer(timer_id) def exit(self): """Close window and terminate interactor.""" self.iren.GetRenderWindow().Finalize() self.iren.TerminateApp()