Beispiel #1
0
class ArrayStackPlot(qt.QWidget):
    """
    Widget for plotting a n-D array (n >= 3) as a stack of images.
    Three axis arrays can be provided to calibrate the axes.

    The signal array can have an arbitrary number of dimensions, the only
    limitation being that the last 3 dimensions must have the same length as
    the axes arrays.

    Sliders are provided to select indices on the first (n - 3) dimensions of
    the signal array, and the plot is updated to load the stack corresponding
    to the selection.
    """
    def __init__(self, parent=None):
        """

        :param parent: Parent QWidget
        """
        super(ArrayStackPlot, self).__init__(parent)

        self.__signal = None
        self.__signal_name = None
        # the Z, Y, X axes apply to the last three dimensions of the signal
        # (in that order)
        self.__z_axis = None
        self.__z_axis_name = None
        self.__y_axis = None
        self.__y_axis_name = None
        self.__x_axis = None
        self.__x_axis_name = None

        self._stack_view = StackView(self)
        self._hline = qt.QFrame(self)
        self._hline.setFrameStyle(qt.QFrame.HLine)
        self._hline.setFrameShadow(qt.QFrame.Sunken)
        self._legend = qt.QLabel(self)
        self._selector = NumpyAxesSelector(self)
        self._selector.setNamedAxesSelectorVisibility(False)
        self.__selector_is_connected = False

        layout = qt.QVBoxLayout()
        layout.addWidget(self._stack_view)
        layout.addWidget(self._hline)
        layout.addWidget(self._legend)
        layout.addWidget(self._selector)

        self.setLayout(layout)

    def getStackView(self):
        """Returns the plot used for the display

        :rtype: StackView
        """
        return self._stack_view

    def setStackData(self,
                     signal,
                     x_axis=None,
                     y_axis=None,
                     z_axis=None,
                     signal_name=None,
                     xlabel=None,
                     ylabel=None,
                     zlabel=None,
                     title=None):
        """

        :param signal: n-D dataset, whose last 3 dimensions are used as the
            3D stack values.
        :param x_axis: 1-D dataset used as the image's x coordinates. If
            provided, its lengths must be equal to the length of the last
            dimension of ``signal``.
        :param y_axis: 1-D dataset used as the image's y. If provided,
            its lengths must be equal to the length of the 2nd to last
            dimension of ``signal``.
        :param z_axis: 1-D dataset used as the image's z. If provided,
            its lengths must be equal to the length of the 3rd to last
            dimension of ``signal``.
        :param signal_name: Label used in the legend
        :param xlabel: Label for X axis
        :param ylabel: Label for Y axis
        :param zlabel: Label for Z axis
        :param title: Graph title
        """
        if self.__selector_is_connected:
            self._selector.selectionChanged.disconnect(self._updateStack)
            self.__selector_is_connected = False

        self.__signal = signal
        self.__signal_name = signal_name or ""
        self.__x_axis = x_axis
        self.__x_axis_name = xlabel
        self.__y_axis = y_axis
        self.__y_axis_name = ylabel
        self.__z_axis = z_axis
        self.__z_axis_name = zlabel

        self._selector.setData(signal)
        self._selector.setAxisNames(["Y", "X", "Z"])

        self._stack_view.setGraphTitle(title or "")
        # by default, the z axis is the image position (dimension not plotted)
        self._stack_view.getPlotWidget().getXAxis().setLabel(self.__x_axis_name
                                                             or "X")
        self._stack_view.getPlotWidget().getYAxis().setLabel(self.__y_axis_name
                                                             or "Y")

        self._updateStack()

        ndims = len(signal.shape)
        self._stack_view.setFirstStackDimension(ndims - 3)

        # the legend label shows the selection slice producing the volume
        # (only interesting for ndim > 3)
        if ndims > 3:
            self._selector.setVisible(True)
            self._legend.setVisible(True)
            self._hline.setVisible(True)
        else:
            self._selector.setVisible(False)
            self._legend.setVisible(False)
            self._hline.setVisible(False)

        if not self.__selector_is_connected:
            self._selector.selectionChanged.connect(self._updateStack)
            self.__selector_is_connected = True

    @staticmethod
    def _get_origin_scale(axis):
        """Assuming axis is a regularly spaced 1D array,
        return a tuple (origin, scale) where:
            - origin = axis[0]
            - scale = (axis[n-1] - axis[0]) / (n -1)
        :param axis: 1D numpy array
        :return: Tuple (axis[0], (axis[-1] - axis[0]) / (len(axis) - 1))
        """
        return axis[0], (axis[-1] - axis[0]) / (len(axis) - 1)

    def _updateStack(self):
        """Update displayed stack according to the current axes selector
        data."""
        stk = self._selector.selectedData()
        x_axis = self.__x_axis
        y_axis = self.__y_axis
        z_axis = self.__z_axis

        calibrations = []
        for axis in [z_axis, y_axis, x_axis]:

            if axis is None:
                calibrations.append(NoCalibration())
            elif len(axis) == 2:
                calibrations.append(
                    LinearCalibration(y_intercept=axis[0], slope=axis[1]))
            else:
                calibrations.append(ArrayCalibration(axis))

        legend = self.__signal_name + "["
        for sl in self._selector.selection():
            if sl == slice(None):
                legend += ":, "
            else:
                legend += str(sl) + ", "
        legend = legend[:-2] + "]"
        self._legend.setText("Displayed data: " + legend)

        self._stack_view.setStack(stk, calibrations=calibrations)
        self._stack_view.setLabels(labels=[
            self.__z_axis_name, self.__y_axis_name, self.__x_axis_name
        ])

    def clear(self):
        old = self._selector.blockSignals(True)
        self._selector.clear()
        self._selector.blockSignals(old)
        self._stack_view.clear()
Beispiel #2
0
class ArrayVolumePlot(qt.QWidget):
    """
    Widget for plotting a n-D array (n >= 3) as a 3D scalar field.
    Three axis arrays can be provided to calibrate the axes.

    The signal array can have an arbitrary number of dimensions, the only
    limitation being that the last 3 dimensions must have the same length as
    the axes arrays.

    Sliders are provided to select indices on the first (n - 3) dimensions of
    the signal array, and the plot is updated to load the stack corresponding
    to the selection.
    """
    def __init__(self, parent=None):
        """

        :param parent: Parent QWidget
        """
        super(ArrayVolumePlot, self).__init__(parent)

        self.__signal = None
        self.__signal_name = None
        # the Z, Y, X axes apply to the last three dimensions of the signal
        # (in that order)
        self.__z_axis = None
        self.__z_axis_name = None
        self.__y_axis = None
        self.__y_axis_name = None
        self.__x_axis = None
        self.__x_axis_name = None

        from ._VolumeWindow import VolumeWindow

        self._view = VolumeWindow(self)

        self._hline = qt.QFrame(self)
        self._hline.setFrameStyle(qt.QFrame.HLine)
        self._hline.setFrameShadow(qt.QFrame.Sunken)
        self._legend = qt.QLabel(self)
        self._selector = NumpyAxesSelector(self)
        self._selector.setNamedAxesSelectorVisibility(False)
        self.__selector_is_connected = False

        layout = qt.QVBoxLayout()
        layout.addWidget(self._view)
        layout.addWidget(self._hline)
        layout.addWidget(self._legend)
        layout.addWidget(self._selector)

        self.setLayout(layout)

    def getVolumeView(self):
        """Returns the plot used for the display

        :rtype: SceneWindow
        """
        return self._view

    def setData(self,
                signal,
                x_axis=None,
                y_axis=None,
                z_axis=None,
                signal_name=None,
                xlabel=None,
                ylabel=None,
                zlabel=None,
                title=None):
        """

        :param signal: n-D dataset, whose last 3 dimensions are used as the
            3D stack values.
        :param x_axis: 1-D dataset used as the image's x coordinates. If
            provided, its lengths must be equal to the length of the last
            dimension of ``signal``.
        :param y_axis: 1-D dataset used as the image's y. If provided,
            its lengths must be equal to the length of the 2nd to last
            dimension of ``signal``.
        :param z_axis: 1-D dataset used as the image's z. If provided,
            its lengths must be equal to the length of the 3rd to last
            dimension of ``signal``.
        :param signal_name: Label used in the legend
        :param xlabel: Label for X axis
        :param ylabel: Label for Y axis
        :param zlabel: Label for Z axis
        :param title: Graph title
        """
        if self.__selector_is_connected:
            self._selector.selectionChanged.disconnect(self._updateVolume)
            self.__selector_is_connected = False

        self.__signal = signal
        self.__signal_name = signal_name or ""
        self.__x_axis = x_axis
        self.__x_axis_name = xlabel
        self.__y_axis = y_axis
        self.__y_axis_name = ylabel
        self.__z_axis = z_axis
        self.__z_axis_name = zlabel

        self._selector.setData(signal)
        self._selector.setAxisNames(["Y", "X", "Z"])

        self._updateVolume()

        # the legend label shows the selection slice producing the volume
        # (only interesting for ndim > 3)
        if signal.ndim > 3:
            self._selector.setVisible(True)
            self._legend.setVisible(True)
            self._hline.setVisible(True)
        else:
            self._selector.setVisible(False)
            self._legend.setVisible(False)
            self._hline.setVisible(False)

        if not self.__selector_is_connected:
            self._selector.selectionChanged.connect(self._updateVolume)
            self.__selector_is_connected = True

    def _updateVolume(self):
        """Update displayed stack according to the current axes selector
        data."""
        x_axis = self.__x_axis
        y_axis = self.__y_axis
        z_axis = self.__z_axis

        offset = []
        scale = []
        for axis in [x_axis, y_axis, z_axis]:
            if axis is None:
                calibration = NoCalibration()
            elif len(axis) == 2:
                calibration = LinearCalibration(y_intercept=axis[0],
                                                slope=axis[1])
            else:
                calibration = ArrayCalibration(axis)
            if not calibration.is_affine():
                _logger.warning("Axis has not linear values, ignored")
                offset.append(0.)
                scale.append(1.)
            else:
                offset.append(calibration(0))
                scale.append(calibration.get_slope())

        legend = self.__signal_name + "["
        for sl in self._selector.selection():
            if sl == slice(None):
                legend += ":, "
            else:
                legend += str(sl) + ", "
        legend = legend[:-2] + "]"
        self._legend.setText("Displayed data: " + legend)

        # Update SceneWidget
        data = self._selector.selectedData()

        volumeView = self.getVolumeView()
        volumeView.setData(data, offset=offset, scale=scale)
        volumeView.setAxesLabels(self.__x_axis_name, self.__y_axis_name,
                                 self.__z_axis_name)

    def clear(self):
        old = self._selector.blockSignals(True)
        self._selector.clear()
        self._selector.blockSignals(old)
        self.getVolumeView().clear()
class ArrayVolumePlot(qt.QWidget):
    """
    Widget for plotting a n-D array (n >= 3) as a 3D scalar field.
    Three axis arrays can be provided to calibrate the axes.

    The signal array can have an arbitrary number of dimensions, the only
    limitation being that the last 3 dimensions must have the same length as
    the axes arrays.

    Sliders are provided to select indices on the first (n - 3) dimensions of
    the signal array, and the plot is updated to load the stack corresponding
    to the selection.
    """
    def __init__(self, parent=None):
        """

        :param parent: Parent QWidget
        """
        super(ArrayVolumePlot, self).__init__(parent)

        self.__signal = None
        self.__signal_name = None
        # the Z, Y, X axes apply to the last three dimensions of the signal
        # (in that order)
        self.__z_axis = None
        self.__z_axis_name = None
        self.__y_axis = None
        self.__y_axis_name = None
        self.__x_axis = None
        self.__x_axis_name = None

        from silx.gui.plot3d.ScalarFieldView import ScalarFieldView
        from silx.gui.plot3d import SFViewParamTree

        self._view = ScalarFieldView(self)

        def computeIsolevel(data):
            data = data[numpy.isfinite(data)]
            if len(data) == 0:
                return 0
            else:
                return numpy.mean(data) + numpy.std(data)

        self._view.addIsosurface(computeIsolevel, '#FF0000FF')

        # Create a parameter tree for the scalar field view
        options = SFViewParamTree.TreeView(self._view)
        options.setSfView(self._view)

        # Add the parameter tree to the main window in a dock widget
        dock = qt.QDockWidget()
        dock.setWidget(options)
        self._view.addDockWidget(qt.Qt.RightDockWidgetArea, dock)

        self._hline = qt.QFrame(self)
        self._hline.setFrameStyle(qt.QFrame.HLine)
        self._hline.setFrameShadow(qt.QFrame.Sunken)
        self._legend = qt.QLabel(self)
        self._selector = NumpyAxesSelector(self)
        self._selector.setNamedAxesSelectorVisibility(False)
        self.__selector_is_connected = False

        layout = qt.QVBoxLayout()
        layout.addWidget(self._view)
        layout.addWidget(self._hline)
        layout.addWidget(self._legend)
        layout.addWidget(self._selector)

        self.setLayout(layout)

    def getVolumeView(self):
        """Returns the plot used for the display

        :rtype: ScalarFieldView
        """
        return self._view

    def normalizeComplexData(self, data):
        """
        Converts a complex data array to its amplitude, if necessary.
        :param data: the data to normalize
        :return:
        """
        if hasattr(data, "dtype"):
            isComplex = numpy.issubdtype(data.dtype, numpy.complexfloating)
        else:
            isComplex = isinstance(data, numbers.Complex)
        if isComplex:
            data = numpy.absolute(data)
        return data

    def setData(self,
                signal,
                x_axis=None,
                y_axis=None,
                z_axis=None,
                signal_name=None,
                xlabel=None,
                ylabel=None,
                zlabel=None,
                title=None):
        """

        :param signal: n-D dataset, whose last 3 dimensions are used as the
            3D stack values.
        :param x_axis: 1-D dataset used as the image's x coordinates. If
            provided, its lengths must be equal to the length of the last
            dimension of ``signal``.
        :param y_axis: 1-D dataset used as the image's y. If provided,
            its lengths must be equal to the length of the 2nd to last
            dimension of ``signal``.
        :param z_axis: 1-D dataset used as the image's z. If provided,
            its lengths must be equal to the length of the 3rd to last
            dimension of ``signal``.
        :param signal_name: Label used in the legend
        :param xlabel: Label for X axis
        :param ylabel: Label for Y axis
        :param zlabel: Label for Z axis
        :param title: Graph title
        """
        signal = self.normalizeComplexData(signal)
        if self.__selector_is_connected:
            self._selector.selectionChanged.disconnect(self._updateVolume)
            self.__selector_is_connected = False

        self.__signal = signal
        self.__signal_name = signal_name or ""
        self.__x_axis = x_axis
        self.__x_axis_name = xlabel
        self.__y_axis = y_axis
        self.__y_axis_name = ylabel
        self.__z_axis = z_axis
        self.__z_axis_name = zlabel

        self._selector.setData(signal)
        self._selector.setAxisNames(["Y", "X", "Z"])

        self._view.setAxesLabels(self.__x_axis_name or 'X', self.__y_axis_name
                                 or 'Y', self.__z_axis_name or 'Z')
        self._updateVolume()

        # the legend label shows the selection slice producing the volume
        # (only interesting for ndim > 3)
        if signal.ndim > 3:
            self._selector.setVisible(True)
            self._legend.setVisible(True)
            self._hline.setVisible(True)
        else:
            self._selector.setVisible(False)
            self._legend.setVisible(False)
            self._hline.setVisible(False)

        if not self.__selector_is_connected:
            self._selector.selectionChanged.connect(self._updateVolume)
            self.__selector_is_connected = True

    def _updateVolume(self):
        """Update displayed stack according to the current axes selector
        data."""
        data = self._selector.selectedData()
        x_axis = self.__x_axis
        y_axis = self.__y_axis
        z_axis = self.__z_axis

        offset = []
        scale = []
        for axis in [x_axis, y_axis, z_axis]:
            if axis is None:
                calibration = NoCalibration()
            elif len(axis) == 2:
                calibration = LinearCalibration(y_intercept=axis[0],
                                                slope=axis[1])
            else:
                calibration = ArrayCalibration(axis)
            if not calibration.is_affine():
                _logger.warning("Axis has not linear values, ignored")
                offset.append(0.)
                scale.append(1.)
            else:
                offset.append(calibration(0))
                scale.append(calibration.get_slope())

        legend = self.__signal_name + "["
        for sl in self._selector.selection():
            if sl == slice(None):
                legend += ":, "
            else:
                legend += str(sl) + ", "
        legend = legend[:-2] + "]"
        self._legend.setText("Displayed data: " + legend)

        self._view.setData(data, copy=False)
        self._view.setScale(*scale)
        self._view.setTranslation(*offset)
        self._view.setAxesLabels(self.__x_axis_name, self.__y_axis_name,
                                 self.__z_axis_name)

    def clear(self):
        old = self._selector.blockSignals(True)
        self._selector.clear()
        self._selector.blockSignals(old)
        self._view.setData(None)
Beispiel #4
0
class ArrayStackPlot(qt.QWidget):
    """
    Widget for plotting a n-D array (n >= 3) as a stack of images.
    Three axis arrays can be provided to calibrate the axes.

    The signal array can have an arbitrary number of dimensions, the only
    limitation being that the last 3 dimensions must have the same length as
    the axes arrays.

    Sliders are provided to select indices on the first (n - 3) dimensions of
    the signal array, and the plot is updated to load the stack corresponding
    to the selection.
    """
    def __init__(self, parent=None):
        """

        :param parent: Parent QWidget
        """
        super(ArrayStackPlot, self).__init__(parent)

        self.__signal = None
        self.__signal_name = None
        # the Z, Y, X axes apply to the last three dimensions of the signal
        # (in that order)
        self.__z_axis = None
        self.__z_axis_name = None
        self.__y_axis = None
        self.__y_axis_name = None
        self.__x_axis = None
        self.__x_axis_name = None

        self._stack_view = StackView(self)
        self._hline = qt.QFrame(self)
        self._hline.setFrameStyle(qt.QFrame.HLine)
        self._hline.setFrameShadow(qt.QFrame.Sunken)
        self._legend = qt.QLabel(self)
        self._selector = NumpyAxesSelector(self)
        self._selector.setNamedAxesSelectorVisibility(False)
        self.__selector_is_connected = False

        layout = qt.QVBoxLayout()
        layout.addWidget(self._stack_view)
        layout.addWidget(self._hline)
        layout.addWidget(self._legend)
        layout.addWidget(self._selector)

        self.setLayout(layout)

    def getStackView(self):
        """Returns the plot used for the display

        :rtype: StackView
        """
        return self._stack_view

    def setStackData(self, signal,
                     x_axis=None, y_axis=None, z_axis=None,
                     signal_name=None,
                     xlabel=None, ylabel=None, zlabel=None,
                     title=None):
        """

        :param signal: n-D dataset, whose last 3 dimensions are used as the
            3D stack values.
        :param x_axis: 1-D dataset used as the image's x coordinates. If
            provided, its lengths must be equal to the length of the last
            dimension of ``signal``.
        :param y_axis: 1-D dataset used as the image's y. If provided,
            its lengths must be equal to the length of the 2nd to last
            dimension of ``signal``.
        :param z_axis: 1-D dataset used as the image's z. If provided,
            its lengths must be equal to the length of the 3rd to last
            dimension of ``signal``.
        :param signal_name: Label used in the legend
        :param xlabel: Label for X axis
        :param ylabel: Label for Y axis
        :param zlabel: Label for Z axis
        :param title: Graph title
        """
        if self.__selector_is_connected:
            self._selector.selectionChanged.disconnect(self._updateStack)
            self.__selector_is_connected = False

        self.__signal = signal
        self.__signal_name = signal_name or ""
        self.__x_axis = x_axis
        self.__x_axis_name = xlabel
        self.__y_axis = y_axis
        self.__y_axis_name = ylabel
        self.__z_axis = z_axis
        self.__z_axis_name = zlabel

        self._selector.setData(signal)
        self._selector.setAxisNames(["Y", "X", "Z"])

        self._stack_view.setGraphTitle(title or "")
        # by default, the z axis is the image position (dimension not plotted)
        self._stack_view.getPlot().getXAxis().setLabel(self.__x_axis_name or "X")
        self._stack_view.getPlot().getYAxis().setLabel(self.__y_axis_name or "Y")

        self._updateStack()

        ndims = len(signal.shape)
        self._stack_view.setFirstStackDimension(ndims - 3)

        # the legend label shows the selection slice producing the volume
        # (only interesting for ndim > 3)
        if ndims > 3:
            self._selector.setVisible(True)
            self._legend.setVisible(True)
            self._hline.setVisible(True)
        else:
            self._selector.setVisible(False)
            self._legend.setVisible(False)
            self._hline.setVisible(False)

        if not self.__selector_is_connected:
            self._selector.selectionChanged.connect(self._updateStack)
            self.__selector_is_connected = True

    @staticmethod
    def _get_origin_scale(axis):
        """Assuming axis is a regularly spaced 1D array,
        return a tuple (origin, scale) where:
            - origin = axis[0]
            - scale = (axis[n-1] - axis[0]) / (n -1)
        :param axis: 1D numpy array
        :return: Tuple (axis[0], (axis[-1] - axis[0]) / (len(axis) - 1))
        """
        return axis[0], (axis[-1] - axis[0]) / (len(axis) - 1)

    def _updateStack(self):
        """Update displayed stack according to the current axes selector
        data."""
        stk = self._selector.selectedData()
        x_axis = self.__x_axis
        y_axis = self.__y_axis
        z_axis = self.__z_axis

        calibrations = []
        for axis in [z_axis, y_axis, x_axis]:

            if axis is None:
                calibrations.append(NoCalibration())
            elif len(axis) == 2:
                calibrations.append(
                        LinearCalibration(y_intercept=axis[0],
                                          slope=axis[1]))
            else:
                calibrations.append(ArrayCalibration(axis))

        legend = self.__signal_name + "["
        for sl in self._selector.selection():
            if sl == slice(None):
                legend += ":, "
            else:
                legend += str(sl) + ", "
        legend = legend[:-2] + "]"
        self._legend.setText("Displayed data: " + legend)

        self._stack_view.setStack(stk, calibrations=calibrations)
        self._stack_view.setLabels(
                labels=[self.__z_axis_name,
                        self.__y_axis_name,
                        self.__x_axis_name])

    def clear(self):
        old = self._selector.blockSignals(True)
        self._selector.clear()
        self._selector.blockSignals(old)
        self._stack_view.clear()
Beispiel #5
0
class XpadVisualisation(QWidget):
    unfoldButtonClicked = pyqtSignal()

    def __init__(self):
        super(QWidget, self).__init__()
        self.layout = QVBoxLayout(self)
        self.raw_data = None
        self.flatfield_image = None
        self.path = None
        self.diagram_data_array = []
        self.angles = []

        # Initialize tab screen
        self.tabs = QTabWidget()
        self.raw_data_tab = QWidget()
        # Create an unfolding data tab
        self.unfolded_data_tab = UnfoldingDataTab(self)
        self.diagram_tab = QWidget()
        self.fitting_data_tab = QWidget()

        # Create raw data display tab
        self.raw_data_tab.layout = QVBoxLayout(self.raw_data_tab)
        self.raw_data_viewer = RawDataViewer(self.raw_data_tab)

        # Create diagram plot data tab
        self.diagram_tab.layout = QVBoxLayout(self.diagram_tab)
        self.diagram_data_plot = Plot1D(self.diagram_tab)

        # Create fitting curve tab
        self.fitting_data_tab.layout = QVBoxLayout(self.fitting_data_tab)
        self.fitting_data_selector = NumpyAxesSelector(self.fitting_data_tab)
        self.fitting_data_plot = Plot1D(self.fitting_data_tab)
        self.fitting_widget = self.fitting_data_plot.getFitAction()
        self.fit_action = FitAction(plot=self.fitting_data_plot,
                                    parent=self.fitting_data_plot)
        self.toolbar = QToolBar("New")

        # Create automatic fitting tab
        self.automatic_fit_tab = FittingDataTab(self)

        self.unfolded_data_tab.viewer.get_unfold_with_flatfield_action(
        ).unfoldWithFlatfieldClicked.connect(self.get_calibration)
        self.unfolded_data_tab.viewer.get_unfold_action(
        ).unfoldClicked.connect(self.get_calibration)
        self.unfolded_data_tab.unfoldingFinished.connect(
            self.create_diagram_array)

        self.init_UI()

    def init_UI(self):

        self.tabs.resize(400, 300)

        # Add tabs
        self.tabs.addTab(self.raw_data_tab, "Raw data")
        self.tabs.addTab(self.unfolded_data_tab, "Unfolded data")
        self.tabs.addTab(self.diagram_tab, "Diffraction diagram")
        self.tabs.addTab(self.fitting_data_tab, "Fitted data")
        self.tabs.addTab(self.automatic_fit_tab, "Automatic fit")

        self.raw_data_tab.layout.addWidget(self.raw_data_viewer)

        self.diagram_tab.layout.addWidget(self.diagram_data_plot)

        self.diagram_data_plot.setGraphTitle(f"Diagram diffraction")
        self.diagram_data_plot.setGraphXLabel("two-theta (°)")
        self.diagram_data_plot.setGraphYLabel("intensity")
        self.diagram_data_plot.setYAxisLogarithmic(True)

        self.fitting_data_selector.setNamedAxesSelectorVisibility(False)
        self.fitting_data_selector.setVisible(True)
        self.fitting_data_selector.setAxisNames("12")

        self.fitting_data_plot.setYAxisLogarithmic(True)
        self.fitting_data_plot.setGraphXLabel("two-theta (°)")
        self.fitting_data_plot.setGraphYLabel("intensity")

        self.fitting_data_plot.getRoiAction().trigger()
        self.fitting_widget.setXRangeUpdatedOnZoom(False)

        self.toolbar.addAction(self.fit_action)
        self.fit_action.setVisible(True)
        self.fitting_data_plot.addToolBar(self.toolbar)
        self.fitting_data_tab.layout.addWidget(self.fitting_data_plot)
        self.fitting_data_tab.layout.addWidget(self.fitting_data_selector)

        # Add tabs to widget
        self.layout.addWidget(self.tabs)

        # self.unfold_timer.timeout.connect(self.unfold_data)
        self.fitting_data_selector.selectionChanged.connect(self.fitting_curve)
        self.fitting_data_plot.getCurvesRoiWidget().sigROIWidgetSignal.connect(
            self.get_roi_list)
        self.unfolded_data_tab.viewer.scatter_selector.selectionChanged.connect(
            self.synchronize_visualisation)

    @pyqtSlot()
    def on_click(self):
        print("\n")
        for currentQTableWidgetItem in self.tableWidget.selectedItems():
            print(currentQTableWidgetItem.row(),
                  currentQTableWidgetItem.column(),
                  currentQTableWidgetItem.text())

    def set_data(self, path: str) -> None:
        self.path = path
        with File(os.path.join(path), mode='r') as h5file:
            self.raw_data = get_dataset(h5file,
                                        DataPath.IMAGE_INTERPRETATION.value)[:]
        # We put the raw data in the dataviewer
        self.raw_data_viewer.set_movie(self.raw_data, self.flatfield_image)
        self.unfolded_data_tab.images = self.raw_data
        self.unfolded_data_tab.path = self.path
        # We allocate a number of view in the stack of unfolded data and fitting data
        self.unfolded_data_tab.viewer.set_stack_slider(self.raw_data.shape[0])
        self.fitting_data_selector.setData(
            numpy.zeros((self.raw_data.shape[0], 1, 1)))

    def set_calibration(self, calibration):
        # Check if there is a empty list of coordinate in the direct beam calibration
        if not [] in [value for value in calibration.values()]:
            self.unfolded_data_tab.set_calibration(calibration)
            if self.unfolded_data_tab.images is not None:
                self.unfolded_data_tab.start_unfolding()
        else:
            print("Direct beam not calibrated yet.")

    def get_calibration(self):
        self.unfoldButtonClicked.emit()

    def create_diagram_array(self):
        self.diagram_data_array = []
        numpy.seterr(divide='ignore', invalid='ignore')
        for image in self.unfolded_data_tab.viewer.get_scatter_items():
            self.diagram_data_array.append(
                extract_diffraction_diagram(
                    image[0],
                    image[1],
                    image[2],
                    1.0 / self.unfolded_data_tab.geometry["calib"],
                    -100,
                    100,
                    patch_data_flag=True))
        self.plot_diagram()
        self.automatic_fit_tab.set_data_to_fit(self.diagram_data_array)
        self.fitting_data_selector.selectionChanged.emit()
        set_plot_limits(self.diagram_data_plot,
                        self.diagram_data_plot.getActiveCurve())

    def plot_diagram(self, images_to_remove=[-1]):
        self.diagram_data_plot.setGraphTitle(
            f"Diagram diffraction of {self.path.split('/')[-1]}")
        for index, curve in enumerate(self.diagram_data_array):
            if index not in images_to_remove:
                self.diagram_data_plot.addCurve(curve[0],
                                                curve[1],
                                                f'Data of image {index}',
                                                color="#0000FF",
                                                replace=False,
                                                symbol='o')

    def get_flatfield(self, flat_img: numpy.ndarray):
        self.flatfield_image = flat_img
        self.raw_data_viewer.get_action_flatfield().set_flatfield(
            self.flatfield_image)
        self.unfolded_data_tab.flatfield = flat_img

    def synchronize_visualisation(self):
        # When user change the unfolded view, it set the raw image to the same frame
        self.raw_data_viewer.setFrameNumber(
            self.unfolded_data_tab.viewer.scatter_selector.selection()[0])

    def fitting_curve(self):
        if len(self.diagram_data_array) > 0:
            self.clear_plot_fitting_widget()
            curve = self.diagram_data_array[
                self.fitting_data_selector.selection()[0]]
            self.fitting_data_plot.addCurve(curve[0], curve[1], symbol='o')
            set_plot_limits(self.fitting_data_plot, curve)

    def get_roi_list(self, events: dict):
        self.rois_list = list(events["roilist"])

    def clear_plot_fitting_widget(self):
        self.fitting_data_plot.clear()
        self.fitting_data_plot.clearMarkers()
Beispiel #6
0
class FittingDataTab(QWidget):
    def __init__(self, parent, data_to_fit=None):
        super().__init__(parent)
        self._data_to_fit = data_to_fit
        self._fitted_data = []
        self.layout = QVBoxLayout(self)
        self.automatic_plot = Plot1D(self)
        self.fitting_data_selector = NumpyAxesSelector(self)
        self.fit = FitManager()

        self.fit.addtheory("pearson7",
                           function=pearson7bg,
                           parameters=[
                               'backgr', 'slopeLin', 'amplitude', 'center',
                               'fwhm', 'exposant'
                           ],
                           estimate=estimate_pearson7)
        """
        self.fitting_widget = self.fitting_data_plot.getFitAction()
        self.fit_action = FitAction(plot=self.fitting_data_plot, parent=self.fitting_data_plot)
        self.toolbar = QToolBar("New")
        """

        self.init_ui()

    def init_ui(self):
        self.setLayout(self.layout)
        self.layout.addWidget(self.automatic_plot)
        self.layout.addWidget(self.fitting_data_selector)

        self.fitting_data_selector.setNamedAxesSelectorVisibility(False)
        self.fitting_data_selector.setVisible(True)
        self.fitting_data_selector.setAxisNames("12")
        # self.fitting_data_selector.selectionChanged.connect(self.automatic_plot_fit)

    def set_data_to_fit(self, data_to_fit):
        self._data_to_fit = data_to_fit
        self.fitting_data_selector.setData(
            numpy.zeros((len(data_to_fit), 1, 1)))
        self.start_automatic_fit()

    def automatic_fit(self):
        if self._data_to_fit is not None:
            prominence = max(
                self._data_to_fit[0][1][~numpy.isnan(self._data_to_fit[0][1])])
            print(prominence)
            peaks, _ = find_peaks(self._data_to_fit[0][1],
                                  prominence=prominence / 3.0)
            plt.plot(peaks, self._data_to_fit[0][1][peaks], "xr")
            plt.plot(self._data_to_fit[0][1])
            plt.legend(['Test detection with prominence'])
            plt.show()

    def plot_fit(self):
        if len(self._fitted_data) > 0 and len(self._data_to_fit) > 0:
            self.automatic_plot.addCurve(
                self._data_to_fit[self.fitting_data_selector.selection()[0]]
                [0], self._data_to_fit[self.fitting_data_selector.selection()
                                       [0]][1], 'Data to fit')
            self.automatic_plot.addCurve(
                self._fitted_data[self.fitting_data_selector.selection()[0]]
                [0], self._fitted_data[self.fitting_data_selector.selection()
                                       [0]][1], 'Fitted data')

    def start_automatic_fit(self):
        self.fit.settheory("pearson7")
        print("Start fitting...")
        for data in self._data_to_fit:
            # copy the data arrays, with all the values even the nan ones to keep the size
            # the copy is used to not erase data on arrays ploted in the last panel
            x = data[0].copy()
            y = data[1].copy()
            # get the max of the y array without any nan value (it would be the maximum)
            maximum = max(y[~numpy.isnan(y)])
            # current maximum / peak we are looking for (for the first iteration it will be the max)
            current_maximum = max(y[~numpy.isnan(y)])
            try:
                cpt_peak = 0
                print("Searching peak and fitting it...")
                # plot the original curve, were we are going to plot each fitted peak.
                self.automatic_plot.addCurve(x, y, "Data to fit")
                print("Max : ", maximum, " Current max : ", current_maximum)
                # this threshold means that we only want peaks that are at least at 1/4 distance in y axis of the max.
                while current_maximum > (maximum / 4.0):
                    peak = numpy.where(y == current_maximum)[0][0]
                    left = peak - 35 if peak - 35 > 0 else 0
                    right = peak + 35 if peak + 35 < len(x) else len(x) - 1
                    x_peak = x[left:right]
                    y_peak = y[left:right]
                    # set the data to fit, i.e only the peak without all the curve
                    self.fit.setdata(x=x_peak, y=y_peak)
                    # use the estimate function we made to make a first guess of the parameters of the function that will fit our peak.
                    self.fit.estimate()
                    # fit the function.
                    self.fit.runfit()
                    # draw the resulted function of the fit.
                    self.automatic_plot.addCurve(
                        x_peak,
                        pearson7bg(
                            x_peak,
                            *(param['fitresult']
                              for param in self.fit.fit_results)),
                        f"Peak number {cpt_peak}")
                    self._fitted_data.append(
                        self.automatic_plot.getActiveCurve())

                    backgr = self.fit.fit_results[0]['fitresult']
                    fwhm = self.fit.fit_results[4]['fitresult']

                    # erase the peak to make it easier to found other peaks.
                    y[left:right] = statistics.mean(y[~numpy.isnan(y)])
                    current_maximum = max(y[~numpy.isnan(y)])
                    cpt_peak += 1
                    print("new current_max : ", current_maximum)

            except (numpy.linalg.LinAlgError, TypeError):
                print(
                    "Singular matrix error: fit is impossible with the given parameters"
                )
Beispiel #7
0
class UnfoldedDataViewer(QWidget):
    def __init__(self, parent):
        super().__init__(parent=parent)

        self.scatter_view = ScatterView(self)
        colormap = Colormap('viridis', normalization='log')
        self.scatter_view.setGraphTitle("Stack of unfolded data")
        self.scatter_view.setColormap(colormap)
        self.plot = self.scatter_view.getPlotWidget()
        self.plot.setGraphXLabel("two-theta (°)")
        self.plot.setGraphYLabel("psi (°)")
        self.plot.setKeepDataAspectRatio(False)
        self.plot.setYAxisInverted(True)

        self.scatter_selector = NumpyAxesSelector(self)
        # Prevent user from changing dimensions for the plot
        self.scatter_selector.setNamedAxesSelectorVisibility(False)
        self.scatter_selector.setVisible(True)
        self.scatter_selector.setAxisNames("12")

        self.layout = QVBoxLayout(self)
        self.layout.addWidget(self.scatter_view)
        self.layout.addWidget(self.scatter_selector)

        self.stack = []

        self.initial_data_flag = True

        self.toolbar = QToolBar("Custom toolbar 1")
        self.scatter_view.addToolBar(self.toolbar)

        self.action_unfold = Unfold(self.plot, parent=self)
        self.action_unfold_with_flatfield = UnfoldWithFlatfield(self.plot,
                                                                parent=self)
        self.action_save = SaveAction(self.plot, parent=self)

        self.toolbar.addAction(self.action_unfold)
        self.toolbar.addAction(self.action_unfold_with_flatfield)
        self.toolbar.addAction(self.action_save)

        self.scatter_selector.selectionChanged.connect(
            self.change_displayed_data)

    def add_scatter(self, scatter_image: tuple, scatter_factor: int):
        # Add an image to the stack. If it is the first, emit the selectionChanged signal to plot the first image
        self.stack.append((scatter_image[0][::scatter_factor],
                           scatter_image[1][::scatter_factor],
                           scatter_image[2][::scatter_factor]))
        if self.initial_data_flag:
            self.scatter_selector.selectionChanged.emit()
            self.initial_data_flag = False

    def set_stack_slider(self, nb_images: int):
        # Set the size of the sliderbar that will let the user navigate the images
        self.clear_scatter_view()
        self.scatter_selector.setData(numpy.zeros((nb_images, 1, 1)))

    def change_displayed_data(self):
        # If there is at least one unfolded image, clear the view, unpack the data and plot a scatter view of the image
        if len(self.stack) > 0:
            self.clear_scatter_view()
            tth_array, psi_array, intensity = self.stack[
                self.scatter_selector.selection()[0]]
            self.plot.setGraphXLimits(
                min(tth_array) - 0.0,
                max(tth_array) + 0.0)
            self.plot.setGraphYLimits(
                min(psi_array) - 5.0,
                max(psi_array) + 5.0)
            start = time.time()
            self.scatter_view.setData(tth_array,
                                      psi_array,
                                      intensity,
                                      copy=False)
            end = time.time()
            print("Setting the data took :", (end - start) * 1000.0, " ms")

    def clear_scatter_view(self):
        self.scatter_view.setData(None, None, None)

    def reset_scatter_view(self):
        self.clear_scatter_view()
        self.stack = []
        self.initial_data_flag = True

    def get_scatter_item(self, index: int) -> tuple:
        return self.stack[index]

    def get_scatter_items(self) -> list:
        return self.stack

    def get_unfold_action(self):
        return self.action_unfold

    def get_unfold_with_flatfield_action(self):
        return self.action_unfold_with_flatfield
Beispiel #8
0
 def test_creation(self):
     data = numpy.arange(3 * 3 * 3)
     data.shape = 3, 3, 3
     widget = NumpyAxesSelector()
     widget.setVisible(True)
Beispiel #9
0
 def test_creation(self):
     data = numpy.arange(3 * 3 * 3)
     data.shape = 3, 3, 3
     widget = NumpyAxesSelector()
     widget.setVisible(True)