Skip to main content
`Ctrl`+`K`

# Source code for silx.gui.plot.backends.BackendBase

    # /*##########################################################################
    #
    # Copyright (c) 2004-2023 European Synchrotron Radiation Facility
    #
    # Permission is hereby granted, free of charge, to any person obtaining a copy
    # of this software and associated documentation files (the "Software"), to deal
    # in the Software without restriction, including without limitation the rights
    # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    # copies of the Software, and to permit persons to whom the Software is
    # furnished to do so, subject to the following conditions:
    #
    # The above copyright notice and this permission notice shall be included in
    # all copies or substantial portions of the Software.
    #
    # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    # THE SOFTWARE.
    #
    # ############################################################################*/
    """Base class for Plot backends.

    It documents the Plot backend API.

    This API is a simplified version of PyMca PlotBackend API.
    """

    from __future__ import annotations

    __authors__ = ["V.A. Sole", "T. Vincent"]
    __license__ = "MIT"
    __date__ = "21/12/2018"

    from collections.abc import Callable
    import weakref
    from silx.gui.colors import RGBAColorType

    from ... import qt

    # Names for setCursor
    CURSOR_DEFAULT = "default"
    CURSOR_POINTING = "pointing"
    CURSOR_SIZE_HOR = "size horizontal"
    CURSOR_SIZE_VER = "size vertical"
    CURSOR_SIZE_ALL = "size all"

    class BackendBase(object):
        """Class defining the API a backend of the Plot should provide."""

        def __init__(self, plot, parent=None):
            """Init.

            :param Plot plot: The Plot this backend is attached to
            :param parent: The parent widget of the plot widget.
            """
            self.__xLimits = 1.0, 100.0
            self.__yLimits = {"left": (1.0, 100.0), "right": (1.0, 100.0)}
            self.__yAxisInverted = False
            self.__keepDataAspectRatio = False
            self.__xAxisTimeSeries = False
            self._xAxisTimeZone = None
            # Store a weakref to get access to the plot state.
            self._setPlot(plot)

        @property
        def _plot(self):
            """The plot this backend is attached to."""
            if self._plotRef is None:
                raise RuntimeError("This backend is not attached to a Plot")

            plot = self._plotRef()
            if plot is None:
                raise RuntimeError("This backend is no more attached to a Plot")
            return plot

        def _setPlot(self, plot):
            """Allow to set plot after init.

            Use with caution, basically **immediately** after init.
            """
            self._plotRef = weakref.ref(plot)

        # Add methods

        def addCurve(
            self,
            x,
            y,
            color,
            gapcolor,
            symbol,
            linewidth,
            linestyle,
            yaxis,
            xerror,
            yerror,
            fill,
            alpha,
            symbolsize,
            baseline,
        ):
            """Add a 1D curve given by x an y to the graph.

            :param numpy.ndarray x: The data corresponding to the x axis
            :param numpy.ndarray y: The data corresponding to the y axis
            :param color: color(s) to be used
            :type color: string ("#RRGGBB") or (npoints, 4) unsigned byte array or
                         one of the predefined color names defined in colors.py
            :param Union[str, None] gapcolor:
                color used to fill dashed line gaps.
            :param str symbol: Symbol to be drawn at each (x, y) position::

                - ' ' or '' no symbol
                - 'o' circle
                - '.' point
                - ',' pixel
                - '+' cross
                - 'x' x-cross
                - 'd' diamond
                - 's' square

            :param float linewidth: The width of the curve in pixels
            :param linestyle: Type of line::

                - ' ' or ''  no line
                - '-'  solid line
                - '--' dashed line
                - '-.' dash-dot line
                - ':'  dotted line
                - (offset, (dash pattern))

            :param str yaxis: The Y axis this curve belongs to in: 'left', 'right'
            :param xerror: Values with the uncertainties on the x values
            :type xerror: numpy.ndarray or None
            :param yerror: Values with the uncertainties on the y values
            :type yerror: numpy.ndarray or None
            :param bool fill: True to fill the curve, False otherwise
            :param float alpha: Curve opacity, as a float in [0., 1.]
            :param float symbolsize: Size of the symbol (if any) drawn
                                     at each (x, y) position.
            :returns: The handle used by the backend to univocally access the curve
            """
            return object()

        def addImage(self, data, origin, scale, colormap, alpha):
            """Add an image to the plot.

            :param numpy.ndarray data: (nrows, ncolumns) data or
                         (nrows, ncolumns, RGBA) ubyte array
            :param origin: (origin X, origin Y) of the data.
                           Default: (0., 0.)
            :type origin: 2-tuple of float
            :param scale: (scale X, scale Y) of the data.
                           Default: (1., 1.)
            :type scale: 2-tuple of float
            :param ~silx.gui.colors.Colormap colormap: Colormap object to use.
                Ignored if data is RGB(A).
            :param float alpha: Opacity of the image, as a float in range [0, 1].
            :returns: The handle used by the backend to univocally access the image
            """
            return object()

        def addTriangles(self, x, y, triangles, color, alpha):
            """Add a set of triangles.

            :param numpy.ndarray x: The data corresponding to the x axis
            :param numpy.ndarray y: The data corresponding to the y axis
            :param numpy.ndarray triangles: The indices to make triangles
                as a (Ntriangle, 3) array
            :param numpy.ndarray color: color(s) as (npoints, 4) array
            :param float alpha: Opacity as a float in [0., 1.]
            :returns: The triangles' unique identifier used by the backend
            """
            return object()

        def addShape(
            self, x, y, shape, color, fill, overlay, linestyle, linewidth, gapcolor
        ):
            """Add an item (i.e. a shape) to the plot.

            :param numpy.ndarray x: The X coords of the points of the shape
            :param numpy.ndarray y: The Y coords of the points of the shape
            :param str shape: Type of item to be drawn in
                              hline, polygon, rectangle, vline, polylines
            :param str color: Color of the item
            :param bool fill: True to fill the shape
            :param bool overlay: True if item is an overlay, False otherwise
            :param linestyle: Style of the line.
                Only relevant for line markers where X or Y is None.
                Value in:

                - ' '  no line
                - '-'  solid line
                - '--' dashed line
                - '-.' dash-dot line
                - ':'  dotted line
                - (offset, (dash pattern))
            :param float linewidth: Width of the line.
                Only relevant for line markers where X or Y is None.
            :param str gapcolor: Background color of the line, e.g., 'blue', 'b',
                '#FF0000'. It is used to draw dotted line using a second color.
            :returns: The handle used by the backend to univocally access the item
            """
            return object()

        def addMarker(
            self,
            x: float | None,
            y: float | None,
            text: str | None,
            color: str,
            symbol: str | None,
            symbolsize: float,
            linestyle: str | tuple[float, tuple[float, ...] | None],
            linewidth: float,
            constraint: Callable[[float, float], tuple[float, float]] | None,
            yaxis: str,
            font: qt.QFont,
            bgcolor: RGBAColorType | None,
        ) -> object:
            """Add a point, vertical line or horizontal line marker to the plot.

            :param x: Horizontal position of the marker in graph coordinates.
                If None, the marker is a horizontal line.
            :param y: Vertical position of the marker in graph coordinates.
                If None, the marker is a vertical line.
            :param text: Text associated to the marker (or None for no text)
            :param color: Color to be used for instance 'blue', 'b', '#FF0000'
            :param bgcolor: Text background color to be used for instance 'blue', 'b', '#FF0000'
            :param symbol: Symbol representing the marker.
                Only relevant for point markers where X and Y are not None.
                Value in:

                - 'o' circle
                - '.' point
                - ',' pixel
                - '+' cross
                - 'x' x-cross
                - 'd' diamond
                - 's' square
            :param linestyle: Style of the line.
                Only relevant for line markers where X or Y is None.
                Value in:

                - ' '  no line
                - '-'  solid line
                - '--' dashed line
                - '-.' dash-dot line
                - ':'  dotted line
                - (offset, (dash pattern))
            :param linewidth: Width of the line.
                Only relevant for line markers where X or Y is None.
            :param constraint: A function filtering marker displacement by
                dragging operations or None for no filter.
                This function is called each time a marker is moved.
                It takes the coordinates of the current cursor position in the plot
                as input and that returns the filtered coordinates.
            :param yaxis: The Y axis this marker belongs to in: 'left', 'right'
            :param font: QFont to use to render text
            :return: Handle used by the backend to univocally access the marker
            """
            return object()

        # Remove methods

        def remove(self, item):
            """Remove an existing item from the plot.

            :param item: A backend specific item handle returned by a add* method
            """
            pass

        # Interaction methods

        def setGraphCursorShape(self, cursor):
            """Set the cursor shape.

            To override in interactive backends.

            :param str cursor: Name of the cursor shape or None
            """
            pass

        def setGraphCursor(self, flag, color, linewidth, linestyle):
            """Toggle the display of a crosshair cursor and set its attributes.

            To override in interactive backends.

            :param bool flag: Toggle the display of a crosshair cursor.
            :param color: The color to use for the crosshair.
            :type color: A string (either a predefined color name in colors.py
                        or "#RRGGBB")) or a 4 columns unsigned byte array.
            :param int linewidth: The width of the lines of the crosshair.
            :param linestyle: Type of line::

                    - ' ' no line
                    - '-' solid line
                    - '--' dashed line
                    - '-.' dash-dot line
                    - ':' dotted line
                    - (offset, (dash pattern))

            :type linestyle: None, one of the predefined styles or (offset, (dash pattern)).
            """
            pass

        def getItemsFromBackToFront(self, condition=None):
            """Returns the list of plot items order as rendered by the backend.

            This is the order used for rendering.
            By default, it takes into account overlays, z value and order of addition of items,
            but backends can override it.

            :param callable condition:
               Callable taking an item as input and returning False for items to skip.
               If None (default), no item is skipped.
            :rtype: List[~silx.gui.plot.items.Item]
            """
            # Sort items: Overlays first, then others
            # and in each category ordered by z and then by order of addition
            # as content keeps this order.
            content = self._plot.getItems()
            if condition is not None:
                content = [item for item in content if condition(item)]

            return sorted(
                content, key=lambda i: ((1 if i.isOverlay() else 0), i.getZValue())
            )

        def pickItem(self, x, y, item):
            """Return picked indices if any, or None.

            :param float x: The x pixel coord where to pick.
            :param float y: The y pixel coord where to pick.
            :param item: A backend item created with add* methods.
            :return: None if item was not picked, else returns
                picked indices information.
            :rtype: Union[None,List]
            """
            return None

        # Update curve

        def setCurveColor(self, curve, color):
            """Set the color of a curve.

            :param curve: The curve handle
            :param str color: The color to use.
            """
            pass

        # Misc.

        def getWidgetHandle(self):
            """Return the widget this backend is drawing to."""
            return None

        def postRedisplay(self):
            """Trigger backend update and repaint."""
            self.replot()

        def replot(self):
            """Redraw the plot."""
            with self._plot._paintContext():
                pass

        def saveGraph(self, fileName, fileFormat, dpi):
            """Save the graph to a file (or a StringIO)

            At least "png", "svg" are supported.

            :param fileName: Destination
            :type fileName: String or StringIO or BytesIO
            :param str fileFormat: String specifying the format
            :param int dpi: The resolution to use or None.
            """
            pass

        # Graph labels

        def setGraphTitle(self, title):
            """Set the main title of the plot.

            :param str title: Title associated to the plot
            """
            pass

        def setGraphXLabel(self, label):
            """Set the X axis label.

            :param str label: label associated to the plot bottom X axis
            """
            pass

        def setGraphYLabel(self, label, axis):
            """Set the left Y axis label.

            :param str label: label associated to the plot left Y axis
            :param str axis: The axis for which to get the limits: left or right
            """
            pass

        # Graph limits

        def setLimits(self, xmin, xmax, ymin, ymax, y2min=None, y2max=None):
            """Set the limits of the X and Y axes at once.

            :param float xmin: minimum bottom axis value
            :param float xmax: maximum bottom axis value
            :param float ymin: minimum left axis value
            :param float ymax: maximum left axis value
            :param float y2min: minimum right axis value
            :param float y2max: maximum right axis value
            """
            self.__xLimits = xmin, xmax
            self.__yLimits["left"] = ymin, ymax
            if y2min is not None and y2max is not None:
                self.__yLimits["right"] = y2min, y2max

        def getGraphXLimits(self):
            """Get the graph X (bottom) limits.

            :return:  Minimum and maximum values of the X axis
            """
            return self.__xLimits

        def setGraphXLimits(self, xmin, xmax):
            """Set the limits of X axis.

            :param float xmin: minimum bottom axis value
            :param float xmax: maximum bottom axis value
            """
            self.__xLimits = xmin, xmax

        def getGraphYLimits(self, axis):
            """Get the graph Y (left) limits.

            :param str axis: The axis for which to get the limits: left or right
            :return: Minimum and maximum values of the Y axis
            """
            return self.__yLimits[axis]

        def setGraphYLimits(self, ymin, ymax, axis):
            """Set the limits of the Y axis.

            :param float ymin: minimum left axis value
            :param float ymax: maximum left axis value
            :param str axis: The axis for which to get the limits: left or right
            """
            self.__yLimits[axis] = ymin, ymax

        # Graph axes

        def getXAxisTimeZone(self):
            """Returns tzinfo that is used if the X-Axis plots date-times.

            None means the datetimes are interpreted as local time.

            :rtype: datetime.tzinfo of None.
            """
            return self._xAxisTimeZone

        def setXAxisTimeZone(self, tz):
            """Sets tzinfo that is used if the X-Axis plots date-times.

            Use None to let the datetimes be interpreted as local time.

            :rtype: datetime.tzinfo of None.
            """
            self._xAxisTimeZone = tz

        def isXAxisTimeSeries(self):
            """Return True if the X-axis scale shows datetime objects.

            :rtype: bool
            """
            return self.__xAxisTimeSeries

        def setXAxisTimeSeries(self, isTimeSeries):
            """Set whether the X-axis is a time series

            :param bool flag: True to switch to time series, False for regular axis.
            """
            self.__xAxisTimeSeries = bool(isTimeSeries)

        def setXAxisLogarithmic(self, flag):
            """Set the X axis scale between linear and log.

            :param bool flag: If True, the bottom axis will use a log scale
            """
            pass

        def setYAxisLogarithmic(self, flag):
            """Set the Y axis scale between linear and log.

            :param bool flag: If True, the left axis will use a log scale
            """
            pass

        def setYAxisInverted(self, flag):
            """Invert the Y axis.

            :param bool flag: If True, put the vertical axis origin on the top
            """
            self.__yAxisInverted = bool(flag)

        def isYAxisInverted(self):
            """Return True if left Y axis is inverted, False otherwise."""
            return self.__yAxisInverted

        def isYRightAxisVisible(self) -> bool:
            """Return True if the Y axis on the right side of the plot is visible"""
            return False

        def isKeepDataAspectRatio(self):
            """Returns whether the plot is keeping data aspect ratio or not."""
            return self.__keepDataAspectRatio

        def setKeepDataAspectRatio(self, flag):
            """Set whether to keep data aspect ratio or not.

            :param flag:  True to respect data aspect ratio
            :type flag: Boolean, default True
            """
            self.__keepDataAspectRatio = bool(flag)

        def setGraphGrid(self, which):
            """Set grid.

            :param which: None to disable grid, 'major' for major grid,
                         'both' for major and minor grid
            """
            pass

        # Data <-> Pixel coordinates conversion

        def dataToPixel(self, x, y, axis):
            """Convert a position in data space to a position in pixels
            in the widget.

            :param x: The X coordinate in data space.
            :type x: float or sequence of float
            :param y: The Y coordinate in data space.
            :type y: float or sequence of float
            :param str axis: The Y axis to use for the conversion
                             ('left' or 'right').
            :returns: The corresponding position in pixels or
                      None if the data position is not in the displayed area.
            :rtype: A tuple of 2 floats: (xPixel, yPixel) or None.
            """
            raise NotImplementedError()

        def pixelToData(self, x, y, axis):
            """Convert a position in pixels in the widget to a position in
            the data space.

            :param float x: The X coordinate in pixels.
            :param float y: The Y coordinate in pixels.
            :param str axis: The Y axis to use for the conversion
                             ('left' or 'right').
            :returns: The corresponding position in data space or
                      None if the pixel position is not in the plot area.
            :rtype: A tuple of 2 floats: (xData, yData) or None.
            """
            raise NotImplementedError()

        def getPlotBoundsInPixels(self):
            """Plot area bounds in widget coordinates in pixels.

            :return: bounds as a 4-tuple of int: (left, top, width, height)
            """
            raise NotImplementedError()

        def setAxesMargins(self, left: float, top: float, right: float, bottom: float):
            """Set the size of plot margins as ratios.

            Values are expected in [0., 1.]

            :param float left:
            :param float top:
            :param float right:
            :param float bottom:
            """
            pass

        def setForegroundColors(self, foregroundColor, gridColor):
            """Set foreground and grid colors used to display this widget.

            :param List[float] foregroundColor: RGBA foreground color of the widget
            :param List[float] gridColor: RGBA grid color of the data view
            """
            pass

        def setBackgroundColors(self, backgroundColor, dataBackgroundColor):
            """Set background colors used to display this widget.

            :param List[float] backgroundColor: RGBA background color of the widget
            :param Union[Tuple[float],None] dataBackgroundColor:
                RGBA background color of the data view
            """
            pass