class QwtText(object):
    """
    A class representing a text

    A `QwtText` is a text including a set of attributes how to render it.

      - Format:

      A text might include control sequences (f.e tags) describing
      how to render it. Each format (f.e MathML, TeX, Qt Rich Text)
      has its own set of control sequences, that can be handles by
      a special `QwtTextEngine` for this format.

      - Background:

      A text might have a background, defined by a `QPen` and `QBrush`
      to improve its visibility. The corners of the background might
      be rounded.

      - Font:

      A text might have an individual font.

      - Color

      A text might have an individual color.

      - Render Flags

      Flags from `Qt.AlignmentFlag` and `Qt.TextFlag` used like in
      `QPainter.drawText()`.

    ..seealso::

        :py:meth:`qwt.text.QwtTextEngine`,
        :py:meth:`qwt.text.QwtTextLabel`

    Text formats:

      * `QwtText.AutoText`:

        The text format is determined using `QwtTextEngine.mightRender()` for
        all available text engines in increasing order > PlainText.
        If none of the text engines can render the text is rendered
        like `QwtText.PlainText`.

      * `QwtText.PlainText`:

        Draw the text as it is, using a QwtPlainTextEngine.

      * `QwtText.RichText`:

        Use the Scribe framework (Qt Rich Text) to render the text.

      * `QwtText.MathMLText`:

        Use a MathML (http://en.wikipedia.org/wiki/MathML) render engine
        to display the text. The Qwt MathML extension offers such an engine
        based on the MathML renderer of the Qt solutions package.
        To enable MathML support the following code needs to be added to the
        application::

            QwtText.setTextEngine(QwtText.MathMLText, QwtMathMLTextEngine())

      * `QwtText.TeXText`:

        Use a TeX (http://en.wikipedia.org/wiki/TeX) render engine
        to display the text ( not implemented yet ).

      * `QwtText.OtherFormat`:

        The number of text formats can be extended using `setTextEngine`.
        Formats >= `QwtText.OtherFormat` are not used by Qwt.

    Paint attributes:

      * `QwtText.PaintUsingTextFont`: The text has an individual font.
      * `QwtText.PaintUsingTextColor`: The text has an individual color.
      * `QwtText.PaintBackground`: The text has an individual background.

    Layout attributes:

      * `QwtText.MinimumLayout`:

        Layout the text without its margins. This mode is useful if a
        text needs to be aligned accurately, like the tick labels of a scale.
        If `QwtTextEngine.textMargins` is not implemented for the format
        of the text, `MinimumLayout` has no effect.

    .. py:class:: QwtText([text=None], [textFormat=None], [other=None])

        :param str text: Text content
        :param int textFormat: Text format
        :param qwt.text.QwtText other: Object to copy (text and textFormat arguments are ignored)
    """

    # enum TextFormat
    AutoText, PlainText, RichText, MathMLText, TeXText = list(range(5))
    OtherFormat = 100

    # enum PaintAttribute
    PaintUsingTextFont = 0x01
    PaintUsingTextColor = 0x02
    PaintBackground = 0x04

    # enum LayoutAttribute
    MinimumLayout = 0x01

    # Optimization: a single text engine for all QwtText objects
    # (this is not how it's implemented in Qwt6 C++ library)
    __map = {PlainText: QwtPlainTextEngine(), RichText: QwtRichTextEngine()}

    def __init__(self, text=None, textFormat=None, other=None):
        self.__desktopwidget = None
        if text is None:
            text = ""
        if textFormat is None:
            textFormat = self.AutoText
        if other is not None:
            text = other
        if isinstance(text, QwtText):
            self.__data = text.__data
            self.__layoutCache = text.__layoutCache
        else:
            self.__data = QwtText_PrivateData()
            self.__data.text = text
            self.__data.textEngine = self.textEngine(text, textFormat)
            self.__layoutCache = QwtText_LayoutCache()

    @classmethod
    def make(
        cls,
        text=None,
        textformat=None,
        renderflags=None,
        font=None,
        family=None,
        pointsize=None,
        weight=None,
        color=None,
        borderradius=None,
        borderpen=None,
        brush=None,
    ):
        """
        Create and setup a new `QwtText` object (convenience function).

        :param str text: Text content
        :param int textformat: Text format
        :param int renderflags: Flags from `Qt.AlignmentFlag` and `Qt.TextFlag`
        :param font: Font
        :type font: QFont or None
        :param family: Font family (default: Helvetica)
        :type family: str or None
        :param pointsize: Font point size (default: 10)
        :type pointsize: int or None
        :param weight: Font weight (default: QFont.Normal)
        :type weight: int or None
        :param color: Pen color
        :type color: QColor or str or None
        :param borderradius: Radius for the corners of the border frame
        :type borderradius: float or None
        :param borderpen: Background pen
        :type borderpen: QPen or None
        :param brush: Background brush
        :type brush: QBrush or None

        .. seealso::

            :py:meth:`setText()`
        """
        item = cls(text=text, textFormat=textformat)
        if renderflags is not None:
            item.setRenderFlags(renderflags)
        if font is not None:
            item.setFont(font)
        elif family is not None or pointsize is not None or weight is not None:
            family = "Helvetica" if family is None else family
            pointsize = 10 if pointsize is None else pointsize
            weight = QFont.Normal if weight is None else weight
            item.setFont(QFont(family, pointsize, weight))
        if color is not None:
            item.setColor(qcolor_from_str(color, Qt.black))
        if borderradius is not None:
            item.setBorderRadius(borderradius)
        if borderpen is not None:
            item.setBorderPen(borderpen)
        if brush is not None:
            item.setBackgroundBrush(brush)
        return item

    @property
    def _desktopwidget(self):
        """
        Property used to store the Application Desktop Widget to avoid calling
        the `QApplication.desktop()" function more than necessary as its
        calling time is not negligible.
        """
        if self.__desktopwidget is None:
            self.__desktopwidget = QApplication.desktop()
        return self.__desktopwidget

    def __eq__(self, other):
        return (
            self.__data.renderFlags == other.__data.renderFlags
            and self.__data.text == other.__data.text
            and self.__data.font == other.__data.font
            and self.__data.color == other.__data.color
            and self.__data.borderRadius == other.__data.borderRadius
            and self.__data.borderPen == other.__data.borderPen
            and self.__data.backgroundBrush == other.__data.backgroundBrush
            and self.__data.paintAttributes == other.__data.paintAttributes
            and self.__data.textEngine == other.__data.textEngine
        )

    def __ne__(self, other):
        return not self.__eq__(other)

    def isEmpty(self):
        """
        :return: True if text is empty
        """
        return len(self.text()) == 0

    def setText(self, text, textFormat=None):
        """
        Assign a new text content

        :param str text: Text content
        :param int textFormat: Text format

        .. seealso::

            :py:meth:`text()`
        """
        if textFormat is None:
            textFormat = self.AutoText
        self.__data.text = text
        self.__data.textEngine = self.textEngine(text, textFormat)
        self.__layoutCache.invalidate()

    def text(self):
        """
        :return: Text content

        .. seealso::

            :py:meth:`setText()`
        """
        return self.__data.text

    def setRenderFlags(self, renderFlags):
        """
        Change the render flags

        The default setting is `Qt.AlignCenter`

        :param int renderFlags: Bitwise OR of the flags used like in `QPainter.drawText()`

        .. seealso::

            :py:meth:`renderFlags()`,
            :py:meth:`qwt.text.QwtTextEngine.draw()`
        """
        renderFlags = Qt.AlignmentFlag(renderFlags)
        if renderFlags != self.__data.renderFlags:
            self.__data.renderFlags = renderFlags
            self.__layoutCache.invalidate()

    def renderFlags(self):
        """
        :return: Render flags

        .. seealso::

            :py:meth:`setRenderFlags()`
        """
        return self.__data.renderFlags

    def setFont(self, font):
        """
        Set the font.

        :param QFont font: Font

        .. note::

            Setting the font might have no effect, when
            the text contains control sequences for setting fonts.

        .. seealso::

            :py:meth:`font()`, :py:meth:`usedFont()`
        """
        self.__data.font = font
        self.setPaintAttribute(self.PaintUsingTextFont)

    def font(self):
        """
        :return: Return the font

        .. seealso::

            :py:meth:`setFont()`, :py:meth:`usedFont()`
        """
        return self.__data.font

    def usedFont(self, defaultFont):
        """
        Return the font of the text, if it has one.
        Otherwise return defaultFont.

        :param QFont defaultFont: Default font
        :return: Font used for drawing the text

        .. seealso::

            :py:meth:`setFont()`, :py:meth:`font()`
        """
        if self.__data.paintAttributes & self.PaintUsingTextFont:
            return self.__data.font
        return defaultFont

    def setColor(self, color):
        """
        Set the pen color used for drawing the text.

        :param QColor color: Color

        .. note::

            Setting the color might have no effect, when
            the text contains control sequences for setting colors.

        .. seealso::

            :py:meth:`color()`, :py:meth:`usedColor()`
        """
        self.__data.color = QColor(color)
        self.setPaintAttribute(self.PaintUsingTextColor)

    def color(self):
        """
        :return: Return the pen color, used for painting the text

        .. seealso::

            :py:meth:`setColor()`, :py:meth:`usedColor()`
        """
        return self.__data.color

    def usedColor(self, defaultColor):
        """
        Return the color of the text, if it has one.
        Otherwise return defaultColor.

        :param QColor defaultColor: Default color
        :return: Color used for drawing the text

        .. seealso::

            :py:meth:`setColor()`, :py:meth:`color()`
        """
        if self.__data.paintAttributes & self.PaintUsingTextColor:
            return self.__data.color
        return defaultColor

    def setBorderRadius(self, radius):
        """
        Set the radius for the corners of the border frame

        :param float radius: Radius of a rounded corner

        .. seealso::

            :py:meth:`borderRadius()`, :py:meth:`setBorderPen()`,
            :py:meth:`setBackgroundBrush()`
        """
        self.__data.borderRadius = max([0.0, radius])

    def borderRadius(self):
        """
        :return: Radius for the corners of the border frame

        .. seealso::

            :py:meth:`setBorderRadius()`, :py:meth:`borderPen()`,
            :py:meth:`backgroundBrush()`
        """
        return self.__data.borderRadius

    def setBorderPen(self, pen):
        """
        Set the background pen

        :param QPen pen: Background pen

        .. seealso::

            :py:meth:`borderPen()`, :py:meth:`setBackgroundBrush()`
        """
        self.__data.borderPen = pen
        self.setPaintAttribute(self.PaintBackground)

    def borderPen(self):
        """
        :return: Background pen

        .. seealso::

            :py:meth:`setBorderPen()`, :py:meth:`backgroundBrush()`
        """
        return self.__data.borderPen

    def setBackgroundBrush(self, brush):
        """
        Set the background brush

        :param QBrush brush: Background brush

        .. seealso::

            :py:meth:`backgroundBrush()`, :py:meth:`setBorderPen()`
        """
        self.__data.backgroundBrush = brush
        self.setPaintAttribute(self.PaintBackground)

    def backgroundBrush(self):
        """
        :return: Background brush

        .. seealso::

            :py:meth:`setBackgroundBrush()`, :py:meth:`borderPen()`
        """
        return self.__data.backgroundBrush

    def setPaintAttribute(self, attribute, on=True):
        """
        Change a paint attribute

        :param int attribute: Paint attribute
        :param bool on: On/Off

        .. note::

            Used by `setFont()`, `setColor()`, `setBorderPen()`
            and `setBackgroundBrush()`

        .. seealso::

            :py:meth:`testPaintAttribute()`
        """
        if on:
            self.__data.paintAttributes |= attribute
        else:
            self.__data.paintAttributes &= ~attribute

    def testPaintAttribute(self, attribute):
        """
        Test a paint attribute

        :param int attribute: Paint attribute
        :return: True, if attribute is enabled

        .. seealso::

            :py:meth:`setPaintAttribute()`
        """
        return self.__data.paintAttributes & attribute

    def setLayoutAttribute(self, attribute, on=True):
        """
        Change a layout attribute

        :param int attribute: Layout attribute
        :param bool on: On/Off

        .. seealso::

            :py:meth:`testLayoutAttribute()`
        """
        if on:
            self.__data.layoutAttributes |= attribute
        else:
            self.__data.layoutAttributes &= ~attribute

    def testLayoutAttribute(self, attribute):
        """
        Test a layout attribute

        :param int attribute: Layout attribute
        :return: True, if attribute is enabled

        .. seealso::

            :py:meth:`setLayoutAttribute()`
        """
        return self.__data.layoutAttributes & attribute

    def heightForWidth(self, width, defaultFont=None):
        """
        Find the height for a given width

        :param float width: Width
        :param QFont defaultFont: Font, used for the calculation if the text has no font
        :return: Calculated height
        """
        if defaultFont is None:
            defaultFont = QFont()
        font = QFont(self.usedFont(defaultFont), self._desktopwidget)
        h = 0
        if self.__data.layoutAttributes & self.MinimumLayout:
            (left, right, top, bottom) = self.__data.textEngine.textMargins(font)
            h = self.__data.textEngine.heightForWidth(
                font, self.__data.renderFlags, self.__data.text, width + left + right
            )
            h -= top + bottom
        else:
            h = self.__data.textEngine.heightForWidth(
                font, self.__data.renderFlags, self.__data.text, width
            )
        return h

    def textSize(self, defaultFont):
        """
        Returns the size, that is needed to render text

        :param QFont defaultFont Font, used for the calculation if the text has no font
        :return: Caluclated size
        """
        font = QFont(self.usedFont(defaultFont), self._desktopwidget)
        if (
            not self.__layoutCache.textSize.isValid()
            or self.__layoutCache.font is not font
        ):
            self.__layoutCache.textSize = self.__data.textEngine.textSize(
                font, self.__data.renderFlags, self.__data.text
            )
            self.__layoutCache.font = font
        sz = self.__layoutCache.textSize
        if self.__data.layoutAttributes & self.MinimumLayout:
            (left, right, top, bottom) = self.__data.textEngine.textMargins(font)
            sz -= QSizeF(left + right, top + bottom)
        return sz

    def draw(self, painter, rect):
        """
        Draw a text into a rectangle

        :param QPainter painter: Painter
        :param QRectF rect: Rectangle
        """
        if self.__data.paintAttributes & self.PaintBackground:
            if (
                self.__data.borderPen != Qt.NoPen
                or self.__data.backgroundBrush != Qt.NoBrush
            ):
                painter.save()
                painter.setPen(self.__data.borderPen)
                painter.setBrush(self.__data.backgroundBrush)
                if self.__data.borderRadius == 0:
                    painter.drawRect(rect)
                else:
                    painter.setRenderHint(QPainter.Antialiasing, True)
                    painter.drawRoundedRect(
                        rect, self.__data.borderRadius, self.__data.borderRadius
                    )
                painter.restore()
        painter.save()
        if self.__data.paintAttributes & self.PaintUsingTextFont:
            painter.setFont(self.__data.font)
        if self.__data.paintAttributes & self.PaintUsingTextColor:
            if self.__data.color.isValid():
                painter.setPen(self.__data.color)
        expandedRect = rect
        if self.__data.layoutAttributes & self.MinimumLayout:
            font = QFont(painter.font(), self._desktopwidget)
            (left, right, top, bottom) = self.__data.textEngine.textMargins(font)
            expandedRect.setTop(rect.top() - top)
            expandedRect.setBottom(rect.bottom() + bottom)
            expandedRect.setLeft(rect.left() - left)
            expandedRect.setRight(rect.right() + right)
        self.__data.textEngine.draw(
            painter, expandedRect, self.__data.renderFlags, self.__data.text
        )
        painter.restore()

    def textEngine(self, text=None, format_=None):
        """
        Find the text engine for a text format

        In case of `QwtText.AutoText` the first text engine
        (beside `QwtPlainTextEngine`) is returned, where
        `QwtTextEngine.mightRender` returns true.
        If there is none `QwtPlainTextEngine` is returned.

        If no text engine is registered for the format `QwtPlainTextEngine`
        is returned.

        :param str text: Text, needed in case of AutoText
        :param int format: Text format
        :return: Corresponding text engine
        """
        if text is None:
            return self.__map.get(format_)
        elif format_ is not None:
            if format_ == QwtText.AutoText:
                for key, engine in list(self.__map.items()):
                    if key != QwtText.PlainText:
                        if engine and engine.mightRender(text):
                            return engine
            engine = self.__map.get(format_)
            if engine is not None:
                return engine
            return self.__map[QwtText.PlainText]
        else:
            raise TypeError(
                "%s().textEngine() takes 1 or 2 argument(s) (none"
                " given)" % self.__class__.__name__
            )

    def setTextEngine(self, format_, engine):
        """
        Assign/Replace a text engine for a text format

        With setTextEngine it is possible to extend `PythonQwt` with
        other types of text formats.

        For `QwtText.PlainText` it is not allowed to assign a engine to None.

        :param int format_: Text format
        :param qwt.text.QwtTextEngine engine: Text engine

        .. seealso::

            :py:meth:`setPaintAttribute()`

        .. warning::

            Using `QwtText.AutoText` does nothing.
        """
        if format_ == QwtText.AutoText:
            return
        if format_ == QwtText.PlainText and engine is None:
            return
        self.__map.setdefault(format_, engine)
 class QwtPlot(QFrame, QwtPlotDict):
    """
    A 2-D plotting widget

    QwtPlot is a widget for plotting two-dimensional graphs.
    An unlimited number of plot items can be displayed on its canvas.
    Plot items might be curves (:py:class:`qwt.plot_curve.QwtPlotCurve`),
    markers (:py:class:`qwt.plot_marker.QwtPlotMarker`),
    the grid (:py:class:`qwt.plot_grid.QwtPlotGrid`), or anything else
    derived from :py:class:`QwtPlotItem`.

    A plot can have up to four axes, with each plot item attached to an x- and
    a y axis. The scales at the axes can be explicitly set (`QwtScaleDiv`), or
    are calculated from the plot items, using algorithms (`QwtScaleEngine`)
    which can be configured separately for each axis.

    The following example is a good starting point to see how to set up a
    plot widget::

        from qtpy import QtWidgets as QW
        import qwt
        import numpy as np

        app = QW.QApplication([])
        x = np.linspace(-10, 10, 500)
        plot = qwt.QwtPlot("Trigonometric functions")
        plot.insertLegend(qwt.QwtLegend(), qwt.QwtPlot.BottomLegend)
        qwt.QwtPlotCurve.make(x, np.cos(x), "Cosinus", plot, linecolor="red", antialiased=True)
        qwt.QwtPlotCurve.make(x, np.sin(x), "Sinus", plot, linecolor="blue", antialiased=True)
        plot.resize(600, 300)
        plot.show()

    .. image:: /images/QwtPlot_example.png

    .. py:class:: QwtPlot([title=""], [parent=None])

        :param str title: Title text
        :param QWidget parent: Parent widget

    .. py:data:: itemAttached

        A signal indicating, that an item has been attached/detached

        :param plotItem: Plot item
        :param on: Attached/Detached

    .. py:data:: legendDataChanged

        A signal with the attributes how to update
        the legend entries for a plot item.

        :param itemInfo: Info about a plot item, build from itemToInfo()
        :param data: Attributes of the entries (usually <= 1) for the plot item.

    """

    itemAttached = Signal(object, bool)
    legendDataChanged = Signal(object, object)

    # enum Axis
    AXES = yLeft, yRight, xBottom, xTop = list(range(4))
    axisCnt = len(AXES)  # Not necessary but ensure compatibility with PyQwt

    # enum LegendPosition
    LeftLegend, RightLegend, BottomLegend, TopLegend = list(range(4))

    def __init__(self, *args):
        if len(args) == 0:
            title, parent = "", None
        elif len(args) == 1:
            if isinstance(args[0], QWidget) or args[0] is None:
                title = ""
                (parent,) = args
            else:
                (title,) = args
                parent = None
        elif len(args) == 2:
            title, parent = args
        else:
            raise TypeError(
                "%s() takes 0, 1 or 2 argument(s) (%s given)"
                % (self.__class__.__name__, len(args))
            )
        QwtPlotDict.__init__(self)
        QFrame.__init__(self, parent)

        self.__layout_state = None

        self.__data = QwtPlot_PrivateData()
        from qwt.plot_layout import QwtPlotLayout

        self.__data.layout = QwtPlotLayout()
        self.__data.autoReplot = False

        self.setAutoReplot(False)
        self.setPlotLayout(self.__data.layout)

        # title
        self.__data.titleLabel = QwtTextLabel(self)
        self.__data.titleLabel.setObjectName("QwtPlotTitle")
        text = QwtText(title)
        text.setRenderFlags(Qt.AlignCenter | Qt.TextWordWrap)
        self.__data.titleLabel.setText(text)

        # footer
        self.__data.footerLabel = QwtTextLabel(self)
        self.__data.footerLabel.setObjectName("QwtPlotFooter")
        footer = QwtText()
        footer.setRenderFlags(Qt.AlignCenter | Qt.TextWordWrap)
        self.__data.footerLabel.setText(footer)

        # legend
        self.__data.legend = None

        # axis
        self.__axisData = []
        self.initAxesData()

        # canvas
        self.__data.canvas = QwtPlotCanvas(self)
        self.__data.canvas.setObjectName("QwtPlotCanvas")
        self.__data.canvas.installEventFilter(self)

        # plot style
        self.setFlatStyle(True)

        self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)

        focusChain = [
            self,
            self.__data.titleLabel,
            self.axisWidget(self.xTop),
            self.axisWidget(self.yLeft),
            self.__data.canvas,
            self.axisWidget(self.yRight),
            self.axisWidget(self.xBottom),
            self.__data.footerLabel,
        ]

        for idx in range(len(focusChain) - 1):
            qwtSetTabOrder(focusChain[idx], focusChain[idx + 1], False)

        qwtEnableLegendItems(self, True)

    def __del__(self):
        self.setAutoReplot(False)
        # XXX Is is really necessary in Python? (pure transcription of C++)
        # The following line have been commented to avoid crashing Python
        # when exiting interpreter (Python 3 only)

    #        self.detachItems(QwtPlotItem.Rtti_PlotItem, self.autoDelete())
    #        self.__data.layout = None
    #        self.deleteAxesData()
    #        self.__data = None

    def setFlatStyle(self, state):
        """
        Set or reset the flatStyle option

        If the flatStyle option is set, the plot will be
        rendered without any margin (scales, canvas, layout).

        Enabling this option makes the plot look flat and compact.

        The flatStyle option is set to True by default.

        :param bool state: True or False.

        .. seealso::

            :py:meth:`flatStyle()`
        """

        def make_font(family=None, size=None, delta_size=None, weight=None):
            finfo = self.fontInfo()
            family = finfo.family() if family is None else family
            weight = -1 if weight is None else weight
            size = size if delta_size is None else finfo.pointSize() + delta_size
            return QFont(family, size, weight)

        if state:
            # New PythonQwt-exclusive flat style
            plot_title_font = make_font(
                family="Helvetica", delta_size=4, weight=QFont.Bold
            )
            axis_title_font = make_font(
                family="Helvetica", delta_size=2, weight=QFont.Bold
            )
            axis_label_font = make_font(family="Helvetica", delta_size=0)
            tick_lighter_factors = (150, 125, 100)
            scale_margin = scale_spacing = 0
            canvas_frame_style = QFrame.NoFrame
            plot_layout_canvas_margin = plot_layout_spacing = 0
            ticks_color = Qt.darkGray
            labels_color = "#444444"
        else:
            # Old PyQwt / Qwt style
            plot_title_font = make_font(size=14, weight=QFont.Bold)
            axis_title_font = make_font(size=12, weight=QFont.Bold)
            axis_label_font = make_font(size=10)
            tick_lighter_factors = (100, 100, 100)
            scale_margin = scale_spacing = 2
            canvas_frame_style = QFrame.Panel | QFrame.Sunken
            plot_layout_canvas_margin = 4
            plot_layout_spacing = 5
            ticks_color = labels_color = Qt.black
        self.canvas().setFrameStyle(canvas_frame_style)
        self.plotLayout().setCanvasMargin(plot_layout_canvas_margin)
        self.plotLayout().setSpacing(plot_layout_spacing)
        palette = self.palette()
        palette.setColor(QPalette.WindowText, QColor(ticks_color))
        palette.setColor(QPalette.Text, QColor(labels_color))
        self.setPalette(palette)
        for axis_id in self.AXES:
            scale_widget = self.axisWidget(axis_id)
            scale_draw = self.axisScaleDraw(axis_id)
            scale_widget.setFont(axis_label_font)
            scale_widget.setMargin(scale_margin)
            scale_widget.setSpacing(scale_spacing)
            scale_title = scale_widget.title()
            scale_title.setFont(axis_title_font)
            scale_widget.setTitle(scale_title)
            for tick_type, factor in enumerate(tick_lighter_factors):
                scale_draw.setTickLighterFactor(tick_type, factor)
        plot_title = self.title()
        plot_title.setFont(plot_title_font)
        self.setTitle(plot_title)
        self.__data.flatStyle = state

    def flatStyle(self):
        """
        :return: True if the flatStyle option is set.

        .. seealso::

            :py:meth:`setFlatStyle()`
        """
        return self.__data.flatStyle

    def initAxesData(self):
        """Initialize axes"""
        self.__axisData = [AxisData() for axisId in self.AXES]

        self.__axisData[self.yLeft].scaleWidget = QwtScaleWidget(
            QwtScaleDraw.LeftScale, self
        )
        self.__axisData[self.yRight].scaleWidget = QwtScaleWidget(
            QwtScaleDraw.RightScale, self
        )
        self.__axisData[self.xTop].scaleWidget = QwtScaleWidget(
            QwtScaleDraw.TopScale, self
        )
        self.__axisData[self.xBottom].scaleWidget = QwtScaleWidget(
            QwtScaleDraw.BottomScale, self
        )

        self.__axisData[self.yLeft].scaleWidget.setObjectName("QwtPlotAxisYLeft")
        self.__axisData[self.yRight].scaleWidget.setObjectName("QwtPlotAxisYRight")
        self.__axisData[self.xTop].scaleWidget.setObjectName("QwtPlotAxisXTop")
        self.__axisData[self.xBottom].scaleWidget.setObjectName("QwtPlotAxisXBottom")

        for axisId in self.AXES:
            d = self.__axisData[axisId]

            d.scaleEngine = QwtLinearScaleEngine()

            d.scaleWidget.setTransformation(d.scaleEngine.transformation())
            d.scaleWidget.setMargin(2)

            text = d.scaleWidget.title()
            d.scaleWidget.setTitle(text)

            d.doAutoScale = True
            d.minValue = 0.0
            d.maxValue = 1000.0
            d.stepSize = 0.0
            d.maxMinor = 5
            d.maxMajor = 8
            d.isValid = False

        self.__axisData[self.yLeft].isEnabled = True
        self.__axisData[self.yRight].isEnabled = False
        self.__axisData[self.xBottom].isEnabled = True
        self.__axisData[self.xTop].isEnabled = False

    def deleteAxesData(self):
        # XXX Is is really necessary in Python? (pure transcription of C++)
        for axisId in self.AXES:
            self.__axisData[axisId].scaleEngine = None
            self.__axisData[axisId] = None

    def axisWidget(self, axisId):
        """
        :param int axisId: Axis index
        :return: Scale widget of the specified axis, or None if axisId is invalid.
        """
        if self.axisValid(axisId):
            return self.__axisData[axisId].scaleWidget

    def setAxisScaleEngine(self, axisId, scaleEngine):
        """
        Change the scale engine for an axis

        :param int axisId: Axis index
        :param qwt.scale_engine.QwtScaleEngine scaleEngine: Scale engine

        .. seealso::

            :py:meth:`axisScaleEngine()`
        """
        if self.axisValid(axisId) and scaleEngine is not None:
            d = self.__axisData[axisId]
            d.scaleEngine = scaleEngine
            self.__axisData[axisId].scaleWidget.setTransformation(
                scaleEngine.transformation()
            )
            d.isValid = False
            self.autoRefresh()

    def axisScaleEngine(self, axisId):
        """
        :param int axisId: Axis index
        :return: Scale engine for a specific axis

        .. seealso::

            :py:meth:`setAxisScaleEngine()`
        """
        if self.axisValid(axisId):
            return self.__axisData[axisId].scaleEngine

    def axisAutoScale(self, axisId):
        """
        :param int axisId: Axis index
        :return: True, if autoscaling is enabled
        """
        if self.axisValid(axisId):
            return self.__axisData[axisId].doAutoScale

    def axisEnabled(self, axisId):
        """
        :param int axisId: Axis index
        :return: True, if a specified axis is enabled
        """
        if self.axisValid(axisId):
            return self.__axisData[axisId].isEnabled

    def axisFont(self, axisId):
        """
        :param int axisId: Axis index
        :return: The font of the scale labels for a specified axis
        """
        if self.axisValid(axisId):
            return self.axisWidget(axisId).font()
        else:
            return QFont()

    def axisMaxMajor(self, axisId):
        """
        :param int axisId: Axis index
        :return: The maximum number of major ticks for a specified axis

        .. seealso::

            :py:meth:`setAxisMaxMajor()`,
            :py:meth:`qwt.scale_engine.QwtScaleEngine.divideScale()`
        """
        if self.axisValid(axisId):
            return self.axisWidget(axisId).maxMajor
        else:
            return 0

    def axisMaxMinor(self, axisId):
        """
        :param int axisId: Axis index
        :return: The maximum number of minor ticks for a specified axis

        .. seealso::

            :py:meth:`setAxisMaxMinor()`,
            :py:meth:`qwt.scale_engine.QwtScaleEngine.divideScale()`
        """
        if self.axisValid(axisId):
            return self.axisWidget(axisId).maxMinor
        else:
            return 0

    def axisScaleDiv(self, axisId):
        """
        :param int axisId: Axis index
        :return: The scale division of a specified axis

        axisScaleDiv(axisId).lowerBound(), axisScaleDiv(axisId).upperBound()
        are the current limits of the axis scale.

        .. seealso::

            :py:class:`qwt.scale_div.QwtScaleDiv`,
            :py:meth:`setAxisScaleDiv()`,
            :py:meth:`qwt.scale_engine.QwtScaleEngine.divideScale()`
        """
        return self.__axisData[axisId].scaleDiv

    def axisScaleDraw(self, axisId):
        """
        :param int axisId: Axis index
        :return: Specified scaleDraw for axis, or NULL if axis is invalid.
        """
        if self.axisValid(axisId):
            return self.axisWidget(axisId).scaleDraw()

    def axisStepSize(self, axisId):
        """
        :param int axisId: Axis index
        :return: step size parameter value

        This doesn't need to be the step size of the current scale.

        .. seealso::

            :py:meth:`setAxisScale()`,
            :py:meth:`qwt.scale_engine.QwtScaleEngine.divideScale()`
        """
        if self.axisValid(axisId):
            return self.axisWidget(axisId).stepSize
        else:
            return 0

    def axisInterval(self, axisId):
        """
        :param int axisId: Axis index
        :return: The current interval of the specified axis

        This is only a convenience function for axisScaleDiv(axisId).interval()

        .. seealso::

            :py:class:`qwt.scale_div.QwtScaleDiv`,
            :py:meth:`axisScaleDiv()`
        """
        if self.axisValid(axisId):
            return self.axisWidget(axisId).scaleDiv.interval()
        else:
            return QwtInterval()

    def axisTitle(self, axisId):
        """
        :param int axisId: Axis index
        :return: Title of a specified axis
        """
        if self.axisValid(axisId):
            return self.axisWidget(axisId).title()
        else:
            return QwtText()

    def enableAxis(self, axisId, tf=True):
        """
        Enable or disable a specified axis

        When an axis is disabled, this only means that it is not
        visible on the screen. Curves, markers and can be attached
        to disabled axes, and transformation of screen coordinates
        into values works as normal.

        Only xBottom and yLeft are enabled by default.

        :param int axisId: Axis index
        :param bool tf: True (enabled) or False (disabled)
        """
        if self.axisValid(axisId) and tf != self.__axisData[axisId].isEnabled:
            self.__axisData[axisId].isEnabled = tf
            self.updateLayout()

    def invTransform(self, axisId, pos):
        """
        Transform the x or y coordinate of a position in the
        drawing region into a value.

        :param int axisId: Axis index
        :param int pos: position

        .. warning::

            The position can be an x or a y coordinate,
            depending on the specified axis.
        """
        if self.axisValid(axisId):
            return self.canvasMap(axisId).invTransform(pos)
        else:
            return 0.0

    def transform(self, axisId, value):
        """
        Transform a value into a coordinate in the plotting region

        :param int axisId: Axis index
        :param fload value: Value
        :return: X or Y coordinate in the plotting region corresponding to the value.
        """
        if self.axisValid(axisId):
            return self.canvasMap(axisId).transform(value)
        else:
            return 0.0

    def setAxisFont(self, axisId, font):
        """
        Change the font of an axis

        :param int axisId: Axis index
        :param QFont font: Font

        .. warning::

            This function changes the font of the tick labels,
            not of the axis title.
        """
        if self.axisValid(axisId):
            return self.axisWidget(axisId).setFont(font)

    def setAxisAutoScale(self, axisId, on=True):
        """
        Enable autoscaling for a specified axis

        This member function is used to switch back to autoscaling mode
        after a fixed scale has been set. Autoscaling is enabled by default.

        :param int axisId: Axis index
        :param bool on: On/Off

        .. seealso::

            :py:meth:`setAxisScale()`, :py:meth:`setAxisScaleDiv()`,
            :py:meth:`updateAxes()`

        .. note::

            The autoscaling flag has no effect until updateAxes() is executed
            ( called by replot() ).
        """
        if self.axisValid(axisId) and self.__axisData[axisId].doAutoScale != on:
            self.__axisData[axisId].doAutoScale = on
            self.autoRefresh()

    def setAxisScale(self, axisId, min_, max_, stepSize=0):
        """
        Disable autoscaling and specify a fixed scale for a selected axis.

        In updateAxes() the scale engine calculates a scale division from the
        specified parameters, that will be assigned to the scale widget. So
        updates of the scale widget usually happen delayed with the next replot.

        :param int axisId: Axis index
        :param float min_: Minimum of the scale
        :param float max_: Maximum of the scale
        :param float stepSize: Major step size. If <code>step == 0</code>, the step size is calculated automatically using the maxMajor setting.

        .. seealso::

            :py:meth:`setAxisMaxMajor()`, :py:meth:`setAxisAutoScale()`,
            :py:meth:`axisStepSize()`,
            :py:meth:`qwt.scale_engine.QwtScaleEngine.divideScale()`
        """
        if self.axisValid(axisId):
            d = self.__axisData[axisId]
            d.doAutoScale = False
            d.isValid = False
            d.minValue = min_
            d.maxValue = max_
            d.stepSize = stepSize
            self.autoRefresh()

    def setAxisScaleDiv(self, axisId, scaleDiv):
        """
        Disable autoscaling and specify a fixed scale for a selected axis.

        The scale division will be stored locally only until the next call
        of updateAxes(). So updates of the scale widget usually happen delayed with
        the next replot.

        :param int axisId: Axis index
        :param qwt.scale_div.QwtScaleDiv scaleDiv: Scale division

        .. seealso::

            :py:meth:`setAxisScale()`, :py:meth:`setAxisAutoScale()`
        """
        if self.axisValid(axisId):
            d = self.__axisData[axisId]
            d.doAutoScale = False
            d.scaleDiv = scaleDiv
            d.isValid = True
            self.autoRefresh()

    def setAxisScaleDraw(self, axisId, scaleDraw):
        """
        Set a scale draw

        :param int axisId: Axis index
        :param qwt.scale_draw.QwtScaleDraw scaleDraw: Object responsible for drawing scales.

        By passing scaleDraw it is possible to extend QwtScaleDraw
        functionality and let it take place in QwtPlot. Please note
        that scaleDraw has to be created with new and will be deleted
        by the corresponding QwtScale member ( like a child object ).

        .. seealso::

            :py:class:`qwt.scale_draw.QwtScaleDraw`,
            :py:class:`qwt.scale_widget.QwtScaleWigdet`

        .. warning::

            The attributes of scaleDraw will be overwritten by those of the
            previous QwtScaleDraw.
        """
        if self.axisValid(axisId):
            self.axisWidget(axisId).setScaleDraw(scaleDraw)
            self.autoRefresh()

    def setAxisLabelAlignment(self, axisId, alignment):
        """
        Change the alignment of the tick labels

        :param int axisId: Axis index
        :param Qt.Alignment alignment: Or'd Qt.AlignmentFlags

        .. seealso::

            :py:meth:`qwt.scale_draw.QwtScaleDraw.setLabelAlignment()`
        """
        if self.axisValid(axisId):
            self.axisWidget(axisId).setLabelAlignment(alignment)

    def setAxisLabelRotation(self, axisId, rotation):
        """
        Rotate all tick labels

        :param int axisId: Axis index
        :param float rotation: Angle in degrees. When changing the label rotation, the label alignment might be adjusted too.

        .. seealso::

            :py:meth:`setLabelRotation()`, :py:meth:`setAxisLabelAlignment()`
        """
        if self.axisValid(axisId):
            self.axisWidget(axisId).setLabelRotation(rotation)

    def setAxisLabelAutoSize(self, axisId, state):
        """
        Set tick labels automatic size option (default: on)

        :param int axisId: Axis index
        :param bool state: On/off

        .. seealso::

            :py:meth:`qwt.scale_draw.QwtScaleDraw.setLabelAutoSize()`
        """
        if self.axisValid(axisId):
            self.axisWidget(axisId).setLabelAutoSize(state)

    def setAxisMaxMinor(self, axisId, maxMinor):
        """
        Set the maximum number of minor scale intervals for a specified axis

        :param int axisId: Axis index
        :param int maxMinor: Maximum number of minor steps

        .. seealso::

            :py:meth:`axisMaxMinor()`
        """
        if self.axisValid(axisId):
            maxMinor = max([0, min([maxMinor, 100])])
            d = self.__axisData[axisId]
            if maxMinor != d.maxMinor:
                d.maxMinor = maxMinor
                d.isValid = False
                self.autoRefresh()

    def setAxisMaxMajor(self, axisId, maxMajor):
        """
        Set the maximum number of major scale intervals for a specified axis

        :param int axisId: Axis index
        :param int maxMajor: Maximum number of major steps

        .. seealso::

            :py:meth:`axisMaxMajor()`
        """
        if self.axisValid(axisId):
            maxMajor = max([1, min([maxMajor, 10000])])
            d = self.__axisData[axisId]
            if maxMajor != d.maxMajor:
                d.maxMajor = maxMajor
                d.isValid = False
                self.autoRefresh()

    def setAxisTitle(self, axisId, title):
        """
        Change the title of a specified axis

        :param int axisId: Axis index
        :param title: axis title
        :type title: qwt.text.QwtText or str
        """
        if self.axisValid(axisId):
            self.axisWidget(axisId).setTitle(title)
            self.updateLayout()

    def updateAxes(self):
        """
        Rebuild the axes scales

        In case of autoscaling the boundaries of a scale are calculated
        from the bounding rectangles of all plot items, having the
        `QwtPlotItem.AutoScale` flag enabled (`QwtScaleEngine.autoScale()`).
        Then a scale division is calculated (`QwtScaleEngine.didvideScale()`)
        and assigned to scale widget.

        When the scale boundaries have been assigned with `setAxisScale()` a
        scale division is calculated (`QwtScaleEngine.didvideScale()`)
        for this interval and assigned to the scale widget.

        When the scale has been set explicitly by `setAxisScaleDiv()` the
        locally stored scale division gets assigned to the scale widget.

        The scale widget indicates modifications by emitting a
        `QwtScaleWidget.scaleDivChanged()` signal.

        `updateAxes()` is usually called by `replot()`.

        .. seealso::

            :py:meth:`setAxisAutoScale()`, :py:meth:`setAxisScale()`,
            :py:meth:`setAxisScaleDiv()`, :py:meth:`replot()`,
            :py:meth:`QwtPlotItem.boundingRect()`
        """
        intv = [QwtInterval() for _i in self.AXES]
        itmList = self.itemList()
        for item in itmList:
            if not item.testItemAttribute(QwtPlotItem.AutoScale):
                continue
            if not item.isVisible():
                continue
            if self.axisAutoScale(item.xAxis()) or self.axisAutoScale(item.yAxis()):
                rect = item.boundingRect()
                if rect.width() >= 0.0:
                    intv[item.xAxis()] |= QwtInterval(rect.left(), rect.right())
                if rect.height() >= 0.0:
                    intv[item.yAxis()] |= QwtInterval(rect.top(), rect.bottom())
        for axisId in self.AXES:
            d = self.__axisData[axisId]
            minValue = d.minValue
            maxValue = d.maxValue
            stepSize = d.stepSize
            if d.doAutoScale and intv[axisId].isValid():
                d.isValid = False
                minValue = intv[axisId].minValue()
                maxValue = intv[axisId].maxValue()
                d.scaleEngine.autoScale(d.maxMajor, minValue, maxValue, stepSize)
            if not d.isValid:
                d.scaleDiv = d.scaleEngine.divideScale(
                    minValue, maxValue, d.maxMajor, d.maxMinor, stepSize
                )
                d.isValid = True
            scaleWidget = self.axisWidget(axisId)
            scaleWidget.setScaleDiv(d.scaleDiv)

            # It is *really* necessary to update border dist!
            # Otherwise, when tick labels are large enough, the ticks
            # may not be aligned with canvas grid.
            # See the following issues for more details:
            # https://github.com/PierreRaybaut/guiqwt/issues/57
            # https://github.com/PierreRaybaut/PythonQwt/issues/30
            startDist, endDist = scaleWidget.getBorderDistHint()
            scaleWidget.setBorderDist(startDist, endDist)

        for item in itmList:
            if item.testItemInterest(QwtPlotItem.ScaleInterest):
                item.updateScaleDiv(
                    self.axisScaleDiv(item.xAxis()), self.axisScaleDiv(item.yAxis())
                )

    def setCanvas(self, canvas):
        """
        Set the drawing canvas of the plot widget.

        The default canvas is a `QwtPlotCanvas`.

        :param QWidget canvas: Canvas Widget

        .. seealso::

            :py:meth:`canvas()`
        """
        if canvas == self.__data.canvas:
            return
        self.__data.canvas = canvas
        if canvas is not None:
            canvas.setParent(self)
            canvas.installEventFilter(self)
            if self.isVisible():
                canvas.show()

    def event(self, event):
        if event.type() == QEvent.LayoutRequest:
            self.updateLayout()
        elif event.type() == QEvent.PolishRequest:
            self.replot()
        return QFrame.event(self, event)

    def eventFilter(self, obj, event):
        if obj is self.__data.canvas:
            if event.type() == QEvent.Resize:
                self.updateCanvasMargins()
            elif event.type() == 178:  # QEvent.ContentsRectChange:
                self.updateLayout()
        return QFrame.eventFilter(self, obj, event)

    def autoRefresh(self):
        """Replots the plot if :py:meth:`autoReplot()` is True."""
        if self.__data.autoReplot:
            self.replot()

    def setAutoReplot(self, tf=True):
        """
        Set or reset the autoReplot option

        If the autoReplot option is set, the plot will be
        updated implicitly by manipulating member functions.
        Since this may be time-consuming, it is recommended
        to leave this option switched off and call :py:meth:`replot()`
        explicitly if necessary.

        The autoReplot option is set to false by default, which
        means that the user has to call :py:meth:`replot()` in order
        to make changes visible.

        :param bool tf: True or False. Defaults to True.

        .. seealso::

            :py:meth:`autoReplot()`
        """
        self.__data.autoReplot = tf

    def autoReplot(self):
        """
        :return: True if the autoReplot option is set.

        .. seealso::

            :py:meth:`setAutoReplot()`
        """
        return self.__data.autoReplot

    def setTitle(self, title):
        """
        Change the plot's title

        :param title: New title
        :type title: str or qwt.text.QwtText

        .. seealso::

            :py:meth:`title()`
        """
        current_title = self.__data.titleLabel.text()
        if isinstance(title, QwtText) and current_title == title:
            return
        elif not isinstance(title, QwtText) and current_title.text() == title:
            return
        self.__data.titleLabel.setText(title)
        self.updateLayout()

    def title(self):
        """
        :return: Title of the plot

        .. seealso::

            :py:meth:`setTitle()`
        """
        return self.__data.titleLabel.text()

    def titleLabel(self):
        """
        :return: Title label widget.
        """
        return self.__data.titleLabel

    def setFooter(self, text):
        """
        Change the text the footer

        :param text: New text of the footer
        :type text: str or qwt.text.QwtText

        .. seealso::

            :py:meth:`footer()`
        """
        current_footer = self.__data.footerLabel.text()
        if isinstance(text, QwtText) and current_footer == text:
            return
        elif not isinstance(text, QwtText) and current_footer.text() == text:
            return
        self.__data.footerLabel.setText(text)
        self.updateLayout()

    def footer(self):
        """
        :return: Text of the footer

        .. seealso::

            :py:meth:`setFooter()`
        """
        return self.__data.footerLabel.text()

    def footerLabel(self):
        """
        :return: Footer label widget.
        """
        return self.__data.footerLabel

    def setPlotLayout(self, layout):
        """
        Assign a new plot layout

        :param layout: Layout
        :type layout: qwt.plot_layout.QwtPlotLayout

        .. seealso::

            :py:meth:`plotLayout()`
        """
        if layout != self.__data.layout:
            self.__data.layout = layout
            self.updateLayout()

    def plotLayout(self):
        """
        :return: the plot's layout

        .. seealso::

            :py:meth:`setPlotLayout()`
        """
        return self.__data.layout

    def legend(self):
        """
        :return: the plot's legend

        .. seealso::

            :py:meth:`insertLegend()`
        """
        return self.__data.legend

    def canvas(self):
        """
        :return: the plot's canvas
        """
        return self.__data.canvas

    def sizeHint(self):
        """
        :return: Size hint for the plot widget

        .. seealso::

            :py:meth:`minimumSizeHint()`
        """
        dw = dh = 0
        for axisId in self.AXES:
            if self.axisEnabled(axisId):
                niceDist = 40
                scaleWidget = self.axisWidget(axisId)
                scaleDiv = scaleWidget.scaleDraw().scaleDiv()
                majCnt = len(scaleDiv.ticks(QwtScaleDiv.MajorTick))
                if axisId in (self.yLeft, self.yRight):
                    hDiff = (
                        majCnt - 1
                    ) * niceDist - scaleWidget.minimumSizeHint().height()
                    if hDiff > dh:
                        dh = hDiff
                else:
                    wDiff = (
                        majCnt - 1
                    ) * niceDist - scaleWidget.minimumSizeHint().width()
                    if wDiff > dw:
                        dw = wDiff
        return self.minimumSizeHint() + QSize(dw, dh)

    def minimumSizeHint(self):
        """
        :return: Return a minimum size hint
        """
        hint = self.__data.layout.minimumSizeHint(self)
        hint += QSize(2 * self.frameWidth(), 2 * self.frameWidth())
        return hint

    def resizeEvent(self, e):
        QFrame.resizeEvent(self, e)
        self.updateLayout()

    def replot(self):
        """
        Redraw the plot

        If the `autoReplot` option is not set (which is the default)
        or if any curves are attached to raw data, the plot has to
        be refreshed explicitly in order to make changes visible.

        .. seealso::

            :py:meth:`updateAxes()`, :py:meth:`setAutoReplot()`
        """
        doAutoReplot = self.autoReplot()
        self.setAutoReplot(False)
        self.updateAxes()

        #  Maybe the layout needs to be updated, because of changed
        #  axes labels. We need to process them here before painting
        #  to avoid that scales and canvas get out of sync.
        QApplication.sendPostedEvents(self, QEvent.LayoutRequest)

        if self.__data.canvas:
            try:
                self.__data.canvas.replot()
            except (AttributeError, TypeError):
                self.__data.canvas.update(self.__data.canvas.contentsRect())

        self.setAutoReplot(doAutoReplot)

    def get_layout_state(self):
        return (
            self.contentsRect(),
            self.__data.titleLabel.text(),
            self.__data.footerLabel.text(),
            [
                (self.axisEnabled(axisId), self.axisTitle(axisId).text())
                for axisId in self.AXES
            ],
            self.__data.legend,
        )

    def updateLayout(self):
        """
        Adjust plot content to its current size.

        .. seealso::

            :py:meth:`resizeEvent()`
        """
        #        state = self.get_layout_state()
        #        if self.__layout_state is not None and\
        #           state == self.__layout_state:
        #            return
        #        self.__layout_state = state

        self.__data.layout.activate(self, self.contentsRect())

        titleRect = self.__data.layout.titleRect().toRect()
        footerRect = self.__data.layout.footerRect().toRect()
        scaleRect = [
            self.__data.layout.scaleRect(axisId).toRect() for axisId in self.AXES
        ]
        legendRect = self.__data.layout.legendRect().toRect()
        canvasRect = self.__data.layout.canvasRect().toRect()

        if self.__data.titleLabel.text():
            self.__data.titleLabel.setGeometry(titleRect)
            if not self.__data.titleLabel.isVisibleTo(self):
                self.__data.titleLabel.show()
        else:
            self.__data.titleLabel.hide()

        if self.__data.footerLabel.text():
            self.__data.footerLabel.setGeometry(footerRect)
            if not self.__data.footerLabel.isVisibleTo(self):
                self.__data.footerLabel.show()
        else:
            self.__data.footerLabel.hide()

        for axisId in self.AXES:
            scaleWidget = self.axisWidget(axisId)
            if self.axisEnabled(axisId):
                if scaleRect[axisId] != scaleWidget.geometry():
                    scaleWidget.setGeometry(scaleRect[axisId])
                    startDist, endDist = scaleWidget.getBorderDistHint()
                    scaleWidget.setBorderDist(startDist, endDist)

                # -------------------------------------------------------------
                # XXX: The following was commented to fix issue #35
                # Note: the same code part in Qwt's original source code is
                # annotated with the mention "do we need this code any
                # longer ???"... I guess not :)
                # if axisId in (self.xBottom, self.xTop):
                #     r = QRegion(scaleRect[axisId])
                #     if self.axisEnabled(self.yLeft):
                #         r = r.subtracted(QRegion(scaleRect[self.yLeft]))
                #     if self.axisEnabled(self.yRight):
                #         r = r.subtracted(QRegion(scaleRect[self.yRight]))
                #     r.translate(-scaleRect[axisId].x(), -scaleRect[axisId].y())
                #     scaleWidget.setMask(r)
                # -------------------------------------------------------------

                if not scaleWidget.isVisibleTo(self):
                    scaleWidget.show()
            else:
                scaleWidget.hide()

        if self.__data.legend:
            if self.__data.legend.isEmpty():
                self.__data.legend.hide()
            else:
                self.__data.legend.setGeometry(legendRect)
                self.__data.legend.show()

        self.__data.canvas.setGeometry(canvasRect)

    def getCanvasMarginsHint(self, maps, canvasRect):
        """
        Calculate the canvas margins

        :param list maps: `QwtPlot.axisCnt` maps, mapping between plot and paint device coordinates
        :param QRectF canvasRect: Bounding rectangle where to paint

        Plot items might indicate, that they need some extra space
        at the borders of the canvas by the `QwtPlotItem.Margins` flag.

        .. seealso::

            :py:meth:`updateCanvasMargins()`, :py:meth:`getCanvasMarginHint()`
        """
        left = top = right = bottom = -1.0

        for item in self.itemList():
            if item.testItemAttribute(QwtPlotItem.Margins):
                m = item.getCanvasMarginHint(
                    maps[item.xAxis()], maps[item.yAxis()], canvasRect
                )
                left = max([left, m[self.yLeft]])
                top = max([top, m[self.xTop]])
                right = max([right, m[self.yRight]])
                bottom = max([bottom, m[self.xBottom]])

        return left, top, right, bottom

    def updateCanvasMargins(self):
        """
        Update the canvas margins

        Plot items might indicate, that they need some extra space
        at the borders of the canvas by the `QwtPlotItem.Margins` flag.

        .. seealso::

            :py:meth:`getCanvasMarginsHint()`,
            :py:meth:`QwtPlotItem.getCanvasMarginHint()`
        """
        maps = [self.canvasMap(axisId) for axisId in self.AXES]
        margins = self.getCanvasMarginsHint(maps, self.canvas().contentsRect())

        doUpdate = False

        for axisId in self.AXES:
            if margins[axisId] >= 0.0:
                m = np.ceil(margins[axisId])
                self.plotLayout().setCanvasMargin(m, axisId)
                doUpdate = True

        if doUpdate:
            self.updateLayout()

    def drawCanvas(self, painter):
        """
        Redraw the canvas.

        :param QPainter painter: Painter used for drawing

        .. warning::

            drawCanvas calls drawItems what is also used
            for printing. Applications that like to add individual
            plot items better overload drawItems()

        .. seealso::

            :py:meth:`getCanvasMarginsHint()`,
            :py:meth:`QwtPlotItem.getCanvasMarginHint()`
        """
        maps = [self.canvasMap(axisId) for axisId in self.AXES]
        self.drawItems(painter, self.__data.canvas.contentsRect(), maps)

    def drawItems(self, painter, canvasRect, maps):
        """
        Redraw the canvas.

        :param QPainter painter: Painter used for drawing
        :param QRectF canvasRect: Bounding rectangle where to paint
        :param list maps: `QwtPlot.axisCnt` maps, mapping between plot and paint device coordinates

        .. note::

            Usually canvasRect is `contentsRect()` of the plot canvas.
            Due to a bug in Qt this rectangle might be wrong for certain
            frame styles ( f.e `QFrame.Box` ) and it might be necessary to
            fix the margins manually using `QWidget.setContentsMargins()`
        """
        for item in self.itemList():
            if item and item.isVisible():
                painter.save()
                painter.setRenderHint(
                    QPainter.Antialiasing,
                    item.testRenderHint(QwtPlotItem.RenderAntialiased),
                )
                painter.setRenderHint(
                    QPainter.HighQualityAntialiasing,
                    item.testRenderHint(QwtPlotItem.RenderAntialiased),
                )
                item.draw(painter, maps[item.xAxis()], maps[item.yAxis()], canvasRect)
                painter.restore()

    def canvasMap(self, axisId):
        """
        :param int axisId: Axis
        :return: Map for the axis on the canvas. With this map pixel coordinates can translated to plot coordinates and vice versa.

        .. seealso::

            :py:class:`qwt.scale_map.QwtScaleMap`,
            :py:meth:`transform()`, :py:meth:`invTransform()`
        """
        map_ = QwtScaleMap()
        if not self.__data.canvas:
            return map_

        map_.setTransformation(self.axisScaleEngine(axisId).transformation())
        sd = self.axisScaleDiv(axisId)
        if sd is None:
            return map_
        map_.setScaleInterval(sd.lowerBound(), sd.upperBound())

        if self.axisEnabled(axisId):
            s = self.axisWidget(axisId)
            if axisId in (self.yLeft, self.yRight):
                y = s.y() + s.startBorderDist() - self.__data.canvas.y()
                h = s.height() - s.startBorderDist() - s.endBorderDist()
                map_.setPaintInterval(y + h, y)
            else:
                x = s.x() + s.startBorderDist() - self.__data.canvas.x()
                w = s.width() - s.startBorderDist() - s.endBorderDist()
                map_.setPaintInterval(x, x + w)
        else:
            canvasRect = self.__data.canvas.contentsRect()
            if axisId in (self.yLeft, self.yRight):
                top = 0
                if not self.plotLayout().alignCanvasToScale(self.xTop):
                    top = self.plotLayout().canvasMargin(self.xTop)
                bottom = 0
                if not self.plotLayout().alignCanvasToScale(self.xBottom):
                    bottom = self.plotLayout().canvasMargin(self.xBottom)
                map_.setPaintInterval(
                    canvasRect.bottom() - bottom, canvasRect.top() + top
                )
            else:
                left = 0
                if not self.plotLayout().alignCanvasToScale(self.yLeft):
                    left = self.plotLayout().canvasMargin(self.yLeft)
                right = 0
                if not self.plotLayout().alignCanvasToScale(self.yRight):
                    right = self.plotLayout().canvasMargin(self.yRight)
                map_.setPaintInterval(
                    canvasRect.left() + left, canvasRect.right() - right
                )
        return map_

    def setCanvasBackground(self, brush):
        """
        Change the background of the plotting area

        Sets brush to `QPalette.Window` of all color groups of
        the palette of the canvas. Using `canvas().setPalette()`
        is a more powerful way to set these colors.

        :param QBrush brush: New background brush

        .. seealso::

            :py:meth:`canvasBackground()`
        """
        pal = self.__data.canvas.palette()
        pal.setBrush(QPalette.Window, QBrush(brush))
        self.canvas().setPalette(pal)

    def canvasBackground(self):
        """
        :return: Background brush of the plotting area.

        .. seealso::

            :py:meth:`setCanvasBackground()`
        """
        return self.canvas().palette().brush(QPalette.Normal, QPalette.Window)

    def axisValid(self, axis_id):
        """
        :param int axis_id: Axis
        :return: True if the specified axis exists, otherwise False
        """
        return axis_id in QwtPlot.AXES

    def insertLegend(self, legend, pos=None, ratio=-1):
        """
        Insert a legend

        If the position legend is `QwtPlot.LeftLegend` or `QwtPlot.RightLegend`
        the legend will be organized in one column from top to down.
        Otherwise the legend items will be placed in a table
        with a best fit number of columns from left to right.

        insertLegend() will set the plot widget as parent for the legend.
        The legend will be deleted in the destructor of the plot or when
        another legend is inserted.

        Legends, that are not inserted into the layout of the plot widget
        need to connect to the legendDataChanged() signal. Calling updateLegend()
        initiates this signal for an initial update. When the application code
        wants to implement its own layout this also needs to be done for
        rendering plots to a document ( see QwtPlotRenderer ).

        :param qwt.legend.QwtAbstractLegend legend: Legend
        :param QwtPlot.LegendPosition pos: The legend's position.
        :param float ratio: Ratio between legend and the bounding rectangle of title, canvas and axes

        .. note::

            For top/left position the number of columns will be limited to 1,
            otherwise it will be set to unlimited.

        .. note::

            The legend will be shrunk if it would need more space than the
            given ratio. The ratio is limited to ]0.0 .. 1.0].
            In case of <= 0.0 it will be reset to the default ratio.
            The default vertical/horizontal ratio is 0.33/0.5.

        .. seealso::

            :py:meth:`legend()`,
            :py:meth:`qwt.plot_layout.QwtPlotLayout.legendPosition()`,
            :py:meth:`qwt.plot_layout.QwtPlotLayout.setLegendPosition()`
        """
        if pos is None:
            pos = self.RightLegend
        self.__data.layout.setLegendPosition(pos, ratio)
        if legend != self.__data.legend:
            if self.__data.legend and self.__data.legend.parent() is self:
                del self.__data.legend
            self.__data.legend = legend
            if self.__data.legend:
                self.legendDataChanged.connect(self.__data.legend.updateLegend)
                if self.__data.legend.parent() is not self:
                    self.__data.legend.setParent(self)

                qwtEnableLegendItems(self, False)
                self.updateLegend()
                qwtEnableLegendItems(self, True)

                lpos = self.__data.layout.legendPosition()

                if legend is not None:
                    if lpos in (self.LeftLegend, self.RightLegend):
                        if legend.maxColumns() == 0:
                            legend.setMaxColumns(1)
                    elif lpos in (self.TopLegend, self.BottomLegend):
                        legend.setMaxColumns(0)

                previousInChain = None
                if lpos == self.LeftLegend:
                    previousInChain = self.axisWidget(QwtPlot.xTop)
                elif lpos == self.TopLegend:
                    previousInChain = self
                elif lpos == self.RightLegend:
                    previousInChain = self.axisWidget(QwtPlot.yRight)
                elif lpos == self.BottomLegend:
                    previousInChain = self.footerLabel()

                if previousInChain is not None:
                    qwtSetTabOrder(previousInChain, legend, True)

        self.updateLayout()

    def updateLegend(self, plotItem=None):
        """
        If plotItem is None, emit QwtPlot.legendDataChanged for all
        plot item. Otherwise, emit the signal for passed plot item.

        :param qwt.plot.QwtPlotItem plotItem: Plot item

        .. seealso::

            :py:meth:`QwtPlotItem.legendData()`, :py:data:`QwtPlot.legendDataChanged`
        """
        if plotItem is None:
            items = list(self.itemList())
        else:
            items = [plotItem]
        for plotItem in items:
            if plotItem is None:
                continue
            legendData = []
            if plotItem.testItemAttribute(QwtPlotItem.Legend):
                legendData = plotItem.legendData()
            self.legendDataChanged.emit(plotItem, legendData)

    def updateLegendItems(self, plotItem, legendData):
        """
        Update all plot items interested in legend attributes

        Call `QwtPlotItem.updateLegend()`, when the
        `QwtPlotItem.LegendInterest` flag is set.

        :param qwt.plot.QwtPlotItem plotItem: Plot item
        :param list legendData: Entries to be displayed for the plot item ( usually 1 )

        .. seealso::

            :py:meth:`QwtPlotItem.LegendInterest()`,
            :py:meth:`QwtPlotItem.updateLegend`
        """
        if plotItem is not None:
            for item in self.itemList():
                if item.testItemInterest(QwtPlotItem.LegendInterest):
                    item.updateLegend(plotItem, legendData)

    def attachItem(self, plotItem, on):
        """
        Attach/Detach a plot item

        :param qwt.plot.QwtPlotItem plotItem: Plot item
        :param bool on: When true attach the item, otherwise detach it
        """
        if plotItem.testItemInterest(QwtPlotItem.LegendInterest):
            for item in self.itemList():
                legendData = []
                if on and item.testItemAttribute(QwtPlotItem.Legend):
                    legendData = item.legendData()
                    plotItem.updateLegend(item, legendData)

        if on:
            self.insertItem(plotItem)
        else:
            self.removeItem(plotItem)

        self.itemAttached.emit(plotItem, on)

        if plotItem.testItemAttribute(QwtPlotItem.Legend):
            if on:
                self.updateLegend(plotItem)
            else:
                self.legendDataChanged.emit(plotItem, [])

        self.autoRefresh()

    def print_(self, printer):
        """
        Print plot to printer

        :param printer: Printer
        :type printer: QPaintDevice or QPrinter or QSvgGenerator
        """
        from qwt.plot_renderer import QwtPlotRenderer

        renderer = QwtPlotRenderer(self)
        renderer.renderTo(self, printer)

    def exportTo(
        self, filename, size=(800, 600), size_mm=None, resolution=72.0, format_=None
    ):
        """
        Export plot to PDF or image file (SVG, PNG, ...)

        :param str filename: Filename
        :param tuple size: (width, height) size in pixels
        :param tuple size_mm: (width, height) size in millimeters
        :param float resolution: Image resolution
        :param str format_: File format (PDF, SVG, PNG, ...)
        """
        if size_mm is None:
            size_mm = tuple(25.4 * np.array(size) / resolution)
        from qwt.plot_renderer import QwtPlotRenderer

        renderer = QwtPlotRenderer(self)
        renderer.renderDocument(self, filename, size_mm, resolution, format_)
 class QwtPlotItem(object):
    """
    Base class for items on the plot canvas

    A plot item is "something", that can be painted on the plot canvas,
    or only affects the scales of the plot widget. They can be categorized as:

    - Representator

      A "Representator" is an item that represents some sort of data
      on the plot canvas. The different representator classes are organized
      according to the characteristics of the data:

          - :py:class:`qwt.plot_marker.QwtPlotMarker`: Represents a point or a
            horizontal/vertical coordinate
          - :py:class:`qwt.plot_curve.QwtPlotCurve`: Represents a series of
            points

    - Decorators

      A "Decorator" is an item, that displays additional information, that
      is not related to any data:

          - :py:class:`qwt.plot_grid.QwtPlotGrid`

    Depending on the `QwtPlotItem.ItemAttribute` flags, an item is included
    into autoscaling or has an entry on the legend.

    Before misusing the existing item classes it might be better to
    implement a new type of plot item
    ( don't implement a watermark as spectrogram ).
    Deriving a new type of `QwtPlotItem` primarily means to implement
    the `YourPlotItem.draw()` method.

    .. seealso::

        The cpuplot example shows the implementation of additional plot items.

    .. py:class:: QwtPlotItem([title=None])

        Constructor

        :param title: Title of the item
        :type title: qwt.text.QwtText or str
    """

    # enum RttiValues
    (
        Rtti_PlotItem,
        Rtti_PlotGrid,
        Rtti_PlotScale,
        Rtti_PlotLegend,
        Rtti_PlotMarker,
        Rtti_PlotCurve,
        Rtti_PlotSpectroCurve,
        Rtti_PlotIntervalCurve,
        Rtti_PlotHistogram,
        Rtti_PlotSpectrogram,
        Rtti_PlotSVG,
        Rtti_PlotTradingCurve,
        Rtti_PlotBarChart,
        Rtti_PlotMultiBarChart,
        Rtti_PlotShape,
        Rtti_PlotTextLabel,
        Rtti_PlotZone,
    ) = list(range(17))
    Rtti_PlotUserItem = 1000

    # enum ItemAttribute
    Legend = 0x01
    AutoScale = 0x02
    Margins = 0x04

    # enum ItemInterest
    ScaleInterest = 0x01
    LegendInterest = 0x02

    # enum RenderHint
    RenderAntialiased = 0x1

    def __init__(self, title=None, icon=None):
        """title: QwtText"""
        if title is None:
            title = QwtText("")
        if hasattr(title, "capitalize"):  # avoids dealing with Py3K compat.
            title = QwtText(title)
        assert isinstance(title, QwtText)
        self.__data = QwtPlotItem_PrivateData()
        self.__data.title = title
        self.__data.icon = icon

    def attach(self, plot):
        """
        Attach the item to a plot.

        This method will attach a `QwtPlotItem` to the `QwtPlot` argument.
        It will first detach the `QwtPlotItem` from any plot from a previous
        call to attach (if necessary). If a None argument is passed, it will
        detach from any `QwtPlot` it was attached to.

        :param qwt.plot.QwtPlot plot: Plot widget

        .. seealso::

            :py:meth:`detach()`
        """
        if plot is self.__data.plot:
            return

        if self.__data.plot:
            self.__data.plot.attachItem(self, False)

        self.__data.plot = plot

        if self.__data.plot:
            self.__data.plot.attachItem(self, True)

    def detach(self):
        """
        Detach the item from a plot.

        This method detaches a `QwtPlotItem` from any `QwtPlot` it has been
        associated with.

        .. seealso::

            :py:meth:`attach()`
        """
        self.attach(None)

    def rtti(self):
        """
        Return rtti for the specific class represented. `QwtPlotItem` is
        simply a virtual interface class, and base classes will implement
        this method with specific rtti values so a user can differentiate
        them.

        :return: rtti value
        """
        return self.Rtti_PlotItem

    def plot(self):
        """
        :return: attached plot
        """
        return self.__data.plot

    def z(self):
        """
        Plot items are painted in increasing z-order.

        :return: item z order

        .. seealso::

            :py:meth:`setZ()`, :py:meth:`QwtPlotDict.itemList()`
        """
        return self.__data.z

    def setZ(self, z):
        """
        Set the z value

        Plot items are painted in increasing z-order.

        :param float z: Z-value

        .. seealso::

            :py:meth:`z()`, :py:meth:`QwtPlotDict.itemList()`
        """
        if self.__data.z != z:
            if self.__data.plot:
                self.__data.plot.attachItem(self, False)
            self.__data.z = z
            if self.__data.plot:
                self.__data.plot.attachItem(self, True)
            self.itemChanged()

    def setTitle(self, title):
        """
        Set a new title

        :param title: Title
        :type title: qwt.text.QwtText or str

        .. seealso::

            :py:meth:`title()`
        """
        if not isinstance(title, QwtText):
            title = QwtText(title)
        if self.__data.title != title:
            self.__data.title = title
        self.legendChanged()

    def title(self):
        """
        :return: Title of the item

        .. seealso::

            :py:meth:`setTitle()`
        """
        return self.__data.title

    def setIcon(self, icon):
        """
        Set item icon

        :param icon: Icon
        :type icon: QIcon

        .. seealso::

            :py:meth:`icon()`
        """
        self.__data.icon = icon

    def icon(self):
        """
        :return: Icon of the item

        .. seealso::

            :py:meth:`setIcon()`
        """
        return self.__data.icon

    def setItemAttribute(self, attribute, on=True):
        """
        Toggle an item attribute

        :param int attribute: Attribute type
        :param bool on: True/False

        .. seealso::

            :py:meth:`testItemAttribute()`
        """
        if bool(self.__data.attributes & attribute) != on:
            if on:
                self.__data.attributes |= attribute
            else:
                self.__data.attributes &= ~attribute
            if attribute == QwtPlotItem.Legend:
                self.legendChanged()
            self.itemChanged()

    def testItemAttribute(self, attribute):
        """
        Test an item attribute

        :param int attribute: Attribute type
        :return: True/False

        .. seealso::

            :py:meth:`setItemAttribute()`
        """
        return bool(self.__data.attributes & attribute)

    def setItemInterest(self, interest, on=True):
        """
        Toggle an item interest

        :param int attribute: Interest type
        :param bool on: True/False

        .. seealso::

            :py:meth:`testItemInterest()`
        """
        if bool(self.__data.interests & interest) != on:
            if on:
                self.__data.interests |= interest
            else:
                self.__data.interests &= ~interest
            self.itemChanged()

    def testItemInterest(self, interest):
        """
        Test an item interest

        :param int attribute: Interest type
        :return: True/False

        .. seealso::

            :py:meth:`setItemInterest()`
        """
        return bool(self.__data.interests & interest)

    def setRenderHint(self, hint, on=True):
        """
        Toggle a render hint

        :param int hint: Render hint
        :param bool on: True/False

        .. seealso::

            :py:meth:`testRenderHint()`
        """
        if bool(self.__data.renderHints & hint) != on:
            if on:
                self.__data.renderHints |= hint
            else:
                self.__data.renderHints &= ~hint
            self.itemChanged()

    def testRenderHint(self, hint):
        """
        Test a render hint

        :param int attribute: Render hint
        :return: True/False

        .. seealso::

            :py:meth:`setRenderHint()`
        """
        return bool(self.__data.renderHints & hint)

    def setLegendIconSize(self, size):
        """
        Set the size of the legend icon

        The default setting is 8x8 pixels

        :param QSize size: Size

        .. seealso::

            :py:meth:`legendIconSize()`, :py:meth:`legendIcon()`
        """
        if self.__data.legendIconSize != size:
            self.__data.legendIconSize = size
            self.legendChanged()

    def legendIconSize(self):
        """
        :return: Legend icon size

        .. seealso::

            :py:meth:`setLegendIconSize()`, :py:meth:`legendIcon()`
        """
        return self.__data.legendIconSize

    def legendIcon(self, index, size):
        """
        :param int index: Index of the legend entry (usually there is only one)
        :param QSizeF size: Icon size
        :return: Icon representing the item on the legend

        The default implementation returns an invalid icon

        .. seealso::

            :py:meth:`setLegendIconSize()`, :py:meth:`legendData()`
        """
        return QwtGraphic()

    def show(self):
        """Show the item"""
        self.setVisible(True)

    def hide(self):
        """Hide the item"""
        self.setVisible(False)

    def setVisible(self, on):
        """
        Show/Hide the item

        :param bool on: Show if True, otherwise hide

        .. seealso::

            :py:meth:`isVisible()`, :py:meth:`show()`, :py:meth:`hide()`
        """
        if on != self.__data.isVisible:
            self.__data.isVisible = on
            self.itemChanged()

    def isVisible(self):
        """
        :return: True if visible

        .. seealso::

            :py:meth:`setVisible()`, :py:meth:`show()`, :py:meth:`hide()`
        """
        return self.__data.isVisible

    def itemChanged(self):
        """
        Update the legend and call `QwtPlot.autoRefresh()` for the
        parent plot.

        .. seealso::

            :py:meth:`QwtPlot.legendChanged()`, :py:meth:`QwtPlot.autoRefresh()`
        """
        if self.__data.plot:
            self.__data.plot.autoRefresh()

    def legendChanged(self):
        """
        Update the legend of the parent plot.

        .. seealso::

            :py:meth:`QwtPlot.updateLegend()`, :py:meth:`itemChanged()`
        """
        if self.testItemAttribute(QwtPlotItem.Legend) and self.__data.plot:
            self.__data.plot.updateLegend(self)

    def setAxes(self, xAxis, yAxis):
        """
        Set X and Y axis

        The item will painted according to the coordinates of its Axes.

        :param int xAxis: X Axis (`QwtPlot.xBottom` or `QwtPlot.xTop`)
        :param int yAxis: Y Axis (`QwtPlot.yLeft` or `QwtPlot.yRight`)

        .. seealso::

            :py:meth:`setXAxis()`, :py:meth:`setYAxis()`,
            :py:meth:`xAxis()`, :py:meth:`yAxis()`
        """
        if xAxis == QwtPlot.xBottom or xAxis == QwtPlot.xTop:
            self.__data.xAxis = xAxis
        if yAxis == QwtPlot.yLeft or yAxis == QwtPlot.yRight:
            self.__data.yAxis = yAxis
        self.itemChanged()

    def setAxis(self, xAxis, yAxis):
        """
        Set X and Y axis

        .. warning::

            `setAxis` has been removed in Qwt6: please use
            :py:meth:`setAxes()` instead
        """
        import warnings

        warnings.warn(
            "`setAxis` has been removed in Qwt6: " "please use `setAxes` instead",
            RuntimeWarning,
        )
        self.setAxes(xAxis, yAxis)

    def setXAxis(self, axis):
        """
        Set the X axis

        The item will painted according to the coordinates its Axes.

        :param int axis: X Axis (`QwtPlot.xBottom` or `QwtPlot.xTop`)

        .. seealso::

            :py:meth:`setAxes()`, :py:meth:`setYAxis()`,
            :py:meth:`xAxis()`, :py:meth:`yAxis()`
        """
        if axis in (QwtPlot.xBottom, QwtPlot.xTop):
            self.__data.xAxis = axis
            self.itemChanged()

    def setYAxis(self, axis):
        """
        Set the Y axis

        The item will painted according to the coordinates its Axes.

        :param int axis: Y Axis (`QwtPlot.yLeft` or `QwtPlot.yRight`)

        .. seealso::

            :py:meth:`setAxes()`, :py:meth:`setXAxis()`,
            :py:meth:`xAxis()`, :py:meth:`yAxis()`
        """
        if axis in (QwtPlot.yLeft, QwtPlot.yRight):
            self.__data.yAxis = axis
            self.itemChanged()

    def xAxis(self):
        """
        :return: xAxis
        """
        return self.__data.xAxis

    def yAxis(self):
        """
        :return: yAxis
        """
        return self.__data.yAxis

    def boundingRect(self):
        """
        :return: An invalid bounding rect: QRectF(1.0, 1.0, -2.0, -2.0)

        .. note::

            A width or height < 0.0 is ignored by the autoscaler
        """
        return QRectF(1.0, 1.0, -2.0, -2.0)

    def getCanvasMarginHint(self, xMap, yMap, canvasRect):
        """
        Calculate a hint for the canvas margin

        When the QwtPlotItem::Margins flag is enabled the plot item
        indicates, that it needs some margins at the borders of the canvas.
        This is f.e. used by bar charts to reserve space for displaying
        the bars.

        The margins are in target device coordinates ( pixels on screen )

        :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
        :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
        :param QRectF canvasRect: Contents rectangle of the canvas in painter coordinates

        .. seealso::

            :py:meth:`QwtPlot.getCanvasMarginsHint()`,
            :py:meth:`QwtPlot.updateCanvasMargins()`,
        """
        left = top = right = bottom = 0.0
        return left, top, right, bottom

    def legendData(self):
        """
        Return all information, that is needed to represent
        the item on the legend

        `QwtLegendData` is basically a list of QVariants that makes it
        possible to overload and reimplement legendData() to
        return almost any type of information, that is understood
        by the receiver that acts as the legend.

        The default implementation returns one entry with
        the title() of the item and the legendIcon().

        :return: Data, that is needed to represent the item on the legend

        .. seealso::

            :py:meth:`title()`, :py:meth:`legendIcon()`,
            :py:class:`qwt.legend.QwtLegend`
        """
        data = QwtLegendData()
        label = self.title()
        label.setRenderFlags(label.renderFlags() & Qt.AlignLeft)
        data.setValue(QwtLegendData.TitleRole, label)
        graphic = self.legendIcon(0, self.legendIconSize())
        if not graphic.isNull():
            data.setValue(QwtLegendData.IconRole, graphic)
        return [data]

    def updateLegend(self, item, data):
        """
        Update the item to changes of the legend info

        Plot items that want to display a legend ( not those, that want to
        be displayed on a legend ! ) will have to implement updateLegend().

        updateLegend() is only called when the LegendInterest interest
        is enabled. The default implementation does nothing.

        :param qwt.plot.QwtPlotItem item: Plot item to be displayed on a legend
        :param list data: Attributes how to display item on the legend

        .. note::

            Plot items, that want to be displayed on a legend
            need to enable the `QwtPlotItem.Legend` flag and to implement
            legendData() and legendIcon()
        """
        pass

    def scaleRect(self, xMap, yMap):
        """
        Calculate the bounding scale rectangle of 2 maps

        :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
        :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
        :return: Bounding scale rect of the scale maps, not normalized
        """
        return QRectF(xMap.s1(), yMap.s1(), xMap.sDist(), yMap.sDist())

    def paintRect(self, xMap, yMap):
        """
        Calculate the bounding paint rectangle of 2 maps

        :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
        :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
        :return: Bounding paint rectangle of the scale maps, not normalized
        """
        return QRectF(xMap.p1(), yMap.p1(), xMap.pDist(), yMap.pDist())
 class QwtPlotItem_PrivateData(object):
    def __init__(self):
        self.plot = None
        self.isVisible = True
        self.attributes = 0
        self.interests = 0
        self.renderHints = 0
        self.z = 0.0
        self.xAxis = QwtPlot.xBottom
        self.yAxis = QwtPlot.yLeft
        self.legendIconSize = QSize(8, 8)
        self.title = None  # QwtText
        self.icon = None
 def qwtSqr(x):
    return x ** 2 class QwtGraphic(QwtNullPaintDevice):
    """
    A paint device for scalable graphics

    `QwtGraphic` is the representation of a graphic that is tailored for
    scalability. Like `QPicture` it will be initialized by `QPainter`
    operations and can be replayed later to any target paint device.

    While the usual image representations `QImage` and `QPixmap` are not
    scalable `Qt` offers two paint devices, that might be candidates
    for representing a vector graphic:

        - `QPicture`:

          Unfortunately `QPicture` had been forgotten, when Qt4
          introduced floating point based render engines. Its API
          is still on integers, what make it unusable for proper scaling.

        - `QSvgRenderer`, `QSvgGenerator`:

          Unfortunately `QSvgRenderer` hides to much information about
          its nodes in internal APIs, that are necessary for proper
          layout calculations. Also it is derived from `QObject` and
          can't be copied like `QImage`/`QPixmap`.

    `QwtGraphic` maps all scalable drawing primitives to a `QPainterPath`
    and stores them together with the painter state changes
    ( pen, brush, transformation ... ) in a list of `QwtPaintCommands`.
    For being a complete `QPaintDevice` it also stores pixmaps or images,
    what is somehow against the idea of the class, because these objects
    can't be scaled without a loss in quality.

    The main issue about scaling a `QwtGraphic` object are the pens used for
    drawing the outlines of the painter paths. While non cosmetic pens
    ( `QPen.isCosmetic()` ) are scaled with the same ratio as the path,
    cosmetic pens have a fixed width. A graphic might have paths with
    different pens - cosmetic and non-cosmetic.

    `QwtGraphic` caches 2 different rectangles:

        - control point rectangle:

          The control point rectangle is the bounding rectangle of all
          control point rectangles of the painter paths, or the target
          rectangle of the pixmaps/images.

        - bounding rectangle:

          The bounding rectangle extends the control point rectangle by
          what is needed for rendering the outline with an unscaled pen.

    Because the offset for drawing the outline depends on the shape
    of the painter path ( the peak of a triangle is different than the flat side )
    scaling with a fixed aspect ratio always needs to be calculated from the
    control point rectangle.

    .. py:class:: QwtGraphic()

        Initializes a null graphic

    .. py:class:: QwtGraphic(other)
        :noindex:

        Copy constructor

        :param qwt.graphic.QwtGraphic other: Source
    """

    # enum RenderHint
    RenderPensUnscaled = 0x1

    def __init__(self, *args):
        QwtNullPaintDevice.__init__(self)
        if len(args) == 0:
            self.setMode(QwtNullPaintDevice.PathMode)
            self.__data = QwtGraphic_PrivateData()
        elif len(args) == 1:
            (other,) = args
            self.setMode(other.mode())
            self.__data = other.__data
        else:
            raise TypeError(
                "%s() takes 0 or 1 argument(s) (%s given)"
                % (self.__class__.__name__, len(args))
            )

    def reset(self):
        """Clear all stored commands"""
        self.__data.commands = []
        self.__data.pathInfos = []
        self.__data.boundingRect = QRectF(0.0, 0.0, -1.0, -1.0)
        self.__data.pointRect = QRectF(0.0, 0.0, -1.0, -1.0)
        self.__data.defaultSize = QSizeF()

    def isNull(self):
        """Return True, when no painter commands have been stored"""
        return len(self.__data.commands) == 0

    def isEmpty(self):
        """Return True, when the bounding rectangle is empty"""
        return self.__data.boundingRect.isEmpty()

    def setRenderHint(self, hint, on=True):
        """Toggle an render hint"""
        if on:
            self.__data.renderHints |= hint
        else:
            self.__data.renderHints &= ~hint

    def testRenderHint(self, hint):
        """Test a render hint"""
        return bool(self.__data.renderHints & hint)

    def boundingRect(self):
        """
        The bounding rectangle is the :py:meth:`controlPointRect`
        extended by the areas needed for rendering the outlines
        with unscaled pens.

        :return: Bounding rectangle of the graphic

        .. seealso::

            :py:meth:`controlPointRect`, :py:meth:`scaledBoundingRect`
        """
        if self.__data.boundingRect.width() < 0:
            return QRectF()
        return self.__data.boundingRect

    def controlPointRect(self):
        """
        The control point rectangle is the bounding rectangle
        of all control points of the paths and the target
        rectangles of the images/pixmaps.

        :return: Control point rectangle

        .. seealso::

            :py:meth:`boundingRect()`, :py:meth:`scaledBoundingRect()`
        """
        if self.__data.pointRect.width() < 0:
            return QRectF()
        return self.__data.pointRect

    def scaledBoundingRect(self, sx, sy):
        """
        Calculate the target rectangle for scaling the graphic

        :param float sx: Horizontal scaling factor
        :param float sy: Vertical scaling factor
        :return: Scaled bounding rectangle

        .. note::

            In case of paths that are painted with a cosmetic pen
            (see :py:meth:`QPen.isCosmetic()`) the target rectangle is
            different to multiplying the bounding rectangle.

        .. seealso::

            :py:meth:`boundingRect()`, :py:meth:`controlPointRect()`
        """
        if sx == 1.0 and sy == 1.0:
            return self.__data.boundingRect
        transform = QTransform()
        transform.scale(sx, sy)
        rect = transform.mapRect(self.__data.pointRect)
        for pathInfo in self.__data.pathInfos:
            rect |= pathInfo.scaledBoundingRect(
                sx, sy, not bool(self.__data.renderHints & self.RenderPensUnscaled)
            )
        return rect

    def sizeMetrics(self):
        """Return Ceiled :py:meth:`defaultSize()`"""
        sz = self.defaultSize()
        return QSize(np.ceil(sz.width()), np.ceil(sz.height()))

    def setDefaultSize(self, size):
        """
        The default size is used in all methods rendering the graphic,
        where no size is explicitly specified. Assigning an empty size
        means, that the default size will be calculated from the bounding
        rectangle.

        :param QSizeF size: Default size

        .. seealso::

            :py:meth:`defaultSize()`, :py:meth:`boundingRect()`
        """
        w = max([0.0, size.width()])
        h = max([0.0, size.height()])
        self.__data.defaultSize = QSizeF(w, h)

    def defaultSize(self):
        """
        When a non empty size has been assigned by setDefaultSize() this
        size will be returned. Otherwise the default size is the size
        of the bounding rectangle.

        The default size is used in all methods rendering the graphic,
        where no size is explicitly specified.

        :return: Default size

        .. seealso::

            :py:meth:`setDefaultSize()`, :py:meth:`boundingRect()`
        """
        if not self.__data.defaultSize.isEmpty():
            return self.__data.defaultSize
        return self.boundingRect().size()

    def render(self, *args):
        """
        .. py:method:: render(painter)
            :noindex:

            Replay all recorded painter commands

            :param QPainter painter: Qt painter

        .. py:method:: render(painter, size, aspectRatioMode)
            :noindex:

            Replay all recorded painter commands

            The graphic is scaled to fit into the rectangle
            of the given size starting at ( 0, 0 ).

            :param QPainter painter: Qt painter
            :param QSizeF size: Size for the scaled graphic
            :param Qt.AspectRatioMode aspectRatioMode: Mode how to scale

        .. py:method:: render(painter, rect, aspectRatioMode)
            :noindex:

            Replay all recorded painter commands

            The graphic is scaled to fit into the given rectangle

            :param QPainter painter: Qt painter
            :param QRectF rect: Rectangle for the scaled graphic
            :param Qt.AspectRatioMode aspectRatioMode: Mode how to scale

        .. py:method:: render(painter, pos, aspectRatioMode)
            :noindex:

            Replay all recorded painter commands

            The graphic is scaled to the :py:meth:`defaultSize()` and aligned
            to a position.

            :param QPainter painter: Qt painter
            :param QPointF pos: Reference point, where to render
            :param Qt.AspectRatioMode aspectRatioMode: Mode how to scale
        """
        if len(args) == 1:
            (painter,) = args
            if self.isNull():
                return
            transform = painter.transform()
            painter.save()
            for command in self.__data.commands:
                qwtExecCommand(
                    painter,
                    command,
                    self.__data.renderHints,
                    transform,
                    self.__data.initialTransform,
                )
            painter.restore()
        elif len(args) in (2, 3) and isinstance(args[1], QSizeF):
            painter, size = args[:2]
            aspectRatioMode = Qt.IgnoreAspectRatio
            if len(args) == 3:
                aspectRatioMode = args[-1]
            r = QRectF(0.0, 0.0, size.width(), size.height())
            self.render(painter, r, aspectRatioMode)
        elif len(args) in (2, 3) and isinstance(args[1], QRectF):
            painter, rect = args[:2]
            aspectRatioMode = Qt.IgnoreAspectRatio
            if len(args) == 3:
                aspectRatioMode = args[-1]
            if self.isEmpty() or rect.isEmpty():
                return
            sx = 1.0
            sy = 1.0
            if self.__data.pointRect.width() > 0.0:
                sx = rect.width() / self.__data.pointRect.width()
            if self.__data.pointRect.height() > 0.0:
                sy = rect.height() / self.__data.pointRect.height()
            scalePens = not bool(self.__data.renderHints & self.RenderPensUnscaled)
            for info in self.__data.pathInfos:
                ssx = info.scaleFactorX(self.__data.pointRect, rect, scalePens)
                if ssx > 0.0:
                    sx = min([sx, ssx])
                ssy = info.scaleFactorY(self.__data.pointRect, rect, scalePens)
                if ssy > 0.0:
                    sy = min([sy, ssy])
            if aspectRatioMode == Qt.KeepAspectRatio:
                s = min([sx, sy])
                sx = s
                sy = s
            elif aspectRatioMode == Qt.KeepAspectRatioByExpanding:
                s = max([sx, sy])
                sx = s
                sy = s
            tr = QTransform()
            tr.translate(
                rect.center().x() - 0.5 * sx * self.__data.pointRect.width(),
                rect.center().y() - 0.5 * sy * self.__data.pointRect.height(),
            )
            tr.scale(sx, sy)
            tr.translate(-self.__data.pointRect.x(), -self.__data.pointRect.y())
            transform = painter.transform()
            if not scalePens and transform.isScaling():
                #  we don't want to scale pens according to sx/sy,
                #  but we want to apply the scaling from the
                #  painter transformation later
                self.__data.initialTransform = QTransform()
                self.__data.initialTransform.scale(transform.m11(), transform.m22())
            painter.setTransform(tr, True)
            self.render(painter)
            painter.setTransform(transform)
            self.__data.initialTransform = None
        elif len(args) in (2, 3) and isinstance(args[1], QPointF):
            painter, pos = args[:2]
            alignment = Qt.AlignTop | Qt.AlignLeft
            if len(args) == 3:
                alignment = args[-1]
            r = QRectF(pos, self.defaultSize())
            if alignment & Qt.AlignLeft:
                r.moveLeft(pos.x())
            elif alignment & Qt.AlignHCenter:
                r.moveCenter(QPointF(pos.x(), r.center().y()))
            elif alignment & Qt.AlignRight:
                r.moveRight(pos.x())
            if alignment & Qt.AlignTop:
                r.moveTop(pos.y())
            elif alignment & Qt.AlignVCenter:
                r.moveCenter(QPointF(r.center().x(), pos.y()))
            elif alignment & Qt.AlignBottom:
                r.moveBottom(pos.y())
            self.render(painter, r)
        else:
            raise TypeError(
                "%s().render() takes 1, 2 or 3 argument(s) (%s "
                "given)" % (self.__class__.__name__, len(args))
            )

    def toPixmap(self, *args):
        """
        Convert the graphic to a `QPixmap`

        All pixels of the pixmap get initialized by `Qt.transparent`
        before the graphic is scaled and rendered on it.

        The size of the pixmap is the default size ( ceiled to integers )
        of the graphic.

        :return: The graphic as pixmap in default size

        .. seealso::

            :py:meth:`defaultSize()`, :py:meth:`toImage()`, :py:meth:`render()`
        """
        if len(args) == 0:
            if self.isNull():
                return QPixmap()
            sz = self.defaultSize()
            w = np.ceil(sz.width())
            h = np.ceil(sz.height())
            pixmap = QPixmap(w, h)
            pixmap.fill(Qt.transparent)
            r = QRectF(0.0, 0.0, sz.width(), sz.height())
            painter = QPainter(pixmap)
            self.render(painter, r, Qt.KeepAspectRatio)
            painter.end()
            return pixmap
        elif len(args) in (1, 2):
            size = args[0]
            aspectRatioMode = Qt.IgnoreAspectRatio
            if len(args) == 2:
                aspectRatioMode = args[-1]
            pixmap = QPixmap(size)
            pixmap.fill(Qt.transparent)
            r = QRect(0, 0, size.width(), size.height())
            painter = QPainter(pixmap)
            self.render(painter, r, aspectRatioMode)
            painter.end()
            return pixmap

    def toImage(self, *args):
        """
        .. py:method:: toImage()
            :noindex:

            Convert the graphic to a `QImage`

            All pixels of the image get initialized by 0 ( transparent )
            before the graphic is scaled and rendered on it.

            The format of the image is `QImage.Format_ARGB32_Premultiplied`.

            The size of the image is the default size ( ceiled to integers )
            of the graphic.

            :return: The graphic as image in default size

        .. py:method:: toImage(size, [aspectRatioMode=Qt.IgnoreAspectRatio])
            :noindex:

            Convert the graphic to a `QImage`

            All pixels of the image get initialized by 0 ( transparent )
            before the graphic is scaled and rendered on it.

            The format of the image is `QImage.Format_ARGB32_Premultiplied`.

            :param QSize size: Size of the image
            :param `Qt.AspectRatioMode` aspectRatioMode: Aspect ratio how to scale the graphic
            :return: The graphic as image

        .. seealso::

            :py:meth:`toPixmap()`, :py:meth:`render()`
        """
        if len(args) == 0:
            if self.isNull():
                return QImage()
            sz = self.defaultSize()
            w = np.ceil(sz.width())
            h = np.ceil(sz.height())
            image = QImage(w, h, QImage.Format_ARGB32)
            image.fill(0)
            r = QRect(0, 0, sz.width(), sz.height())
            painter = QPainter(image)
            self.render(painter, r, Qt.KeepAspectRatio)
            painter.end()
            return image
        elif len(args) in (1, 2):
            size = args[0]
            aspectRatioMode = Qt.IgnoreAspectRatio
            if len(args) == 2:
                aspectRatioMode = args[-1]
            image = QImage(size, QImage.Format_ARGB32_Premultiplied)
            image.fill(0)
            r = QRect(0, 0, size.width(), size.height())
            painter = QPainter(image)
            self.render(painter, r, aspectRatioMode)
            return image

    def drawPath(self, path):
        """
        Store a path command in the command list

        :param QPainterPath path: Painter path

        .. seealso::

            :py:meth:`QPaintEngine.drawPath()`
        """
        painter = self.paintEngine().painter()
        if painter is None:
            return
        self.__data.commands += [QwtPainterCommand(path)]
        if not path.isEmpty():
            scaledPath = painter.transform().map(path)
            pointRect = scaledPath.boundingRect()
            boundingRect = QRectF(pointRect)
            if (
                painter.pen().style() != Qt.NoPen
                and painter.pen().brush().style() != Qt.NoBrush
            ):
                boundingRect = qwtStrokedPathRect(painter, path)
            self.updateControlPointRect(pointRect)
            self.updateBoundingRect(boundingRect)
            self.__data.pathInfos += [
                PathInfo(pointRect, boundingRect, qwtHasScalablePen(painter))
            ]

    def drawPixmap(self, rect, pixmap, subRect):
        """
        Store a pixmap command in the command list

        :param QRectF rect: target rectangle
        :param QPixmap pixmap: Pixmap to be painted
        :param QRectF subRect: Reactangle of the pixmap to be painted

        .. seealso::

            :py:meth:`QPaintEngine.drawPixmap()`
        """
        painter = self.paintEngine().painter()
        if painter is None:
            return
        self.__data.commands += [QwtPainterCommand(rect, pixmap, subRect)]
        r = painter.transform().mapRect(rect)
        self.updateControlPointRect(r)
        self.updateBoundingRect(r)

    def drawImage(self, rect, image, subRect, flags):
        """
        Store a image command in the command list

        :param QRectF rect: target rectangle
        :param QImage image: Pixmap to be painted
        :param QRectF subRect: Reactangle of the pixmap to be painted
        :param Qt.ImageConversionFlags flags: Pixmap to be painted

        .. seealso::

            :py:meth:`QPaintEngine.drawImage()`
        """
        painter = self.paintEngine().painter()
        if painter is None:
            return
        self.__data.commands += [QwtPainterCommand(rect, image, subRect, flags)]
        r = painter.transform().mapRect(rect)
        self.updateControlPointRect(r)
        self.updateBoundingRect(r)

    def updateState(self, state):
        """
        Store a state command in the command list

        :param QPaintEngineState state: State to be stored

        .. seealso::

            :py:meth:`QPaintEngine.updateState()`
        """
        # XXX: shall we call the parent's implementation of updateState?
        self.__data.commands += [QwtPainterCommand(state)]

    def updateBoundingRect(self, rect):
        br = QRectF(rect)
        painter = self.paintEngine().painter()
        if painter and painter.hasClipping():
            cr = painter.clipRegion().boundingRect()
            cr = painter.transform().mapRect(cr)
            br &= cr
        if self.__data.boundingRect.width() < 0:
            self.__data.boundingRect = br
        else:
            self.__data.boundingRect |= br

    def updateControlPointRect(self, rect):
        if self.__data.pointRect.width() < 0.0:
            self.__data.pointRect = rect
        else:
            self.__data.pointRect |= rect

    def commands(self):
        return self.__data.commands

    def setCommands(self, commands):
        self.reset()
        painter = QPainter(self)
        for cmd in commands:
            qwtExecCommand(painter, cmd, 0, QTransform(), None)
        painter.end()
 class QwtPlotSeriesItem(QwtPlotItem):
    """
    Base class for plot items representing a series of samples
    """

    def __init__(self, title):
        if not isinstance(title, QwtText):
            title = QwtText(title)
        QwtPlotItem.__init__(self, title)
        self.__data = QwtPlotSeriesItem_PrivateData()
        self.setItemInterest(QwtPlotItem.ScaleInterest, True)

    def setOrientation(self, orientation):
        """
        Set the orientation of the item. Default is `Qt.Horizontal`.

        The `orientation()` might be used in specific way by a plot item.
        F.e. a QwtPlotCurve uses it to identify how to display the curve
        int `QwtPlotCurve.Steps` or `QwtPlotCurve.Sticks` style.

        .. seealso::

            :py:meth`orientation()`
        """
        if self.__data.orientation != orientation:
            self.__data.orientation = orientation
            self.legendChanged()
            self.itemChanged()

    def orientation(self):
        """
        :return: Orientation of the plot item

        .. seealso::

            :py:meth`setOrientation()`
        """
        return self.__data.orientation

    def draw(self, painter, xMap, yMap, canvasRect):
        """
        Draw the complete series

        :param QPainter painter: Painter
        :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
        :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
        :param QRectF canvasRect: Contents rectangle of the canvas
        """
        self.drawSeries(painter, xMap, yMap, canvasRect, 0, -1)

    def drawSeries(self, painter, xMap, yMap, canvasRect, from_, to):
        """
        Draw a subset of the samples

        :param QPainter painter: Painter
        :param qwt.scale_map.QwtScaleMap xMap: Maps x-values into pixel coordinates.
        :param qwt.scale_map.QwtScaleMap yMap: Maps y-values into pixel coordinates.
        :param QRectF canvasRect: Contents rectangle of the canvas
        :param int from_: Index of the first point to be painted
        :param int to: Index of the last point to be painted. If to < 0 the curve will be painted to its last point.

        .. seealso::

            This method is implemented in `qwt.plot_curve.QwtPlotCurve`
        """
        raise NotImplementedError

    def boundingRect(self):
        return self.dataRect()  # dataRect method is implemented in QwtSeriesStore

    def updateScaleDiv(self, xScaleDiv, yScaleDiv):
        rect = QRectF(
            xScaleDiv.lowerBound(),
            yScaleDiv.lowerBound(),
            xScaleDiv.range(),
            yScaleDiv.range(),
        )
        self.setRectOfInterest(
            rect
        )  # setRectOfInterest method is implemented in QwtSeriesData

    def dataChanged(self):
        self.itemChanged() class QwtSeriesStore(object):
    """
    Class storing a `QwtSeriesData` object

    `QwtSeriesStore` and `QwtPlotSeriesItem` are intended as base classes for
    all plot items iterating over a series of samples.
    """

    def __init__(self):
        self.__series = None

    def setData(self, series):
        """
        Assign a series of samples

        :param qwt.plot_series.QwtSeriesData series: Data

        .. warning::

            The item takes ownership of the data object, deleting it
            when its not used anymore.
        """
        if self.__series != series:
            self.__series = series
            self.dataChanged()

    def dataChanged(self):
        raise NotImplementedError

    def data(self):
        """
        :return: the series data
        """
        return self.__series

    def sample(self, index):
        """
        :param int index: Index
        :return: Sample at position index
        """
        if self.__series:
            return self.__series.sample(index)
        else:
            return

    def dataSize(self):
        """
        :return: Number of samples of the series

        .. seealso::

            :py:meth:`setData()`,
            :py:meth:`qwt.plot_series.QwtSeriesData.size()`
        """
        if self.__series is None:
            return 0
        return self.__series.size()

    def dataRect(self):
        """
        :return: Bounding rectangle of the series or an invalid rectangle, when no series is stored

        .. seealso::

            :py:meth:`qwt.plot_series.QwtSeriesData.boundingRect()`
        """
        if self.__series is None or self.dataSize() == 0:
            return QRectF(1.0, 1.0, -2.0, -2.0)
        return self.__series.boundingRect()

    def setRectOfInterest(self, rect):
        """
        Set a the "rect of interest" for the series

        :param QRectF rect: Rectangle of interest

        .. seealso::

            :py:meth:`qwt.plot_series.QwtSeriesData.setRectOfInterest()`
        """
        if self.__series:
            self.__series.setRectOfInterest(rect)

    def swapData(self, series):
        """
        Replace a series without deleting the previous one

        :param qwt.plot_series.QwtSeriesData series: New series
        :return: Previously assigned series
        """
        swappedSeries = self.__series
        self.__series = series
        return swappedSeries class QwtSeriesData(object):
    """
    Abstract interface for iterating over samples

    `PythonQwt` offers several implementations of the QwtSeriesData API,
    but in situations, where data of an application specific format
    needs to be displayed, without having to copy it, it is recommended
    to implement an individual data access.

    A subclass of `QwtSeriesData` must implement:

      - size():

        Should return number of data points.

     - sample()

        Should return values x and y values of the sample at specific position
        as QPointF object.

     - boundingRect()

        Should return the bounding rectangle of the data series.
        It is used for autoscaling and might help certain algorithms for
        displaying the data.
        The member `_boundingRect` is intended for caching the calculated
        rectangle.
    """

    def __init__(self):
        self._boundingRect = QRectF(0.0, 0.0, -1.0, -1.0)

    def setRectOfInterest(self, rect):
        """
        Set a the "rect of interest"

        QwtPlotSeriesItem defines the current area of the plot canvas
        as "rectangle of interest" ( QwtPlotSeriesItem::updateScaleDiv() ).
        It can be used to implement different levels of details.

        The default implementation does nothing.

        :param QRectF rect: Rectangle of interest
        """
        pass

    def size(self):
        """
        :return: Number of samples
        """
        pass

    def sample(self, i):
        """
        Return a sample

        :param int i: Index
        :return: Sample at position i
        """
        pass

    def boundingRect(self):
        """
        Calculate the bounding rect of all samples

        The bounding rect is necessary for autoscaling and can be used
        for a couple of painting optimizations.

        :return: Bounding rectangle
        """
        pass class QwtPointArrayData(QwtSeriesData):
    """
    Interface for iterating over two array objects

    .. py:class:: QwtCQwtPointArrayDataolorMap(x, y, [size=None])

        :param x: Array of x values
        :type x: list or tuple or numpy.array
        :param y: Array of y values
        :type y: list or tuple or numpy.array
        :param int size: Size of the x and y arrays
        :param bool finite: if True, keep only finite array elements (remove all infinity and not a number values), otherwise do not filter array elements
    """

    def __init__(self, x=None, y=None, size=None, finite=None):
        QwtSeriesData.__init__(self)
        if x is None and y is not None:
            x = np.arange(len(y))
        elif y is None and x is not None:
            y = x
            x = np.arange(len(y))
        elif x is None and y is None:
            x = np.array([])
            y = np.array([])
        if isinstance(x, (tuple, list)):
            x = np.array(x)
        if isinstance(y, (tuple, list)):
            y = np.array(y)
        if size is not None:
            x = np.resize(x, (size,))
            y = np.resize(y, (size,))
        if len(x) != len(y):
            minlen = min(len(x), len(y))
            x = np.resize(x, (minlen,))
            y = np.resize(y, (minlen,))
        if finite if finite is not None else True:
            indexes = np.logical_and(np.isfinite(x), np.isfinite(y))
            self.__x = x[indexes]
            self.__y = y[indexes]
        else:
            self.__x = x
            self.__y = y

    def boundingRect(self):
        """
        Calculate the bounding rectangle

        The bounding rectangle is calculated once by iterating over all
        points and is stored for all following requests.

        :return: Bounding rectangle
        """
        xmin = self.__x.min()
        xmax = self.__x.max()
        ymin = self.__y.min()
        ymax = self.__y.max()
        return QRectF(xmin, ymin, xmax - xmin, ymax - ymin)

    def size(self):
        """
        :return: Size of the data set
        """
        return min([self.__x.size, self.__y.size])

    def sample(self, index):
        """
        :param int index: Index
        :return: Sample at position `index`
        """
        return QPointF(self.__x[index], self.__y[index])

    def xData(self):
        """
        :return: Array of the x-values
        """
        return self.__x

    def yData(self):
        """
        :return: Array of the y-values
        """
        return self.__y class QwtSymbol(object):
    """
    A class for drawing symbols

    Symbol styles:

      * `QwtSymbol.NoSymbol`: No Style. The symbol cannot be drawn.
      * `QwtSymbol.Ellipse`: Ellipse or circle
      * `QwtSymbol.Rect`: Rectangle
      * `QwtSymbol.Diamond`: Diamond
      * `QwtSymbol.Triangle`: Triangle pointing upwards
      * `QwtSymbol.DTriangle`: Triangle pointing downwards
      * `QwtSymbol.UTriangle`: Triangle pointing upwards
      * `QwtSymbol.LTriangle`: Triangle pointing left
      * `QwtSymbol.RTriangle`: Triangle pointing right
      * `QwtSymbol.Cross`: Cross (+)
      * `QwtSymbol.XCross`: Diagonal cross (X)
      * `QwtSymbol.HLine`: Horizontal line
      * `QwtSymbol.VLine`: Vertical line
      * `QwtSymbol.Star1`: X combined with +
      * `QwtSymbol.Star2`: Six-pointed star
      * `QwtSymbol.Hexagon`: Hexagon
      * `QwtSymbol.Path`: The symbol is represented by a painter path, where
        the origin (0, 0) of the path coordinate system is mapped to the
        position of the symbol

        ..seealso::

            :py:meth:`setPath()`, :py:meth:`path()`
      * `QwtSymbol.Pixmap`: The symbol is represented by a pixmap.
        The pixmap is centered or aligned to its pin point.

        ..seealso::

            :py:meth:`setPinPoint()`
      * `QwtSymbol.Graphic`: The symbol is represented by a graphic.
        The graphic is centered or aligned to its pin point.

        ..seealso::

            :py:meth:`setPinPoint()`
      * `QwtSymbol.SvgDocument`: The symbol is represented by a SVG graphic.
        The graphic is centered or aligned to its pin point.

        ..seealso::

            :py:meth:`setPinPoint()`
      * `QwtSymbol.UserStyle`: Styles >= `QwtSymbol.UserStyle` are reserved
        for derived classes of `QwtSymbol` that overload `drawSymbols()` with
        additional application specific symbol types.

    Cache policies:

        Depending on the render engine and the complexity of the
        symbol shape it might be faster to render the symbol
        to a pixmap and to paint this pixmap.

        F.e. the raster paint engine is a pure software renderer
        where in cache mode a draw operation usually ends in
        raster operation with the the backing store, that are usually
        faster, than the algorithms for rendering polygons.
        But the opposite can be expected for graphic pipelines
        that can make use of hardware acceleration.

        The default setting is AutoCache

        ..seealso::

            :py:meth:`setCachePolicy()`, :py:meth:`cachePolicy()`

        .. note::

            The policy has no effect, when the symbol is painted
            to a vector graphics format (PDF, SVG).

        .. warning::

            Since Qt 4.8 raster is the default backend on X11

        Valid cache policies:

          * `QwtSymbol.NoCache`: Don't use a pixmap cache
          * `QwtSymbol.Cache`: Always use a pixmap cache
          * `QwtSymbol.AutoCache`: Use a cache when the symbol is rendered
            with the software renderer (`QPaintEngine.Raster`)

    .. py:class:: QwtSymbol([style=QwtSymbol.NoSymbol])

        The symbol is constructed with gray interior,
        black outline with zero width, no size and style 'NoSymbol'.

        :param int style: Symbol Style

    .. py:class:: QwtSymbol(style, brush, pen, size)
        :noindex:

        :param int style: Symbol Style
        :param QBrush brush: Brush to fill the interior
        :param QPen pen: Outline pen
        :param QSize size: Size

    .. py:class:: QwtSymbol(path, brush, pen)
        :noindex:

        :param QPainterPath path: Painter path
        :param QBrush brush: Brush to fill the interior
        :param QPen pen: Outline pen

    .. seealso::

        :py:meth:`setPath()`, :py:meth:`setBrush()`,
        :py:meth:`setPen()`, :py:meth:`setSize()`
    """

    # enum Style
    Style = int
    NoSymbol = -1
    (
        Ellipse,
        Rect,
        Diamond,
        Triangle,
        DTriangle,
        UTriangle,
        LTriangle,
        RTriangle,
        Cross,
        XCross,
        HLine,
        VLine,
        Star1,
        Star2,
        Hexagon,
        Path,
        Pixmap,
        Graphic,
        SvgDocument,
    ) = list(range(19))
    UserStyle = 1000

    # enum CachePolicy
    NoCache, Cache, AutoCache = list(range(3))

    def __init__(self, *args):
        if len(args) in (0, 1):
            if args:
                (style,) = args
            else:
                style = QwtSymbol.NoSymbol
            self.__data = QwtSymbol_PrivateData(
                style, QBrush(Qt.gray), QPen(Qt.black, 0), QSize()
            )
        elif len(args) == 4:
            style, brush, pen, size = args
            self.__data = QwtSymbol_PrivateData(style, brush, pen, size)
        elif len(args) == 3:
            path, brush, pen = args
            self.__data = QwtSymbol_PrivateData(QwtSymbol.Path, brush, pen, QSize())
            self.setPath(path)
        else:
            raise TypeError(
                "%s() takes 1, 3, or 4 argument(s) (%s given)"
                % (self.__class__.__name__, len(args))
            )

    @classmethod
    def make(
        cls,
        style=None,
        brush=None,
        pen=None,
        size=None,
        path=None,
        pixmap=None,
        graphic=None,
        svgdocument=None,
        pinpoint=None,
    ):
        """
        Create and setup a new `QwtSymbol` object (convenience function).

        :param style: Symbol Style
        :type style: int or None
        :param brush: Brush to fill the interior
        :type brush: QBrush or None
        :param pen: Outline pen
        :type pen: QPen or None
        :param size: Size
        :type size: QSize or None
        :param path: Painter path
        :type path: QPainterPath or None
        :param path: Painter path
        :type path: QPainterPath or None
        :param pixmap: Pixmap as symbol
        :type pixmap: QPixmap or None
        :param graphic: Graphic
        :type graphic: qwt.graphic.QwtGraphic or None
        :param svgdocument: SVG icon as symbol

        .. seealso::

            :py:meth:`setPixmap()`, :py:meth:`setGraphic()`, :py:meth:`setPath()`
        """
        style = QwtSymbol.NoSymbol if style is None else style
        brush = QBrush(Qt.gray) if brush is None else QBrush(brush)
        pen = QPen(Qt.black, 0) if pen is None else QPen(pen)
        size = QSize() if size is None else size
        if not isinstance(size, QSize):
            if isinstance(size, tuple) and len(size) == 2:
                size = QSize(size[0], size[1])
            else:
                raise TypeError("Invalid size %r" % size)
        item = cls(style, brush, pen, size)
        if path is not None:
            item.setPath(path)
        elif pixmap is not None:
            item.setPixmap(pixmap)
        elif graphic is not None:
            item.setGraphic(graphic)
        elif svgdocument is not None:
            item.setSvgDocument(svgdocument)
        if pinpoint is not None:
            item.setPinPoint(pinpoint)
        return item

    def setCachePolicy(self, policy):
        """
        Change the cache policy

        The default policy is AutoCache

        :param int policy: Cache policy

        .. seealso::

            :py:meth:`cachePolicy()`
        """
        if self.__data.cache.policy != policy:
            self.__data.cache.policy = policy
            self.invalidateCache()

    def cachePolicy(self):
        """
        :return: Cache policy

        .. seealso::

            :py:meth:`setCachePolicy()`
        """
        return self.__data.cache.policy

    def setPath(self, path):
        """
        Set a painter path as symbol

        The symbol is represented by a painter path, where the
        origin (0, 0) of the path coordinate system is mapped to
        the position of the symbol.

        When the symbol has valid size the painter path gets scaled
        to fit into the size. Otherwise the symbol size depends on
        the bounding rectangle of the path.

        The following code defines a symbol drawing an arrow::

            from qtpy.QtGui import QApplication, QPen, QPainterPath, QTransform
            from qtpy.QtCore import Qt, QPointF
            from qwt import QwtPlot, QwtPlotCurve, QwtSymbol
            import numpy as np

            app = QApplication([])

            # --- Construct custom symbol ---

            path = QPainterPath()
            path.moveTo(0, 8)
            path.lineTo(0, 5)
            path.lineTo(-3, 5)
            path.lineTo(0, 0)
            path.lineTo(3, 5)
            path.lineTo(0, 5)

            transform = QTransform()
            transform.rotate(-30.0)
            path = transform.map(path)

            pen = QPen(Qt.black, 2 );
            pen.setJoinStyle(Qt.MiterJoin)

            symbol = QwtSymbol()
            symbol.setPen(pen)
            symbol.setBrush(Qt.red)
            symbol.setPath(path)
            symbol.setPinPoint(QPointF(0., 0.))
            symbol.setSize(10, 14)

            # --- Test it within a simple plot ---

            curve = QwtPlotCurve()
            curve_pen = QPen(Qt.blue)
            curve_pen.setStyle(Qt.DotLine)
            curve.setPen(curve_pen)
            curve.setSymbol(symbol)
            x = np.linspace(0, 10, 10)
            curve.setData(x, np.sin(x))

            plot = QwtPlot()
            curve.attach(plot)
            plot.resize(600, 300)
            plot.replot()
            plot.show()

            app.exec_()

        .. image:: /images/symbol_path_example.png

        :param QPainterPath path: Painter path

        .. seealso::

            :py:meth:`path()`, :py:meth:`setSize()`
        """
        self.__data.style = QwtSymbol.Path
        self.__data.path.path = path
        self.__data.path.graphic.reset()

    def path(self):
        """
        :return: Painter path for displaying the symbol

        .. seealso::

            :py:meth:`setPath()`
        """
        return self.__data.path.path

    def setPixmap(self, pixmap):
        """
        Set a pixmap as symbol

        :param QPixmap pixmap: Pixmap

        .. seealso::

            :py:meth:`pixmap()`, :py:meth:`setGraphic()`

        .. note::

            The `style()` is set to `QwtSymbol.Pixmap`

        .. note::

            `brush()` and `pen()` have no effect
        """
        self.__data.style = QwtSymbol.Pixmap
        self.__data.pixmap.pixmap = pixmap

    def pixmap(self):
        """
        :return: Assigned pixmap

        .. seealso::

            :py:meth:`setPixmap()`
        """
        return self.__data.pixmap.pixmap

    def setGraphic(self, graphic):
        """
        Set a graphic as symbol

        :param qwt.graphic.QwtGraphic graphic: Graphic

        .. seealso::

            :py:meth:`graphic()`, :py:meth:`setPixmap()`

        .. note::

            The `style()` is set to `QwtSymbol.Graphic`

        .. note::

            `brush()` and `pen()` have no effect
        """
        self.__data.style = QwtSymbol.Graphic
        self.__data.graphic.graphic = graphic

    def graphic(self):
        """
        :return: Assigned graphic

        .. seealso::

            :py:meth:`setGraphic()`
        """
        return self.__data.graphic.graphic

    def setSvgDocument(self, svgDocument):
        """
        Set a SVG icon as symbol

        :param svgDocument: SVG icon

        .. seealso::

            :py:meth:`setGraphic()`, :py:meth:`setPixmap()`

        .. note::

            The `style()` is set to `QwtSymbol.SvgDocument`

        .. note::

            `brush()` and `pen()` have no effect
        """
        self.__data.style = QwtSymbol.SvgDocument
        if self.__data.svg.renderer is None:
            self.__data.svg.renderer = QSvgRenderer()
        self.__data.svg.renderer.load(svgDocument)

    def setSize(self, *args):
        """
        Specify the symbol's size

        .. py:method:: setSize(width, [height=-1])
            :noindex:

            :param int width: Width
            :param int height: Height

        .. py:method:: setSize(size)
            :noindex:

            :param QSize size: Size

        .. seealso::

            :py:meth:`size()`
        """
        if len(args) == 2:
            width, height = args
            if width >= 0 and height < 0:
                height = width
            self.setSize(QSize(width, height))
        elif len(args) == 1:
            if isinstance(args[0], QSize):
                (size,) = args
                if size.isValid() and size != self.__data.size:
                    self.__data.size = size
                    self.invalidateCache()
            else:
                (width,) = args
                self.setSize(width, -1)
        else:
            raise TypeError(
                "%s().setSize() takes 1 or 2 argument(s) (%s given)"
                % (self.__class__.__name__, len(args))
            )

    def size(self):
        """
        :return: Size

        .. seealso::

            :py:meth:`setSize()`
        """
        return self.__data.size

    def setBrush(self, brush):
        """
        Assign a brush

        The brush is used to draw the interior of the symbol.

        :param QBrush brush: Brush

        .. seealso::

            :py:meth:`brush()`
        """
        if brush != self.__data.brush:
            self.__data.brush = brush
            self.invalidateCache()
            if self.__data.style == QwtSymbol.Path:
                self.__data.path.graphic.reset()

    def brush(self):
        """
        :return: Brush

        .. seealso::

            :py:meth:`setBrush()`
        """
        return self.__data.brush

    def setPen(self, *args):
        """
        Build and/or assign a pen, depending on the arguments.

        .. py:method:: setPen(color, width, style)
            :noindex:

            Build and assign a pen

            In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
            non cosmetic (see `QPen.isCosmetic()`). This method signature has
            been introduced to hide this incompatibility.

            :param QColor color: Pen color
            :param float width: Pen width
            :param Qt.PenStyle style: Pen style

        .. py:method:: setPen(pen)
            :noindex:

            Assign a pen

            :param QPen pen: New pen

        .. seealso::

            :py:meth:`pen()`, :py:meth:`brush()`
        """
        if len(args) == 3:
            color, width, style = args
            self.setPen(QPen(color, width, style))
        elif len(args) == 1:
            (pen,) = args
            if pen != self.__data.pen:
                self.__data.pen = pen
                self.invalidateCache()
                if self.__data.style == QwtSymbol.Path:
                    self.__data.path.graphic.reset()
        else:
            raise TypeError(
                "%s().setPen() takes 1 or 3 argument(s) (%s given)"
                % (self.__class__.__name__, len(args))
            )

    def pen(self):
        """
        :return: Pen

        .. seealso::

            :py:meth:`setPen()`, :py:meth:`brush()`
        """
        return self.__data.pen

    def setColor(self, color):
        """
        Set the color of the symbol

        Change the color of the brush for symbol types with a filled area.
        For all other symbol types the color will be assigned to the pen.

        :param QColor color: Color

        .. seealso::

            :py:meth:`setPen()`, :py:meth:`setBrush()`,
            :py:meth:`brush()`, :py:meth:`pen()`
        """
        if self.__data.style in (
            QwtSymbol.Ellipse,
            QwtSymbol.Rect,
            QwtSymbol.Diamond,
            QwtSymbol.Triangle,
            QwtSymbol.UTriangle,
            QwtSymbol.DTriangle,
            QwtSymbol.RTriangle,
            QwtSymbol.LTriangle,
            QwtSymbol.Star2,
            QwtSymbol.Hexagon,
        ):
            if self.__data.brush.color() != color:
                self.__data.brush.setColor(color)
                self.invalidateCache()
        elif self.__data.style in (
            QwtSymbol.Cross,
            QwtSymbol.XCross,
            QwtSymbol.HLine,
            QwtSymbol.VLine,
            QwtSymbol.Star1,
        ):
            if self.__data.pen.color() != color:
                self.__data.pen.setColor(color)
                self.invalidateCache()
        else:
            if self.__data.brush.color() != color or self.__data.pen.color() != color:
                self.invalidateCache()
            self.__data.brush.setColor(color)
            self.__data.pen.setColor(color)

    def setPinPoint(self, pos, enable=True):
        """
        Set and enable a pin point

        The position of a complex symbol is not always aligned to its center
        ( f.e an arrow, where the peak points to a position ). The pin point
        defines the position inside of a Pixmap, Graphic, SvgDocument
        or PainterPath symbol where the represented point has to
        be aligned to.

        :param QPointF pos: Position
        :enable bool enable: En/Disable the pin point alignment

        .. seealso::

            :py:meth:`pinPoint()`, :py:meth:`setPinPointEnabled()`
        """
        if self.__data.pinPoint != pos:
            self.__data.pinPoint = pos
            if self.__data.isPinPointEnabled:
                self.invalidateCache()
        self.setPinPointEnabled(enable)

    def pinPoint(self):
        """
        :return: Pin point

        .. seealso::

            :py:meth:`setPinPoint()`, :py:meth:`setPinPointEnabled()`
        """
        return self.__data.pinPoint

    def setPinPointEnabled(self, on):
        """
        En/Disable the pin point alignment

        :param bool on: Enabled, when on is true

        .. seealso::

            :py:meth:`setPinPoint()`, :py:meth:`isPinPointEnabled()`
        """
        if self.__data.isPinPointEnabled != on:
            self.__data.isPinPointEnabled = on
            self.invalidateCache()

    def isPinPointEnabled(self):
        """
        :return: True, when the pin point translation is enabled

        .. seealso::

            :py:meth:`setPinPoint()`, :py:meth:`setPinPointEnabled()`
        """
        return self.__data.isPinPointEnabled

    def drawSymbols(self, painter, points, numPoints=None):
        """
        Render an array of symbols

        Painting several symbols is more effective than drawing symbols
        one by one, as a couple of layout calculations and setting of pen/brush
        can be done once for the complete array.

        :param QPainter painter: Painter
        :param QPolygonF points: Positions of the symbols in screen coordinates
        """
        # TODO: remove argument numPoints (not necessary in `PythonQwt`)
        if numPoints is not None and numPoints <= 0:
            return
        painter.save()
        self.renderSymbols(painter, points, numPoints)
        painter.restore()

    def drawSymbol(self, painter, point_or_rect):
        """
        Draw the symbol into a rectangle

        The symbol is painted centered and scaled into the target rectangle.
        It is always painted uncached and the pin point is ignored.

        This method is primarily intended for drawing a symbol to the legend.

        :param QPainter painter: Painter
        :param point_or_rect: Position or target rectangle of the symbol in screen coordinates
        :type point_or_rect: QPointF or QPoint or QRectF
        """
        if isinstance(point_or_rect, (QPointF, QPoint)):
            # drawSymbol( QPainter *, const QPointF & )
            self.drawSymbols(painter, [point_or_rect])
            return
        # drawSymbol( QPainter *, const QRectF & )
        rect = point_or_rect
        assert isinstance(rect, QRectF)
        if self.__data.style == QwtSymbol.NoSymbol:
            return
        if self.__data.style == QwtSymbol.Graphic:
            self.__data.graphic.graphic.render(painter, rect, Qt.KeepAspectRatio)
        elif self.__data.style == QwtSymbol.Path:
            if self.__data.path.graphic.isNull():
                self.__data.path.graphic = qwtPathGraphic(
                    self.__data.path.path, self.__data.pen, self.__data.brush
                )
            self.__data.path.graphic.render(painter, rect, Qt.KeepAspectRatio)
            return
        elif self.__data.style == QwtSymbol.SvgDocument:
            if self.__data.svg.renderer is not None:
                scaledRect = QRectF()
                sz = QSizeF(self.__data.svg.renderer.viewBoxF().size())
                if not sz.isEmpty():
                    sz.scale(rect.size(), Qt.KeepAspectRatio)
                    scaledRect.setSize(sz)
                    scaledRect.moveCenter(rect.center())
                else:
                    scaledRect = rect
                self.__data.svg.renderer.render(painter, scaledRect)
        else:
            br = QRect(self.boundingRect())
            ratio = min([rect.width() / br.width(), rect.height() / br.height()])
            painter.save()
            painter.translate(rect.center())
            painter.scale(ratio, ratio)
            isPinPointEnabled = self.__data.isPinPointEnabled
            self.__data.isPinPointEnabled = False
            pos = QPointF()
            self.renderSymbols(painter, pos, 1)
            self.__data.isPinPointEnabled = isPinPointEnabled
            painter.restore()

    def renderSymbols(self, painter, points, numPoints=None):
        """
        Render the symbol to series of points

        :param QPainter painter: Painter
        :param point_or_rect: Positions of the symbols
        """
        # TODO: remove argument numPoints (not necessary in `PythonQwt`)
        try:
            assert numPoints is None
        except AssertionError:
            raise RuntimeError(
                "argument numPoints is not implemented " "in `PythonQwt`"
            )
        if self.__data.style == QwtSymbol.Ellipse:
            qwtDrawEllipseSymbols(painter, points, numPoints, self)
        elif self.__data.style == QwtSymbol.Rect:
            qwtDrawRectSymbols(painter, points, numPoints, self)
        elif self.__data.style == QwtSymbol.Diamond:
            qwtDrawDiamondSymbols(painter, points, numPoints, self)
        elif self.__data.style == QwtSymbol.Cross:
            qwtDrawLineSymbols(
                painter, Qt.Horizontal | Qt.Vertical, points, numPoints, self
            )
        elif self.__data.style == QwtSymbol.XCross:
            qwtDrawXCrossSymbols(painter, points, numPoints, self)
        elif self.__data.style in (QwtSymbol.Triangle, QwtSymbol.UTriangle):
            qwtDrawTriangleSymbols(painter, QwtTriangle.Up, points, numPoints, self)
        elif self.__data.style == QwtSymbol.DTriangle:
            qwtDrawTriangleSymbols(painter, QwtTriangle.Down, points, numPoints, self)
        elif self.__data.style == QwtSymbol.RTriangle:
            qwtDrawTriangleSymbols(painter, QwtTriangle.Right, points, numPoints, self)
        elif self.__data.style == QwtSymbol.LTriangle:
            qwtDrawTriangleSymbols(painter, QwtTriangle.Left, points, numPoints, self)
        elif self.__data.style == QwtSymbol.HLine:
            qwtDrawLineSymbols(painter, Qt.Horizontal, points, numPoints, self)
        elif self.__data.style == QwtSymbol.VLine:
            qwtDrawLineSymbols(painter, Qt.Vertical, points, numPoints, self)
        elif self.__data.style == QwtSymbol.Star1:
            qwtDrawStar1Symbols(painter, points, numPoints, self)
        elif self.__data.style == QwtSymbol.Star2:
            qwtDrawStar2Symbols(painter, points, numPoints, self)
        elif self.__data.style == QwtSymbol.Hexagon:
            qwtDrawHexagonSymbols(painter, points, numPoints, self)
        elif self.__data.style == QwtSymbol.Path:
            if self.__data.path.graphic.isNull():
                self.__data.path.graphic = qwtPathGraphic(
                    self.__data.path.path, self.__data.pen, self.__data.brush
                )
            qwtDrawGraphicSymbols(
                painter, points, numPoints, self.__data.path.graphic, self
            )
        elif self.__data.style == QwtSymbol.Pixmap:
            qwtDrawPixmapSymbols(painter, points, numPoints, self)
        elif self.__data.style == QwtSymbol.Graphic:
            qwtDrawGraphicSymbols(
                painter, points, numPoints, self.__data.graphic.graphic, self
            )
        elif self.__data.style == QwtSymbol.SvgDocument:
            qwtDrawSvgSymbols(
                painter, points, numPoints, self.__data.svg.renderer, self
            )

    def boundingRect(self):
        """
        Calculate the bounding rectangle for a symbol at position (0,0).

        :return: Bounding rectangle
        """
        rect = QRectF()
        pinPointTranslation = False
        if self.__data.style in (QwtSymbol.Ellipse, QwtSymbol.Rect, QwtSymbol.Hexagon):
            pw = 0.0
            if self.__data.pen.style() != Qt.NoPen:
                pw = max([self.__data.pen.widthF(), 1.0])
            rect.setSize(self.__data.size + QSizeF(pw, pw))
            rect.moveCenter(QPointF(0.0, 0.0))
        elif self.__data.style in (
            QwtSymbol.XCross,
            QwtSymbol.Diamond,
            QwtSymbol.Triangle,
            QwtSymbol.UTriangle,
            QwtSymbol.DTriangle,
            QwtSymbol.RTriangle,
            QwtSymbol.LTriangle,
            QwtSymbol.Star1,
            QwtSymbol.Star2,
        ):
            pw = 0.0
            if self.__data.pen.style() != Qt.NoPen:
                pw = max([self.__data.pen.widthF(), 1.0])
            rect.setSize(QSizeF(self.__data.size) + QSizeF(2 * pw, 2 * pw))
            rect.moveCenter(QPointF(0.0, 0.0))
        elif self.__data.style == QwtSymbol.Path:
            if self.__data.path.graphic.isNull():
                self.__data.path.graphic = qwtPathGraphic(
                    self.__data.path.path, self.__data.pen, self.__data.brush
                )
            rect = qwtScaleBoundingRect(self.__data.path.graphic, self.__data.size)
            pinPointTranslation = True
        elif self.__data.style == QwtSymbol.Pixmap:
            if self.__data.size.isEmpty():
                rect.setSize(self.__data.pixmap.pixmap.size())
            else:
                rect.setSize(self.__data.size)
            pinPointTranslation = True
        elif self.__data.style == QwtSymbol.Graphic:
            rect = qwtScaleBoundingRect(self.__data.graphic.graphic, self.__data.size)
            pinPointTranslation = True
        elif self.__data.style == QwtSymbol.SvgDocument:
            if self.__data.svg.renderer is not None:
                rect = self.__data.svg.renderer.viewBoxF()
            if self.__data.size.isValid() and not rect.isEmpty():
                sz = QSizeF(rect.size())
                sx = self.__data.size.width() / sz.width()
                sy = self.__data.size.height() / sz.height()
                transform = QTransform()
                transform.scale(sx, sy)
                rect = transform.mapRect(rect)
            pinPointTranslation = True
        else:
            rect.setSize(self.__data.size)
            rect.moveCenter(QPointF(0.0, 0.0))
        if pinPointTranslation:
            pinPoint = QPointF(0.0, 0.0)
            if self.__data.isPinPointEnabled:
                pinPoint = rect.center() - self.__data.pinPoint
            rect.moveCenter(pinPoint)
        r = QRect()
        r.setLeft(np.floor(rect.left()))
        r.setTop(np.floor(rect.top()))
        r.setRight(np.floor(rect.right()))
        r.setBottom(np.floor(rect.bottom()))
        if self.__data.style != QwtSymbol.Pixmap:
            r.adjust(-1, -1, 1, 1)
        return r

    def invalidateCache(self):
        """
        Invalidate the cached symbol pixmap

        The symbol invalidates its cache, whenever an attribute is changed
        that has an effect ob how to display a symbol. In case of derived
        classes with individual styles (>= `QwtSymbol.UserStyle`) it
        might be necessary to call invalidateCache() for attributes
        that are relevant for this style.

        .. seealso::

            :py:meth:`setCachePolicy()`, :py:meth:`drawSymbols()`
        """
        if self.__data.cache.pixmap is not None:
            self.__data.cache.pixmap = QPixmap()

    def setStyle(self, style):
        """
        Specify the symbol style

        :param int style: Style

        .. seealso::

            :py:meth:`style()`
        """
        if self.__data.style != style:
            self.__data.style = style
            self.invalidateCache()

    def style(self):
        """
        :return: Current symbol style

        .. seealso::

            :py:meth:`setStyle()`
        """
        return self.__data.style
 class QwtPlotDirectPainter(QObject):
    """
    Painter object trying to paint incrementally

    Often applications want to display samples while they are
    collected. When there are too many samples complete replots
    will be expensive to be processed in a collection cycle.

    `QwtPlotDirectPainter` offers an API to paint
    subsets (f.e all additions points) without erasing/repainting
    the plot canvas.

    On certain environments it might be important to calculate a proper
    clip region before painting. F.e. for Qt Embedded only the clipped part
    of the backing store will be copied to a (maybe unaccelerated)
    frame buffer.

    .. warning::

        Incremental painting will only help when no replot is triggered
        by another operation (like changing scales) and nothing needs
        to be erased.

    Paint attributes:

      * `QwtPlotDirectPainter.AtomicPainter`:

        Initializing a `QPainter` is an expensive operation.
        When `AtomicPainter` is set each call of `drawSeries()` opens/closes
        a temporary `QPainter`. Otherwise `QwtPlotDirectPainter` tries to
        use the same `QPainter` as long as possible.

      * `QwtPlotDirectPainter.FullRepaint`:

        When `FullRepaint` is set the plot canvas is explicitly repainted
        after the samples have been rendered.

      * `QwtPlotDirectPainter.CopyBackingStore`:

        When `QwtPlotCanvas.BackingStore` is enabled the painter
        has to paint to the backing store and the widget. In certain
        situations/environments it might be faster to paint to
        the backing store only and then copy the backing store to the canvas.
        This flag can also be useful for settings, where Qt fills the
        the clip region with the widget background.
    """

    # enum Attribute
    AtomicPainter = 0x01
    FullRepaint = 0x02
    CopyBackingStore = 0x04

    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.__data = QwtPlotDirectPainter_PrivateData()

    def setAttribute(self, attribute, on=True):
        """
        Change an attribute

        :param int attribute: Attribute to change
        :param bool on: On/Off

        .. seealso::

            :py:meth:`testAttribute()`
        """
        if self.testAttribute(attribute) != on:
            self.__data.attributes |= attribute
        else:
            self.__data.attributes &= ~attribute
        if attribute == self.AtomicPainter and on:
            self.reset()

    def testAttribute(self, attribute):
        """
        :param int attribute: Attribute to be tested
        :return: True, when attribute is enabled

        .. seealso::

            :py:meth:`setAttribute()`
        """
        return self.__data.attributes & attribute

    def setClipping(self, enable):
        """
        En/Disables clipping

        :param bool enable: Enables clipping is true, disable it otherwise

        .. seealso::

            :py:meth:`hasClipping()`, :py:meth:`clipRegion()`,
            :py:meth:`setClipRegion()`
        """
        self.__data.hasClipping = enable

    def hasClipping(self):
        """
        :return: Return true, when clipping is enabled

        .. seealso::

            :py:meth:`setClipping()`, :py:meth:`clipRegion()`,
            :py:meth:`setClipRegion()`
        """
        return self.__data.hasClipping

    def setClipRegion(self, region):
        """
        Assign a clip region and enable clipping

        Depending on the environment setting a proper clip region might
        improve the performance heavily. F.e. on Qt embedded only the clipped
        part of the backing store will be copied to a (maybe unaccelerated)
        frame buffer device.

        :param QRegion region: Clip region

        .. seealso::

            :py:meth:`hasClipping()`, :py:meth:`setClipping()`,
            :py:meth:`clipRegion()`
        """
        self.__data.clipRegion = region
        self.__data.hasClipping = True

    def clipRegion(self):
        """
        :return: Return Currently set clip region.

        .. seealso::

            :py:meth:`hasClipping()`, :py:meth:`setClipping()`,
            :py:meth:`setClipRegion()`
        """
        return self.__data.clipRegion

    def drawSeries(self, seriesItem, from_, to):
        """
        Draw a set of points of a seriesItem.

        When observing a measurement while it is running, new points have
        to be added to an existing seriesItem. drawSeries() can be used to
        display them avoiding a complete redraw of the canvas.

        Setting `plot().canvas().setAttribute(Qt.WA_PaintOutsidePaintEvent, True)`
        will result in faster painting, if the paint engine of the canvas widget
        supports this feature.

        :param qwt.plot_series.QwtPlotSeriesItem seriesItem: Item to be painted
        :param int from_: Index of the first point to be painted
        :param int to: Index of the last point to be painted. If to < 0 the series will be painted to its last point.
        """
        if seriesItem is None or seriesItem.plot() is None:
            return
        canvas = seriesItem.plot().canvas()
        canvasRect = canvas.contentsRect()
        plotCanvas = canvas  # XXX: cast to QwtPlotCanvas
        if plotCanvas and qwtHasBackingStore(plotCanvas):
            painter = QPainter(
                plotCanvas.backingStore()
            )  # XXX: cast plotCanvas.backingStore() to QPixmap
            if self.__data.hasClipping:
                painter.setClipRegion(self.__data.clipRegion)
            qwtRenderItem(painter, canvasRect, seriesItem, from_, to)
            painter.end()
            if self.testAttribute(self.FullRepaint):
                plotCanvas.repaint()
                return
        immediatePaint = True
        if not canvas.testAttribute(Qt.WA_WState_InPaintEvent):
            if QT_MAJOR_VERSION >= 5 or not canvas.testAttribute(
                Qt.WA_PaintOutsidePaintEvent
            ):
                immediatePaint = False
        if immediatePaint:
            if not self.__data.painter.isActive():
                self.reset()
                self.__data.painter.begin(canvas)
                canvas.installEventFilter(self)
            if self.__data.hasClipping:
                self.__data.painter.setClipRegion(
                    QRegion(canvasRect) & self.__data.clipRegion
                )
            elif not self.__data.painter.hasClipping():
                self.__data.painter.setClipRect(canvasRect)
            qwtRenderItem(self.__data.painter, canvasRect, seriesItem, from_, to)
            if self.__data.attributes & self.AtomicPainter:
                self.reset()
            elif self.__data.hasClipping:
                self.__data.painter.setClipping(False)
        else:
            self.reset()
            self.__data.seriesItem = seriesItem
            self.__data.from_ = from_
            self.__data.to = to
            clipRegion = QRegion(canvasRect)
            if self.__data.hasClipping:
                clipRegion &= self.__data.clipRegion
            canvas.installEventFilter(self)
            canvas.repaint(clipRegion)
            canvas.removeEventFilter(self)
            self.__data.seriesItem = None

    def reset(self):
        """Close the internal QPainter"""
        if self.__data.painter.isActive():
            w = self.__data.painter.device()  # XXX: cast to QWidget
            if w:
                w.removeEventFilter(self)
            self.__data.painter.end()

    def eventFilter(self, obj_, event):
        if event.type() == QEvent.Paint:
            self.reset()
            if self.__data.seriesItem:
                pe = event  # XXX: cast to QPaintEvent
                canvas = self.__data.seriesItem.plot().canvas()
                painter = QPainter(canvas)
                painter.setClipRegion(pe.region())
                doCopyCache = self.testAttribute(self.CopyBackingStore)
                if doCopyCache:
                    plotCanvas = canvas  # XXX: cast to QwtPlotCanvas
                    if plotCanvas:
                        doCopyCache = qwtHasBackingStore(plotCanvas)
                        if doCopyCache:
                            painter.drawPixmap(
                                plotCanvas.rect().topLeft(), plotCanvas.backingStore()
                            )
                if not doCopyCache:
                    qwtRenderItem(
                        painter,
                        canvas.contentsRect(),
                        self.__data.seriesItem,
                        self.__data.from_,
                        self.__data.to,
                    )
                return True
        return False
 def qcolor_from_str(color, default):
    """Return QColor object from str

    :param color: Input color
    :type color: QColor or str or None
    :param QColor default: Default color (returned if color is None)

    If color is already a QColor instance, simply return color.
    If color is None, return default color.
    If color is neither an str nor a QColor instance nor None, raise TypeError.
    """
    if color is None:
        return default
    elif isinstance(color, str):
        try:
            return getattr(Qt, color)
        except AttributeError:
            raise ValueError("Unknown Qt color %r" % color)
    else:
        try:
            return QG.QColor(color)
        except TypeError:
            raise TypeError("Invalid color %r" % color)
from qwt.text import QwtText
from qwt.plot import QwtPlot, QwtPlotItem, QwtPlotItem_PrivateData
from qwt._math import qwtSqr
from qwt.graphic import QwtGraphic
from qwt.plot_series import (
    QwtPlotSeriesItem,
    QwtSeriesStore,
    QwtSeriesData,
    QwtPointArrayData,
)
from qwt.symbol import QwtSymbol
from qwt.plot_directpainter import QwtPlotDirectPainter
from qwt.qthelpers import qcolor_from_str
from qtpy import PYSIDE2
from qtpy.QtGui import QPen, QBrush, QPainter, QPolygonF, QColor
from qtpy.QtCore import QSize, Qt, QRectF, QPointF
    import shiboken2
    import ctypes
import numpy as np

        .. seealso::

            :py:meth:`setSymbol()`
        """
        return self.__data.symbol

    def setPen(self, *args):
        """
        Build and/or assign a pen, depending on the arguments.

        .. py:method:: setPen(color, width, style)
            :noindex:

            Build and assign a pen

            In Qt5 the default pen width is 1.0 ( 0.0 in Qt4 ) what makes it
            non cosmetic (see `QPen.isCosmetic()`). This method signature has
            been introduced to hide this incompatibility.

            :param QColor color: Pen color
            :param float width: Pen width
            :param Qt.PenStyle style: Pen style

        .. py:method:: setPen(pen)
            :noindex:

            Assign a pen

            :param QPen pen: New pen

        .. seealso::

            :py:meth:`pen()`, :py:meth:`brush()`
        """
        if len(args) == 3:
            color, width, style = args
            pen = QPen(color, width, style)
        elif len(args) == 1:
            (pen,) = args
        else:
            raise TypeError(
                "%s().setPen() takes 1 or 3 argument(s) (%s given)"
                % (self.__class__.__name__, len(args))
            )
        if pen != self.__data.pen:
            if isinstance(pen, QColor):
                pen = QPen(pen)
            else:
                assert isinstance(pen, QPen)
            self.__data.pen = pen
            self.legendChanged()
            self.itemChanged()

    def pen(self):
        """
        :return: Pen used to draw the lines

        .. seealso::

            :py:meth:`setPen()`, :py:meth:`brush()`
        """
        return self.__data.pen

    def setBrush(self, brush):
        """
        Assign a brush.

        In case of `brush.style() != QBrush.NoBrush`
        and `style() != QwtPlotCurve.Sticks`
        the area between the curve and the baseline will be filled.

        In case `not brush.color().isValid()` the area will be filled by
        `pen.color()`. The fill algorithm simply connects the first and the
        last curve point to the baseline. So the curve data has to be sorted
        (ascending or descending).

        :param brush: New brush
        :type brush: QBrush or QColor

        .. seealso::

            :py:meth:`brush()`, :py:meth:`setBaseline()`, :py:meth:`baseline()`
        """
        if isinstance(brush, QColor):
            brush = QBrush(brush)
        else:
            assert isinstance(brush, QBrush)
        if brush != self.__data.brush:
            self.__data.brush = brush
            self.legendChanged()
            self.itemChanged()

    def brush(self):
        """
        :return: Brush used to fill the area between lines and the baseline

        .. seealso::

            :py:meth:`setBrush()`, :py:meth:`setBaseline()`,
            :py:meth:`baseline()`
        """
        return self.__data.brush

    def directPaint(self, from_, to):
        """
        When observing a measurement while it is running, new points have
        to be added to an existing seriesItem. This method can be used to
        display them avoiding a complete redraw of the canvas.

        Setting `plot().canvas().setAttribute(Qt.WA_PaintOutsidePaintEvent, True)`
        will result in faster painting, if the paint engine of the canvas
        widget supports this feature.

        :param int from_: Index of the first point to be painted
        :param int to: Index of the last point to be painted

        .. seealso::

            :py:meth:`drawSeries()`
        """

        directPainter = QwtPlotDirectPainter(self.plot())
