Beispiel #1
0
class InsertTomographyFromFile(QtWidgets.QUndoCommand):
    """Creates command to load tomography data from an HDF file or set of TIFF files to the project

    :param filepath: path of file
    :type filepath: str
    :param presenter: main window presenter instance
    :type presenter: MainWindowPresenter
    :param pixel_sizes: Array of voxel sizes along the (x, y, z) axes in mm
    :type pixel_sizes: List[float, float, float]
    :param volume_centre: Coordinates of the centre of the image along the (x, y, z) axes in mm
    :type volume_centre: List[float, float, float]
    """
    def __init__(self, filepath, presenter, pixel_sizes=None, volume_centre=None):
        super().__init__()

        self.filepath = filepath
        self.presenter = presenter
        self.pixel_sizes = pixel_sizes
        self.volume_centre = volume_centre
        self.old_volume = None

    def redo(self):
        """Using a worker thread to load in tomography data"""
        self.presenter.view.progress_dialog.showMessage('Loading Tomography Data')
        self.old_volume = self.presenter.model.volume
        self.worker = Worker(self.loadTomo, [])
        self.worker.job_succeeded.connect(self.onImportSuccess)
        self.worker.finished.connect(self.presenter.view.progress_dialog.close)
        self.worker.job_failed.connect(self.onImportFailed)
        self.worker.start()

    def loadTomo(self):
        """Choose between loading TIFFS or an HDF file based on if self.pixel_sizes is None"""
        if self.pixel_sizes is None:
            self.presenter.model.volume = read_tomoproc_hdf(self.filepath)
        else:
            self.presenter.model.volume = create_data_from_tiffs(*[self.filepath, self.pixel_sizes, self.volume_centre])

    def undo(self):
        self.presenter.model.volume = self.old_volume

    def onImportSuccess(self):
        pass

    def onImportFailed(self, exception):
        """Logs error and clean up after failed import

        :param exception: exception when importing data
        :type exception: Exception
        """
        msg = f'Failed to load files: {exception}'

        logging.error(msg, exc_info=exception)
        self.presenter.view.showMessage(msg)

        # Remove the failed command from the undo_stack
        self.setObsolete(True)
        self.presenter.view.undo_stack.undo()
Beispiel #2
0
 def redo(self):
     """Using a worker thread to load in tomography data"""
     self.presenter.view.progress_dialog.showMessage('Loading Tomography Data')
     self.old_volume = self.presenter.model.volume
     self.worker = Worker(self.loadTomo, [])
     self.worker.job_succeeded.connect(self.onImportSuccess)
     self.worker.finished.connect(self.presenter.view.progress_dialog.close)
     self.worker.job_failed.connect(self.onImportFailed)
     self.worker.start()
Beispiel #3
0
 def redo(self):
     if self.new_vectors is None:
         self.presenter.view.progress_dialog.showMessage('Creating Measurement vectors')
         self.worker = Worker(self.createVectors, [])
         self.worker.job_succeeded.connect(self.onSuccess)
         self.worker.job_failed.connect(self.onFailed)
         self.worker.finished.connect(self.presenter.view.progress_dialog.close)
         self.worker.start()
     else:
         self.presenter.model.measurement_vectors = np.copy(self.new_vectors)
Beispiel #4
0
 def redo(self):
     if self.new_vectors is None:
         load_vectors_args = [self.filename]
         self.presenter.view.progress_dialog.showMessage('Loading Measurement vectors')
         self.worker = Worker(self.presenter.model.loadVectors, load_vectors_args)
         self.worker.job_succeeded.connect(self.onImportSuccess)
         self.worker.finished.connect(self.presenter.view.progress_dialog.close)
         self.worker.job_failed.connect(self.onImportFailed)
         self.worker.start()
     else:
         self.presenter.model.measurement_vectors = np.copy(self.new_vectors)
Beispiel #5
0
 def redo(self):
     if self.new_points is None:
         load_points_args = [self.filename, self.point_type]
         self.presenter.view.progress_dialog.showMessage(f'Loading {self.point_type.value} Points')
         self.worker = Worker(self.presenter.model.loadPoints, load_points_args)
         self.worker.job_succeeded.connect(self.onImportSuccess)
         self.worker.finished.connect(self.presenter.view.progress_dialog.close)
         self.worker.job_failed.connect(self.onImportFailed)
         self.worker.start()
     else:
         self.presenter.model.addPointsToProject(self.new_points, self.point_type)
Beispiel #6
0
 def redo(self):
     if self.option == InsertSampleOptions.Replace:
         self.old_sample = self.presenter.model.sample
     if self.new_mesh is None:
         load_sample_args = [self.filename, self.option]
         self.presenter.view.progress_dialog.showMessage('Loading 3D Model')
         self.worker = Worker(self.presenter.model.loadSample, load_sample_args)
         self.worker.job_succeeded.connect(self.onImportSuccess)
         self.worker.finished.connect(self.presenter.view.progress_dialog.close)
         self.worker.job_failed.connect(self.onImportFailed)
         self.worker.start()
     else:
         self.presenter.model.addMeshToProject(self.sample_key, self.new_mesh, option=self.option)
Beispiel #7
0
    def useWorker(self,
                  func,
                  args,
                  on_success=None,
                  on_failure=None,
                  on_complete=None):
        """Calls the given function from a new worker thread object

        :param func: function to run on ``QThread``
        :type func: Callable[..., Any]
        :param args: arguments of function ``func``
        :type args: Tuple[Any, ...]
        :param on_success: function to call if success
        :type on_success: Union[Callable[..., None], None]
        :param on_failure: function to call if failed
        :type on_failure: Union[Callable[..., None], None]
        :param on_complete: function to call when complete
        :type on_complete: Union[Callable[..., None], None]
        """
        self.worker = Worker.callFromWorker(func, args, on_success, on_failure,
                                            on_complete)
Beispiel #8
0
class InsertVectors(QtWidgets.QUndoCommand):
    """Creates command to compute and insert measurement vectors into project

    :param presenter: main window presenter instance
    :type presenter: MainWindowPresenter
    :param point_index: index of measurement point, when index is -1 adds vectors for all points
    :type point_index: int
    :param strain_component: type of strain component
    :type strain_component: StrainComponents
    :param alignment: index of alignment
    :type alignment: int
    :param detector: index of detector
    :type detector: int
    :param key_in: custom vector
    :type key_in: Union[None, List[float]]
    :param reverse: flag indicating vector should be reversed
    :type reverse: bool
    """
    def __init__(self, presenter, point_index, strain_component, alignment, detector, key_in=None, reverse=False):
        super().__init__()

        self.point_index = point_index
        self.strain_component = strain_component
        self.alignment = alignment
        self.detector = detector
        self.key_in = key_in
        self.reverse = reverse
        self.presenter = presenter
        self.old_vectors = np.copy(self.presenter.model.measurement_vectors)
        self.new_vectors = None

        self.setText('Insert Measurement Vectors')

    def redo(self):
        if self.new_vectors is None:
            self.presenter.view.progress_dialog.showMessage('Creating Measurement vectors')
            self.worker = Worker(self.createVectors, [])
            self.worker.job_succeeded.connect(self.onSuccess)
            self.worker.job_failed.connect(self.onFailed)
            self.worker.finished.connect(self.presenter.view.progress_dialog.close)
            self.worker.start()
        else:
            self.presenter.model.measurement_vectors = np.copy(self.new_vectors)

    def undo(self):
        self.new_vectors = np.copy(self.presenter.model.measurement_vectors)
        self.presenter.model.measurement_vectors = np.copy(self.old_vectors)

    def createVectors(self):
        """Creates measurement vectors using the specified strain component type"""
        if self.point_index == -1:
            index = slice(None)
            num_of_points = self.presenter.model.measurement_points.size
        else:
            index = slice(self.point_index, self.point_index + 1)
            num_of_points = 1

        vectors = []
        if self.strain_component == StrainComponents.parallel_to_x:
            vectors = [[1.0, 0.0, 0.0]] * num_of_points
        elif self.strain_component == StrainComponents.parallel_to_y:
            vectors = [[0.0, 1.0, 0.0]] * num_of_points
        elif self.strain_component == StrainComponents.parallel_to_z:
            vectors = [[0.0, 0.0, 1.0]] * num_of_points
        elif self.strain_component == StrainComponents.normal_to_surface:
            vectors = self.normalMeasurementVector(index)
        elif self.strain_component == StrainComponents.orthogonal_to_normal_no_x:
            surface_normals = self.normalMeasurementVector(index)
            vectors = np.cross(surface_normals, [[1.0, 0.0, 0.0]] * num_of_points)
        elif self.strain_component == StrainComponents.orthogonal_to_normal_no_y:
            surface_normals = self.normalMeasurementVector(index)
            vectors = np.cross(surface_normals, [[0.0, 1.0, 0.0]] * num_of_points)
        elif self.strain_component == StrainComponents.orthogonal_to_normal_no_z:
            surface_normals = self.normalMeasurementVector(index)
            vectors = np.cross(surface_normals, [[0.0, 0.0, 1.0]] * num_of_points)
        elif self.strain_component == StrainComponents.custom:
            v = np.array(self.key_in) / np.linalg.norm(self.key_in)
            vectors = [v] * num_of_points

        vectors = np.array(vectors) if not self.reverse else -np.array(vectors)
        self.presenter.model.addVectorsToProject(vectors, index, self.alignment, self.detector)

    def normalMeasurementVector(self, index):
        """Computes measurement vectors for specified point indices by finding the closest face
        in the sample mesh to the points and calculating the surface normal of that face. Only
        first or main sample model is used to compute vectors

        :param index: point indices to compute vector
        :type index: slice
        :return: surface normal measurement vectors
        :rtype: numpy.ndarray
        """
        mesh = list(self.presenter.model.sample.items())[0][1]
        points = self.presenter.model.measurement_points.points[index]
        vertices = mesh.vertices[mesh.indices]
        face_vertices = vertices.reshape(-1, 9)
        faces = closest_triangle_to_point(face_vertices, points)

        return compute_face_normals(faces)

    def onSuccess(self):
        """Opens vector manager after successfully addition"""
        self.presenter.view.docks.showVectorManager()

    def onFailed(self, exception):
        """Logs error and clean up after failed creation

        :param exception: exception when importing points
        :type exception: Exception
        """
        msg = 'An error occurred while creating the measurement vectors'
        logging.error(msg, exc_info=exception)
        self.presenter.view.showMessage(msg)

        # Remove the failed command from the undo_stack
        self.setObsolete(True)
        self.presenter.view.undo_stack.undo()
Beispiel #9
0
class CreateVectorsWithEulerAngles(QtWidgets.QUndoCommand):
    """Creates command to insert measurement vectors from file

    :param filename: path of file
    :type filename: str
    :param presenter: main window presenter instance
    :type presenter: MainWindowPresenter
    """
    def __init__(self, filename, presenter):
        super().__init__()

        self.filename = filename
        self.presenter = presenter
        self.old_vectors = np.copy(self.presenter.model.measurement_vectors)
        self.new_vectors = None

        self.setText('Create Measurement Vectors with Euler Angles')

    def redo(self):
        if self.new_vectors is None:
            self.presenter.view.progress_dialog.showMessage('Loading Euler Angles')
            self.worker = Worker(self.createVectors, [])
            self.worker.job_succeeded.connect(self.onSuccess)
            self.worker.finished.connect(self.presenter.view.progress_dialog.close)
            self.worker.job_failed.connect(self.onFailed)
            self.worker.start()
        else:
            self.presenter.model.measurement_vectors = np.copy(self.new_vectors)

    def undo(self):
        self.new_vectors = np.copy(self.presenter.model.measurement_vectors)
        self.presenter.model.measurement_vectors = np.copy(self.old_vectors)

    def createVectors(self):
        angles, order = read_angles(self.filename)
        detector_count = len(self.presenter.model.instrument.detectors)

        vectors = np.zeros((angles.shape[0], 3 * detector_count), np.float32)
        q_vectors = np.array(self.presenter.model.instrument.q_vectors)
        for i, angle in enumerate(angles):
            matrix = matrix_from_pose([0., 0., 0., *angle], order=order)[:3, :3]
            m_vectors = q_vectors @ matrix.transpose()
            vectors[i, :] = m_vectors.flatten()

        return self.presenter.model.correctVectorAlignments(vectors)

    def onSuccess(self, return_code):
        """Opens vector manager after successfully import

        :param return_code: return code from 'loadVectors' function
        :type return_code: LoadVector
        """
        if return_code == LoadVector.Smaller_than_points:
            msg = 'Fewer euler angles than points were loaded from the file. The empty vectors have been ' \
                  'assigned a zero vector.'
            self.presenter.view.showMessage(msg, MessageType.Information)
        elif return_code == LoadVector.Larger_than_points:
            msg = 'More euler angles than points were loaded from the file. The extra vectors have been  ' \
                  'added as secondary alignments.'
            self.presenter.view.showMessage(msg, MessageType.Information)

        self.presenter.view.docks.showVectorManager()

    def onFailed(self, exception):
        """Logs error and clean up after failed import

        :param exception: exception when importing points
        :type exception: Exception
        """
        if isinstance(exception, ValueError):
            msg = f'Measurement vectors could not be read from {self.filename} because it has incorrect ' \
                  f'data: {exception}'
        elif isinstance(exception, OSError):
            msg = 'An error occurred while opening this file.\nPlease check that ' \
                  f'the file exist and also that this user has access privileges for this file.\n({self.filename})'
        else:
            msg = f'An unknown error occurred while opening {self.filename}.'

        logging.error(msg, exc_info=exception)
        self.presenter.view.showMessage(msg)

        # Remove the failed command from the undo_stack
        self.setObsolete(True)
        self.presenter.view.undo_stack.undo()
Beispiel #10
0
class InsertVectorsFromFile(QtWidgets.QUndoCommand):
    """Creates command to insert measurement vectors from file

    :param filename: path of file
    :type filename: str
    :param presenter: main window presenter instance
    :type presenter: MainWindowPresenter
    """
    def __init__(self, filename, presenter):
        super().__init__()

        self.filename = filename
        self.presenter = presenter
        self.old_vectors = np.copy(self.presenter.model.measurement_vectors)
        self.new_vectors = None

        self.setText('Import Measurement Vectors')

    def redo(self):
        if self.new_vectors is None:
            load_vectors_args = [self.filename]
            self.presenter.view.progress_dialog.showMessage('Loading Measurement vectors')
            self.worker = Worker(self.presenter.model.loadVectors, load_vectors_args)
            self.worker.job_succeeded.connect(self.onImportSuccess)
            self.worker.finished.connect(self.presenter.view.progress_dialog.close)
            self.worker.job_failed.connect(self.onImportFailed)
            self.worker.start()
        else:
            self.presenter.model.measurement_vectors = np.copy(self.new_vectors)

    def undo(self):
        self.new_vectors = np.copy(self.presenter.model.measurement_vectors)
        self.presenter.model.measurement_vectors = np.copy(self.old_vectors)

    def onImportSuccess(self, return_code):
        """Opens vector manager after successfully import

        :param return_code: return code from 'loadVectors' function
        :type return_code: LoadVector
        """
        if return_code == LoadVector.Smaller_than_points:
            msg = 'Fewer measurements vectors than points were loaded from the file. The remaining have been ' \
                  'assigned a zero vector.'
            self.presenter.view.showMessage(msg, MessageType.Information)
        elif return_code == LoadVector.Larger_than_points:
            msg = 'More measurements vectors than points were loaded from the file. The extra vectors have been  ' \
                  'added as secondary alignments.'
            self.presenter.view.showMessage(msg, MessageType.Information)

        self.presenter.view.docks.showVectorManager()

    def onImportFailed(self, exception):
        """Logs error and clean up after failed import

        :param exception: exception when importing points
        :type exception: Exception
        """
        if isinstance(exception, ValueError):
            msg = f'Measurement vectors could not be read from {self.filename} because it has incorrect ' \
                  f'data: {exception}'
        elif isinstance(exception, OSError):
            msg = 'An error occurred while opening this file.\nPlease check that ' \
                  f'the file exist and also that this user has access privileges for this file.\n({self.filename})'
        else:
            msg = f'An unknown error occurred while opening {self.filename}.'

        logging.error(msg, exc_info=exception)
        self.presenter.view.showMessage(msg)

        # Remove the failed command from the undo_stack
        self.setObsolete(True)
        self.presenter.view.undo_stack.undo()
Beispiel #11
0
class InsertSampleFromFile(QtWidgets.QUndoCommand):
    """Creates command to insert a sample model from a file to the project

    :param filename: path of file
    :type filename: str
    :param presenter: main window presenter instance
    :type presenter: MainWindowPresenter
    :param option: option for inserting sample
    :type option: InsertSampleOptions
    """
    def __init__(self, filename, presenter, option):
        super().__init__()

        self.filename = filename
        self.presenter = presenter
        self.option = option
        self.new_mesh = None
        self.old_sample = None

        base_name = os.path.basename(filename)
        name, ext = os.path.splitext(base_name)
        ext = ext.replace('.', '').lower()
        self.sample_key = self.presenter.model.uniqueKey(name, ext)
        self.setText(f'Import {base_name}')

    def redo(self):
        if self.option == InsertSampleOptions.Replace:
            self.old_sample = self.presenter.model.sample
        if self.new_mesh is None:
            load_sample_args = [self.filename, self.option]
            self.presenter.view.progress_dialog.showMessage('Loading 3D Model')
            self.worker = Worker(self.presenter.model.loadSample, load_sample_args)
            self.worker.job_succeeded.connect(self.onImportSuccess)
            self.worker.finished.connect(self.presenter.view.progress_dialog.close)
            self.worker.job_failed.connect(self.onImportFailed)
            self.worker.start()
        else:
            self.presenter.model.addMeshToProject(self.sample_key, self.new_mesh, option=self.option)

    def undo(self):
        self.new_mesh = self.presenter.model.sample[self.sample_key].copy()
        if self.option == InsertSampleOptions.Combine:
            self.presenter.model.removeMeshFromProject(self.sample_key)
        else:
            self.presenter.model.sample = self.old_sample

    def onImportSuccess(self):
        """Opens sample Manager after successfully import"""
        self.presenter.view.docks.showSampleManager()

    def onImportFailed(self, exception):
        """Logs error and clean up after failed import

        :param exception: exception when importing mesh
        :type exception: Exception
        """
        msg = 'An error occurred while loading the 3D model.\n\nPlease check that the file is valid.'

        logging.error(msg, exc_info=exception)
        self.presenter.view.showMessage(msg)

        # Remove the failed command from the undo_stack
        self.setObsolete(True)
        self.presenter.view.undo_stack.undo()
Beispiel #12
0
class InsertPointsFromFile(QtWidgets.QUndoCommand):
    """Creates command to insert measurement or fiducial points from file

    :param filename: path of file
    :type filename: str
    :param point_type: point type
    :type point_type: PointType
    :param presenter: main window presenter instance
    :type presenter: MainWindowPresenter
    """
    def __init__(self, filename, point_type, presenter):
        super().__init__()

        self.filename = filename
        self.presenter = presenter
        self.point_type = point_type
        self.new_points = None

        if self.point_type == PointType.Fiducial:
            self.old_count = len(self.presenter.model.fiducials)
        else:
            self.old_count = len(self.presenter.model.measurement_points)

        self.setText(f'Import {self.point_type.value} Points')

    def redo(self):
        if self.new_points is None:
            load_points_args = [self.filename, self.point_type]
            self.presenter.view.progress_dialog.showMessage(f'Loading {self.point_type.value} Points')
            self.worker = Worker(self.presenter.model.loadPoints, load_points_args)
            self.worker.job_succeeded.connect(self.onImportSuccess)
            self.worker.finished.connect(self.presenter.view.progress_dialog.close)
            self.worker.job_failed.connect(self.onImportFailed)
            self.worker.start()
        else:
            self.presenter.model.addPointsToProject(self.new_points, self.point_type)

    def undo(self):
        if self.point_type == PointType.Fiducial:
            current_count = len(self.presenter.model.fiducials)
            indices = slice(self.old_count, current_count, None)
            self.new_points = np.copy(self.presenter.model.fiducials[indices])
        else:
            current_count = len(self.presenter.model.measurement_points)
            indices = slice(self.old_count, current_count, None)
            self.new_points = np.copy(self.presenter.model.measurement_points[indices])

        self.presenter.model.removePointsFromProject(indices, self.point_type)

    def onImportSuccess(self):
        """Opens point Manager after successfully import"""
        self.presenter.view.docks.showPointManager(self.point_type)

    def onImportFailed(self, exception):
        """Logs error and clean up after failed import

        :param exception: exception when importing points
        :type exception: Exception
        """
        if isinstance(exception, ValueError):
            msg = f'{self.point_type.value} points could not be read from {self.filename} because it has incorrect ' \
                  f'data: {exception}'
        elif isinstance(exception, OSError):
            msg = 'An error occurred while opening this file.\nPlease check that ' \
                  f'the file exist and also that this user has access privileges for this file.\n({self.filename})'
        else:
            msg = f'An unknown error occurred while opening {self.filename}.'

        logging.error(msg, exc_info=exception)
        self.presenter.view.showMessage(msg)

        # Remove the failed command from the undo_stack
        self.setObsolete(True)
        self.presenter.view.undo_stack.undo()