Ejemplo n.º 1
0
class ArrayImagePlot(qt.QWidget):
    """
    Widget for plotting an image from a multi-dimensional signal array
    and two 1D axes array.

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

    Sliders are provided to select indices on the first (n - 2) dimensions of
    the signal array, and the plot is updated to show the image corresponding
    to the selection.

    If one or both of the axes does not have regularly spaced values, the
    the image is plotted as a coloured scatter plot.
    """
    def __init__(self, parent=None):
        """

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

        self.__signals = None
        self.__signals_names = None
        self.__x_axis = None
        self.__x_axis_name = None
        self.__y_axis = None
        self.__y_axis_name = None

        self._plot = Plot2D(self)
        self._plot.setDefaultColormap(
            Colormap(name="viridis",
                     vmin=None,
                     vmax=None,
                     normalization=Colormap.LINEAR))
        self._plot.getIntensityHistogramAction().setVisible(True)
        self._plot.setKeepDataAspectRatio(True)

        # not closable
        self._selector = NumpyAxesSelector(self)
        self._selector.setNamedAxesSelectorVisibility(False)
        self._selector.selectionChanged.connect(self._updateImage)

        self._auxSigSlider = HorizontalSliderWithBrowser(parent=self)
        self._auxSigSlider.setMinimum(0)
        self._auxSigSlider.setValue(0)
        self._auxSigSlider.valueChanged[int].connect(self._sliderIdxChanged)
        self._auxSigSlider.setToolTip("Select auxiliary signals")

        layout = qt.QVBoxLayout()
        layout.addWidget(self._plot)
        layout.addWidget(self._selector)
        layout.addWidget(self._auxSigSlider)

        self.setLayout(layout)

    def _sliderIdxChanged(self, value):
        self._updateImage()

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

        :rtype: Plot2D
        """
        return self._plot

    def setImageData(self,
                     signals,
                     x_axis=None,
                     y_axis=None,
                     signals_names=None,
                     xlabel=None,
                     ylabel=None,
                     title=None,
                     isRgba=False,
                     xscale=None,
                     yscale=None):
        """

        :param signals: list of n-D datasets, whose last 2 dimensions are used as the
            image's values, or list of 3D datasets interpreted as RGBA image.
        :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 signals_names: Names for each image, used as subtitle and legend.
        :param xlabel: Label for X axis
        :param ylabel: Label for Y axis
        :param title: Graph title
        :param isRgba: True if data is a 3D RGBA image
        :param str xscale: Scale of X axis in (None, 'linear', 'log')
        :param str yscale: Scale of Y axis in (None, 'linear', 'log')
        """
        self._selector.selectionChanged.disconnect(self._updateImage)
        self._auxSigSlider.valueChanged.disconnect(self._sliderIdxChanged)

        self.__signals = signals
        self.__signals_names = signals_names
        self.__x_axis = x_axis
        self.__x_axis_name = xlabel
        self.__y_axis = y_axis
        self.__y_axis_name = ylabel
        self.__title = title

        self._selector.clear()
        if not isRgba:
            self._selector.setAxisNames(["Y", "X"])
            img_ndim = 2
        else:
            self._selector.setAxisNames(["Y", "X", "RGB(A) channel"])
            img_ndim = 3
        self._selector.setData(signals[0])

        if len(signals[0].shape) <= img_ndim:
            self._selector.hide()
        else:
            self._selector.show()

        self._auxSigSlider.setMaximum(len(signals) - 1)
        if len(signals) > 1:
            self._auxSigSlider.show()
        else:
            self._auxSigSlider.hide()
        self._auxSigSlider.setValue(0)

        self._axis_scales = xscale, yscale
        self._updateImage()
        self._plot.resetZoom()

        self._selector.selectionChanged.connect(self._updateImage)
        self._auxSigSlider.valueChanged.connect(self._sliderIdxChanged)

    def _updateImage(self):
        selection = self._selector.selection()
        auxSigIdx = self._auxSigSlider.value()

        legend = self.__signals_names[auxSigIdx]

        images = [img[selection] for img in self.__signals]
        image = images[auxSigIdx]

        x_axis = self.__x_axis
        y_axis = self.__y_axis

        if x_axis is None and y_axis is None:
            xcalib = NoCalibration()
            ycalib = NoCalibration()
        else:
            if x_axis is None:
                # no calibration
                x_axis = numpy.arange(image.shape[1])
            elif numpy.isscalar(x_axis) or len(x_axis) == 1:
                # constant axis
                x_axis = x_axis * numpy.ones((image.shape[1], ))
            elif len(x_axis) == 2:
                # linear calibration
                x_axis = x_axis[0] * numpy.arange(image.shape[1]) + x_axis[1]

            if y_axis is None:
                y_axis = numpy.arange(image.shape[0])
            elif numpy.isscalar(y_axis) or len(y_axis) == 1:
                y_axis = y_axis * numpy.ones((image.shape[0], ))
            elif len(y_axis) == 2:
                y_axis = y_axis[0] * numpy.arange(image.shape[0]) + y_axis[1]

            xcalib = ArrayCalibration(x_axis)
            ycalib = ArrayCalibration(y_axis)

        self._plot.remove(kind=(
            "scatter",
            "image",
        ))
        if xcalib.is_affine() and ycalib.is_affine():
            # regular image
            xorigin, xscale = xcalib(0), xcalib.get_slope()
            yorigin, yscale = ycalib(0), ycalib.get_slope()
            origin = (xorigin, yorigin)
            scale = (xscale, yscale)

            self._plot.getXAxis().setScale('linear')
            self._plot.getYAxis().setScale('linear')
            self._plot.addImage(image,
                                legend=legend,
                                origin=origin,
                                scale=scale,
                                replace=True,
                                resetzoom=False)
        else:
            xaxisscale, yaxisscale = self._axis_scales

            if xaxisscale is not None:
                self._plot.getXAxis().setScale('log' if xaxisscale ==
                                               'log' else 'linear')
            if yaxisscale is not None:
                self._plot.getYAxis().setScale('log' if yaxisscale ==
                                               'log' else 'linear')

            scatterx, scattery = numpy.meshgrid(x_axis, y_axis)
            # fixme: i don't think this can handle "irregular" RGBA images
            self._plot.addScatter(numpy.ravel(scatterx),
                                  numpy.ravel(scattery),
                                  numpy.ravel(image),
                                  legend=legend)

        if self.__title:
            title = self.__title
            if len(self.__signals_names) > 1:
                # Append dataset name only when there is many datasets
                title += '\n' + self.__signals_names[auxSigIdx]
        else:
            title = self.__signals_names[auxSigIdx]
        self._plot.setGraphTitle(title)
        self._plot.getXAxis().setLabel(self.__x_axis_name)
        self._plot.getYAxis().setLabel(self.__y_axis_name)

    def clear(self):
        old = self._selector.blockSignals(True)
        self._selector.clear()
        self._selector.blockSignals(old)
        self._plot.clear()
Ejemplo n.º 2
0
class ArrayComplexImagePlot(qt.QWidget):
    """
    Widget for plotting an image of complex from a multi-dimensional signal array
    and two 1D axes array.

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

    Sliders are provided to select indices on the first (n - 2) dimensions of
    the signal array, and the plot is updated to show the image corresponding
    to the selection.

    If one or both of the axes does not have regularly spaced values, the
    the image is plotted as a coloured scatter plot.
    """
    def __init__(self, parent=None, colormap=None):
        """

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

        self.__signals = None
        self.__signals_names = None
        self.__x_axis = None
        self.__x_axis_name = None
        self.__y_axis = None
        self.__y_axis_name = None

        self._plot = ComplexImageView(self)
        if colormap is not None:
            for mode in (ComplexImageView.ComplexMode.ABSOLUTE,
                         ComplexImageView.ComplexMode.SQUARE_AMPLITUDE,
                         ComplexImageView.ComplexMode.REAL,
                         ComplexImageView.ComplexMode.IMAGINARY):
                self._plot.setColormap(colormap, mode)

        self._plot.getPlot().getIntensityHistogramAction().setVisible(True)
        self._plot.setKeepDataAspectRatio(True)

        # not closable
        self._selector = NumpyAxesSelector(self)
        self._selector.setNamedAxesSelectorVisibility(False)
        self._selector.selectionChanged.connect(self._updateImage)

        self._auxSigSlider = HorizontalSliderWithBrowser(parent=self)
        self._auxSigSlider.setMinimum(0)
        self._auxSigSlider.setValue(0)
        self._auxSigSlider.valueChanged[int].connect(self._sliderIdxChanged)
        self._auxSigSlider.setToolTip("Select auxiliary signals")

        layout = qt.QVBoxLayout()
        layout.addWidget(self._plot)
        layout.addWidget(self._selector)
        layout.addWidget(self._auxSigSlider)

        self.setLayout(layout)

    def _sliderIdxChanged(self, value):
        self._updateImage()

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

        :rtype: PlotWidget
        """
        return self._plot.getPlot()

    def setImageData(self,
                     signals,
                     x_axis=None,
                     y_axis=None,
                     signals_names=None,
                     xlabel=None,
                     ylabel=None,
                     title=None):
        """

        :param signals: list of n-D datasets, whose last 2 dimensions are used as the
            image's values, or list of 3D datasets interpreted as RGBA image.
        :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 signals_names: Names for each image, used as subtitle and legend.
        :param xlabel: Label for X axis
        :param ylabel: Label for Y axis
        :param title: Graph title
        """
        self._selector.selectionChanged.disconnect(self._updateImage)
        self._auxSigSlider.valueChanged.disconnect(self._sliderIdxChanged)

        self.__signals = signals
        self.__signals_names = signals_names
        self.__x_axis = x_axis
        self.__x_axis_name = xlabel
        self.__y_axis = y_axis
        self.__y_axis_name = ylabel
        self.__title = title

        self._selector.clear()
        self._selector.setAxisNames(["Y", "X"])
        self._selector.setData(signals[0])

        if len(signals[0].shape) <= 2:
            self._selector.hide()
        else:
            self._selector.show()

        self._auxSigSlider.setMaximum(len(signals) - 1)
        if len(signals) > 1:
            self._auxSigSlider.show()
        else:
            self._auxSigSlider.hide()
        self._auxSigSlider.setValue(0)

        self._updateImage()
        self._plot.getPlot().resetZoom()

        self._selector.selectionChanged.connect(self._updateImage)
        self._auxSigSlider.valueChanged.connect(self._sliderIdxChanged)

    def _updateImage(self):
        selection = self._selector.selection()
        auxSigIdx = self._auxSigSlider.value()

        images = [img[selection] for img in self.__signals]
        image = images[auxSigIdx]

        x_axis = self.__x_axis
        y_axis = self.__y_axis

        if x_axis is None and y_axis is None:
            xcalib = NoCalibration()
            ycalib = NoCalibration()
        else:
            if x_axis is None:
                # no calibration
                x_axis = numpy.arange(image.shape[1])
            elif numpy.isscalar(x_axis) or len(x_axis) == 1:
                # constant axis
                x_axis = x_axis * numpy.ones((image.shape[1], ))
            elif len(x_axis) == 2:
                # linear calibration
                x_axis = x_axis[0] * numpy.arange(image.shape[1]) + x_axis[1]

            if y_axis is None:
                y_axis = numpy.arange(image.shape[0])
            elif numpy.isscalar(y_axis) or len(y_axis) == 1:
                y_axis = y_axis * numpy.ones((image.shape[0], ))
            elif len(y_axis) == 2:
                y_axis = y_axis[0] * numpy.arange(image.shape[0]) + y_axis[1]

            xcalib = ArrayCalibration(x_axis)
            ycalib = ArrayCalibration(y_axis)

        self._plot.setData(image)
        if xcalib.is_affine():
            xorigin, xscale = xcalib(0), xcalib.get_slope()
        else:
            _logger.warning("Unsupported complex image X axis calibration")
            xorigin, xscale = 0., 1.

        if ycalib.is_affine():
            yorigin, yscale = ycalib(0), ycalib.get_slope()
        else:
            _logger.warning("Unsupported complex image Y axis calibration")
            yorigin, yscale = 0., 1.

        self._plot.setOrigin((xorigin, yorigin))
        self._plot.setScale((xscale, yscale))

        if self.__title:
            title = self.__title
            if len(self.__signals_names) > 1:
                # Append dataset name only when there is many datasets
                title += '\n' + self.__signals_names[auxSigIdx]
        else:
            title = self.__signals_names[auxSigIdx]
        self._plot.setGraphTitle(title)
        self._plot.getXAxis().setLabel(self.__x_axis_name)
        self._plot.getYAxis().setLabel(self.__y_axis_name)

    def clear(self):
        old = self._selector.blockSignals(True)
        self._selector.clear()
        self._selector.blockSignals(old)
        self._plot.setData(None)
Ejemplo n.º 3
0
class XYVScatterPlot(qt.QWidget):
    """
    Widget for plotting one or more scatters
    (with identical x, y coordinates).
    """
    def __init__(self, parent=None):
        """

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

        self.__y_axis = None
        """1D array"""
        self.__y_axis_name = None
        self.__values = None
        """List of 1D arrays (for multiple scatters with identical
        x, y coordinates)"""

        self.__x_axis = None
        self.__x_axis_name = None
        self.__x_axis_errors = None
        self.__y_axis = None
        self.__y_axis_name = None
        self.__y_axis_errors = None

        self._plot = ScatterView(self)
        self._plot.setColormap(
            Colormap(name="viridis",
                     vmin=None,
                     vmax=None,
                     normalization=Colormap.LINEAR))

        self._slider = HorizontalSliderWithBrowser(parent=self)
        self._slider.setMinimum(0)
        self._slider.setValue(0)
        self._slider.valueChanged[int].connect(self._sliderIdxChanged)
        self._slider.setToolTip("Select auxiliary signals")

        layout = qt.QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self._plot, 0, 0)
        layout.addWidget(self._slider, 1, 0)

        self.setLayout(layout)

    def _sliderIdxChanged(self, value):
        self._updateScatter()

    def getScatterView(self):
        """Returns the :class:`ScatterView` used for the display

        :rtype: ScatterView
        """
        return self._plot

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

        :rtype: PlotWidget
        """
        return self._plot.getPlotWidget()

    def setScattersData(self,
                        y,
                        x,
                        values,
                        yerror=None,
                        xerror=None,
                        ylabel=None,
                        xlabel=None,
                        title="",
                        scatter_titles=None,
                        xscale=None,
                        yscale=None):
        """

        :param ndarray y: 1D array  for y (vertical) coordinates.
        :param ndarray x: 1D array for x coordinates.
        :param List[ndarray] values: List of 1D arrays of values.
            This will be used to compute the color map and assign colors
            to the points. There should be as many arrays in the list as
            scatters to be represented.
        :param ndarray yerror: 1D array of errors for y (same shape), or None.
        :param ndarray xerror: 1D array of errors for x, or None
        :param str ylabel: Label for Y axis
        :param str xlabel: Label for X axis
        :param str title: Main graph title
        :param List[str] scatter_titles:  Subtitles (one per scatter)
        :param str xscale: Scale of X axis in (None, 'linear', 'log')
        :param str yscale: Scale of Y axis in (None, 'linear', 'log')
        """
        self.__y_axis = y
        self.__x_axis = x
        self.__x_axis_name = xlabel or "X"
        self.__y_axis_name = ylabel or "Y"
        self.__x_axis_errors = xerror
        self.__y_axis_errors = yerror
        self.__values = values

        self.__graph_title = title or ""
        self.__scatter_titles = scatter_titles

        self._slider.valueChanged[int].disconnect(self._sliderIdxChanged)
        self._slider.setMaximum(len(values) - 1)
        if len(values) > 1:
            self._slider.show()
        else:
            self._slider.hide()
        self._slider.setValue(0)
        self._slider.valueChanged[int].connect(self._sliderIdxChanged)

        if xscale is not None:
            self._plot.getXAxis().setScale('log' if xscale ==
                                           'log' else 'linear')
        if yscale is not None:
            self._plot.getYAxis().setScale('log' if yscale ==
                                           'log' else 'linear')

        self._updateScatter()

    def _updateScatter(self):
        x = self.__x_axis
        y = self.__y_axis

        idx = self._slider.value()

        if self.__graph_title:
            title = self.__graph_title  # main NXdata @title
            if len(self.__scatter_titles) > 1:
                # Append dataset name only when there is many datasets
                title += '\n' + self.__scatter_titles[idx]
        else:
            title = self.__scatter_titles[idx]  # scatter dataset name

        self._plot.setGraphTitle(title)
        self._plot.setData(x,
                           y,
                           self.__values[idx],
                           xerror=self.__x_axis_errors,
                           yerror=self.__y_axis_errors)
        self._plot.resetZoom()
        self._plot.getXAxis().setLabel(self.__x_axis_name)
        self._plot.getYAxis().setLabel(self.__y_axis_name)

    def clear(self):
        self._plot.getPlotWidget().clear()
Ejemplo n.º 4
0
class ArrayImagePlot(qt.QWidget):
    """
    Widget for plotting an image from a multi-dimensional signal array
    and two 1D axes array.

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

    Sliders are provided to select indices on the first (n - 2) dimensions of
    the signal array, and the plot is updated to show the image corresponding
    to the selection.

    If one or both of the axes does not have regularly spaced values, the
    the image is plotted as a coloured scatter plot.
    """
    def __init__(self, parent=None):
        """

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

        self.__signals = None
        self.__signals_names = None
        self.__x_axis = None
        self.__x_axis_name = None
        self.__y_axis = None
        self.__y_axis_name = None

        self._plot = Plot2D(self)
        self._plot.setDefaultColormap(Colormap(name="viridis",
                                               vmin=None, vmax=None,
                                               normalization=Colormap.LINEAR))
        self._plot.getIntensityHistogramAction().setVisible(True)

        # not closable
        self._selector = NumpyAxesSelector(self)
        self._selector.setNamedAxesSelectorVisibility(False)
        self._selector.selectionChanged.connect(self._updateImage)

        self._auxSigSlider = HorizontalSliderWithBrowser(parent=self)
        self._auxSigSlider.setMinimum(0)
        self._auxSigSlider.setValue(0)
        self._auxSigSlider.valueChanged[int].connect(self._sliderIdxChanged)
        self._auxSigSlider.setToolTip("Select auxiliary signals")

        layout = qt.QVBoxLayout()
        layout.addWidget(self._plot)
        layout.addWidget(self._selector)
        layout.addWidget(self._auxSigSlider)

        self.setLayout(layout)

    def _sliderIdxChanged(self, value):
        self._updateImage()

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

        :rtype: Plot2D
        """
        return self._plot

    def setImageData(self, signals,
                     x_axis=None, y_axis=None,
                     signals_names=None,
                     xlabel=None, ylabel=None,
                     title=None, isRgba=False):
        """

        :param signals: list of n-D datasets, whose last 2 dimensions are used as the
            image's values, or list of 3D datasets interpreted as RGBA image.
        :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 signals_names: Names for each image, used as subtitle and legend.
        :param xlabel: Label for X axis
        :param ylabel: Label for Y axis
        :param title: Graph title
        :param isRgba: True if data is a 3D RGBA image
        """
        self._selector.selectionChanged.disconnect(self._updateImage)
        self._auxSigSlider.valueChanged.disconnect(self._sliderIdxChanged)

        self.__signals = signals
        self.__signals_names = signals_names
        self.__x_axis = x_axis
        self.__x_axis_name = xlabel
        self.__y_axis = y_axis
        self.__y_axis_name = ylabel
        self.__title = title

        self._selector.clear()
        if not isRgba:
            self._selector.setAxisNames(["Y", "X"])
            img_ndim = 2
        else:
            self._selector.setAxisNames(["Y", "X", "RGB(A) channel"])
            img_ndim = 3
        self._selector.setData(signals[0])

        if len(signals[0].shape) <= img_ndim:
            self._selector.hide()
        else:
            self._selector.show()

        self._auxSigSlider.setMaximum(len(signals) - 1)
        if len(signals) > 1:
            self._auxSigSlider.show()
        else:
            self._auxSigSlider.hide()
        self._auxSigSlider.setValue(0)

        self._updateImage()

        self._selector.selectionChanged.connect(self._updateImage)
        self._auxSigSlider.valueChanged.connect(self._sliderIdxChanged)

    def _updateImage(self):
        selection = self._selector.selection()
        auxSigIdx = self._auxSigSlider.value()

        legend = self.__signals_names[auxSigIdx]

        images = [img[selection] for img in self.__signals]
        image = images[auxSigIdx]

        x_axis = self.__x_axis
        y_axis = self.__y_axis

        if x_axis is None and y_axis is None:
            xcalib = NoCalibration()
            ycalib = NoCalibration()
        else:
            if x_axis is None:
                # no calibration
                x_axis = numpy.arange(image.shape[1])
            elif numpy.isscalar(x_axis) or len(x_axis) == 1:
                # constant axis
                x_axis = x_axis * numpy.ones((image.shape[1], ))
            elif len(x_axis) == 2:
                # linear calibration
                x_axis = x_axis[0] * numpy.arange(image.shape[1]) + x_axis[1]

            if y_axis is None:
                y_axis = numpy.arange(image.shape[0])
            elif numpy.isscalar(y_axis) or len(y_axis) == 1:
                y_axis = y_axis * numpy.ones((image.shape[0], ))
            elif len(y_axis) == 2:
                y_axis = y_axis[0] * numpy.arange(image.shape[0]) + y_axis[1]

            xcalib = ArrayCalibration(x_axis)
            ycalib = ArrayCalibration(y_axis)

        self._plot.remove(kind=("scatter", "image",))
        if xcalib.is_affine() and ycalib.is_affine():
            # regular image
            xorigin, xscale = xcalib(0), xcalib.get_slope()
            yorigin, yscale = ycalib(0), ycalib.get_slope()
            origin = (xorigin, yorigin)
            scale = (xscale, yscale)

            self._plot.addImage(image, legend=legend,
                                origin=origin, scale=scale,
                                replace=True)
        else:
            scatterx, scattery = numpy.meshgrid(x_axis, y_axis)
            # fixme: i don't think this can handle "irregular" RGBA images
            self._plot.addScatter(numpy.ravel(scatterx),
                                  numpy.ravel(scattery),
                                  numpy.ravel(image),
                                  legend=legend)

        title = ""
        if self.__title:
            title += self.__title
        if not title.strip().endswith(self.__signals_names[auxSigIdx]):
            title += "\n" + self.__signals_names[auxSigIdx]
        self._plot.setGraphTitle(title)
        self._plot.getXAxis().setLabel(self.__x_axis_name)
        self._plot.getYAxis().setLabel(self.__y_axis_name)
        self._plot.resetZoom()

    def clear(self):
        old = self._selector.blockSignals(True)
        self._selector.clear()
        self._selector.blockSignals(old)
        self._plot.clear()
Ejemplo n.º 5
0
class XYVScatterPlot(qt.QWidget):
    """
    Widget for plotting one or more scatters
    (with identical x, y coordinates).
    """
    def __init__(self, parent=None):
        """

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

        self.__y_axis = None
        """1D array"""
        self.__y_axis_name = None
        self.__values = None
        """List of 1D arrays (for multiple scatters with identical
        x, y coordinates)"""

        self.__x_axis = None
        self.__x_axis_name = None
        self.__x_axis_errors = None
        self.__y_axis = None
        self.__y_axis_name = None
        self.__y_axis_errors = None

        self._plot = ScatterView(self)
        self._plot.setColormap(Colormap(name="viridis",
                                        vmin=None, vmax=None,
                                        normalization=Colormap.LINEAR))

        self._slider = HorizontalSliderWithBrowser(parent=self)
        self._slider.setMinimum(0)
        self._slider.setValue(0)
        self._slider.valueChanged[int].connect(self._sliderIdxChanged)
        self._slider.setToolTip("Select auxiliary signals")

        layout = qt.QGridLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self._plot, 0, 0)
        layout.addWidget(self._slider, 1, 0)

        self.setLayout(layout)

    def _sliderIdxChanged(self, value):
        self._updateScatter()

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

        :rtype: PlotWidget
        """
        return self._plot.getPlotWidget()

    def setScattersData(self, y, x, values,
                        yerror=None, xerror=None,
                        ylabel=None, xlabel=None,
                        title="", scatter_titles=None):
        """

        :param ndarray y: 1D array  for y (vertical) coordinates.
        :param ndarray x: 1D array for x coordinates.
        :param List[ndarray] values: List of 1D arrays of values.
            This will be used to compute the color map and assign colors
            to the points. There should be as many arrays in the list as
            scatters to be represented.
        :param ndarray yerror: 1D array of errors for y (same shape), or None.
        :param ndarray xerror: 1D array of errors for x, or None
        :param str ylabel: Label for Y axis
        :param str xlabel: Label for X axis
        :param str title: Main graph title
        :param List[str] scatter_titles:  Subtitles (one per scatter)
        """
        self.__y_axis = y
        self.__x_axis = x
        self.__x_axis_name = xlabel or "X"
        self.__y_axis_name = ylabel or "Y"
        self.__x_axis_errors = xerror
        self.__y_axis_errors = yerror
        self.__values = values

        self.__graph_title = title or ""
        self.__scatter_titles = scatter_titles

        self._slider.valueChanged[int].disconnect(self._sliderIdxChanged)
        self._slider.setMaximum(len(values) - 1)
        if len(values) > 1:
            self._slider.show()
        else:
            self._slider.hide()
        self._slider.setValue(0)
        self._slider.valueChanged[int].connect(self._sliderIdxChanged)

        self._updateScatter()

    def _updateScatter(self):
        x = self.__x_axis
        y = self.__y_axis

        idx = self._slider.value()

        title = ""
        if self.__graph_title:
            title += self.__graph_title + "\n"  # main NXdata @title
        title += self.__scatter_titles[idx]     # scatter dataset name

        self._plot.setGraphTitle(title)
        self._plot.setData(x, y, self.__values[idx],
                           xerror=self.__x_axis_errors,
                           yerror=self.__y_axis_errors)
        self._plot.resetZoom()
        self._plot.getXAxis().setLabel(self.__x_axis_name)
        self._plot.getYAxis().setLabel(self.__y_axis_name)

    def clear(self):
        self._plot.getPlotWidget().clear()
Ejemplo n.º 6
0
class _Axis(qt.QWidget):
    """Widget displaying an axis.

    It allows to display and scroll in the axis, and provide a widget to
    map the axis with a named axis (the one from the view).
    """

    valueChanged = qt.Signal(int)
    """Emitted when the location on the axis change."""

    axisNameChanged = qt.Signal(object)
    """Emitted when the user change the name of the axis."""
    def __init__(self, parent=None):
        """Constructor

        :param parent: Parent of the widget
        """
        super(_Axis, self).__init__(parent)
        self.__axisNumber = None
        self.__customAxisNames = set([])
        self.__label = qt.QLabel(self)
        self.__axes = qt.QComboBox(self)
        self.__axes.currentIndexChanged[int].connect(self.__axisMappingChanged)
        self.__slider = HorizontalSliderWithBrowser(self)
        self.__slider.valueChanged[int].connect(self.__sliderValueChanged)
        layout = qt.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.__label)
        layout.addWidget(self.__axes)
        layout.addWidget(self.__slider, 10000)
        layout.addStretch(1)
        self.setLayout(layout)

    def slider(self):
        """Returns the slider used to display axes location.

        :rtype: HorizontalSliderWithBrowser
        """
        return self.__slider

    def setAxis(self, number, position, size):
        """Set axis information.

        :param int number: The number of the axis (from the original numpy
            array)
        :param int position: The current position in the axis (for a slicing)
        :param int size: The size of this axis (0..n)
        """
        self.__label.setText("Dimension %s" % number)
        self.__axisNumber = number
        self.__slider.setMaximum(size - 1)

    def axisNumber(self):
        """Returns the axis number.

        :rtype: int
        """
        return self.__axisNumber

    def setAxisName(self, axisName):
        """Set the current used axis name.

        If this name is not available an exception is raised. An empty string
        means that no name is selected.

        :param str axisName: The new name of the axis
        :raise ValueError: When the name is not available
        """
        if axisName == "" and self.__axes.count() == 0:
            self.__axes.setCurrentIndex(-1)
            self.__updateSliderVisibility()
        for index in range(self.__axes.count()):
            name = self.__axes.itemData(index)
            if name == axisName:
                self.__axes.setCurrentIndex(index)
                self.__updateSliderVisibility()
                return
        raise ValueError("Axis name '%s' not found", axisName)

    def axisName(self):
        """Returns the selected axis name.

        If no names are selected, an empty string is retruned.

        :rtype: str
        """
        index = self.__axes.currentIndex()
        if index == -1:
            return ""
        return self.__axes.itemData(index)

    def setAxisNames(self, axesNames):
        """Set the available list of names for the axis.

        :param list[str] axesNames: List of available names
        """
        self.__axes.clear()
        previous = self.__axes.blockSignals(True)
        self.__axes.addItem(" ", "")
        for axis in axesNames:
            self.__axes.addItem(axis, axis)
        self.__axes.blockSignals(previous)
        self.__updateSliderVisibility()

    def setCustomAxis(self, axesNames):
        """Set the available list of named axis which can be set to a value.

        :param list[str] axesNames: List of customable axis names
        """
        self.__customAxisNames = set(axesNames)
        self.__updateSliderVisibility()

    def __axisMappingChanged(self, index):
        """Called when the selected name change.

        :param int index: Selected index
        """
        self.__updateSliderVisibility()
        name = self.axisName()
        self.axisNameChanged.emit(name)

    def __updateSliderVisibility(self):
        """Update the visibility of the slider according to axis names and
        customable axis names."""
        name = self.axisName()
        isVisible = name == "" or name in self.__customAxisNames
        self.__slider.setVisible(isVisible)

    def value(self):
        """Returns the current selected position in the axis.

        :rtype: int
        """
        return self.__slider.value()

    def __sliderValueChanged(self, value):
        """Called when the selected position in the axis change.

        :param int value: Position of the axis
        """
        self.valueChanged.emit(value)

    def setNamedAxisSelectorVisibility(self, visible):
        """Hide or show the named axis combobox.
        If both the selector and the slider are hidden,
        hide the entire widget.

        :param visible: boolean
        """
        self.__axes.setVisible(visible)
        name = self.axisName()

        if not visible and name != "":
            self.setVisible(False)
        else:
            self.setVisible(True)
Ejemplo n.º 7
0
class _Axis(qt.QWidget):
    """Widget displaying an axis.

    It allows to display and scroll in the axis, and provide a widget to
    map the axis with a named axis (the one from the view).
    """

    valueChanged = qt.Signal(int)
    """Emitted when the location on the axis change."""

    axisNameChanged = qt.Signal(object)
    """Emitted when the user change the name of the axis."""

    def __init__(self, parent=None):
        """Constructor

        :param parent: Parent of the widget
        """
        super(_Axis, self).__init__(parent)
        self.__axisNumber = None
        self.__customAxisNames = set([])
        self.__label = qt.QLabel(self)
        self.__axes = qt.QComboBox(self)
        self.__axes.currentIndexChanged[int].connect(self.__axisMappingChanged)
        self.__slider = HorizontalSliderWithBrowser(self)
        self.__slider.valueChanged[int].connect(self.__sliderValueChanged)
        layout = qt.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.__label)
        layout.addWidget(self.__axes)
        layout.addWidget(self.__slider, 10000)
        layout.addStretch(1)
        self.setLayout(layout)

    def slider(self):
        """Returns the slider used to display axes location.

        :rtype: HorizontalSliderWithBrowser
        """
        return self.__slider

    def setAxis(self, number, position, size):
        """Set axis information.

        :param int number: The number of the axis (from the original numpy
            array)
        :param int position: The current position in the axis (for a slicing)
        :param int size: The size of this axis (0..n)
        """
        self.__label.setText("Dimension %s" % number)
        self.__axisNumber = number
        self.__slider.setMaximum(size - 1)

    def axisNumber(self):
        """Returns the axis number.

        :rtype: int
        """
        return self.__axisNumber

    def setAxisName(self, axisName):
        """Set the current used axis name.

        If this name is not available an exception is raised. An empty string
        means that no name is selected.

        :param str axisName: The new name of the axis
        :raise ValueError: When the name is not available
        """
        if axisName == "" and self.__axes.count() == 0:
            self.__axes.setCurrentIndex(-1)
            self.__updateSliderVisibility()
        for index in range(self.__axes.count()):
            name = self.__axes.itemData(index)
            if name == axisName:
                self.__axes.setCurrentIndex(index)
                self.__updateSliderVisibility()
                return
        raise ValueError("Axis name '%s' not found", axisName)

    def axisName(self):
        """Returns the selected axis name.

        If no names are selected, an empty string is retruned.

        :rtype: str
        """
        index = self.__axes.currentIndex()
        if index == -1:
            return ""
        return self.__axes.itemData(index)

    def setAxisNames(self, axesNames):
        """Set the available list of names for the axis.

        :param List[str] axesNames: List of available names
        """
        self.__axes.clear()
        previous = self.__axes.blockSignals(True)
        self.__axes.addItem(" ", "")
        for axis in axesNames:
            self.__axes.addItem(axis, axis)
        self.__axes.blockSignals(previous)
        self.__updateSliderVisibility()

    def setCustomAxis(self, axesNames):
        """Set the available list of named axis which can be set to a value.

        :param List[str] axesNames: List of customable axis names
        """
        self.__customAxisNames = set(axesNames)
        self.__updateSliderVisibility()

    def __axisMappingChanged(self, index):
        """Called when the selected name change.

        :param int index: Selected index
        """
        self.__updateSliderVisibility()
        name = self.axisName()
        self.axisNameChanged.emit(name)

    def __updateSliderVisibility(self):
        """Update the visibility of the slider according to axis names and
        customable axis names."""
        name = self.axisName()
        isVisible = name == "" or name in self.__customAxisNames
        self.__slider.setVisible(isVisible)

    def value(self):
        """Returns the current selected position in the axis.

        :rtype: int
        """
        return self.__slider.value()

    def __sliderValueChanged(self, value):
        """Called when the selected position in the axis change.

        :param int value: Position of the axis
        """
        self.valueChanged.emit(value)

    def setNamedAxisSelectorVisibility(self, visible):
        """Hide or show the named axis combobox.
        If both the selector and the slider are hidden,
        hide the entire widget.

        :param visible: boolean
        """
        self.__axes.setVisible(visible)
        name = self.axisName()

        if not visible and name != "":
            self.setVisible(False)
        else:
            self.setVisible(True)