From 68e2b77cdf30a4eeaabc60481df6f68167ef5b2f Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Tue, 26 Jul 2022 14:40:09 +0100 Subject: [PATCH 01/33] Add border widget and example --- .../Python/build/lib/ccpi/viewer/widgets.py | 109 ++++++++++++++++++ Wrappers/Python/examples/SliceBorderWidget.py | 35 ++++++ 2 files changed, 144 insertions(+) create mode 100644 Wrappers/Python/build/lib/ccpi/viewer/widgets.py create mode 100644 Wrappers/Python/examples/SliceBorderWidget.py diff --git a/Wrappers/Python/build/lib/ccpi/viewer/widgets.py b/Wrappers/Python/build/lib/ccpi/viewer/widgets.py new file mode 100644 index 00000000..9705d70f --- /dev/null +++ b/Wrappers/Python/build/lib/ccpi/viewer/widgets.py @@ -0,0 +1,109 @@ +import vtk + +def CreateViewerSliceBorder(viewer, orientation='horizontal', coord=0, color=(1., 0., 0.), width=1, widget_name='slice_outline_widget', ): + ''' Creates a border in [viewer], around a slice with number [coord] in + the [orientation] plane. + This appears as an overlay, so appears on every projection. + E.g. if orientation = 'vertical', coord = 5, width=1 then it creates a + border around the voxels with vertical value of 5. + width controls the width of the box, but it always starts at [coord]. + E.g. if orientation = 'vertical', coord = 5, width = 5 then it creates a + border around the voxels with vertical values of 5-9. + + Parameters + ---------- + viewer: viewer2D or viewer3D + A viewer upon which the border will be displayed + orientation: {'horizontal', 'vertical'} + Which axis the slice is on + coord: int + The coordinate of the slice + color: tuple + colour of the border + width: float + The width of the border. The border starts from the bound of the + slice with number coord. + widget_name: str + The name to associate the border widget with in the viewer. + + ''' + extent = viewer.img3D.GetExtent() + spac = viewer.img3D.GetSpacing() + + if orientation == "vertical": + x_length = extent[1] + pos1 = [0, coord] + pos2 = [x_length, coord+width] + else: + y_length = extent[3] + pos1 = [coord, y_length] + pos2 = [coord + width, 0] + + + CreateViewerOverlayBorder(viewer, pos1, pos2, color, widget_name) + + + +def CreateViewerOverlayBorder(viewer, pos1, pos2, + color=(1., 0., 0.), widget_name='box_widget'): + ''' + Creates a border in [viewer], with the two alternate corner + positions at [pos1], [pos2] + This appears as an overlay, so appears on every projection. + + Parameters + ---------- + viewer: viewer2D or viewer3D + A viewer upon which the border will be displayed + pos1: tuple + coordinates of lower left corner of the border widget (in image coordinates) + pos2: tuple + coordinates of top right corner of the border widget (in image coordinates) + color: tuple + colour of the border + widget_name: str + The name to associate the border widget with in the viewer. + ''' + + if hasattr(viewer, 'widgets'): + box_widget = widget_name in viewer.widgets + else: + viewer.widgets={} + box_widget = False + + if not box_widget: + borderWidget = vtk.vtkBorderWidget(); + representation = vtk.vtkBorderRepresentation() + borderWidget.SetRepresentation(representation) + #borderWidget.CreateDefaultRepresentation() + borderWidget.SetResizable(False) + borderWidget.SetProcessEvents(False) + borderWidget.SelectableOff() + borderWidget.SetInteractor(viewer.iren) + viewer.widgets[widget_name] = borderWidget + + else: + borderWidget = viewer.widgets[widget_name] + + # coord conversion + # pos1 = list(pos1) + # pos2 = list(pos2) + # pos1.append(0) + # pos2.append(0) + # print("POS 1") + # print(pos1) + + # pos1 = viewer.style.image2world(pos1)[:2] + # print("POS 2") + # print(pos2) + # pos2 = viewer.style.image2world(pos2)[:2] + + # borderWidget.GetBorderRepresentation().SetPosition((0.05,0.05)) + # borderWidget.GetBorderRepresentation().SetPosition2((1,1)) + # borderWidget.GetBorderRepresentation().GetBorderProperty().SetColor(color) + # print( borderWidget.GetBorderRepresentation().GetPosition() , borderWidget.GetBorderRepresentation().GetPosition2()) + # print( borderWidget.GetBorderRepresentation().GetPosition() , borderWidget.GetBorderRepresentation().GetPosition2()) + print(borderWidget.GetBorderRepresentation().GetBounds()) + borderWidget.On() + + viewer.style.UpdatePipeline() \ No newline at end of file diff --git a/Wrappers/Python/examples/SliceBorderWidget.py b/Wrappers/Python/examples/SliceBorderWidget.py new file mode 100644 index 00000000..84d8fa47 --- /dev/null +++ b/Wrappers/Python/examples/SliceBorderWidget.py @@ -0,0 +1,35 @@ +import sys +import vtk +from PySide2 import QtCore, QtWidgets +from ccpi.viewer import viewer2D, viewer3D +from ccpi.viewer.QCILViewerWidget import QCILViewerWidget +from ccpi.viewer.utils import example_data +from SingleViewerCentralWidget import SingleViewerCenterWidget +from ccpi.viewer.widgets import CreateViewerSliceBorder + + +class SingleViewerCenterWidget(QtWidgets.QMainWindow): + + def __init__(self, parent=None, viewer=viewer2D): + QtWidgets.QMainWindow.__init__(self, parent) + + self.frame = QCILViewerWidget(viewer=viewer, shape=(600, 600)) + + head = example_data.HEAD.get() + + self.frame.viewer.setInputData(head) + + self.setCentralWidget(self.frame) + + self.show() + + +if __name__ == "__main__": + + app = QtWidgets.QApplication(sys.argv) + # can change the behaviour by setting which viewer you want + # between viewer2D and viewer3D + window = SingleViewerCenterWidget(viewer=viewer2D) + CreateViewerSliceBorder(window.frame.viewer, 'horizontal', 5) + + sys.exit(app.exec_()) From 3ef75821aa3f43a2497eabcece622ae9c3a626de Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 27 Jul 2022 10:44:01 +0100 Subject: [PATCH 02/33] box widget methods --- .gitignore | 2 + .../Python/build/lib/ccpi/viewer/widgets.py | 109 -------- Wrappers/Python/ccpi/viewer/CILViewer2D.py | 107 +------- Wrappers/Python/ccpi/viewer/CILViewerBase.py | 10 +- .../Python/ccpi/viewer/widgets/__init__.py | 0 .../Python/ccpi/viewer/widgets/box_widgets.py | 242 ++++++++++++++++++ Wrappers/Python/examples/SliceBorderWidget.py | 41 +-- Wrappers/Python/setup.py | 3 +- 8 files changed, 270 insertions(+), 244 deletions(-) delete mode 100644 Wrappers/Python/build/lib/ccpi/viewer/widgets.py create mode 100644 Wrappers/Python/ccpi/viewer/widgets/__init__.py create mode 100644 Wrappers/Python/ccpi/viewer/widgets/box_widgets.py diff --git a/.gitignore b/.gitignore index 2fcd2499..7bd70ee6 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ Wrappers/Python/ccpi/web_viewer/data/* # Hide PyCharm or other Jetbrains IDE files .idea +Wrappers/Python/build/* + diff --git a/Wrappers/Python/build/lib/ccpi/viewer/widgets.py b/Wrappers/Python/build/lib/ccpi/viewer/widgets.py deleted file mode 100644 index 9705d70f..00000000 --- a/Wrappers/Python/build/lib/ccpi/viewer/widgets.py +++ /dev/null @@ -1,109 +0,0 @@ -import vtk - -def CreateViewerSliceBorder(viewer, orientation='horizontal', coord=0, color=(1., 0., 0.), width=1, widget_name='slice_outline_widget', ): - ''' Creates a border in [viewer], around a slice with number [coord] in - the [orientation] plane. - This appears as an overlay, so appears on every projection. - E.g. if orientation = 'vertical', coord = 5, width=1 then it creates a - border around the voxels with vertical value of 5. - width controls the width of the box, but it always starts at [coord]. - E.g. if orientation = 'vertical', coord = 5, width = 5 then it creates a - border around the voxels with vertical values of 5-9. - - Parameters - ---------- - viewer: viewer2D or viewer3D - A viewer upon which the border will be displayed - orientation: {'horizontal', 'vertical'} - Which axis the slice is on - coord: int - The coordinate of the slice - color: tuple - colour of the border - width: float - The width of the border. The border starts from the bound of the - slice with number coord. - widget_name: str - The name to associate the border widget with in the viewer. - - ''' - extent = viewer.img3D.GetExtent() - spac = viewer.img3D.GetSpacing() - - if orientation == "vertical": - x_length = extent[1] - pos1 = [0, coord] - pos2 = [x_length, coord+width] - else: - y_length = extent[3] - pos1 = [coord, y_length] - pos2 = [coord + width, 0] - - - CreateViewerOverlayBorder(viewer, pos1, pos2, color, widget_name) - - - -def CreateViewerOverlayBorder(viewer, pos1, pos2, - color=(1., 0., 0.), widget_name='box_widget'): - ''' - Creates a border in [viewer], with the two alternate corner - positions at [pos1], [pos2] - This appears as an overlay, so appears on every projection. - - Parameters - ---------- - viewer: viewer2D or viewer3D - A viewer upon which the border will be displayed - pos1: tuple - coordinates of lower left corner of the border widget (in image coordinates) - pos2: tuple - coordinates of top right corner of the border widget (in image coordinates) - color: tuple - colour of the border - widget_name: str - The name to associate the border widget with in the viewer. - ''' - - if hasattr(viewer, 'widgets'): - box_widget = widget_name in viewer.widgets - else: - viewer.widgets={} - box_widget = False - - if not box_widget: - borderWidget = vtk.vtkBorderWidget(); - representation = vtk.vtkBorderRepresentation() - borderWidget.SetRepresentation(representation) - #borderWidget.CreateDefaultRepresentation() - borderWidget.SetResizable(False) - borderWidget.SetProcessEvents(False) - borderWidget.SelectableOff() - borderWidget.SetInteractor(viewer.iren) - viewer.widgets[widget_name] = borderWidget - - else: - borderWidget = viewer.widgets[widget_name] - - # coord conversion - # pos1 = list(pos1) - # pos2 = list(pos2) - # pos1.append(0) - # pos2.append(0) - # print("POS 1") - # print(pos1) - - # pos1 = viewer.style.image2world(pos1)[:2] - # print("POS 2") - # print(pos2) - # pos2 = viewer.style.image2world(pos2)[:2] - - # borderWidget.GetBorderRepresentation().SetPosition((0.05,0.05)) - # borderWidget.GetBorderRepresentation().SetPosition2((1,1)) - # borderWidget.GetBorderRepresentation().GetBorderProperty().SetColor(color) - # print( borderWidget.GetBorderRepresentation().GetPosition() , borderWidget.GetBorderRepresentation().GetPosition2()) - # print( borderWidget.GetBorderRepresentation().GetPosition() , borderWidget.GetBorderRepresentation().GetPosition2()) - print(borderWidget.GetBorderRepresentation().GetBounds()) - borderWidget.On() - - viewer.style.UpdatePipeline() \ No newline at end of file diff --git a/Wrappers/Python/ccpi/viewer/CILViewer2D.py b/Wrappers/Python/ccpi/viewer/CILViewer2D.py index 998ba74e..d7c3caa7 100644 --- a/Wrappers/Python/ccpi/viewer/CILViewer2D.py +++ b/Wrappers/Python/ccpi/viewer/CILViewer2D.py @@ -22,6 +22,8 @@ from ccpi.viewer.CILViewerBase import CILViewerBase from ccpi.viewer.utils import Converter +from ccpi.viewer.widgets.box_widgets import CreateMoveableBoxWidget, get_box_bounds_from_event_position + class CILInteractorStyle(vtk.vtkInteractorStyle): @@ -224,106 +226,13 @@ def SetCharEvent(self, char): def validateValue(self, value, axis): return self._viewer.validateValue(value, axis) - def _truncateBox(self, start_pos, world_max_array, axis): - """ - Make sure that the value for the upper corner of the box is within the world extent. - - :param start_pos: Lower left corner value on specified axis - :param world_max_array: Array containing (x,y,z) of the maximum extent of the world - :param axis: The axis of interest eg. "x" - :return: The start position + a percentage of the world truncated to the edges of the world - """ - - # Set up scale factor and get index for axis - scale_factor = 0.3 - axis_dict = {"x": 0, "y": 1, "z": 2} - axis_int = axis_dict[axis] - - # Create the upper right coordinate point with scale offset - value = start_pos + world_max_array[axis_int] * scale_factor - - # Check to make sure that it is within the image world. - if value > world_max_array[axis_int]: - return world_max_array[axis_int] - else: - return value - def InitialiseBox(self, clickPosition): """ Set the initial values for the box borders :param clickPosition: Display coordinates for the mouse event """ - # Current render orientation - orientation = self.GetSliceOrientation() - - # Scale factor for initial box - scale_factor = 0.3 - - # Translate the mouse click display coordinates into world coordinates - coord = vtk.vtkCoordinate() - coord.SetCoordinateSystemToDisplay() - coord.SetValue(clickPosition[0], clickPosition[1]) - world_mouse_pos = coord.GetComputedWorldValue(self.GetRenderer()) - - # Get maximum extents of the image in world coords - world_image_max = self.GetImageWorldExtent() - - # Set the minimum world value - world_image_min = (0, 0, 0) - - # Initialise the box position in format [xmin, xmax, ymin, ymax,...] - box_pos = [0, 0, 0, 0, 0, 0] - - # place the mouse click as bottom left in current orientation - if orientation == 2: - # Looking along z - # Lower left is xmin, ymin - box_pos[0] = world_mouse_pos[0] - box_pos[2] = world_mouse_pos[1] - - # Set top right point - # Top right is xmax, ymax - box_pos[1] = self._truncateBox(box_pos[0], world_image_max, "x") - box_pos[3] = self._truncateBox(box_pos[2], world_image_max, "y") - - # Set the scroll axis to maximum extent eg. min-max - # zmin, zmax - box_pos[4] = world_image_min[2] - box_pos[5] = world_image_max[2] - - elif orientation == 1: - # Looking along y - # Lower left is xmin, zmin - box_pos[0] = world_mouse_pos[0] - box_pos[4] = world_mouse_pos[2] - - # Set top right point. - # Top right is xmax, zmax - box_pos[1] = self._truncateBox(box_pos[0], world_image_max, "x") - box_pos[5] = self._truncateBox(box_pos[4], world_image_max, "z") - - # Set the scroll axis to maximum extent eg. min-max - # ymin, ymax - box_pos[2] = world_image_min[1] - box_pos[3] = world_image_max[1] - - else: - # orientation == 0 - # Looking along x - # Lower left is ymin, zmin - box_pos[2] = world_mouse_pos[1] - box_pos[4] = world_mouse_pos[2] - - # Set top right point - # Top right is ymax, zmax - box_pos[3] = self._truncateBox(box_pos[2], world_image_max, "y") - box_pos[5] = self._truncateBox(box_pos[4], world_image_max, "z") - - # Set the scroll axis to maximum extent eg. min-max - # xmin, xmax - box_pos[0] = world_image_min[0] - box_pos[1] = world_image_max[0] + box_pos = get_box_bounds_from_event_position(self._viewer, clickPosition) # Set widget placement and make visible self._viewer.ROIWidget.PlaceWidget(box_pos) @@ -1118,15 +1027,7 @@ def __init__(self, dimx=600, dimy=600, ren=None, renWin=None, iren=None, debug=T self.AddActor(self.helpActor, HELP_ACTOR) # ROI Widget - self.ROIWidget = vtk.vtkBoxWidget() - self.ROIWidget.SetInteractor(self.iren) - self.ROIWidget.HandlesOn() - self.ROIWidget.TranslationEnabledOn() - self.ROIWidget.RotationEnabledOff() - self.ROIWidget.GetOutlineProperty().SetColor(0, 1, 0) - self.ROIWidget.OutlineCursorWiresOff() - self.ROIWidget.SetPlaceFactor(1) - self.ROIWidget.KeyPressActivationOff() + self.ROIWidget = CreateMoveableBoxWidget(self) self.ROIWidget.AddObserver(vtk.vtkWidgetEvent.Select, self.style.OnROIModifiedEvent, 1.0) diff --git a/Wrappers/Python/ccpi/viewer/CILViewerBase.py b/Wrappers/Python/ccpi/viewer/CILViewerBase.py index 8ea5d03f..91677397 100644 --- a/Wrappers/Python/ccpi/viewer/CILViewerBase.py +++ b/Wrappers/Python/ccpi/viewer/CILViewerBase.py @@ -107,8 +107,9 @@ def __init__(self, dimx=600, dimy=600, renWin=None, iren=None, ren=None, debug=F ori.InteractiveOff() self.orientation_marker = ori - # holder for list of actors + # holder for list of actors and widgets self.actors = {} + self.widgets = {} #initial Window/Level self.InitialLevel = 0 @@ -297,3 +298,10 @@ def validateValue(self, value, axis): def setInput3DData(self, imageData): raise NotImplementedError("Implemented in the subclasses.") + + def addWidgetReference(self, widget, name): + '''Adds widget to dictionary of widgets''' + self.widgets[name] = widget + + def getWidget(self, name): + return self.widgets.get(name) diff --git a/Wrappers/Python/ccpi/viewer/widgets/__init__.py b/Wrappers/Python/ccpi/viewer/widgets/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py new file mode 100644 index 00000000..9f03b2c1 --- /dev/null +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -0,0 +1,242 @@ +import vtk +from ccpi.viewer import SLICE_ORIENTATION_XY, SLICE_ORIENTATION_XZ, SLICE_ORIENTATION_YZ + + +def CreateFixedBoxWidget(viewer, outline_colour=(0,1,0)): + ''' + Creates a vtkBoxWidget which the user can't move + viewer + The viewer that the box widget will later be working on. + The interactor for the widget will be set to this viewer's + interactor. + outline_color + The color of the outline of the box widget + ''' + widget = vtk.vtkBoxWidget() + widget.SetInteractor(viewer.iren) + widget.HandlesOff() + widget.TranslationEnabledOff() + widget.RotationEnabledOff() + widget.GetOutlineProperty().SetColor(outline_colour) + widget.OutlineCursorWiresOff() + widget.SetPlaceFactor(1) + widget.KeyPressActivationOff() + return widget + +def CreateMoveableBoxWidget(viewer, outline_colour=(0,1,0)): + ''' + Creates a vtkBoxWidget which the user can move + viewer + The viewer that the box widget will later be working on. + The interactor for the widget will be set to this viewer's + interactor. + outline_color + The color of the outline of the box widget + ''' + widget = vtk.vtkBoxWidget() + widget.SetInteractor(viewer.iren) + widget.HandlesOn() + widget.TranslationEnabledOn() + widget.RotationEnabledOff() + widget.GetOutlineProperty().SetColor(outline_colour) + widget.OutlineCursorWiresOff() + widget.SetPlaceFactor(1) + widget.KeyPressActivationOff() + return widget + +def CreateBoxWidgetAroundSlice(viewer, orientation='horizontal', + coord=0, outline_color=(1., 0., 0.), width=1, widget_name='slice_outline_widget'): + ''' + Creates a border in [viewer], around a slice with number [coord] in + the [orientation] plane. This border is a box widget. + This appears as an overlay, so appears on every projection. + E.g. if orientation = 'vertical', coord = 5, width=1 then it creates a + border around the voxels with vertical value of 5. + width controls the width of the box, but it always starts at [coord]. + E.g. if orientation = 'vertical', coord = 5, width = 5 then it creates a + border around the voxels with vertical values of 5-9. + + Note, setting width=0 creates a line rather than a box + + Parameters + ---------- + viewer: viewer2D or viewer3D + A viewer upon which the border will be displayed + orientation: {'horizontal', 'vertical'} + Which axis the slice is on + coord: int + The coordinate of the slice + outline_color: tuple + colour of the border + width: float + The width of the border. The border starts from the bound of the + slice with number coord. + widget_name: str + The name to associate the border widget with in the viewer. + ''' + + widget = CreateFixedBoxWidget(viewer, outline_color) + + # Only makes sense to do this on the Z axis: + render_orientation = SLICE_ORIENTATION_XY + try: + viewer.setSliceOrientation(render_orientation) + except: # method doesn't exist on 3D viewer + pass + + # Get maximum extents of the image in world coords + world_image_max = viewer.style.GetImageWorldExtent() + + # Set the minimum world value + world_image_min = (0,0,0) + + z_coords = [world_image_min[render_orientation], world_image_max[render_orientation]] + + if orientation == 'horizontal': + # Displaying slice at fixed coord in X direction: + x_coords = [coord, coord+width] + y_coords = [world_image_min[1], world_image_max[1]] + + else: + # Displaying slice at fixed coord in Y direction: + y_coords = [coord, coord+width] + x_coords = [world_image_min[0], world_image_max[0]] + + coords = x_coords + y_coords + z_coords + + widget.PlaceWidget(coords) + viewer.addWidgetReference(widget, widget_name) + + return widget + + +def CreateMoveableBoxWidgetAtEventPosition(viewer, position, widget_name, outline_colour=(0,1,0)): + ''' + Place a moveable box widget on the viewer at the event position. + Parameters + ---------- + viewer + The 2D viewer that the box will be displayed on + position + The event position + widget_name + The name the reference to the widget will be stored as + outline_color + The outline color of the box widget + ''' + # ROI Widget + widget = CreateMoveableBoxWidget(viewer, outline_colour) + coords = get_box_bounds_from_event_position(viewer, widget, position) + # Set widget placement and make visible + widget.PlaceWidget(coords) + widget.On() + viewer.addWidgetReference(widget, widget_name) + return widget + + +def get_box_bounds_from_event_position(viewer, position, scale_factor=0.3): + ''' + Get the coordinates for the bounds of a box from the event position + Parameters + ---------- + viewer + The 2D viewer that the box will be displayed on + position + The event position + scale_factor + Factor for scaling the size of the box + ''' + # Current render orientation + orientation = viewer.style.GetSliceOrientation() + + # Translate the mouse click display coordinates into world coordinates + coord = vtk.vtkCoordinate() + coord.SetCoordinateSystemToDisplay() + coord.SetValue(position[0], position[1]) + world_mouse_pos = coord.GetComputedWorldValue(viewer.style.GetRenderer()) + + # Get maximum extents of the image in world coords + world_image_max = viewer.style.GetImageWorldExtent() + + # Set the minimum world value + world_image_min = (0,0,0) + + # Initialise the box position in format [xmin, xmax, ymin, ymax,...] + box_pos = [0, 0, 0, 0, 0, 0] + + # place the mouse click as bottom left in current orientation + if orientation == SLICE_ORIENTATION_XY: + # Looking along z + # Lower left is xmin, ymin + box_pos[0] = world_mouse_pos[0] + box_pos[2] = world_mouse_pos[1] + + # Set top right point + # Top right is xmax, ymax + box_pos[1] = truncate_box(box_pos[0], world_image_max, "x", scale_factor) + box_pos[3] = truncate_box(box_pos[2], world_image_max, "y", scale_factor) + + # Set the scroll axis to maximum extent eg. min-max + # zmin, zmax + box_pos[4] = world_image_min[orientation] + box_pos[5] = world_image_max[orientation] + + elif orientation == SLICE_ORIENTATION_XZ: + # Looking along y + # Lower left is xmin, zmin + box_pos[0] = world_mouse_pos[0] + box_pos[4] = world_mouse_pos[2] + + # Set top right point. + # Top right is xmax, zmax + box_pos[1] = truncate_box(box_pos[0], world_image_max, "x") + box_pos[5] = truncate_box(box_pos[4], world_image_max, "z") + + # Set the scroll axis to maximum extent eg. min-max + # ymin, ymax + box_pos[2] = world_image_min[orientation] + box_pos[3] = world_image_max[orientation] + + else: + # orientation == 0 + # Looking along x + # Lower left is ymin, zmin + box_pos[2] = world_mouse_pos[1] + box_pos[4] = world_mouse_pos[2] + + # Set top right point + # Top right is ymax, zmax + box_pos[3] = truncate_box(box_pos[2], world_image_max,"y") + box_pos[5] = truncate_box(box_pos[4], world_image_max, "z") + + # Set the scroll axis to maximum extent eg. min-max + # xmin, xmax + box_pos[0] = world_image_min[orientation] + box_pos[1] = world_image_max[orientation] + + return box_pos + + +def truncate_box(start_pos, world_max_array, axis, scale_factor=0.3): + """ + Make sure that the value for the upper corner of the box is within the world extent. + + :param start_pos: Lower left corner value on specified axis + :param world_max_array: Array containing (x,y,z) of the maximum extent of the world + :param axis: The axis of interest eg. "x" + :param scale_factor: + :return: The start position + a percentage of the world truncated to the edges of the world + """ + + # get index for axis + axis_dict = {"x": SLICE_ORIENTATION_YZ, "y": SLICE_ORIENTATION_XZ, "z": SLICE_ORIENTATION_XY} + axis_int = axis_dict[axis] + + # Create the upper right coordinate point with scale offset + value = start_pos + world_max_array[axis_int] * scale_factor + + # Check to make sure that it is within the image world. + if value > world_max_array[axis_int]: + return world_max_array[axis_int] + else: + return value \ No newline at end of file diff --git a/Wrappers/Python/examples/SliceBorderWidget.py b/Wrappers/Python/examples/SliceBorderWidget.py index 84d8fa47..5ce7f038 100644 --- a/Wrappers/Python/examples/SliceBorderWidget.py +++ b/Wrappers/Python/examples/SliceBorderWidget.py @@ -1,35 +1,18 @@ import sys -import vtk -from PySide2 import QtCore, QtWidgets +from PySide2 import QtWidgets from ccpi.viewer import viewer2D, viewer3D -from ccpi.viewer.QCILViewerWidget import QCILViewerWidget -from ccpi.viewer.utils import example_data from SingleViewerCentralWidget import SingleViewerCenterWidget -from ccpi.viewer.widgets import CreateViewerSliceBorder +from ccpi.viewer.widgets.box_widgets import CreateBoxWidgetAroundSlice -class SingleViewerCenterWidget(QtWidgets.QMainWindow): +app = QtWidgets.QApplication(sys.argv) +# can change the behaviour by setting which viewer you want +# between viewer2D and viewer3D +window = SingleViewerCenterWidget(viewer=viewer2D) +line_widget = CreateBoxWidgetAroundSlice(window.frame.viewer, 'horizontal', 5, width=0) +line_widget.On() +box_widget = CreateBoxWidgetAroundSlice(window.frame.viewer, 'vertical', 10, width=1, outline_color=(0,0,1)) +box_widget.On() +window.frame.viewer.updatePipeline() - def __init__(self, parent=None, viewer=viewer2D): - QtWidgets.QMainWindow.__init__(self, parent) - - self.frame = QCILViewerWidget(viewer=viewer, shape=(600, 600)) - - head = example_data.HEAD.get() - - self.frame.viewer.setInputData(head) - - self.setCentralWidget(self.frame) - - self.show() - - -if __name__ == "__main__": - - app = QtWidgets.QApplication(sys.argv) - # can change the behaviour by setting which viewer you want - # between viewer2D and viewer3D - window = SingleViewerCenterWidget(viewer=viewer2D) - CreateViewerSliceBorder(window.frame.viewer, 'horizontal', 5) - - sys.exit(app.exec_()) +sys.exit(app.exec_()) diff --git a/Wrappers/Python/setup.py b/Wrappers/Python/setup.py index bf51bf83..d7a74b48 100644 --- a/Wrappers/Python/setup.py +++ b/Wrappers/Python/setup.py @@ -15,7 +15,6 @@ from setuptools import setup import os -import sys import subprocess @@ -59,7 +58,7 @@ def version2pep440(version): setup( name="ccpi-viewer", version=version, - packages=['ccpi', 'ccpi.viewer', 'ccpi.viewer.utils', 'ccpi.web_viewer'], + packages=['ccpi', 'ccpi.viewer', 'ccpi.viewer.utils', 'ccpi.web_viewer', 'ccpi.viewer.widgets'], install_requires=requires, zip_safe=False, # metadata for upload to PyPI From 830fff9ccbda4b487d7fc4c1c7ba49cc94c78442 Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 27 Jul 2022 11:23:09 +0100 Subject: [PATCH 03/33] test preveting selection --- Wrappers/Python/ccpi/viewer/widgets/box_widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 9f03b2c1..5e3cb256 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -21,6 +21,7 @@ def CreateFixedBoxWidget(viewer, outline_colour=(0,1,0)): widget.OutlineCursorWiresOff() widget.SetPlaceFactor(1) widget.KeyPressActivationOff() + widget.ScalingEnabledOff() return widget def CreateMoveableBoxWidget(viewer, outline_colour=(0,1,0)): From 63dc5718942c377277b6f41c87f01800393054c1 Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 27 Jul 2022 11:44:05 +0100 Subject: [PATCH 04/33] rename --- .../examples/{SliceBorderWidget.py => BoxWidgetAroundSlice.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Wrappers/Python/examples/{SliceBorderWidget.py => BoxWidgetAroundSlice.py} (100%) diff --git a/Wrappers/Python/examples/SliceBorderWidget.py b/Wrappers/Python/examples/BoxWidgetAroundSlice.py similarity index 100% rename from Wrappers/Python/examples/SliceBorderWidget.py rename to Wrappers/Python/examples/BoxWidgetAroundSlice.py From 3d1df1a42700de430aad35af9a101e3f538cff5c Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 27 Jul 2022 10:46:07 +0000 Subject: [PATCH 05/33] Automated autoyapf fixes --- .../Python/ccpi/viewer/widgets/box_widgets.py | 30 +++++++++++-------- .../Python/examples/BoxWidgetAroundSlice.py | 3 +- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 5e3cb256..7581c100 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -2,7 +2,7 @@ from ccpi.viewer import SLICE_ORIENTATION_XY, SLICE_ORIENTATION_XZ, SLICE_ORIENTATION_YZ -def CreateFixedBoxWidget(viewer, outline_colour=(0,1,0)): +def CreateFixedBoxWidget(viewer, outline_colour=(0, 1, 0)): ''' Creates a vtkBoxWidget which the user can't move viewer @@ -24,7 +24,8 @@ def CreateFixedBoxWidget(viewer, outline_colour=(0,1,0)): widget.ScalingEnabledOff() return widget -def CreateMoveableBoxWidget(viewer, outline_colour=(0,1,0)): + +def CreateMoveableBoxWidget(viewer, outline_colour=(0, 1, 0)): ''' Creates a vtkBoxWidget which the user can move viewer @@ -45,8 +46,13 @@ def CreateMoveableBoxWidget(viewer, outline_colour=(0,1,0)): widget.KeyPressActivationOff() return widget -def CreateBoxWidgetAroundSlice(viewer, orientation='horizontal', - coord=0, outline_color=(1., 0., 0.), width=1, widget_name='slice_outline_widget'): + +def CreateBoxWidgetAroundSlice(viewer, + orientation='horizontal', + coord=0, + outline_color=(1., 0., 0.), + width=1, + widget_name='slice_outline_widget'): ''' Creates a border in [viewer], around a slice with number [coord] in the [orientation] plane. This border is a box widget. @@ -82,25 +88,25 @@ def CreateBoxWidgetAroundSlice(viewer, orientation='horizontal', render_orientation = SLICE_ORIENTATION_XY try: viewer.setSliceOrientation(render_orientation) - except: # method doesn't exist on 3D viewer + except: # method doesn't exist on 3D viewer pass # Get maximum extents of the image in world coords world_image_max = viewer.style.GetImageWorldExtent() # Set the minimum world value - world_image_min = (0,0,0) + world_image_min = (0, 0, 0) z_coords = [world_image_min[render_orientation], world_image_max[render_orientation]] if orientation == 'horizontal': # Displaying slice at fixed coord in X direction: - x_coords = [coord, coord+width] + x_coords = [coord, coord + width] y_coords = [world_image_min[1], world_image_max[1]] else: # Displaying slice at fixed coord in Y direction: - y_coords = [coord, coord+width] + y_coords = [coord, coord + width] x_coords = [world_image_min[0], world_image_max[0]] coords = x_coords + y_coords + z_coords @@ -111,7 +117,7 @@ def CreateBoxWidgetAroundSlice(viewer, orientation='horizontal', return widget -def CreateMoveableBoxWidgetAtEventPosition(viewer, position, widget_name, outline_colour=(0,1,0)): +def CreateMoveableBoxWidgetAtEventPosition(viewer, position, widget_name, outline_colour=(0, 1, 0)): ''' Place a moveable box widget on the viewer at the event position. Parameters @@ -160,7 +166,7 @@ def get_box_bounds_from_event_position(viewer, position, scale_factor=0.3): world_image_max = viewer.style.GetImageWorldExtent() # Set the minimum world value - world_image_min = (0,0,0) + world_image_min = (0, 0, 0) # Initialise the box position in format [xmin, xmax, ymin, ymax,...] box_pos = [0, 0, 0, 0, 0, 0] @@ -207,7 +213,7 @@ def get_box_bounds_from_event_position(viewer, position, scale_factor=0.3): # Set top right point # Top right is ymax, zmax - box_pos[3] = truncate_box(box_pos[2], world_image_max,"y") + box_pos[3] = truncate_box(box_pos[2], world_image_max, "y") box_pos[5] = truncate_box(box_pos[4], world_image_max, "z") # Set the scroll axis to maximum extent eg. min-max @@ -240,4 +246,4 @@ def truncate_box(start_pos, world_max_array, axis, scale_factor=0.3): if value > world_max_array[axis_int]: return world_max_array[axis_int] else: - return value \ No newline at end of file + return value diff --git a/Wrappers/Python/examples/BoxWidgetAroundSlice.py b/Wrappers/Python/examples/BoxWidgetAroundSlice.py index 5ce7f038..dbf91bee 100644 --- a/Wrappers/Python/examples/BoxWidgetAroundSlice.py +++ b/Wrappers/Python/examples/BoxWidgetAroundSlice.py @@ -4,14 +4,13 @@ from SingleViewerCentralWidget import SingleViewerCenterWidget from ccpi.viewer.widgets.box_widgets import CreateBoxWidgetAroundSlice - app = QtWidgets.QApplication(sys.argv) # can change the behaviour by setting which viewer you want # between viewer2D and viewer3D window = SingleViewerCenterWidget(viewer=viewer2D) line_widget = CreateBoxWidgetAroundSlice(window.frame.viewer, 'horizontal', 5, width=0) line_widget.On() -box_widget = CreateBoxWidgetAroundSlice(window.frame.viewer, 'vertical', 10, width=1, outline_color=(0,0,1)) +box_widget = CreateBoxWidgetAroundSlice(window.frame.viewer, 'vertical', 10, width=1, outline_color=(0, 0, 1)) box_widget.On() window.frame.viewer.updatePipeline() From 10ffdfb46a35ea2ebda3212905dee8298983e708 Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 27 Jul 2022 15:49:18 +0100 Subject: [PATCH 06/33] Add separate method for getting coordinates for box widget around slice --- .../Python/ccpi/viewer/widgets/box_widgets.py | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 5e3cb256..131f70b0 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -66,7 +66,7 @@ def CreateBoxWidgetAroundSlice(viewer, orientation='horizontal', orientation: {'horizontal', 'vertical'} Which axis the slice is on coord: int - The coordinate of the slice + The coordinate of the slice in world coords outline_color: tuple colour of the border width: float @@ -77,6 +77,30 @@ def CreateBoxWidgetAroundSlice(viewer, orientation='horizontal', ''' widget = CreateFixedBoxWidget(viewer, outline_color) + coords = get_coords_for_box_widget_around_slice(viewer, orientation, coord, width) + widget.PlaceWidget(coords) + viewer.addWidgetReference(widget, widget_name) + return widget + + +def get_coords_for_box_widget_around_slice(viewer, orientation='horizontal', + coord=0, width=1): + ''' + Generate coordinates for positioning BoxWidget around slice + + Parameters + ---------- + viewer: viewer2D or viewer3D + A viewer upon which the border will be displayed + orientation: {'horizontal', 'vertical'} + Which axis the slice is on + coord: int + The coordinate of the slice in world coords + width: float + The width of the border. The border starts from the bound of the + slice with number coord. + + ''' # Only makes sense to do this on the Z axis: render_orientation = SLICE_ORIENTATION_XY @@ -85,6 +109,8 @@ def CreateBoxWidgetAroundSlice(viewer, orientation='horizontal', except: # method doesn't exist on 3D viewer pass + spacing = viewer.img3D.GetSpacing() + # Get maximum extents of the image in world coords world_image_max = viewer.style.GetImageWorldExtent() @@ -95,23 +121,20 @@ def CreateBoxWidgetAroundSlice(viewer, orientation='horizontal', if orientation == 'horizontal': # Displaying slice at fixed coord in X direction: - x_coords = [coord, coord+width] + x_coords = [coord, coord+width*spacing[0]] y_coords = [world_image_min[1], world_image_max[1]] else: # Displaying slice at fixed coord in Y direction: - y_coords = [coord, coord+width] + y_coords = [coord, coord+width*spacing[1]] x_coords = [world_image_min[0], world_image_max[0]] coords = x_coords + y_coords + z_coords - widget.PlaceWidget(coords) - viewer.addWidgetReference(widget, widget_name) - - return widget + return coords -def CreateMoveableBoxWidgetAtEventPosition(viewer, position, widget_name, outline_colour=(0,1,0)): +def CreateMoveableBoxWidgetAtEventPosition(viewer, position, widget_name, outline_colour=(0,1,0), scale_factor=0.3): ''' Place a moveable box widget on the viewer at the event position. Parameters @@ -124,10 +147,12 @@ def CreateMoveableBoxWidgetAtEventPosition(viewer, position, widget_name, outlin The name the reference to the widget will be stored as outline_color The outline color of the box widget + scale_factor + Factor for scaling the size of the box ''' # ROI Widget widget = CreateMoveableBoxWidget(viewer, outline_colour) - coords = get_box_bounds_from_event_position(viewer, widget, position) + coords = get_box_bounds_from_event_position(viewer, position, scale_factor) # Set widget placement and make visible widget.PlaceWidget(coords) widget.On() From 20f0fc0cfdea8ef9a1b133f89c0a4b5e814eb3ba Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 27 Jul 2022 14:51:15 +0000 Subject: [PATCH 07/33] Automated autoyapf fixes --- Wrappers/Python/ccpi/viewer/widgets/box_widgets.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 6717e5e5..81a23827 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -89,8 +89,7 @@ def CreateBoxWidgetAroundSlice(viewer, return widget -def get_coords_for_box_widget_around_slice(viewer, orientation='horizontal', - coord=0, width=1): +def get_coords_for_box_widget_around_slice(viewer, orientation='horizontal', coord=0, width=1): ''' Generate coordinates for positioning BoxWidget around slice @@ -127,12 +126,12 @@ def get_coords_for_box_widget_around_slice(viewer, orientation='horizontal', if orientation == 'horizontal': # Displaying slice at fixed coord in X direction: - x_coords = [coord, coord+width*spacing[0]] + x_coords = [coord, coord + width * spacing[0]] y_coords = [world_image_min[1], world_image_max[1]] else: # Displaying slice at fixed coord in Y direction: - y_coords = [coord, coord+width*spacing[1]] + y_coords = [coord, coord + width * spacing[1]] x_coords = [world_image_min[0], world_image_max[0]] coords = x_coords + y_coords + z_coords @@ -140,7 +139,7 @@ def get_coords_for_box_widget_around_slice(viewer, orientation='horizontal', return coords -def CreateMoveableBoxWidgetAtEventPosition(viewer, position, widget_name, outline_colour=(0,1,0), scale_factor=0.3): +def CreateMoveableBoxWidgetAtEventPosition(viewer, position, widget_name, outline_colour=(0, 1, 0), scale_factor=0.3): ''' Place a moveable box widget on the viewer at the event position. Parameters From ac09b3afa396f4c6eb45a66d9057b6d25d0763f5 Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 3 Aug 2022 14:21:24 +0100 Subject: [PATCH 08/33] Restructure into classes for box and line widgets --- Wrappers/Python/ccpi/viewer/CILViewer2D.py | 6 +- .../Python/ccpi/viewer/widgets/box_widgets.py | 598 ++++++++++-------- .../Python/examples/BoxWidgetAroundSlice.py | 6 +- .../examples/SingleViewerCentralWidget.py | 2 +- 4 files changed, 337 insertions(+), 275 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/CILViewer2D.py b/Wrappers/Python/ccpi/viewer/CILViewer2D.py index 8b42bb5c..04ba2c0e 100644 --- a/Wrappers/Python/ccpi/viewer/CILViewer2D.py +++ b/Wrappers/Python/ccpi/viewer/CILViewer2D.py @@ -22,7 +22,7 @@ from ccpi.viewer.CILViewerBase import CILViewerBase from ccpi.viewer.utils import Converter -from ccpi.viewer.widgets.box_widgets import CreateMoveableBoxWidget, get_box_bounds_from_event_position +from ccpi.viewer.widgets.box_widgets import cilviewerBoxWidget class CILInteractorStyle(vtk.vtkInteractorStyle): @@ -234,7 +234,7 @@ def InitialiseBox(self, clickPosition): :param clickPosition: Display coordinates for the mouse event """ - box_pos = get_box_bounds_from_event_position(self._viewer, clickPosition) + box_pos = cilviewerBoxWidget.GetBoxBoundsFromEventPosition(self._viewer, clickPosition) # Set widget placement and make visible self._viewer.ROIWidget.PlaceWidget(box_pos) @@ -1022,7 +1022,7 @@ def __init__(self, dimx=600, dimy=600, ren=None, renWin=None, iren=None, debug=T self.AddActor(self.helpActor, HELP_ACTOR) # ROI Widget - self.ROIWidget = CreateMoveableBoxWidget(self) + self.ROIWidget = cilviewerBoxWidget.Moveable(self) self.ROIWidget.AddObserver(vtk.vtkWidgetEvent.Select, self.style.OnROIModifiedEvent, 1.0) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 81a23827..9f771670 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -2,272 +2,334 @@ from ccpi.viewer import SLICE_ORIENTATION_XY, SLICE_ORIENTATION_XZ, SLICE_ORIENTATION_YZ -def CreateFixedBoxWidget(viewer, outline_colour=(0, 1, 0)): - ''' - Creates a vtkBoxWidget which the user can't move - viewer - The viewer that the box widget will later be working on. - The interactor for the widget will be set to this viewer's - interactor. - outline_color - The color of the outline of the box widget - ''' - widget = vtk.vtkBoxWidget() - widget.SetInteractor(viewer.iren) - widget.HandlesOff() - widget.TranslationEnabledOff() - widget.RotationEnabledOff() - widget.GetOutlineProperty().SetColor(outline_colour) - widget.OutlineCursorWiresOff() - widget.SetPlaceFactor(1) - widget.KeyPressActivationOff() - widget.ScalingEnabledOff() - return widget - - -def CreateMoveableBoxWidget(viewer, outline_colour=(0, 1, 0)): - ''' - Creates a vtkBoxWidget which the user can move - viewer - The viewer that the box widget will later be working on. - The interactor for the widget will be set to this viewer's - interactor. - outline_color - The color of the outline of the box widget - ''' - widget = vtk.vtkBoxWidget() - widget.SetInteractor(viewer.iren) - widget.HandlesOn() - widget.TranslationEnabledOn() - widget.RotationEnabledOff() - widget.GetOutlineProperty().SetColor(outline_colour) - widget.OutlineCursorWiresOff() - widget.SetPlaceFactor(1) - widget.KeyPressActivationOff() - return widget - - -def CreateBoxWidgetAroundSlice(viewer, - orientation='horizontal', - coord=0, - outline_color=(1., 0., 0.), - width=1, - widget_name='slice_outline_widget'): - ''' - Creates a border in [viewer], around a slice with number [coord] in - the [orientation] plane. This border is a box widget. - This appears as an overlay, so appears on every projection. - E.g. if orientation = 'vertical', coord = 5, width=1 then it creates a - border around the voxels with vertical value of 5. - width controls the width of the box, but it always starts at [coord]. - E.g. if orientation = 'vertical', coord = 5, width = 5 then it creates a - border around the voxels with vertical values of 5-9. - - Note, setting width=0 creates a line rather than a box - - Parameters - ---------- - viewer: viewer2D or viewer3D - A viewer upon which the border will be displayed - orientation: {'horizontal', 'vertical'} - Which axis the slice is on - coord: int - The coordinate of the slice in world coords - outline_color: tuple - colour of the border - width: float - The width of the border. The border starts from the bound of the - slice with number coord. - widget_name: str - The name to associate the border widget with in the viewer. - ''' - - widget = CreateFixedBoxWidget(viewer, outline_color) - coords = get_coords_for_box_widget_around_slice(viewer, orientation, coord, width) - widget.PlaceWidget(coords) - viewer.addWidgetReference(widget, widget_name) - return widget - - -def get_coords_for_box_widget_around_slice(viewer, orientation='horizontal', coord=0, width=1): - ''' - Generate coordinates for positioning BoxWidget around slice - - Parameters - ---------- - viewer: viewer2D or viewer3D - A viewer upon which the border will be displayed - orientation: {'horizontal', 'vertical'} - Which axis the slice is on - coord: int - The coordinate of the slice in world coords - width: float - The width of the border. The border starts from the bound of the - slice with number coord. +class cilviewerBoxWidget(): + + @staticmethod + def Fixed(viewer, outline_colour=(0, 1, 0)): + ''' + Creates a vtkBoxWidget which the user can't move + + Parameters + ---------- + viewer + The viewer that the box widget will later be working on. + The interactor for the widget will be set to this viewer's + interactor. + outline_color + The color of the outline of the box widget + + Returns + ------- + widget: vtk.vtkBoxWidget + ''' + widget = vtk.vtkBoxWidget() + widget.SetInteractor(viewer.iren) + widget.HandlesOff() + widget.TranslationEnabledOff() + widget.RotationEnabledOff() + widget.GetOutlineProperty().SetColor(outline_colour) + widget.OutlineCursorWiresOff() + widget.SetPlaceFactor(1) + widget.KeyPressActivationOff() + widget.ScalingEnabledOff() + return widget + + @staticmethod + def Moveable(viewer, outline_colour=(0, 1, 0)): + ''' + Creates a vtkBoxWidget which the user can move + + Parameters + ---------- + viewer + The viewer that the box widget will later be working on. + The interactor for the widget will be set to this viewer's + interactor. + outline_color + The color of the outline of the box widget + + Returns + ------- + widget: vtk.vtkBoxWidget + ''' + widget = vtk.vtkBoxWidget() + widget.SetInteractor(viewer.iren) + widget.HandlesOn() + widget.TranslationEnabledOn() + widget.RotationEnabledOff() + widget.GetOutlineProperty().SetColor(outline_colour) + widget.OutlineCursorWiresOff() + widget.SetPlaceFactor(1) + widget.KeyPressActivationOff() + return widget + + @staticmethod + def AroundSliceOnXYPlane(viewer, + axis='x', + coord=0, + outline_color=(1., 0., 0.), + width=1, + widget_name='slice_outline_widget'): + ''' + Creates a border in [viewer], around a slice with number [coord] on + the [axis] axis. This border is a vtkBoxWidget. + This appears as an overlay, so appears on every projection. + E.g. if orientation = 'y', coord = 5, width=1 then it creates a + border around the voxels with y value of 5. + width controls the width of the box, but it always starts at [coord]. + E.g. if orientation = 'y', coord = 5, width = 5 then it creates a + border around the voxels with y values of 5-9. + + Note, setting width=0 creates a line rather than a box + + Parameters + ---------- + viewer: viewer2D or viewer3D + A viewer upon which the border will be displayed + axis: {'x', 'y'} + Which axis the slice is on + coord: int + The coordinate of the slice in world coords + outline_color: tuple + colour of the border + width: float + The width of the border. The border starts from the bound of the + slice with number coord. + widget_name: str + The name to associate the border widget with in the viewer. + ''' + + widget = cilviewerBoxWidget.Fixed(viewer, outline_color) + coords = cilviewerBoxWidget.GetCoordsForBoxWidgetAroundSlice(viewer, axis, coord, width) + widget.PlaceWidget(coords) + viewer.addWidgetReference(widget, widget_name) + return widget + - ''' - - # Only makes sense to do this on the Z axis: - render_orientation = SLICE_ORIENTATION_XY - try: - viewer.setSliceOrientation(render_orientation) - except: # method doesn't exist on 3D viewer - pass - - spacing = viewer.img3D.GetSpacing() - - # Get maximum extents of the image in world coords - world_image_max = viewer.style.GetImageWorldExtent() - - # Set the minimum world value - world_image_min = (0, 0, 0) - - z_coords = [world_image_min[render_orientation], world_image_max[render_orientation]] - - if orientation == 'horizontal': - # Displaying slice at fixed coord in X direction: - x_coords = [coord, coord + width * spacing[0]] - y_coords = [world_image_min[1], world_image_max[1]] - - else: - # Displaying slice at fixed coord in Y direction: - y_coords = [coord, coord + width * spacing[1]] - x_coords = [world_image_min[0], world_image_max[0]] - - coords = x_coords + y_coords + z_coords - - return coords - - -def CreateMoveableBoxWidgetAtEventPosition(viewer, position, widget_name, outline_colour=(0, 1, 0), scale_factor=0.3): - ''' - Place a moveable box widget on the viewer at the event position. - Parameters - ---------- - viewer - The 2D viewer that the box will be displayed on - position - The event position - widget_name - The name the reference to the widget will be stored as - outline_color - The outline color of the box widget - scale_factor - Factor for scaling the size of the box - ''' - # ROI Widget - widget = CreateMoveableBoxWidget(viewer, outline_colour) - coords = get_box_bounds_from_event_position(viewer, position, scale_factor) - # Set widget placement and make visible - widget.PlaceWidget(coords) - widget.On() - viewer.addWidgetReference(widget, widget_name) - return widget - - -def get_box_bounds_from_event_position(viewer, position, scale_factor=0.3): - ''' - Get the coordinates for the bounds of a box from the event position - Parameters - ---------- - viewer - The 2D viewer that the box will be displayed on - position - The event position - scale_factor - Factor for scaling the size of the box - ''' - # Current render orientation - orientation = viewer.style.GetSliceOrientation() - - # Translate the mouse click display coordinates into world coordinates - coord = vtk.vtkCoordinate() - coord.SetCoordinateSystemToDisplay() - coord.SetValue(position[0], position[1]) - world_mouse_pos = coord.GetComputedWorldValue(viewer.style.GetRenderer()) - - # Get maximum extents of the image in world coords - world_image_max = viewer.style.GetImageWorldExtent() - - # Set the minimum world value - world_image_min = (0, 0, 0) - - # Initialise the box position in format [xmin, xmax, ymin, ymax,...] - box_pos = [0, 0, 0, 0, 0, 0] - - # place the mouse click as bottom left in current orientation - if orientation == SLICE_ORIENTATION_XY: - # Looking along z - # Lower left is xmin, ymin - box_pos[0] = world_mouse_pos[0] - box_pos[2] = world_mouse_pos[1] - - # Set top right point - # Top right is xmax, ymax - box_pos[1] = truncate_box(box_pos[0], world_image_max, "x", scale_factor) - box_pos[3] = truncate_box(box_pos[2], world_image_max, "y", scale_factor) - - # Set the scroll axis to maximum extent eg. min-max - # zmin, zmax - box_pos[4] = world_image_min[orientation] - box_pos[5] = world_image_max[orientation] - - elif orientation == SLICE_ORIENTATION_XZ: - # Looking along y - # Lower left is xmin, zmin - box_pos[0] = world_mouse_pos[0] - box_pos[4] = world_mouse_pos[2] - - # Set top right point. - # Top right is xmax, zmax - box_pos[1] = truncate_box(box_pos[0], world_image_max, "x") - box_pos[5] = truncate_box(box_pos[4], world_image_max, "z") - - # Set the scroll axis to maximum extent eg. min-max - # ymin, ymax - box_pos[2] = world_image_min[orientation] - box_pos[3] = world_image_max[orientation] - - else: - # orientation == 0 - # Looking along x - # Lower left is ymin, zmin - box_pos[2] = world_mouse_pos[1] - box_pos[4] = world_mouse_pos[2] - - # Set top right point - # Top right is ymax, zmax - box_pos[3] = truncate_box(box_pos[2], world_image_max, "y") - box_pos[5] = truncate_box(box_pos[4], world_image_max, "z") - - # Set the scroll axis to maximum extent eg. min-max - # xmin, xmax - box_pos[0] = world_image_min[orientation] - box_pos[1] = world_image_max[orientation] - - return box_pos - - -def truncate_box(start_pos, world_max_array, axis, scale_factor=0.3): - """ - Make sure that the value for the upper corner of the box is within the world extent. - - :param start_pos: Lower left corner value on specified axis - :param world_max_array: Array containing (x,y,z) of the maximum extent of the world - :param axis: The axis of interest eg. "x" - :param scale_factor: - :return: The start position + a percentage of the world truncated to the edges of the world - """ - - # get index for axis - axis_dict = {"x": SLICE_ORIENTATION_YZ, "y": SLICE_ORIENTATION_XZ, "z": SLICE_ORIENTATION_XY} - axis_int = axis_dict[axis] - - # Create the upper right coordinate point with scale offset - value = start_pos + world_max_array[axis_int] * scale_factor - - # Check to make sure that it is within the image world. - if value > world_max_array[axis_int]: - return world_max_array[axis_int] - else: - return value + @staticmethod + def GetCoordsForBoxWidgetAroundSlice(viewer, axis='x', coord=0, width=1): + ''' + Generate coordinates for positioning BoxWidget around slice + + Parameters + ---------- + viewer: viewer2D or viewer3D + A viewer upon which the border will be displayed + orientation: {'x', 'y'} + Which axis the slice is on + coord: int + The coordinate of the slice in world coords + width: float + The width of the border. The border starts from the bound of the + slice with number coord. + + ''' + + # Only makes sense to do this on the Z axis: + render_orientation = SLICE_ORIENTATION_XY + try: + viewer.setSliceOrientation(render_orientation) + except: # method doesn't exist on 3D viewer + pass + + spacing = viewer.img3D.GetSpacing() + + # Get maximum extents of the image in world coords + world_image_max = viewer.style.GetImageWorldExtent() + + # Set the minimum world value + world_image_min = (0, 0, 0) + + z_coords = [world_image_min[render_orientation], world_image_max[render_orientation]] + + if axis == 'x': + # Displaying slice at fixed coord in X direction: + x_coords = [coord, coord + width * spacing[0]] + y_coords = [world_image_min[1], world_image_max[1]] + + else: + # Displaying slice at fixed coord in Y direction: + y_coords = [coord, coord + width * spacing[1]] + x_coords = [world_image_min[0], world_image_max[0]] + + coords = x_coords + y_coords + z_coords + + return coords + + @staticmethod + def MoveableAtEventPosition(viewer, position, widget_name, outline_colour=(0, 1, 0), scale_factor=0.3): + ''' + Place a moveable box widget on the viewer at the event position. + Parameters + ---------- + viewer + The 2D viewer that the box will be displayed on + position + The event position + widget_name + The name the reference to the widget will be stored as + outline_color + The outline color of the box widget + scale_factor + Factor for scaling the size of the box + ''' + # ROI Widget + widget = cilviewerBoxWidget.Moveable(viewer, outline_colour) + coords = cilviewerBoxWidget.GetBoxBoundsFromEventPosition(viewer, position, scale_factor) + # Set widget placement and make visible + widget.PlaceWidget(coords) + widget.On() + viewer.addWidgetReference(widget, widget_name) + return widget + + @staticmethod + def GetBoxBoundsFromEventPosition(viewer, position, scale_factor=0.3): + ''' + Get the coordinates for the bounds of a box from the event position + + Parameters + ---------- + viewer + The 2D viewer that the box will be displayed on + position + The event position + scale_factor + Factor for scaling the size of the box + ''' + # Current render orientation + orientation = viewer.style.GetSliceOrientation() + + # Translate the mouse click display coordinates into world coordinates + coord = vtk.vtkCoordinate() + coord.SetCoordinateSystemToDisplay() + coord.SetValue(position[0], position[1]) + world_mouse_pos = coord.GetComputedWorldValue(viewer.style.GetRenderer()) + + # Get maximum extents of the image in world coords + world_image_max = viewer.style.GetImageWorldExtent() + + # Set the minimum world value + world_image_min = (0, 0, 0) + + # Initialise the box position in format [xmin, xmax, ymin, ymax,...] + box_pos = [0, 0, 0, 0, 0, 0] + + # place the mouse click as bottom left in current orientation + if orientation == SLICE_ORIENTATION_XY: + # Looking along z + # Lower left is xmin, ymin + box_pos[0] = world_mouse_pos[0] + box_pos[2] = world_mouse_pos[1] + + # Set top right point + # Top right is xmax, ymax + box_pos[1] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[0], world_image_max, "x", scale_factor) + box_pos[3] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[2], world_image_max, "y", scale_factor) + + # Set the scroll axis to maximum extent eg. min-max + # zmin, zmax + box_pos[4] = world_image_min[orientation] + box_pos[5] = world_image_max[orientation] + + elif orientation == SLICE_ORIENTATION_XZ: + # Looking along y + # Lower left is xmin, zmin + box_pos[0] = world_mouse_pos[0] + box_pos[4] = world_mouse_pos[2] + + # Set top right point. + # Top right is xmax, zmax + box_pos[1] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[0], world_image_max, "x") + box_pos[5] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[4], world_image_max, "z") + + # Set the scroll axis to maximum extent eg. min-max + # ymin, ymax + box_pos[2] = world_image_min[orientation] + box_pos[3] = world_image_max[orientation] + + else: + # orientation == 0 + # Looking along x + # Lower left is ymin, zmin + box_pos[2] = world_mouse_pos[1] + box_pos[4] = world_mouse_pos[2] + + # Set top right point + # Top right is ymax, zmax + box_pos[3] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[2], world_image_max, "y") + box_pos[5] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[4], world_image_max, "z") + + # Set the scroll axis to maximum extent eg. min-max + # xmin, xmax + box_pos[0] = world_image_min[orientation] + box_pos[1] = world_image_max[orientation] + + return box_pos + + @staticmethod + def GetTruncatedBoxCoord(start_pos, world_max_array, axis, scale_factor=0.3): + """ + Make sure that the value for the upper corner of the box is within the world extent. + + Parameters + ---------- + start_pos: + Lower left corner value on specified axis + world_max_array: + Array containing (x,y,z) of the maximum extent of the world + slice_orientation: str, {'x', 'y', 'z'} + The axis to truncate on. + scale_factor: + The start position + a percentage of the world truncated to the edges of the world + """ + + # get index for axis + axis_dict = {"x": SLICE_ORIENTATION_YZ, "y": SLICE_ORIENTATION_XZ, "z": SLICE_ORIENTATION_XY} + axis_int = axis_dict[axis] + + # Create the upper right coordinate point with scale offset + value = start_pos + world_max_array[axis_int] * scale_factor + + # Check to make sure that it is within the image world. + if value > world_max_array[axis_int]: + return world_max_array[axis_int] + else: + return value + + +class cilviewerLineWidget(): + @staticmethod + def AtCoordOnXYPlane(viewer, + axis='x', + coord=0, + outline_color=(1., 0., 0.), + widget_name='slice_line_widget'): + ''' + Creates a line in [viewer], at [coord] on the + the [axis] axis. This line is made using a vtkBoxWidget. + When created, the direction of view is forced to be SLICE_ORIENTATION_XY, + so only the X or Y axis can be chosen. + This appears as an overlay, so appears on every projection. + E.g. if orientation = 'y', coord = 5, then it creates a + line with y value of 5. + + Parameters + ---------- + viewer: viewer2D or viewer3D + A viewer upon which the border will be displayed + axis: {'x', 'y'} + Which axis the slice is on + coord: int + The coordinate of the slice in world coords + outline_color: tuple + colour of the border + widget_name: str + The name to associate the border widget with in the viewer. + ''' + + widget = cilviewerBoxWidget.AroundSliceOnXYPlane(viewer, + axis, + coord, + outline_color, + width=0, widget_name=widget_name) + return widget diff --git a/Wrappers/Python/examples/BoxWidgetAroundSlice.py b/Wrappers/Python/examples/BoxWidgetAroundSlice.py index dbf91bee..75cabb3c 100644 --- a/Wrappers/Python/examples/BoxWidgetAroundSlice.py +++ b/Wrappers/Python/examples/BoxWidgetAroundSlice.py @@ -2,15 +2,15 @@ from PySide2 import QtWidgets from ccpi.viewer import viewer2D, viewer3D from SingleViewerCentralWidget import SingleViewerCenterWidget -from ccpi.viewer.widgets.box_widgets import CreateBoxWidgetAroundSlice +from ccpi.viewer.widgets.box_widgets import cilviewerBoxWidget, cilviewerLineWidget app = QtWidgets.QApplication(sys.argv) # can change the behaviour by setting which viewer you want # between viewer2D and viewer3D window = SingleViewerCenterWidget(viewer=viewer2D) -line_widget = CreateBoxWidgetAroundSlice(window.frame.viewer, 'horizontal', 5, width=0) +line_widget = cilviewerLineWidget.AtCoordOnXYPlane(window.frame.viewer, 'x', 5) line_widget.On() -box_widget = CreateBoxWidgetAroundSlice(window.frame.viewer, 'vertical', 10, width=1, outline_color=(0, 0, 1)) +box_widget = cilviewerBoxWidget.AroundSliceOnXYPlane(window.frame.viewer, 'y', 10, width=1, outline_color=(0, 0, 1)) box_widget.On() window.frame.viewer.updatePipeline() diff --git a/Wrappers/Python/examples/SingleViewerCentralWidget.py b/Wrappers/Python/examples/SingleViewerCentralWidget.py index 90a752e8..c6b3f69c 100644 --- a/Wrappers/Python/examples/SingleViewerCentralWidget.py +++ b/Wrappers/Python/examples/SingleViewerCentralWidget.py @@ -27,6 +27,6 @@ def __init__(self, parent=None, viewer=viewer2D): app = QtWidgets.QApplication(sys.argv) # can change the behaviour by setting which viewer you want # between viewer2D and viewer3D - window = SingleViewerCenterWidget(viewer=viewer3D) + window = SingleViewerCenterWidget(viewer=viewer2D) sys.exit(app.exec_()) From 7035618a90d295b7dfe0ff06c0324fc521b7d097 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 3 Aug 2022 13:22:23 +0000 Subject: [PATCH 09/33] Automated autoyapf fixes --- Wrappers/Python/ccpi/viewer/CILViewer2D.py | 2 +- .../Python/ccpi/viewer/widgets/box_widgets.py | 33 +++++++++---------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/CILViewer2D.py b/Wrappers/Python/ccpi/viewer/CILViewer2D.py index 04ba2c0e..47336dc4 100644 --- a/Wrappers/Python/ccpi/viewer/CILViewer2D.py +++ b/Wrappers/Python/ccpi/viewer/CILViewer2D.py @@ -22,7 +22,7 @@ from ccpi.viewer.CILViewerBase import CILViewerBase from ccpi.viewer.utils import Converter -from ccpi.viewer.widgets.box_widgets import cilviewerBoxWidget +from ccpi.viewer.widgets.box_widgets import cilviewerBoxWidget class CILInteractorStyle(vtk.vtkInteractorStyle): diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 9f771670..e9b93852 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -65,11 +65,11 @@ def Moveable(viewer, outline_colour=(0, 1, 0)): @staticmethod def AroundSliceOnXYPlane(viewer, - axis='x', - coord=0, - outline_color=(1., 0., 0.), - width=1, - widget_name='slice_outline_widget'): + axis='x', + coord=0, + outline_color=(1., 0., 0.), + width=1, + widget_name='slice_outline_widget'): ''' Creates a border in [viewer], around a slice with number [coord] on the [axis] axis. This border is a vtkBoxWidget. @@ -105,7 +105,6 @@ def AroundSliceOnXYPlane(viewer, viewer.addWidgetReference(widget, widget_name) return widget - @staticmethod def GetCoordsForBoxWidgetAroundSlice(viewer, axis='x', coord=0, width=1): ''' @@ -298,13 +297,10 @@ def GetTruncatedBoxCoord(start_pos, world_max_array, axis, scale_factor=0.3): class cilviewerLineWidget(): + @staticmethod - def AtCoordOnXYPlane(viewer, - axis='x', - coord=0, - outline_color=(1., 0., 0.), - widget_name='slice_line_widget'): - ''' + def AtCoordOnXYPlane(viewer, axis='x', coord=0, outline_color=(1., 0., 0.), widget_name='slice_line_widget'): + ''' Creates a line in [viewer], at [coord] on the the [axis] axis. This line is made using a vtkBoxWidget. When created, the direction of view is forced to be SLICE_ORIENTATION_XY, @@ -327,9 +323,10 @@ def AtCoordOnXYPlane(viewer, The name to associate the border widget with in the viewer. ''' - widget = cilviewerBoxWidget.AroundSliceOnXYPlane(viewer, - axis, - coord, - outline_color, - width=0, widget_name=widget_name) - return widget + widget = cilviewerBoxWidget.AroundSliceOnXYPlane(viewer, + axis, + coord, + outline_color, + width=0, + widget_name=widget_name) + return widget From 05fe08750b7324cef77403cda33e46ccd76c0c5d Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 3 Aug 2022 14:34:02 +0100 Subject: [PATCH 10/33] Reinstate 'Create' in names --- Wrappers/Python/ccpi/viewer/CILViewer2D.py | 2 +- .../Python/ccpi/viewer/widgets/box_widgets.py | 43 ++++++++++--------- .../Python/examples/BoxWidgetAroundSlice.py | 4 +- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/CILViewer2D.py b/Wrappers/Python/ccpi/viewer/CILViewer2D.py index 47336dc4..72cc5893 100644 --- a/Wrappers/Python/ccpi/viewer/CILViewer2D.py +++ b/Wrappers/Python/ccpi/viewer/CILViewer2D.py @@ -1022,7 +1022,7 @@ def __init__(self, dimx=600, dimy=600, ren=None, renWin=None, iren=None, debug=T self.AddActor(self.helpActor, HELP_ACTOR) # ROI Widget - self.ROIWidget = cilviewerBoxWidget.Moveable(self) + self.ROIWidget = cilviewerBoxWidget.CreateMoveable(self) self.ROIWidget.AddObserver(vtk.vtkWidgetEvent.Select, self.style.OnROIModifiedEvent, 1.0) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index e9b93852..cae4cece 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -5,7 +5,7 @@ class cilviewerBoxWidget(): @staticmethod - def Fixed(viewer, outline_colour=(0, 1, 0)): + def CreateFixed(viewer, outline_colour=(0, 1, 0)): ''' Creates a vtkBoxWidget which the user can't move @@ -35,7 +35,7 @@ def Fixed(viewer, outline_colour=(0, 1, 0)): return widget @staticmethod - def Moveable(viewer, outline_colour=(0, 1, 0)): + def CreateMoveable(viewer, outline_colour=(0, 1, 0)): ''' Creates a vtkBoxWidget which the user can move @@ -64,12 +64,12 @@ def Moveable(viewer, outline_colour=(0, 1, 0)): return widget @staticmethod - def AroundSliceOnXYPlane(viewer, - axis='x', - coord=0, - outline_color=(1., 0., 0.), - width=1, - widget_name='slice_outline_widget'): + def CreateAroundSliceOnXYPlane(viewer, + axis='x', + coord=0, + outline_color=(1., 0., 0.), + width=1, + widget_name='slice_outline_widget'): ''' Creates a border in [viewer], around a slice with number [coord] on the [axis] axis. This border is a vtkBoxWidget. @@ -99,7 +99,7 @@ def AroundSliceOnXYPlane(viewer, The name to associate the border widget with in the viewer. ''' - widget = cilviewerBoxWidget.Fixed(viewer, outline_color) + widget = cilviewerBoxWidget.CreateFixed(viewer, outline_color) coords = cilviewerBoxWidget.GetCoordsForBoxWidgetAroundSlice(viewer, axis, coord, width) widget.PlaceWidget(coords) viewer.addWidgetReference(widget, widget_name) @@ -156,7 +156,7 @@ def GetCoordsForBoxWidgetAroundSlice(viewer, axis='x', coord=0, width=1): return coords @staticmethod - def MoveableAtEventPosition(viewer, position, widget_name, outline_colour=(0, 1, 0), scale_factor=0.3): + def CreateMoveableAtEventPosition(viewer, position, widget_name, outline_colour=(0, 1, 0), scale_factor=0.3): ''' Place a moveable box widget on the viewer at the event position. Parameters @@ -173,7 +173,7 @@ def MoveableAtEventPosition(viewer, position, widget_name, outline_colour=(0, 1, Factor for scaling the size of the box ''' # ROI Widget - widget = cilviewerBoxWidget.Moveable(viewer, outline_colour) + widget = cilviewerBoxWidget.CreateMoveable(viewer, outline_colour) coords = cilviewerBoxWidget.GetBoxBoundsFromEventPosition(viewer, position, scale_factor) # Set widget placement and make visible widget.PlaceWidget(coords) @@ -299,8 +299,12 @@ def GetTruncatedBoxCoord(start_pos, world_max_array, axis, scale_factor=0.3): class cilviewerLineWidget(): @staticmethod - def AtCoordOnXYPlane(viewer, axis='x', coord=0, outline_color=(1., 0., 0.), widget_name='slice_line_widget'): - ''' + def CreateAtCoordOnXYPlane(viewer, + axis='x', + coord=0, + outline_color=(1., 0., 0.), + widget_name='slice_line_widget'): + ''' Creates a line in [viewer], at [coord] on the the [axis] axis. This line is made using a vtkBoxWidget. When created, the direction of view is forced to be SLICE_ORIENTATION_XY, @@ -323,10 +327,9 @@ def AtCoordOnXYPlane(viewer, axis='x', coord=0, outline_color=(1., 0., 0.), widg The name to associate the border widget with in the viewer. ''' - widget = cilviewerBoxWidget.AroundSliceOnXYPlane(viewer, - axis, - coord, - outline_color, - width=0, - widget_name=widget_name) - return widget + widget = cilviewerBoxWidget.CreateAroundSliceOnXYPlane(viewer, + axis, + coord, + outline_color, + width=0, widget_name=widget_name) + return widget diff --git a/Wrappers/Python/examples/BoxWidgetAroundSlice.py b/Wrappers/Python/examples/BoxWidgetAroundSlice.py index 75cabb3c..869d9bd0 100644 --- a/Wrappers/Python/examples/BoxWidgetAroundSlice.py +++ b/Wrappers/Python/examples/BoxWidgetAroundSlice.py @@ -8,9 +8,9 @@ # can change the behaviour by setting which viewer you want # between viewer2D and viewer3D window = SingleViewerCenterWidget(viewer=viewer2D) -line_widget = cilviewerLineWidget.AtCoordOnXYPlane(window.frame.viewer, 'x', 5) +line_widget = cilviewerLineWidget.CreateAtCoordOnXYPlane(window.frame.viewer, 'x', 5) line_widget.On() -box_widget = cilviewerBoxWidget.AroundSliceOnXYPlane(window.frame.viewer, 'y', 10, width=1, outline_color=(0, 0, 1)) +box_widget = cilviewerBoxWidget.CreateAroundSliceOnXYPlane(window.frame.viewer, 'y', 10, width=1, outline_color=(0, 0, 1)) box_widget.On() window.frame.viewer.updatePipeline() From b26e35f947d68118844d2fe4c82085b1177d4636 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 3 Aug 2022 13:34:49 +0000 Subject: [PATCH 11/33] Automated autoyapf fixes --- .../Python/ccpi/viewer/widgets/box_widgets.py | 31 +++++++++---------- .../Python/examples/BoxWidgetAroundSlice.py | 6 +++- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index cae4cece..901e8a83 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -65,11 +65,11 @@ def CreateMoveable(viewer, outline_colour=(0, 1, 0)): @staticmethod def CreateAroundSliceOnXYPlane(viewer, - axis='x', - coord=0, - outline_color=(1., 0., 0.), - width=1, - widget_name='slice_outline_widget'): + axis='x', + coord=0, + outline_color=(1., 0., 0.), + width=1, + widget_name='slice_outline_widget'): ''' Creates a border in [viewer], around a slice with number [coord] on the [axis] axis. This border is a vtkBoxWidget. @@ -299,12 +299,8 @@ def GetTruncatedBoxCoord(start_pos, world_max_array, axis, scale_factor=0.3): class cilviewerLineWidget(): @staticmethod - def CreateAtCoordOnXYPlane(viewer, - axis='x', - coord=0, - outline_color=(1., 0., 0.), - widget_name='slice_line_widget'): - ''' + def CreateAtCoordOnXYPlane(viewer, axis='x', coord=0, outline_color=(1., 0., 0.), widget_name='slice_line_widget'): + ''' Creates a line in [viewer], at [coord] on the the [axis] axis. This line is made using a vtkBoxWidget. When created, the direction of view is forced to be SLICE_ORIENTATION_XY, @@ -327,9 +323,10 @@ def CreateAtCoordOnXYPlane(viewer, The name to associate the border widget with in the viewer. ''' - widget = cilviewerBoxWidget.CreateAroundSliceOnXYPlane(viewer, - axis, - coord, - outline_color, - width=0, widget_name=widget_name) - return widget + widget = cilviewerBoxWidget.CreateAroundSliceOnXYPlane(viewer, + axis, + coord, + outline_color, + width=0, + widget_name=widget_name) + return widget diff --git a/Wrappers/Python/examples/BoxWidgetAroundSlice.py b/Wrappers/Python/examples/BoxWidgetAroundSlice.py index 869d9bd0..fe5e4833 100644 --- a/Wrappers/Python/examples/BoxWidgetAroundSlice.py +++ b/Wrappers/Python/examples/BoxWidgetAroundSlice.py @@ -10,7 +10,11 @@ window = SingleViewerCenterWidget(viewer=viewer2D) line_widget = cilviewerLineWidget.CreateAtCoordOnXYPlane(window.frame.viewer, 'x', 5) line_widget.On() -box_widget = cilviewerBoxWidget.CreateAroundSliceOnXYPlane(window.frame.viewer, 'y', 10, width=1, outline_color=(0, 0, 1)) +box_widget = cilviewerBoxWidget.CreateAroundSliceOnXYPlane(window.frame.viewer, + 'y', + 10, + width=1, + outline_color=(0, 0, 1)) box_widget.On() window.frame.viewer.updatePipeline() From 6717444bca0d97d0fc8ab779438ddb0962f5f446 Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 14:37:47 +0100 Subject: [PATCH 12/33] correction in docstring --- Wrappers/Python/ccpi/viewer/widgets/box_widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 901e8a83..5bf5f136 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -276,7 +276,7 @@ def GetTruncatedBoxCoord(start_pos, world_max_array, axis, scale_factor=0.3): Lower left corner value on specified axis world_max_array: Array containing (x,y,z) of the maximum extent of the world - slice_orientation: str, {'x', 'y', 'z'} + axis: str, {'x', 'y', 'z'} The axis to truncate on. scale_factor: The start position + a percentage of the world truncated to the edges of the world From 9bd147744f8fdc3581effd40aa07e9ded2bce65e Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 3 Aug 2022 15:02:54 +0100 Subject: [PATCH 13/33] change behaviour of scale factor in truncation --- .../Python/ccpi/viewer/widgets/box_widgets.py | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 5bf5f136..a5a5aeac 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -210,6 +210,8 @@ def GetBoxBoundsFromEventPosition(viewer, position, scale_factor=0.3): # Set the minimum world value world_image_min = (0, 0, 0) + world_extent = [0, world_image_max[0], 0, world_image_max[1], 0, world_image_max[2]] + # Initialise the box position in format [xmin, xmax, ymin, ymax,...] box_pos = [0, 0, 0, 0, 0, 0] @@ -222,13 +224,13 @@ def GetBoxBoundsFromEventPosition(viewer, position, scale_factor=0.3): # Set top right point # Top right is xmax, ymax - box_pos[1] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[0], world_image_max, "x", scale_factor) - box_pos[3] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[2], world_image_max, "y", scale_factor) + box_pos[1] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[0], world_extent, "x", scale_factor) + box_pos[3] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[2], world_extent, "y", scale_factor) # Set the scroll axis to maximum extent eg. min-max # zmin, zmax box_pos[4] = world_image_min[orientation] - box_pos[5] = world_image_max[orientation] + box_pos[5] = world_extent[orientation] elif orientation == SLICE_ORIENTATION_XZ: # Looking along y @@ -238,13 +240,13 @@ def GetBoxBoundsFromEventPosition(viewer, position, scale_factor=0.3): # Set top right point. # Top right is xmax, zmax - box_pos[1] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[0], world_image_max, "x") - box_pos[5] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[4], world_image_max, "z") + box_pos[1] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[0], world_extent, "x") + box_pos[5] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[4], world_extent, "z") # Set the scroll axis to maximum extent eg. min-max # ymin, ymax box_pos[2] = world_image_min[orientation] - box_pos[3] = world_image_max[orientation] + box_pos[3] = world_extent[orientation] else: # orientation == 0 @@ -255,43 +257,51 @@ def GetBoxBoundsFromEventPosition(viewer, position, scale_factor=0.3): # Set top right point # Top right is ymax, zmax - box_pos[3] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[2], world_image_max, "y") - box_pos[5] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[4], world_image_max, "z") + box_pos[3] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[2], world_extent, "y") + box_pos[5] = cilviewerBoxWidget.GetTruncatedBoxCoord(box_pos[4], world_extent, "z") # Set the scroll axis to maximum extent eg. min-max # xmin, xmax box_pos[0] = world_image_min[orientation] - box_pos[1] = world_image_max[orientation] + box_pos[1] = world_extent[orientation] return box_pos @staticmethod - def GetTruncatedBoxCoord(start_pos, world_max_array, axis, scale_factor=0.3): + def GetTruncatedBoxCoord(start_pos, world_extent, axis, scale_factor=0.3): """ - Make sure that the value for the upper corner of the box is within the world extent. + Returns a coordinate for the edge of the box on axis [axis], scaled + by factor [scale_factor], and truncated to make sure the box does not + extent over the edge of the image shown. Parameters ---------- - start_pos: + start_pos: float Lower left corner value on specified axis - world_max_array: - Array containing (x,y,z) of the maximum extent of the world - axis: str, {'x', 'y', 'z'} + world_extent: + Array containing the extent of the world + slice_orientation: str, {'x', 'y', 'z'} The axis to truncate on. - scale_factor: - The start position + a percentage of the world truncated to the edges of the world + scale_factor: float + The factor used to scale the length of the box, compared to the extent + of the world on the given axis. + A scale factor of 1 would mean the length of the box on the given axis is + the same as the length of the image on that axis, unless the start position + is greater than the minimum value on that axis, in which case, the box would + be suitably truncated. """ # get index for axis axis_dict = {"x": SLICE_ORIENTATION_YZ, "y": SLICE_ORIENTATION_XZ, "z": SLICE_ORIENTATION_XY} axis_int = axis_dict[axis] - # Create the upper right coordinate point with scale offset - value = start_pos + world_max_array[axis_int] * scale_factor + world_length = world_extent[2*axis_int+1] - world_extent[2*axis_int] + dist = world_length*scale_factor + value = start_pos + dist # Check to make sure that it is within the image world. - if value > world_max_array[axis_int]: - return world_max_array[axis_int] + if value > world_extent[2*axis_int+1]: + return world_extent[2*axis_int+1] else: return value From 7a4431208ca6e3a450bdefb67993d3f1d763631b Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 3 Aug 2022 14:03:43 +0000 Subject: [PATCH 14/33] Automated autoyapf fixes --- Wrappers/Python/ccpi/viewer/widgets/box_widgets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index a5a5aeac..cc04a354 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -295,13 +295,13 @@ def GetTruncatedBoxCoord(start_pos, world_extent, axis, scale_factor=0.3): axis_dict = {"x": SLICE_ORIENTATION_YZ, "y": SLICE_ORIENTATION_XZ, "z": SLICE_ORIENTATION_XY} axis_int = axis_dict[axis] - world_length = world_extent[2*axis_int+1] - world_extent[2*axis_int] - dist = world_length*scale_factor + world_length = world_extent[2 * axis_int + 1] - world_extent[2 * axis_int] + dist = world_length * scale_factor value = start_pos + dist # Check to make sure that it is within the image world. - if value > world_extent[2*axis_int+1]: - return world_extent[2*axis_int+1] + if value > world_extent[2 * axis_int + 1]: + return world_extent[2 * axis_int + 1] else: return value From 6470e8981a8fd22676fb5f94e4af9d5431e306c6 Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 3 Aug 2022 15:06:44 +0100 Subject: [PATCH 15/33] Tidy imports --- Wrappers/Python/ccpi/viewer/CILViewer2D.py | 2 +- Wrappers/Python/ccpi/viewer/widgets/__init__.py | 1 + Wrappers/Python/examples/BoxWidgetAroundSlice.py | 2 +- Wrappers/Python/examples/SingleViewerCentralWidget.py | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/CILViewer2D.py b/Wrappers/Python/ccpi/viewer/CILViewer2D.py index 72cc5893..700e52cd 100644 --- a/Wrappers/Python/ccpi/viewer/CILViewer2D.py +++ b/Wrappers/Python/ccpi/viewer/CILViewer2D.py @@ -22,7 +22,7 @@ from ccpi.viewer.CILViewerBase import CILViewerBase from ccpi.viewer.utils import Converter -from ccpi.viewer.widgets.box_widgets import cilviewerBoxWidget +from ccpi.viewer.widgets import cilviewerBoxWidget class CILInteractorStyle(vtk.vtkInteractorStyle): diff --git a/Wrappers/Python/ccpi/viewer/widgets/__init__.py b/Wrappers/Python/ccpi/viewer/widgets/__init__.py index e69de29b..0f1eb0a8 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/__init__.py +++ b/Wrappers/Python/ccpi/viewer/widgets/__init__.py @@ -0,0 +1 @@ +from .box_widgets import cilviewerBoxWidget, cilviewerLineWidget \ No newline at end of file diff --git a/Wrappers/Python/examples/BoxWidgetAroundSlice.py b/Wrappers/Python/examples/BoxWidgetAroundSlice.py index fe5e4833..9bc73ea6 100644 --- a/Wrappers/Python/examples/BoxWidgetAroundSlice.py +++ b/Wrappers/Python/examples/BoxWidgetAroundSlice.py @@ -2,7 +2,7 @@ from PySide2 import QtWidgets from ccpi.viewer import viewer2D, viewer3D from SingleViewerCentralWidget import SingleViewerCenterWidget -from ccpi.viewer.widgets.box_widgets import cilviewerBoxWidget, cilviewerLineWidget +from ccpi.viewer.widgets import cilviewerBoxWidget, cilviewerLineWidget app = QtWidgets.QApplication(sys.argv) # can change the behaviour by setting which viewer you want diff --git a/Wrappers/Python/examples/SingleViewerCentralWidget.py b/Wrappers/Python/examples/SingleViewerCentralWidget.py index c6b3f69c..90a752e8 100644 --- a/Wrappers/Python/examples/SingleViewerCentralWidget.py +++ b/Wrappers/Python/examples/SingleViewerCentralWidget.py @@ -27,6 +27,6 @@ def __init__(self, parent=None, viewer=viewer2D): app = QtWidgets.QApplication(sys.argv) # can change the behaviour by setting which viewer you want # between viewer2D and viewer3D - window = SingleViewerCenterWidget(viewer=viewer2D) + window = SingleViewerCenterWidget(viewer=viewer3D) sys.exit(app.exec_()) From 47565bd0f98be04f7c4ec1c44f86ceb214f65e5c Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:14:21 +0100 Subject: [PATCH 16/33] no longer exclude test version --- .github/workflows/docker_build_test_publish.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index 2ac21e39..46c7bc87 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -33,5 +33,5 @@ jobs: - name: Run docker container with tests shell: bash -l {0} run: | - docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && conda install cil-data pytest -c ccpi && python -m pytest /root/source_code/Wrappers/Python -k 'not test_version'" - # TODO: publish to come later \ No newline at end of file + docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && conda install cil-data pytest -c ccpi && python -m pytest /root/source_code/Wrappers/Python" + # TODO: publish to come later From 34daba1215c66dc0e8a7200e27d5b512cc8854ab Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:18:16 +0100 Subject: [PATCH 17/33] Add dependencies for ccpi.viewer --- docker/web-app/environment.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docker/web-app/environment.yml b/docker/web-app/environment.yml index a6d0dff3..f0055b5c 100644 --- a/docker/web-app/environment.yml +++ b/docker/web-app/environment.yml @@ -5,8 +5,13 @@ dependencies: - python==3.9 - matplotlib # Optional for more colormaps - h5py +- numpy +- pyside2 +- eqt +- importlib_metadata # [py<38] +- cil-data >=22.0.0 - pip - pip: # Have to install Trame via pip due to unavailability on conda - trame <3, >=2.1.1 # Unpinned worked with version 2.1.1, should work with higher versions. - - vtk==9.1 \ No newline at end of file + - vtk==9.1 From 4450e8d54654d4e98911fb7008a39a2231dd00ee Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:23:23 +0100 Subject: [PATCH 18/33] add channels --- docker/web-app/environment.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docker/web-app/environment.yml b/docker/web-app/environment.yml index f0055b5c..3bc9fec2 100644 --- a/docker/web-app/environment.yml +++ b/docker/web-app/environment.yml @@ -1,15 +1,17 @@ name: cilviewer_webapp channels: - conda-forge + - ccpi + - paskino dependencies: - python==3.9 - matplotlib # Optional for more colormaps - h5py - numpy - pyside2 -- eqt +- paskino::eqt - importlib_metadata # [py<38] -- cil-data >=22.0.0 +- ccpi::cil-data >=22.0.0 - pip - pip: # Have to install Trame via pip due to unavailability on conda From 115cc14569249fc205d70c19fc0a0c1268e24206 Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:33:23 +0100 Subject: [PATCH 19/33] remove cil-data and pytest install from action file --- .github/workflows/docker_build_test_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index 46c7bc87..0444605a 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -33,5 +33,5 @@ jobs: - name: Run docker container with tests shell: bash -l {0} run: | - docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && conda install cil-data pytest -c ccpi && python -m pytest /root/source_code/Wrappers/Python" + docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && python -m pytest /root/source_code/Wrappers/Python" # TODO: publish to come later From ea07bd37d361f4688b700217ddc015b76b77d45e Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:33:41 +0100 Subject: [PATCH 20/33] Add pytest to environment.yml --- docker/web-app/environment.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/web-app/environment.yml b/docker/web-app/environment.yml index 3bc9fec2..3f1a86c6 100644 --- a/docker/web-app/environment.yml +++ b/docker/web-app/environment.yml @@ -12,6 +12,7 @@ dependencies: - paskino::eqt - importlib_metadata # [py<38] - ccpi::cil-data >=22.0.0 +- pytest - pip - pip: # Have to install Trame via pip due to unavailability on conda From 35b890a4b73e3405aa0fcdccac5cb879caa6c6b1 Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 15:51:22 +0100 Subject: [PATCH 21/33] print contents of ccpi/viewer folder to check for version file --- .github/workflows/docker_build_test_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index 0444605a..0cc345f8 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -33,5 +33,5 @@ jobs: - name: Run docker container with tests shell: bash -l {0} run: | - docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && python -m pytest /root/source_code/Wrappers/Python" + docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && python -m pytest /root/source_code/Wrappers/Python" # TODO: publish to come later From 7a05b050b250c7bc84fb4d9e3b68c52dde1b384b Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:01:01 +0100 Subject: [PATCH 22/33] try to print where ccpi-viewer package is installed --- .github/workflows/docker_build_test_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index 0cc345f8..9199acc8 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -33,5 +33,5 @@ jobs: - name: Run docker container with tests shell: bash -l {0} run: | - docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && python -m pytest /root/source_code/Wrappers/Python" + docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && pip show ccpi-viewer && python -m pytest /root/source_code/Wrappers/Python" # TODO: publish to come later From 4f191e0a765b5fffd1d8c563177860bf3c3a650c Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:08:44 +0100 Subject: [PATCH 23/33] change directory where test happens --- .github/workflows/docker_build_test_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index 9199acc8..41445923 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -33,5 +33,5 @@ jobs: - name: Run docker container with tests shell: bash -l {0} run: | - docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && pip show ccpi-viewer && python -m pytest /root/source_code/Wrappers/Python" + docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && pip show ccpi-viewer && python -m pytest /home/abc/mambaforge/envs/cilviewer_webapp/lib/python3.9/site-packages/ccpi-viewer/Wrappers/Python" # TODO: publish to come later From 17db2596b5d212680c8300b09b138b6a42078f5d Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:27:33 +0100 Subject: [PATCH 24/33] change file for tests --- .github/workflows/docker_build_test_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index 41445923..ff693870 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -33,5 +33,5 @@ jobs: - name: Run docker container with tests shell: bash -l {0} run: | - docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && pip show ccpi-viewer && python -m pytest /home/abc/mambaforge/envs/cilviewer_webapp/lib/python3.9/site-packages/ccpi-viewer/Wrappers/Python" + docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && pip show ccpi-viewer && python -m pytest /home/abc/mambaforge/envs/cilviewer_webapp/lib/python3.9/site-packages/ccpi/viewer" # TODO: publish to come later From 2c91f6ff7a8d8740a5742452e24960688fc3d09a Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:35:55 +0100 Subject: [PATCH 25/33] print contents of installed ccpi viewer folder --- .github/workflows/docker_build_test_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index ff693870..4958a133 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -33,5 +33,5 @@ jobs: - name: Run docker container with tests shell: bash -l {0} run: | - docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && pip show ccpi-viewer && python -m pytest /home/abc/mambaforge/envs/cilviewer_webapp/lib/python3.9/site-packages/ccpi/viewer" + docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && pip show ccpi-viewer && ls /home/abc/mambaforge/envs/cilviewer_webapp/lib/python3.9/site-packages/ccpi/viewer && python -m pytest /root/source_code/Wrappers/Python/ccpi/viewer/" # TODO: publish to come later From e1ef7c6ff6e83e1f6f24da8832b3ce8aac1b0eb2 Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 3 Aug 2022 16:44:47 +0100 Subject: [PATCH 26/33] fix path to test --- .github/workflows/docker_build_test_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index 4958a133..0b46477d 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -33,5 +33,5 @@ jobs: - name: Run docker container with tests shell: bash -l {0} run: | - docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && ls /root/source_code/Wrappers/Python/ccpi/viewer/ && pip show ccpi-viewer && ls /home/abc/mambaforge/envs/cilviewer_webapp/lib/python3.9/site-packages/ccpi/viewer && python -m pytest /root/source_code/Wrappers/Python/ccpi/viewer/" + docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && pip show ccpi-viewer && ls /home/abc/mambaforge/envs/cilviewer_webapp/lib/python3.9/site-packages/ccpi/viewer && python -m pytest /root/source_code/Wrappers/Python/" # TODO: publish to come later From 0e9f89149b2bb11c05680093dc2dff4e560a2c64 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 3 Aug 2022 15:45:35 +0000 Subject: [PATCH 27/33] Automated autoyapf fixes --- Wrappers/Python/ccpi/viewer/widgets/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/__init__.py b/Wrappers/Python/ccpi/viewer/widgets/__init__.py index 0f1eb0a8..f07740b2 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/__init__.py +++ b/Wrappers/Python/ccpi/viewer/widgets/__init__.py @@ -1 +1 @@ -from .box_widgets import cilviewerBoxWidget, cilviewerLineWidget \ No newline at end of file +from .box_widgets import cilviewerBoxWidget, cilviewerLineWidget From 478fb7849889f8fa1f85f74239c1d2a0c3364a39 Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 3 Aug 2022 16:56:27 +0100 Subject: [PATCH 28/33] check file permissions --- .github/workflows/docker_build_test_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index 0b46477d..9257ee77 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -33,5 +33,5 @@ jobs: - name: Run docker container with tests shell: bash -l {0} run: | - docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && pip show ccpi-viewer && ls /home/abc/mambaforge/envs/cilviewer_webapp/lib/python3.9/site-packages/ccpi/viewer && python -m pytest /root/source_code/Wrappers/Python/" + docker run --rm --entrypoint /bin/bash -v /home/runner/work/CILViewer/CILViewer:/root/source_code cil-viewer -c "source ./mambaforge/etc/profile.d/conda.sh && conda activate cilviewer_webapp && pip show ccpi-viewer && ls -l /home/abc/mambaforge/envs/cilviewer_webapp/lib/python3.9/site-packages/ccpi/viewer && python -m pytest /root/source_code/Wrappers/Python/" # TODO: publish to come later From 78d9ed1c08e7a9ebe6a1a5f99e56037f91ad8cea Mon Sep 17 00:00:00 2001 From: lauramurgatroyd Date: Wed, 3 Aug 2022 18:12:04 +0100 Subject: [PATCH 29/33] import version in init --- Wrappers/Python/ccpi/viewer/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/Wrappers/Python/ccpi/viewer/__init__.py b/Wrappers/Python/ccpi/viewer/__init__.py index eec62ab3..e55701f3 100644 --- a/Wrappers/Python/ccpi/viewer/__init__.py +++ b/Wrappers/Python/ccpi/viewer/__init__.py @@ -19,3 +19,4 @@ from .CILViewer2D import CILViewer2D as viewer2D from .CILViewer import CILInteractorStyle as istyle3D from .CILViewer2D import CILInteractorStyle as istyle2D +from .version import version as dversion From 27488657676f0cef884e732c63d71802a7e18894 Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Thu, 4 Aug 2022 08:59:08 +0100 Subject: [PATCH 30/33] revert import --- Wrappers/Python/ccpi/viewer/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Wrappers/Python/ccpi/viewer/__init__.py b/Wrappers/Python/ccpi/viewer/__init__.py index e55701f3..eec62ab3 100644 --- a/Wrappers/Python/ccpi/viewer/__init__.py +++ b/Wrappers/Python/ccpi/viewer/__init__.py @@ -19,4 +19,3 @@ from .CILViewer2D import CILViewer2D as viewer2D from .CILViewer import CILInteractorStyle as istyle3D from .CILViewer2D import CILInteractorStyle as istyle2D -from .version import version as dversion From 2fabf26ab00f649705d19a8e5f56799a51de609b Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Thu, 4 Aug 2022 09:00:43 +0100 Subject: [PATCH 31/33] Update Wrappers/Python/ccpi/viewer/widgets/box_widgets.py --- Wrappers/Python/ccpi/viewer/widgets/box_widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 059f5add..6ce2f472 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -272,7 +272,7 @@ def GetTruncatedBoxCoord(start_pos, world_extent, axis, scale_factor=0.3): """ Returns a coordinate for the edge of the box on axis [axis], scaled by factor [scale_factor], and truncated to make sure the box does not - extent over the edge of the image shown. + extend over the edge of the image shown. extend over the edge of the image shown. From 4b72ac65c2997cbfb14a93323ffa343fee658f8d Mon Sep 17 00:00:00 2001 From: Laura <60604372+lauramurgatroyd@users.noreply.github.com> Date: Thu, 4 Aug 2022 09:01:01 +0100 Subject: [PATCH 32/33] Update Wrappers/Python/ccpi/viewer/widgets/box_widgets.py --- Wrappers/Python/ccpi/viewer/widgets/box_widgets.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py index 6ce2f472..42da69dd 100644 --- a/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py +++ b/Wrappers/Python/ccpi/viewer/widgets/box_widgets.py @@ -273,8 +273,6 @@ def GetTruncatedBoxCoord(start_pos, world_extent, axis, scale_factor=0.3): Returns a coordinate for the edge of the box on axis [axis], scaled by factor [scale_factor], and truncated to make sure the box does not extend over the edge of the image shown. - extend over the edge of the image shown. - Parameters ---------- From 4a8a5ad76c9de107153e313fe4bf3d11ca0b9a95 Mon Sep 17 00:00:00 2001 From: Laura Murgatroyd <60604372+lauramurgatroyd@users.noreply.github.com> Date: Wed, 10 Aug 2022 10:55:28 +0100 Subject: [PATCH 33/33] add step to create and upload artifact of image --- .github/workflows/docker_build_test_publish.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/docker_build_test_publish.yml b/.github/workflows/docker_build_test_publish.yml index 9257ee77..710db6de 100644 --- a/.github/workflows/docker_build_test_publish.yml +++ b/.github/workflows/docker_build_test_publish.yml @@ -29,6 +29,20 @@ jobs: shell: bash -l {0} run: | docker build -t cil-viewer docker/web-app + + - name : docker save container + # https://docs.docker.com/engine/reference/commandline/save/ + shell: bash -l {0} + run: | + docker image ls + docker save cil-viewer | gzip > cil-viewer.tar.gz + ls -l + + - name: Upload artifact of the image. + uses: actions/upload-artifact@v2 + with: + name: cil-viewer + path: cil-viewer.tar.gz - name: Run docker container with tests shell: bash -l {0}