class VolumeView(BaseQtWidget): def __init__(self, _model: VolumeViewModel): self._model = _model self._model.register(self.update) self._canvas = SceneCanvas() self._viewbox = ViewBox(parent=self._canvas.scene) self._canvas.central_widget.add_widget(self._viewbox) self._viewbox.camera = ArcballCamera(fov=0) self._atlas_volume = Volume(self._model.atlas_volume, parent=self._viewbox.scene) # , interpolation='nearest') self._atlas_volume.attach(filters.ColorFilter((1., .5, 0., 1.))) self._atlas_volume.set_gl_state('additive', depth_test=False) self._section_image = Image(parent=self._viewbox.scene, cmap='grays') self._section_image.attach(filters.ColorFilter((0., .5, 1., 1.))) self._section_image.set_gl_state('additive', depth_test=False) self._canvas.events.key_press.connect(lambda event: self._model.press_key(event.key.name)) @property def qt_widget(self) -> QWidget: return self._canvas.native def update(self, changed: str) -> None: render_funs = { 'atlas_volume': self._render_volume, 'section_image': self._render_section, 'section_transform': self._render_section_transform, 'clim': self._render_section_clim, 'sections': (lambda: ...), } render_funs[changed]() def _render_section_clim(self) -> None: self._section_image.clim = self._model.clim self._canvas.update() def _render_section_transform(self) -> None: self._section_image.transform = MatrixTransform(self._model.section_transform.T) self._canvas.update() def _render_section(self) -> None: self._section_image.set_data(self._model.section_image.T) self._canvas.update() def _render_volume(self) -> None: model = self._model self._atlas_volume.set_data(model.atlas_volume.swapaxes(0, 2), clim=model.volume_clim) self._viewbox.camera.center = model.camera_center self._viewbox.camera.scale_factor = model.camera_distance self._canvas.update()
class VolumeView(BaseQtView): def __init__(self): self._canvas = SceneCanvas() self._viewbox = ViewBox(parent=self._canvas.scene) self._canvas.central_widget.add_widget(self._viewbox) self._viewbox.camera = TurntableCamera(fov=0, azimuth=0, elevation=90, distance=1000) self._atlas_volume = Volume( array([[[1, 2]]]) * 1000, parent=self._viewbox.scene) #, interpolation='nearest') self._atlas_volume.attach(filters.ColorFilter((1., .5, 0., 1.))) self._atlas_volume.set_gl_state('additive', depth_test=False) self._section_image = Image(parent=self._viewbox.scene, cmap='grays') self._section_image.attach(filters.ColorFilter((0., .5, 1., 1.))) self._section_image.set_gl_state('additive', depth_test=False) self._canvas.events.key_press.connect( self._handle_vispy_key_press_events) # View Code @property def qt_widget(self) -> QWidget: return self._canvas.native def on_atlas_update(self, volume: ndarray, transform: ndarray): self._atlas_volume.set_data(volume, clim=(np.min(volume), np.max(volume))) self._atlas_volume.transform = MatrixTransform(transform.T) self._viewbox.camera.center = (0, 0, 0) self._viewbox.camera.scale_factor = transform[0, 0] * volume.shape[0] self._canvas.update() def on_section_loaded(self, image: ndarray, transform: ndarray): self._section_image.set_data(image) self._section_image.clim = np.min(image), np.max(image) if transform is not None: self._section_image.transform = MatrixTransform(transform.T) self._canvas.update() def on_channel_select(self, image: ndarray, channel: int): self._section_image.set_data(image) self._section_image.clim = np.min(image), np.max(image) self._canvas.update() def on_section_moved(self, transform: ndarray, atlas_slice_image: ndarray): self._section_image.transform = MatrixTransform(transform.T) self._canvas.update() # Controller Code def _handle_vispy_key_press_events(self, event: KeyEvent) -> None: """Router: Calls AppCommands functions based on the event that's given.""" key_commands = { '1': lambda: self.select_channel(1), '2': lambda: self.select_channel(2), '3': lambda: self.select_channel(3), '4': lambda: self.select_channel(4), 'W': lambda: self.move_section(z=30), 'S': lambda: self.move_section(z=-30), 'A': lambda: self.move_section(x=-30), 'D': lambda: self.move_section(x=30), 'Q': lambda: self.move_section(y=-30), 'E': lambda: self.move_section(y=30), 'I': lambda: self.move_section(rz=3), 'K': lambda: self.move_section(rz=-3), 'J': lambda: self.move_section(rx=-3), 'L': lambda: self.move_section(rx=3), 'U': lambda: self.move_section(ry=-3), 'O': lambda: self.move_section(ry=3), 'Escape': use_app().quit, } if command := key_commands.get(event.key.name): command()
class SliceView(BaseQtView): def __init__(self): self._canvas = SceneCanvas() self._viewbox = ViewBox(parent=self._canvas.scene) self._canvas.central_widget.add_widget(self._viewbox) self._viewbox.camera = TurntableCamera( interactive=False, fov=0, # Makes it an ortho camera. azimuth=0, elevation=-90, ) self._reference_slice = Image(cmap='grays', parent=self._viewbox.scene) self._reference_slice.attach(ColorFilter((1., .5, 0., 1.))) self._reference_slice.set_gl_state('additive', depth_test=False) self._slice = Image(cmap='grays', parent=self._viewbox.scene) self._slice.attach(ColorFilter((0., .5, 1., 1.))) self._slice.set_gl_state('additive', depth_test=False) self._canvas.events.mouse_press.connect(self._vispy_mouse_event) self._canvas.events.mouse_move.connect(self._vispy_mouse_event) self._canvas.events.mouse_release.connect(self._vispy_mouse_event) self._canvas.events.mouse_wheel.connect(self._vispy_mouse_event) @property def qt_widget(self) -> QWidget: return self._canvas.native def on_section_loaded(self, image: ndarray, transform: ndarray) -> None: self.update_slice_image(image=image) def on_channel_select(self, image: ndarray, channel: int) -> None: self.update_slice_image(image=image) def on_section_moved(self, transform: ndarray, atlas_slice_image: ndarray) -> None: self.update_ref_slice_image(image=atlas_slice_image) def update_slice_image(self, image: ndarray): self._slice.set_data(image) self._slice.clim = np.min(image), np.max(image) self._viewbox.camera.center = image.shape[1] / 2, image.shape[0] / 2, 0. self._viewbox.camera.scale_factor = image.shape[1] self._canvas.update() def update_ref_slice_image(self, image: ndarray): self._reference_slice.set_data(image) self._reference_slice.clim = (np.min(image), np.max(image)) if np.max(image) - np.min(image) > 0 else (0, 1) self._canvas.update() def _vispy_mouse_event(self, event: SceneMouseEvent) -> None: if event.type == 'mouse_press': event.handled = True elif event.type == 'mouse_move': if event.press_event is None: return x1, y1 = event.last_event.pos x2, y2 = event.pos if event.button == 1: # Left Mouse Button self._on_left_mouse_drag(x1=x1, y1=y1, x2=x2, y2=y2) elif event.button == 2: # Right Mouse Button self._on_right_mouse_drag(x1=x1, y1=y1, x2=x2, y2=y2) elif event.type == 'mouse_wheel': self._on_mousewheel_move(increment=int(event.delta[1])) def _on_left_mouse_drag(self, x1: int, y1: int, x2: int, y2: int): x_amp = abs(x2 - x1) y_amp = abs(y2 - y1) x_dir = ((x2 > x1) * 2) - 1 y_dir = ((y2 > y1) * 2) - 1 scale = 4. x_slice_offset = x_amp * x_dir * scale y_slice_offset = y_amp * y_dir * scale self.move_section(x=x_slice_offset, y=y_slice_offset) self.get_coord_data(i=0, j=0) # todo: replace with mouse highlighting def _on_right_mouse_drag(self, x1: int, y1: int, x2: int, y2: int): x_amp = abs(x2 - x1) x_dir = ((x2 > x1) * 2) - 1 scale = .1 x_slice_offset = x_amp * x_dir * scale self.move_section(ry=x_slice_offset) def _on_mousewheel_move(self, increment: int): self.move_section(z=10 * increment) def move_section(self, x=0., y=0., z=0., rx=0., ry=0., rz=0.) -> None: raise NotImplementedError("Wire up to MoveSectionCommand to use this.") def get_coord_data(self, i: int, j: int): raise NotImplementedError("Wire up to GetPixelRegistrationDataCommand to use this.")
class SliceView(BaseQtWidget): def __init__(self, _model: SliceViewModel): self._model = _model self._model.register(self.update) self._canvas = SceneCanvas() self._viewbox = ViewBox(parent=self._canvas.scene) self._canvas.central_widget.add_widget(self._viewbox) self._viewbox.camera = TurntableCamera( interactive=False, fov=0, # Makes it an ortho camera. azimuth=0, elevation=-90, ) self._reference_slice = Image(cmap='grays', parent=self._viewbox.scene) self._reference_slice.attach(ColorFilter((1., .5, 0., 1.))) self._reference_slice.set_gl_state('additive', depth_test=False) self._slice = Image(cmap='grays', parent=self._viewbox.scene) self._slice.attach(ColorFilter((0., .5, 1., 1.))) self._slice.set_gl_state('additive', depth_test=False) self._canvas.events.mouse_press.connect(self.mouse_press) self._canvas.events.mouse_move.connect(self.mouse_move) self._canvas.events.mouse_wheel.connect(self.mouse_wheel) @property def qt_widget(self) -> QWidget: return self._canvas.native def mouse_press(self, event: SceneMouseEvent) -> None: event.handled = True def mouse_move(self, event: SceneMouseEvent) -> None: if event.press_event is None: return x1, y1 = event.last_event.pos x2, y2 = event.pos if event.button == 1: # Left Mouse Button self._model.on_left_mouse_drag(x1=x1, x2=x2, y1=y1, y2=y2) elif event.button == 2: # Right Mouse Button self._model.on_right_mouse_drag(x1=x1, y1=y1, x2=x2, y2=y2) def mouse_wheel(self, event: SceneMouseEvent): self._model.on_mousewheel_move(increment=int(event.delta[1])) def update(self, changed: str) -> None: render_funs = { 'section_image': self._render_section_image, 'clim': self._render_section_clim, 'atlas_image': self._render_atlas_image, } render_funs[changed]() def _render_atlas_image(self): image = self._model.atlas_image self._reference_slice.set_data(image) self._reference_slice.clim = ( np.min(image), np.max(image)) if np.max(image) - np.min(image) > 0 else (0, 1) self._canvas.update() def _render_section_clim(self): self._slice.clim = self._model.clim self._canvas.update() def _render_section_image(self): image = self._model.section_image self._slice.set_data(image) self._viewbox.camera.center = image.shape[1] / 2, image.shape[0] / 2, 0. self._viewbox.camera.scale_factor = image.shape[1] self._canvas.update()