Example #1
0
class VLine(ChartItem):
    """ Simple vertical line without vertical bounds.

    :param parent: Parent item.
    """
    def __init__(self, parent=None):
        super(VLine, self).__init__(parent)
        self.chartItemFlags = ChartItemFlags.FLAG_NO_AUTO_RANGE

        self._x = None
        self._label = None

        self._pen = QPen(QBrush(Qt.green), 1.0, Qt.SolidLine)
        self._pen.setCosmetic(True)
        self._brush = QBrush(QColor(255, 255, 255, 0))

        finfo = np.finfo(np.float32)
        self.b_rect = QRectF(0, finfo.min / 2.0, 0, finfo.max)

    @property
    def label(self):
        return self._label

    def setX(self, x, label=None, color=QColor(Qt.red)):
        """ Sets the Vline's x value.

        :param x: abscissa value.
        :param label:
        :param color:
        """
        self._x = x
        self.setPos(QPointF(self._x, 0))

        if label is not None:
            self._label = label

        self._color = color
        self._pen = QPen(QBrush(color), 1.0, Qt.SolidLine)
        self._pen.setCosmetic(True)
        self._brush = QBrush(QColor(255, 255, 255, 0))

    def visibleRangeChanged(self, rect):
        b = min(rect.bottom(), rect.top())
        self.b_rect = QRectF(0, b - 10, 0, rect.height() + 20)
        self.prepareGeometryChange()

    def paint(self, p=QPainter(), o=QStyleOptionGraphicsItem(), widget=None):
        # p.setRenderHint(QPainter.Antialiasing)
        p.setPen(self._pen)
        p.setBrush(self._brush)

        p.drawLine(
            QLineF(QPointF(0, self.b_rect.bottom()),
                   QPointF(0, self.b_rect.top())))

        super(VLine, self).paint(p, o, widget)

    def __del__(self):
        _log.debug("Finalize VLine {}".format(self))
Example #2
0
class HLine(ChartItem):
    """ Simple horizontal line without horizontal bounds.

    :param parent: Parent item.
    """
    def __init__(self, parent=None):
        super(HLine, self).__init__(parent)
        self.chartItemFlags = ChartItemFlags.FLAG_NO_AUTO_RANGE

        self._y = None
        self._label = None

        self._pen = QPen(QBrush(Qt.green), 1.0, Qt.SolidLine)
        self._pen.setCosmetic(True)
        self._brush = QBrush(QColor(255, 255, 255, 0))

        finfo = np.finfo(np.float32)
        self.b_rect = QRectF(finfo.min / 2.0, 0, finfo.max, 0)

    @property
    def label(self):
        return self._label

    def setY(self, y, label=None, color=QColor(Qt.red)):
        """ Sets the Hline's y value

        :param y: Ordinate value
        :param label:
        :param color:
        """
        self._y = y
        self.setPos(QPointF(0, self._y))

        if label is not None:
            self._label = label

        self._color = color
        self._pen = QPen(QBrush(color), 1.0, Qt.SolidLine)
        self._pen.setCosmetic(True)
        self._brush = QBrush(QColor(255, 255, 255, 0))

    def visibleRangeChanged(self, rect):
        b = min(rect.left(), rect.right())
        self.b_rect = QRectF(b - 10, 0, rect.width() + 20, 0)
        self.prepareGeometryChange()

    def paint(self, p=QPainter(), o=QStyleOptionGraphicsItem(), widget=None):
        # p.setRenderHint(QPainter.N)
        p.setPen(self._pen)
        p.setBrush(self._brush)
        p.drawLine(
            QLineF(QPointF(self.b_rect.left(), 0),
                   QPointF(self.b_rect.right(), 0)))
        super(HLine, self).paint(p, o, widget)

    def __del__(self):
        _log.debug("Finalize HLine {}".format(self))
Example #3
0
    def addMark(self, new_mark: VideoSliderMark, update: bool = True):
        """Adds a marked value to the slider.

        Args:
            new_mark: value to mark
            update: Whether to redraw slider with new mark.

        Returns:
            None.
        """
        # check if mark is within slider range
        if new_mark.val > self._val_max:
            return
        if new_mark.val < self._val_min:
            return

        self._marks.add(new_mark)

        v_top_pad = self._header_height + 1
        v_bottom_pad = 1
        v_top_pad += new_mark.top_pad
        v_bottom_pad += new_mark.bottom_pad

        width = new_mark.visual_width

        v_offset = v_top_pad

        height = new_mark.get_height(container_height=self.box_rect.height() -
                                     self._header_height)

        color = new_mark.QColor
        pen = QPen(color, 0.5)
        pen.setCosmetic(True)
        brush = QBrush(color) if new_mark.filled else QBrush()

        line = self.scene.addRect(-width // 2, v_offset, width, height, pen,
                                  brush)
        self._mark_items[new_mark] = line

        if new_mark.mark_type == "tick":
            # Show tick mark behind other slider marks
            self._mark_items[new_mark].setZValue(0)

            # Add a text label to show in header area
            mark_label_text = (
                # sci notation if large
                f"{new_mark.val + self.tick_index_offset:g}")
            self._mark_labels[new_mark] = self.scene.addSimpleText(
                mark_label_text, self._base_font)
        else:
            # Show in front of tick marks and behind track lines
            self._mark_items[new_mark].setZValue(1)

        if update:
            self._update_visual_positions()
Example #4
0
def makePen(color, lineWidth=1.0, lineStyle=Qt.SolidLine, cosmetic=True):
    """ Creates and returns a pen.

    :param color: Any value, the `QColor <http://doc.qt.io/qt-4.8/qcolor.html#QColor>`_
        constructor takes as argument
    :param lineWidth: line width
    :param lineStyle: see `http://doc.qt.io/qt-4.8/qt.html#PenStyle-enum`
    :param cosmetic: When set to true the pens with is transformation invariant
    :return: The constructed pen
    :rtype: `QPen <http://doc.qt.io/qt-4.8/qpen.html>`_
    """
    # qColor = QColor(color)
    pen = QPen(color, lineWidth, lineStyle)
    pen.setCosmetic(cosmetic)

    return pen
Example #5
0
    def __init__(self, data, color=Qt.green, parent=None):
        if ("size_x" in data.index) and ("size_y" in data.index):
            size_x = data["size_x"]
            size_y = data["size_y"]
        else:
            size_x = size_y = data["size"]
        super().__init__(data["x"] - size_x + 0.5, data["y"] - size_y + 0.5,
                         2 * size_x, 2 * size_y, parent)
        pen = QPen()
        pen.setWidthF(1.25)
        pen.setCosmetic(True)
        pen.setColor(color)
        self.setPen(pen)

        ttStr = "\n".join([
            "{}: {:.2f}".format(k, v) for k, v in data.items() if k != "frame"
        ])
        self.setToolTip(ttStr)
    def showTracks(self):
        # clear self.pathitems
        self.clearTracks()

        frame = self.window.imageview.currentIndex
        if frame<len(self.tracks_by_frame):
            tracks = self.tracks_by_frame[frame]
            pen = QPen(Qt.green, .4)
            pen.setCosmetic(True)
            pen.setWidth(2)
            for track_idx in tracks:
                pathitem = QGraphicsPathItem(self.window.imageview.view)
                pathitem.setPen(pen)
                self.window.imageview.view.addItem(pathitem)
                self.pathitems.append(pathitem)
                pts = self.txy_pts[self.tracks[track_idx]]
                x = pts[:, 1]+.5; y = pts[:,2]+.5
                path = QPainterPath(QPointF(x[0],y[0]))
                for i in np.arange(1, len(pts)):
                    path.lineTo(QPointF(x[i],y[i]))
                pathitem.setPath(path)
Example #7
0
def cosmetic_pen(color, width=2):
    pen = QPen(QColor.fromRgbF(*color))
    pen.setCosmetic(True)
    pen.setWidth(width)
    return pen
Example #8
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()
Example #9
0
class InteractiveVerticalLine(ChartWidgetItem):
    """ Vertical Line which can be moved by mouse interaction. Usefull for timelines. """

    positionChange = Signal(object)

    def __init__(self, parent=None):
        super(InteractiveVerticalLine, self).__init__(parent)
        self.setFlags(
            QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemSendsGeometryChanges
        )
        self.chartItemFlags = ChartItemFlags.FLAG_NO_AUTO_RANGE
        self.setZValue(1e6)

        self.setAcceptHoverEvents(True)

        self._x = None
        self._label = None

        self._pen = QPen(QBrush(Qt.green), 1.0, Qt.SolidLine)
        self._pen.setCosmetic(True)
        self._brush = QBrush(QColor(255, 255, 255, 0))

    @property
    def label(self):
        return self._label

    def setX(self, x, label=None, color=None):
        """ Sets the line's x value.

        :param x: abscissa value.
        :param label:
        :param color:
        """
        if label is not None:
            self._label = label

        if color is not None:
            self._color = color
            self._pen = QPen(QBrush(color), 1.0, Qt.SolidLine)
            self._pen.setCosmetic(True)
        # self._brush = QBrush(QColor(255, 255, 255, 0))

        if self._x != x:
            self._x = x
            self.setPos(QPointF(self._x, 0))

    def visibleRangeChanged(self, rect):
        t = self.parentItem().transform()
        bb = 4 / t.m11()
        _log.debug("{}, {} -> {}".format(rect.bottom(), rect.height(), bb))

        b = min(rect.bottom(), rect.top())
        self.b_rect = QRectF(-bb, b, 2 * bb, rect.height())
        self.prepareGeometryChange()

    def paint(self, p=QPainter(), o=QStyleOptionGraphicsItem(), widget=None):
        # p.setRenderHint(QPainter.Antialiasing)
        p.setPen(self._pen)
        p.setBrush(self._brush)

        p.drawLine(
            QLineF(QPointF(0, self.b_rect.bottom()), QPointF(0, self.b_rect.top()))
        )

        p.setPen(Qt.transparent)
        p.drawRect(self.b_rect)

        if CONFIG.debug:
            p.setPen(Qt.yellow)
            p.drawRect(self.b_rect)

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange:
            old_pos = self.pos()
            # value = value #.toPointF()

            e = InteractiveChangeEvent()
            e.position = value
            self.positionChange.emit(e)

            return QPointF(value.x(), old_pos.y())

        return super(InteractiveVerticalLine, self).itemChange(change, value)

    def hoverEnterEvent(self, e):
        super(InteractiveVerticalLine, self).hoverEnterEvent(e)
        self._brush = QBrush(QColor(255, 255, 255, 40))
        self.update()

    def hoverLeaveEvent(self, e):
        super(InteractiveVerticalLine, self).hoverLeaveEvent(e)
        self._brush = QBrush(QColor(255, 255, 255, 0))
        self.update()

    def __del__(self):
        _log.debug("Finalize Interactive vLine {}".format(self))
Example #10
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.
    """
    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()
Example #11
0
class MicroViewScene(QGraphicsScene):
    def __init__(self, parent):
        super().__init__(parent)

        self._imageItem = ImageGraphicsItem()
        self.addItem(self._imageItem)
        self._roiMode = False

        self._drawingRoi = False

        self.roiPen = QPen()
        self.roiPen.setWidthF(1.25)
        self.roiPen.setCosmetic(True)
        self.roiPen.setColor(Qt.yellow)

        self._roiPolygon = QPolygonF()
        self._roiItem = QGraphicsPolygonItem(self._roiPolygon)
        self._roiItem.setPen(self.roiPen)
        self.addItem(self._roiItem)

    def setImage(self, img):
        if isinstance(img, QImage):
            img = QPixmap.fromImage(img)
        self._imageItem.setPixmap(img)

    @Property(ImageGraphicsItem)
    def imageItem(self):
        return self._imageItem

    def enableRoiMode(self, enable):
        if enable == self._roiMode:
            return
        if enable:
            self._roiPolygon = QPolygonF()
            self._roiItem.setPolygon(self._roiPolygon)
            self._tempRoiPolygon = QPolygonF(self._roiPolygon)
            self._tempRoiPolygon.append(QPointF())
        if not enable:
            self._roiItem.setPolygon(self._roiPolygon)
            self.roiChanged.emit(self._roiPolygon)
        self._roiMode = enable
        self.roiModeChanged.emit(enable)

    roiModeChanged = Signal(bool)

    @Property(bool, fset=enableRoiMode)
    def roiMode(self):
        return self._roiMode

    def setRoi(self, roi):
        if self._roiPolygon == roi:
            return
        self._roiPolygon = roi
        self._roiItem.setPolygon(self._roiPolygon)
        self.roiChanged.emit(roi)

    roiChanged = Signal(QPolygonF)

    @Property(QPolygonF,
              fset=setRoi,
              doc="Polygon describing the region of interest (ROI)")
    def roi(self):
        return self._roiPolygon

    def _appendPointToRoi(self, pos, polygon, replace_last=False):
        br = self._imageItem.boundingRect()
        topLeft = br.topLeft()
        bottomRight = br.bottomRight()
        # Make sure we stay inside the image boundaries
        xInBr = max(topLeft.x(), pos.x())
        xInBr = min(bottomRight.x(), xInBr)
        yInBr = max(topLeft.y(), pos.y())
        yInBr = min(bottomRight.y(), yInBr)
        pointInBr = QPointF(xInBr, yInBr)

        if replace_last:
            polygon[-1] = pointInBr
        else:
            polygon.append(pointInBr)

    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        if not self._roiMode:
            return
        self._appendPointToRoi(event.scenePos(), self._roiPolygon, False)
        self._roiItem.setPolygon(self._roiPolygon)
        self._tempRoiPolygon = QPolygonF(self._roiPolygon)
        self._tempRoiPolygon.append(QPointF())

    def mouseMoveEvent(self, event):
        super().mouseMoveEvent(event)
        if not self._roiMode:
            return
        self._appendPointToRoi(event.scenePos(), self._tempRoiPolygon, True)
        self._roiItem.setPolygon(self._tempRoiPolygon)

    def mouseDoubleClickEvent(self, event):
        super().mouseDoubleClickEvent(event)
        # the first click of the double click is a normal mousePressEvent,
        # thus the current point has already been added. Simply exit ROI mode
        self.roiMode = False
Example #12
0
    def __init__(
        self,
        orientation=-1,  # for compatibility with QSlider
        min=0,
        max=1,
        val=0,
        marks=None,
        *args,
        **kwargs,
    ):
        super(VideoSlider, self).__init__(*args, **kwargs)

        self.scene = QtWidgets.QGraphicsScene()
        self.setScene(self.scene)
        self.setAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignTop)

        self.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                           QtWidgets.QSizePolicy.Fixed)
        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)

        self.setMouseTracking(True)

        self._get_val_tooltip = None

        self.tick_index_offset = 1
        self.zoom_factor = 1

        self._header_label_height = 20
        self._header_graph_height = 40
        self._header_height = self._header_label_height  # room for frame labels
        self._min_height = 19 + self._header_height

        self._base_font = QtGui.QFont()
        self._base_font.setPixelSize(10)

        self._tick_marks = []

        # Add border rect
        outline_rect = QtCore.QRectF(0, 0, 200, self._min_height - 3)
        self.box_rect = outline_rect
        # self.outlineBox = self.scene.addRect(outline_rect)
        # self.outlineBox.setPen(QPen(QColor("black", alpha=0)))

        # Add drag handle rect
        self._handle_width = 6
        handle_rect = QtCore.QRectF(0, self._handle_top, self._handle_width,
                                    self._handle_height)
        self.setMinimumHeight(self._min_height)
        self.setMaximumHeight(self._min_height)
        self.handle = self.scene.addRect(
            QtCore.QRectF(0, self._handle_top, self._handle_width,
                          self._handle_height))
        self.handle.setPen(QPen(QColor(80, 80, 80)))
        self.handle.setBrush(QColor(128, 128, 128, 128))

        # Add (hidden) rect to highlight selection
        self.select_box = self.scene.addRect(
            QtCore.QRectF(0, 1, 0,
                          outline_rect.height() - 2))
        self.select_box.setPen(QPen(QColor(80, 80, 255)))
        self.select_box.setBrush(QColor(80, 80, 255, 128))
        self.select_box.hide()

        self.zoom_box = self.scene.addRect(
            QtCore.QRectF(0, 1, 0,
                          outline_rect.height() - 2))
        self.zoom_box.setPen(QPen(QColor(80, 80, 80, 64)))
        self.zoom_box.setBrush(QColor(80, 80, 80, 64))
        self.zoom_box.hide()

        self.scene.setBackgroundBrush(QBrush(QColor(200, 200, 200)))

        self.clearSelection()
        self.setEnabled(True)
        self.setMinimum(min)
        self.setMaximum(max)
        self.setValue(val)
        self.setMarks(marks)

        pen = QPen(QColor(80, 80, 255), 0.5)
        pen.setCosmetic(True)
        self.poly = self.scene.addPath(QPainterPath(), pen,
                                       self.select_box.brush())
        self.headerSeries = dict()
        self._draw_header()
Example #13
0
class RectangularRegion(ChartItem):
    """ Rectangular overlay, that (optionally) can be resized by handles.

    TODO:
      * Notification framework
      * Modifiers to allow for more flexible mouse interactions
    """
    def __init__(self, x, y, width=1, height=1, rotation=0, parent=None):
        """ Constructor.

        :param x: Initial x position of ROIs center.
        :param y: Initial y position of ROIs center.
        :param width: Initial width
        :param height: Initial height
        :param rotation: Initial rotation in radians
        :param parent: Optional parent item.
        """
        super(RectangularRegion, self).__init__(parent)

        self.setFlags(QGraphicsItem.ItemIsMovable
                      | QGraphicsItem.ItemIsFocusable
                      | QGraphicsItem.ItemSendsGeometryChanges)

        # self.setHandlesChildEvents(False)
        # self.setFiltersChildEvents(True)
        self.__in_move = False

        self._path = QPainterPath()
        self.pen = QPen(QBrush(QColor(161, 0, 161, 255)), 1.0, Qt.SolidLine)
        self.pen.setCosmetic(True)
        self.brush = QBrush(QColor(255, 255, 255, 40))

        self.state = RoiState()
        self.state.pos = QPointF(x, y)
        self.state.w0 = width
        self.state.w1 = width
        self.state.h0 = height
        self.state.h1 = height

        self.state.rotation = rotation

        self.setPos(self.state.pos)
        self.updatePath()

        self._handles = []

    @property
    def handles(self):
        """ Returns all handles which are attached to the ROI.

        :return: list
        """
        return self._handles

    def addHandle(self, handle):
        """ Adds a handle to the ROI.

        :param handle: Resize or rotate handle
        """
        handle.setParentItem(self)
        # self.addToGroup(handle)
        self.handles.append(handle)
        handle.updatePosition()

    def removeHandle(self, handle):
        """ Removes the given handle from the ROI.

        :param handle: Handle
        :return:
        """
        if handle not in self.handles:
            _log.warning("Handle not part of ROI.")
            return

        self.handles.remove(handle)
        # self.removeFromGroup(handle)
        self.scene().removeItem(handle)

    def boundingRect(self):
        return self._path.boundingRect()

    def updatePath(self):
        p0 = Vec2(self.state.w0, self.state.h0)
        p1 = Vec2(-self.state.w1, self.state.h0)
        p2 = Vec2(-self.state.w1, -self.state.h1)
        p3 = Vec2(self.state.w0, -self.state.h1)

        p0t = p0.rotate(self.state.rotation)
        p1t = p1.rotate(self.state.rotation)
        p2t = p2.rotate(self.state.rotation)
        p3t = p3.rotate(self.state.rotation)

        self._path = QPainterPath()
        self._path.moveTo(p0t.x, p0t.y)
        self._path.lineTo(p1t.x, p1t.y)
        self._path.lineTo(p2t.x, p2t.y)
        self._path.lineTo(p3t.x, p3t.y)
        self._path.closeSubpath()

        self.prepareGeometryChange()

    def paint(self, p=QPainter(), o=QStyleOptionGraphicsItem(), widget=None):
        p.setRenderHint(QPainter.Antialiasing)
        p.setPen(self.pen)
        p.setBrush(self.brush)
        p.drawPath(self._path)

        p.setBrush(Qt.transparent)
        p.drawLine(QLineF(-0.5, 0, 0.5, 0))
        p.drawLine(QLineF(0, -0.5, 0, 0.5))
        p.drawEllipse(QPointF(0, 0), 0.25, 0.25)

    def mousePressEvent(self, e):
        # !!!F*****g leason learned, call super to avoid weird jumps in position!!!
        super(RectangularRegion, self).mousePressEvent(e)
        self.__in_move = True

    def mouseReleaseEvent(self, e):
        super(RectangularRegion, self).mouseReleaseEvent(e)
        self.__in_move = False

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange and self.__in_move:
            old_pos = self.pos()
            new_pos = value  # .toPointF()

            delta = old_pos - new_pos
            _log.debug("Delta: {}".format(delta))

            self.state.pos = new_pos
            value = new_pos

            for handle in self.handles:
                handle.updatePosition()

        return super(RectangularRegion, self).itemChange(change, value)

    def rotation(self):
        """ Override takes rotation from the state instead of items transformation.

        :return: rotation in degrees
        """
        return self.state.rotation * 180 / np.pi

    def setRotation(self, degree):
        """ Override to propagate rotation to state instead of applying it to the items transform

        :param degree: Rotation in degrees
        """
        self.state.rotation = (degree % 360) * np.pi / 180.0

        for h in self.handles:
            h.updatePosition()

        self.updatePath()