예제 #1
0
def draw_sketch_line(painter, connection, style):
    if not connection.requires_port:
        return

    p = QPen()
    p.setWidth(style.construction_line_width)
    p.setColor(style.construction_color)
    p.setStyle(Qt.DashLine)

    painter.setPen(p)
    painter.setBrush(Qt.NoBrush)

    geom = connection.geometry

    cubic = cubic_path(geom)
    # cubic spline
    painter.drawPath(cubic)
예제 #2
0
class BaseSymbolIcon(QWidget):
    """
    Base class to be used for all the Symbol Icon widgets.
    This class holds most of the properties to be exposed and takes care of 90%
    of the drawing code needed.

    Parameters
    ----------
    parent : QWidget
        The parent widget for this widget.
    """
    def __init__(self, parent=None):
        self._brush = QBrush(QColor(0, 255, 0), Qt.SolidPattern)
        self._original_brush = None
        self._rotation = 0
        self._pen_style = Qt.SolidLine
        self._pen = QPen(self._pen_style)
        self._pen.setCosmetic(True)
        self._pen_width = 1.0
        self._pen_color = QColor(0, 0, 0)
        self._pen.setWidthF(self._pen_width)
        self._pen.setColor(self._pen_color)
        self._original_pen_style = self._pen_style
        self._original_pen_color = self._pen_color
        super(BaseSymbolIcon, self).__init__(parent)
        self.setObjectName("icon")

    def minimumSizeHint(self):
        return QSize(32, 32)

    def paintEvent(self, event):
        """
        Paint events are sent to widgets that need to update themselves,
        for instance when part of a widget is exposed because a covering
        widget was moved.

        This method handles the painting with parameters from the stylesheet,
        configures the brush, pen and calls ```draw_icon``` so the specifics
        can be performed for each of the drawing classes.

        Parameters
        ----------
        event : QPaintEvent
        """
        opt = QStyleOption()
        opt.initFrom(self)
        painter = QPainter(self)
        painter.setClipping(True)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)
        painter.setRenderHint(QPainter.Antialiasing)
        x = event.rect().x()
        y = event.rect().y()
        w = event.rect().width()
        h = event.rect().height()
        painter.translate(w / 2.0, h / 2.0)
        painter.rotate(self._rotation)
        painter.translate(-w / 2.0, -h / 2.0)
        painter.translate(self._pen_width / 2.0, self._pen_width / 2.0)
        painter.scale(w - self._pen_width, h - self._pen_width)
        painter.translate(x, y)
        painter.setBrush(self._brush)
        painter.setPen(self._pen)
        self.draw_icon(painter)

        QWidget.paintEvent(self, event)

    def draw_icon(self, painter):
        """
        Method responsible for the drawing of the icon part of the paintEvent.
        This method must be implemented by the symbol icon widgets to include
        their specific drawings.

        Parameters
        ----------
        painter : QPainter
        """
        raise NotImplementedError("draw_icon must be implemented by subclass")

    @Property(QBrush)
    def brush(self):
        """
        PyQT Property for the brush object to be used when coloring the
        drawing

        Returns
        -------
        QBrush
        """
        return self._brush

    @brush.setter
    def brush(self, new_brush):
        """
        PyQT Property for the brush object to be used when coloring the
        drawing

        Parameters
        ----------
        new_brush : QBrush
        """
        if new_brush != self._brush:
            self._brush = new_brush
            self.update()

    @Property(Qt.PenStyle)
    def penStyle(self):
        """
        PyQT Property for the pen style to be used when drawing the border

        Returns
        -------
        int
            Index at Qt.PenStyle enum
        """
        return self._pen_style

    @penStyle.setter
    def penStyle(self, new_style):
        """
        PyQT Property for the pen style to be used when drawing the border

        Parameters
        ----------
        new_style : int
            Index at Qt.PenStyle enum
        """
        if new_style != self._pen_style:
            self._pen_style = new_style
            self._pen.setStyle(new_style)
            self.update()

    @Property(QColor)
    def penColor(self):
        """
        PyQT Property for the pen color to be used when drawing the border

        Returns
        -------
        QColor
        """
        return self._pen_color

    @penColor.setter
    def penColor(self, new_color):
        """
        PyQT Property for the pen color to be used when drawing the border

        Parameters
        ----------
        new_color : QColor
        """
        if new_color != self._pen_color:
            self._pen_color = new_color
            self._pen.setColor(new_color)
            self.update()

    @Property(float)
    def penWidth(self):
        """
        PyQT Property for the pen width to be used when drawing the border

        Returns
        -------
        float
        """
        return self._pen_width

    @penWidth.setter
    def penWidth(self, new_width):
        """
        PyQT Property for the pen width to be used when drawing the border

        Parameters
        ----------
        new_width : float
        """
        if new_width < 0:
            return
        if new_width != self._pen_width:
            self._pen_width = new_width
            self._pen.setWidth(self._pen_width)
            self.update()

    @Property(float)
    def rotation(self):
        """
        PyQt Property for the rotation.
        This rotates the icon coordinate system clockwise.

        Returns
        -------
        angle : float
            The angle in degrees
        """
        return self._rotation

    @rotation.setter
    def rotation(self, angle):
        """
        PyQt Property for the rotation.
        This rotates the icon coordinate system clockwise.

        Parameters
        ----------
        angle : float
            The angle in degrees
        """
        self._rotation = angle
        self.update()
예제 #3
0
파일: base.py 프로젝트: pcdshub/pcdswidgets
class BaseSymbolIcon(QWidget):
    """
    Base class to be used for all the Symbol Icon widgets.
    This class holds most of the properties to be exposed and takes care of 90%
    of the drawing code needed.

    Parameters
    ----------
    parent : QWidget
        The parent widget for this widget.
    """
    clicked = Signal()

    def __init__(self, parent=None):
        self._brush = QBrush(QColor(0, 255, 0), Qt.SolidPattern)
        self._original_brush = None
        self._rotation = 0
        self._pen_style = Qt.SolidLine
        self._pen = QPen(self._pen_style)
        self._pen.setCosmetic(True)
        self._pen_width = 1.0
        self._pen_color = QColor(0, 0, 0)
        self._pen.setWidthF(self._pen_width)
        self._pen.setColor(self._pen_color)
        self._original_pen_style = self._pen_style
        self._original_pen_color = self._pen_color
        super(BaseSymbolIcon, self).__init__(parent=parent)
        self.setObjectName("icon")
        if not is_qt_designer():
            # We should  install the Event Filter only if we are running
            # and not at the Designer
            self.installEventFilter(self)

    def eventFilter(self, obj, event):
        """
        EventFilter to redirect "middle click" to :meth:`.show_address_tooltip`
        """
        # Override the eventFilter to capture all middle mouse button events,
        # and show a tooltip if needed.
        if event.type() == QEvent.MouseButtonPress:
            if event.button() == Qt.MiddleButton:
                self.show_state_channel(event)
                return True
        return False

    def show_state_channel(self, event):
        """
        Show the State Channel Tooltip and copy address to clipboard

        This is intended to replicate the behavior of the "middle click" from
        EDM. If the parent is not PCDSSymbolBase and does not have a valid
        State Channel nothing will be displayed.
        """
        from ..vacuum.base import PCDSSymbolBase

        p = find_ancestor_for_widget(self, PCDSSymbolBase)
        if not p:
            return

        state_suffix = getattr(p, '_state_suffix', None)
        if not state_suffix:
            return

        addr = "{}{}".format(p.channelsPrefix, state_suffix)
        QToolTip.showText(event.globalPos(), addr)
        # If the address has a protocol, strip it out before putting it on the
        # clipboard.
        copy_text = remove_protocol(addr)

        clipboard = QApplication.clipboard()
        clipboard.setText(copy_text)
        event = QEvent(QEvent.Clipboard)
        QApplication.instance().sendEvent(clipboard, event)

    def minimumSizeHint(self):
        return QSize(32, 32)

    def paintEvent(self, event):
        """
        Paint events are sent to widgets that need to update themselves,
        for instance when part of a widget is exposed because a covering
        widget was moved.

        This method handles the painting with parameters from the stylesheet,
        configures the brush, pen and calls ```draw_icon``` so the specifics
        can be performed for each of the drawing classes.

        Parameters
        ----------
        event : QPaintEvent
        """
        opt = QStyleOption()
        opt.initFrom(self)
        painter = QPainter(self)
        painter.setClipping(True)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)
        painter.setRenderHint(QPainter.Antialiasing)
        w = self.width()
        h = self.height()
        painter.translate(w / 2.0, h / 2.0)
        painter.rotate(self._rotation)
        painter.translate(-w / 2.0, -h / 2.0)
        painter.translate(self._pen_width / 2.0, self._pen_width / 2.0)
        painter.scale(w - self._pen_width, h - self._pen_width)
        painter.translate(0, 0)
        painter.setBrush(self._brush)
        painter.setPen(self._pen)
        self.draw_icon(painter)

        QWidget.paintEvent(self, event)

    def draw_icon(self, painter):
        """
        Method responsible for the drawing of the icon part of the paintEvent.
        This method must be implemented by the symbol icon widgets to include
        their specific drawings.

        Parameters
        ----------
        painter : QPainter
        """
        raise NotImplementedError("draw_icon must be implemented by subclass")

    @Property(QBrush)
    def brush(self):
        """
        PyQT Property for the brush object to be used when coloring the
        drawing

        Returns
        -------
        QBrush
        """
        return self._brush

    @brush.setter
    def brush(self, new_brush):
        """
        PyQT Property for the brush object to be used when coloring the
        drawing

        Parameters
        ----------
        new_brush : QBrush
        """
        if new_brush != self._brush:
            self._brush = new_brush
            self.update()

    @Property(Qt.PenStyle)
    def penStyle(self):
        """
        PyQT Property for the pen style to be used when drawing the border

        Returns
        -------
        int
            Index at Qt.PenStyle enum
        """
        return self._pen_style

    @penStyle.setter
    def penStyle(self, new_style):
        """
        PyQT Property for the pen style to be used when drawing the border

        Parameters
        ----------
        new_style : int
            Index at Qt.PenStyle enum
        """
        if new_style != self._pen_style:
            self._pen_style = new_style
            self._pen.setStyle(new_style)
            self.update()

    @Property(QColor)
    def penColor(self):
        """
        PyQT Property for the pen color to be used when drawing the border

        Returns
        -------
        QColor
        """
        return self._pen_color

    @penColor.setter
    def penColor(self, new_color):
        """
        PyQT Property for the pen color to be used when drawing the border

        Parameters
        ----------
        new_color : QColor
        """
        if new_color != self._pen_color:
            self._pen_color = new_color
            self._pen.setColor(new_color)
            self.update()

    @Property(float)
    def penWidth(self):
        """
        PyQT Property for the pen width to be used when drawing the border

        Returns
        -------
        float
        """
        return self._pen_width

    @penWidth.setter
    def penWidth(self, new_width):
        """
        PyQT Property for the pen width to be used when drawing the border

        Parameters
        ----------
        new_width : float
        """
        if new_width < 0:
            return
        if new_width != self._pen_width:
            self._pen_width = new_width
            self._pen.setWidth(self._pen_width)
            self.update()

    @Property(float)
    def rotation(self):
        """
        PyQt Property for the rotation.
        This rotates the icon coordinate system clockwise.

        Returns
        -------
        angle : float
            The angle in degrees
        """
        return self._rotation

    @rotation.setter
    def rotation(self, angle):
        """
        PyQt Property for the rotation.
        This rotates the icon coordinate system clockwise.

        Parameters
        ----------
        angle : float
            The angle in degrees
        """
        self._rotation = angle
        self.update()

    def mousePressEvent(self, evt):
        """Clicking the icon fires the ``clicked`` signal"""
        # Default Qt reaction to the icon press
        super().mousePressEvent(evt)
        if evt.button() == Qt.LeftButton:
            self.clicked.emit()
예제 #4
0
파일: drawing.py 프로젝트: tynanford/pydm
class PyDMDrawing(QWidget, PyDMWidget):
    """
    Base class to be used for all PyDM Drawing Widgets.
    This class inherits from QWidget and PyDMWidget.

    Parameters
    ----------
    parent : QWidget
        The parent widget for the Label
    init_channel : str, optional
        The channel to be used by the widget.
    """
    def __init__(self, parent=None, init_channel=None):
        self._rotation = 0.0
        self._brush = QBrush(Qt.SolidPattern)
        self._original_brush = None
        self._painter = QPainter()
        self._pen = QPen(Qt.NoPen)
        self._pen_style = Qt.NoPen
        self._pen_cap_style = Qt.SquareCap
        self._pen_join_style = Qt.MiterJoin
        self._pen_width = 0
        self._pen_color = QColor(0, 0, 0)
        self._pen.setCapStyle(self._pen_cap_style)
        self._pen.setJoinStyle(self._pen_join_style)
        self._original_pen_style = self._pen_style
        self._original_pen_color = self._pen_color
        QWidget.__init__(self, parent)
        PyDMWidget.__init__(self, init_channel=init_channel)
        self.alarmSensitiveBorder = False

    def sizeHint(self):
        return QSize(100, 100)

    def paintEvent(self, _):
        """
        Paint events are sent to widgets that need to update themselves,
        for instance when part of a widget is exposed because a covering
        widget was moved.

        At PyDMDrawing this method handles the alarm painting with parameters
        from the stylesheet, configures the brush, pen and calls ```draw_item```
        so the specifics can be performed for each of the drawing classes.

        Parameters
        ----------
        event : QPaintEvent
        """
        painter = QPainter(self)
        opt = QStyleOption()
        opt.initFrom(self)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)
        painter.setRenderHint(QPainter.Antialiasing)

        painter.setBrush(self._brush)
        painter.setPen(self._pen)

        self.draw_item(painter)

    def draw_item(self, painter):
        """
        The classes inheriting from PyDMDrawing must overwrite this method.
        This method translate the painter to the center point given by
        ```get_center``` and rotate the canvas by the given amount of
        degrees.
        """
        xc, yc = self.get_center()
        painter.translate(xc, yc)
        painter.rotate(-self._rotation)

    def get_center(self):
        """
        Simple calculation of the canvas' center point.

        Returns
        -------
        x, y : float
            Tuple with X and Y coordinates of the center.
        """
        return self.width() * 0.5, self.height() * 0.5

    def get_bounds(self, maxsize=False, force_no_pen=False):
        """
        Returns a tuple containing the useful area for the drawing.

        Parameters
        ----------
        maxsize : bool, default is False
            If True, width and height information are based on the
            maximum inner rectangle dimensions given by ```get_inner_max```,
            otherwise width and height will receive the widget size.

        force_no_pen : bool, default is False
            If True the pen width will not be considered when calculating
            the bounds.

        Returns
        -------
        x, y, w, h : tuple
            Tuple with X and Y coordinates followed by the maximum width
            and height.
        """
        w, h = self.width(), self.height()

        if maxsize:
            w, h = self.get_inner_max()

        xc, yc = w * 0.5, h * 0.5

        if self.has_border() and not force_no_pen:
            w = max(0, w - 2 * self._pen_width)
            h = max(0, h - 2 * self._pen_width)
            x = max(0, self._pen_width)
            y = max(0, self._pen_width)
        else:
            x = 0
            y = 0
        return x - xc, y - yc, w, h

    def has_border(self):
        """
        Check whether or not the drawing have a border based on the
        Pen Style and Pen width.

        Returns
        -------
        bool
            True if the drawing has a border, False otherwise.
        """
        if self._pen.style() != Qt.NoPen and self._pen_width > 0:
            return True
        else:
            return False

    def is_square(self):
        """
        Check if the widget has the same width and height values.

        Returns
        -------
        bool
            True in case the widget has a square shape, False otherwise.
        """
        return self.height() == self.width()

    def get_inner_max(self):
        """
        Calculates the largest inner rectangle in a rotated rectangle.
        This implementation was based on https://stackoverflow.com/a/18402507

        Returns
        -------
        w, h : tuple
            The width and height of the largest rectangle.
        """
        # Based on https://stackoverflow.com/a/18402507
        w0 = 0
        h0 = 0
        angle = math.radians(self._rotation)
        origWidth = self.width()
        origHeight = self.height()

        if origWidth == 0:
            logger.error(
                "Invalid width. The value must be greater than {0}".format(
                    origWidth))
            return

        if origHeight == 0:
            logger.error(
                "Invalid height. The value must be greater than {0}".format(
                    origHeight))
            return

        if (origWidth <= origHeight):
            w0 = origWidth
            h0 = origHeight
        else:
            w0 = origHeight
            h0 = origWidth
        # Angle normalization in range [-PI..PI)
        ang = angle - math.floor(
            (angle + math.pi) / (2 * math.pi)) * 2 * math.pi
        ang = math.fabs(ang)
        if ang > math.pi / 2:
            ang = math.pi - ang
        c = w0 / (h0 * math.sin(ang) + w0 * math.cos(ang))
        w = 0
        h = 0
        if (origWidth <= origHeight):
            w = w0 * c
            h = h0 * c
        else:
            w = h0 * c
            h = w0 * c
        return w, h

    @Property(QBrush)
    def brush(self):
        """
        PyQT Property for the brush object to be used when coloring the
        drawing

        Returns
        -------
        QBrush
        """
        return self._brush

    @brush.setter
    def brush(self, new_brush):
        """
        PyQT Property for the brush object to be used when coloring the
        drawing

        Parameters
        ----------
        new_brush : QBrush
        """
        if new_brush != self._brush:
            if self._alarm_state == PyDMWidget.ALARM_NONE:
                self._original_brush = new_brush
            self._brush = new_brush
            self.update()

    @Property(Qt.PenStyle)
    def penStyle(self):
        """
        PyQT Property for the pen style to be used when drawing the border

        Returns
        -------
        int
            Index at Qt.PenStyle enum
        """
        return self._pen_style

    @penStyle.setter
    def penStyle(self, new_style):
        """
        PyQT Property for the pen style to be used when drawing the border

        Parameters
        ----------
        new_style : int
            Index at Qt.PenStyle enum
        """
        if self._alarm_state == PyDMWidget.ALARM_NONE:
            self._original_pen_style = new_style
        if new_style != self._pen_style:
            self._pen_style = new_style
            self._pen.setStyle(new_style)
            self.update()

    @Property(Qt.PenCapStyle)
    def penCapStyle(self):
        """
        PyQT Property for the pen cap to be used when drawing the border

        Returns
        -------
        int
            Index at Qt.PenCapStyle enum
        """
        return self._pen_cap_style

    @penCapStyle.setter
    def penCapStyle(self, new_style):
        """
        PyQT Property for the pen cap style to be used when drawing the border

        Parameters
        ----------
        new_style : int
            Index at Qt.PenStyle enum
        """
        if new_style != self._pen_cap_style:
            self._pen_cap_style = new_style
            self._pen.setCapStyle(new_style)
            self.update()

    @Property(Qt.PenJoinStyle)
    def penJoinStyle(self):
        """
        PyQT Property for the pen join style to be used when drawing the border

        Returns
        -------
        int
            Index at Qt.PenJoinStyle enum
        """
        return self._pen_join_style

    @penJoinStyle.setter
    def penJoinStyle(self, new_style):
        """
        PyQT Property for the pen join style to be used when drawing the border

        Parameters
        ----------
        new_style : int
            Index at Qt.PenStyle enum
        """
        if new_style != self._pen_join_style:
            self._pen_join_style = new_style
            self._pen.setJoinStyle(new_style)
            self.update()

    @Property(QColor)
    def penColor(self):
        """
        PyQT Property for the pen color to be used when drawing the border

        Returns
        -------
        QColor
        """
        return self._pen_color

    @penColor.setter
    def penColor(self, new_color):
        """
        PyQT Property for the pen color to be used when drawing the border

        Parameters
        ----------
        new_color : QColor
        """
        if self._alarm_state == PyDMWidget.ALARM_NONE:
            self._original_pen_color = new_color

        if new_color != self._pen_color:
            self._pen_color = new_color
            self._pen.setColor(new_color)
            self.update()

    @Property(float)
    def penWidth(self):
        """
        PyQT Property for the pen width to be used when drawing the border

        Returns
        -------
        float
        """
        return self._pen_width

    @penWidth.setter
    def penWidth(self, new_width):
        """
        PyQT Property for the pen width to be used when drawing the border

        Parameters
        ----------
        new_width : float
        """
        if new_width < 0:
            return
        if new_width != self._pen_width:
            self._pen_width = new_width
            self._pen.setWidth(self._pen_width)
            self.update()

    @Property(float)
    def rotation(self):
        """
        PyQT Property for the counter-clockwise rotation in degrees
        to be applied to the drawing.

        Returns
        -------
        float
        """
        return self._rotation

    @rotation.setter
    def rotation(self, new_angle):
        """
        PyQT Property for the counter-clockwise rotation in degrees
        to be applied to the drawing.

        Parameters
        ----------
        new_angle : float
        """
        if new_angle != self._rotation:
            self._rotation = new_angle
            self.update()

    def alarm_severity_changed(self, new_alarm_severity):
        PyDMWidget.alarm_severity_changed(self, new_alarm_severity)
        if new_alarm_severity == PyDMWidget.ALARM_NONE:
            if self._original_brush is not None:
                self.brush = self._original_brush
            if self._original_pen_color is not None:
                self.penColor = self._original_pen_color
            if self._original_pen_style is not None:
                self.penStyle = self._original_pen_style
예제 #5
0
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, 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()

plot.grab().save(
    osp.join(osp.abspath(osp.dirname(__file__)), "images",
             "symbol_path_example.png"))