Ejemplo n.º 1
0
    def setAnchorPath(self, path):
        # type: (QPainterPath) -> None
        """
        Set the anchor's curve path as a :class:`QPainterPath`.
        """
        self.__anchorPath = QPainterPath(path)
        # Create a stroke of the path.
        stroke_path = QPainterPathStroker()
        stroke_path.setCapStyle(Qt.RoundCap)

        # Shape is wider (bigger mouse hit area - should be settable)
        stroke_path.setWidth(25)
        self.prepareGeometryChange()
        self.__shape = stroke_path.createStroke(path)

        # The full stroke
        stroke_path.setWidth(3)
        self.__fullStroke = stroke_path.createStroke(path)

        # The dotted stroke (when not connected to anything)
        stroke_path.setDashPattern(Qt.DotLine)
        self.__dottedStroke = stroke_path.createStroke(path)

        if self.anchored:
            assert self.__fullStroke is not None
            self.setPath(self.__fullStroke)
            self.__shadow.setPath(self.__fullStroke)
            brush = self.connectedHoverBrush if self.__hover else self.connectedBrush
            self.setBrush(brush)
        else:
            assert self.__dottedStroke is not None
            self.setPath(self.__dottedStroke)
            self.__shadow.setPath(self.__dottedStroke)
            brush = self.normalHoverBrush if self.__hover else self.normalBrush
            self.setBrush(brush)
Ejemplo n.º 2
0
    def __init__(self, parent, *args):
        GraphicsPathObject.__init__(self, parent, *args)
        self.setAcceptHoverEvents(True)
        self.setPen(QPen(Qt.NoPen))
        self.normalBrush = QBrush(QColor("#CDD5D9"))
        self.connectedBrush = QBrush(QColor("#9CACB4"))
        self.setBrush(self.normalBrush)

        self.shadow = QGraphicsDropShadowEffect(blurRadius=10,
                                                color=QColor(SHADOW_COLOR),
                                                offset=QPointF(0, 0))

        self.setGraphicsEffect(self.shadow)
        self.shadow.setEnabled(False)

        # Does this item have any anchored links.
        self.anchored = False

        if isinstance(parent, NodeItem):
            self.__parentNodeItem = parent
        else:
            self.__parentNodeItem = None

        self.__anchorPath = QPainterPath()
        self.__points = []
        self.__pointPositions = []

        self.__fullStroke = None
        self.__dottedStroke = None
        self.__shape = None
Ejemplo n.º 3
0
 def __updateFrame(self):
     # type: () -> None
     rect = self.geometry()
     rect.moveTo(0, 0)
     path = QPainterPath()
     path.addRect(rect)
     self.__framePathItem.setPath(path)
Ejemplo n.º 4
0
    def __updateCurve(self):
        self.prepareGeometryChange()
        self.__boundingRect = None
        if self.sourceAnchor and self.sinkAnchor:
            source_pos = self.sourceAnchor.anchorScenePos()
            sink_pos = self.sinkAnchor.anchorScenePos()
            source_pos = self.curveItem.mapFromScene(source_pos)
            sink_pos = self.curveItem.mapFromScene(sink_pos)

            # Adaptive offset for the curve control points to avoid a
            # cusp when the two points have the same y coordinate
            # and are close together
            delta = source_pos - sink_pos
            dist = math.sqrt(delta.x() ** 2 + delta.y() ** 2)
            cp_offset = min(dist / 2.0, 60.0)

            # TODO: make the curve tangent orthogonal to the anchors path.
            path = QPainterPath()
            path.moveTo(source_pos)
            path.cubicTo(source_pos + QPointF(cp_offset, 0),
                         sink_pos - QPointF(cp_offset, 0),
                         sink_pos)

            self.curveItem.setCurvePath(path)
            self.sourceIndicator.setPos(source_pos)
            self.sinkIndicator.setPos(sink_pos)
            self.__updateText()
        else:
            self.setHoverState(False)
            self.curveItem.setPath(QPainterPath())
Ejemplo n.º 5
0
    def _relayout(self):
        if self._root is None:
            return

        scale = self._height_scale_factor()
        base = scale * self._root.value.height
        self._layout = dendrogram_path(self._root,
                                       self.orientation,
                                       scaleh=scale)
        for node_geom in postorder(self._layout):
            node, geom = node_geom.value
            item = self._items[node]
            item.element = geom
            # the untransformed source path
            item.sourcePath = path_toQtPath(geom)
            r = item.sourcePath.boundingRect()

            if self.orientation == Left:
                r.setRight(base)
            elif self.orientation == Right:
                r.setLeft(0)
            elif self.orientation == Top:
                r.setBottom(base)
            else:
                r.setTop(0)

            hitarea = QPainterPath()
            hitarea.addRect(r)
            item.sourceAreaShape = hitarea
            item.setGeometryData(item.sourcePath, item.sourceAreaShape)
            item.setZValue(-node.value.height)
Ejemplo n.º 6
0
    def test_shapeFromPath(self):
        path = QPainterPath()
        path.addRect(10, 10, 20, 20)

        pen = QPen(QColor("#FFF"), 2.0)
        path = shapeFromPath(path, pen)

        self.assertGreaterEqual(area(path.controlPointRect()), (20 + 2.0)**2)
Ejemplo n.º 7
0
def violin_shape(x, p):
    # type: (Sequence[float], Sequence[float]) -> QPainterPath
    points = [QPointF(pi, xi) for xi, pi in zip(x, p)]
    points += [QPointF(-pi, xi) for xi, pi in reversed(list(zip(x, p)))]
    poly = QPolygonF(points)
    path = QPainterPath()
    path.addPolygon(poly)
    return path
Ejemplo n.º 8
0
    def __init__(self, parent=None, **kwargs):
        QGraphicsObject.__init__(self, parent, **kwargs)
        self.setFlag(QGraphicsObject.ItemSendsGeometryChanges)

        self.__path = QPainterPath()
        self.__brush = QBrush(Qt.NoBrush)
        self.__pen = QPen()
        self.__boundingRect = None
Ejemplo n.º 9
0
    def test_shapeFromPath(self):
        path = QPainterPath()
        path.addRect(10, 10, 20, 20)

        pen = QPen(QColor("#FFF"), 2.0)
        path = shapeFromPath(path, pen)

        self.assertGreaterEqual(area(path.controlPointRect()),
                                (20 + 2.0) ** 2)
Ejemplo n.º 10
0
 def redraw_path(self):
     self.path = QPainterPath()
     for segment in self.segment(self.data()):
         if self.fitted:
             self.draw_cubic_path(segment)
         else:
             self.draw_normal_path(segment)
     self._item.setPath(self.graph_transform().map(self.path))
     self._item.setPen(self.pen())
Ejemplo n.º 11
0
    def setShapeRect(self, rect):
        """
        Set the item's shape `rect`. The item should be confined within
        this rect.

        """
        path = QPainterPath()
        path.addEllipse(rect)
        self.setPath(path)
        self.__shapeRect = rect
Ejemplo n.º 12
0
 def move_label(label, frm, to):
     label.setX(to)
     to += t_box.width() / 2
     path = QPainterPath()
     path.lineTo(0, 4)
     path.lineTo(to - frm, 4)
     path.lineTo(to - frm, 8)
     p = QGraphicsPathItem(path)
     p.setPos(frm, 12)
     labels.addToGroup(p)
Ejemplo n.º 13
0
    def setShapeRect(self, rect):
        """
        Set the item's shape `rect`. The item should be confined within
        this rect.

        """
        path = QPainterPath()
        path.addEllipse(rect)
        self.setPath(path)
        self.__shapeRect = rect
Ejemplo n.º 14
0
 def _contains_point(item: pg.FillBetweenItem, point: QPointF) -> bool:
     curve1, curve2 = item.curves
     x_data_lower, y_data_lower = curve1.curve.getData()
     x_data_upper, y_data_upper = curve2.curve.getData()
     pts = [QPointF(x, y) for x, y in zip(x_data_lower, y_data_lower)]
     pts += [QPointF(x, y) for x, y in
             reversed(list(zip(x_data_upper, y_data_upper)))]
     pts += pts[:1]
     path = QPainterPath()
     path.addPolygon(QPolygonF(pts))
     return path.contains(point)
    def test_layout(self):
        file_desc, disc_desc, bayes_desc = self.widget_desc()
        file_item = NodeItem()
        file_item.setWidgetDescription(file_desc)
        file_item.setPos(0, 150)
        self.scene.add_node_item(file_item)

        bayes_item = NodeItem()
        bayes_item.setWidgetDescription(bayes_desc)
        bayes_item.setPos(200, 0)
        self.scene.add_node_item(bayes_item)

        disc_item = NodeItem()
        disc_item.setWidgetDescription(disc_desc)
        disc_item.setPos(200, 300)
        self.scene.add_node_item(disc_item)

        link = LinkItem()
        link.setSourceItem(file_item)
        link.setSinkItem(disc_item)
        self.scene.add_link_item(link)

        link = LinkItem()
        link.setSourceItem(file_item)
        link.setSinkItem(bayes_item)
        self.scene.add_link_item(link)

        layout = AnchorLayout()
        self.scene.addItem(layout)
        self.scene.set_anchor_layout(layout)

        layout.invalidateNode(file_item)
        layout.activate()

        p1, p2 = file_item.outputAnchorItem.anchorPositions()
        self.assertGreater(p1, p2)

        self.scene.node_item_position_changed.connect(layout.invalidateNode)

        path = QPainterPath()
        path.addEllipse(125, 0, 50, 300)

        def advance():
            t = time.clock()
            bayes_item.setPos(path.pointAtPercent(t % 1.0))
            disc_item.setPos(path.pointAtPercent((t + 0.5) % 1.0))

            self.singleShot(20, advance)

        advance()

        self.app.exec_()
Ejemplo n.º 16
0
    def test_layout(self):
        one_desc, negate_desc, cons_desc = self.widget_desc()
        one_item = NodeItem()
        one_item.setWidgetDescription(one_desc)
        one_item.setPos(0, 150)
        self.scene.add_node_item(one_item)

        cons_item = NodeItem()
        cons_item.setWidgetDescription(cons_desc)
        cons_item.setPos(200, 0)
        self.scene.add_node_item(cons_item)

        negate_item = NodeItem()
        negate_item.setWidgetDescription(negate_desc)
        negate_item.setPos(200, 300)
        self.scene.add_node_item(negate_item)

        link = LinkItem()
        link.setSourceItem(one_item)
        link.setSinkItem(negate_item)
        self.scene.add_link_item(link)

        link = LinkItem()
        link.setSourceItem(one_item)
        link.setSinkItem(cons_item)
        self.scene.add_link_item(link)

        layout = AnchorLayout()
        self.scene.addItem(layout)
        self.scene.set_anchor_layout(layout)

        layout.invalidateNode(one_item)
        layout.activate()

        p1, p2 = one_item.outputAnchorItem.anchorPositions()
        self.assertTrue(p1 > p2)

        self.scene.node_item_position_changed.connect(layout.invalidateNode)

        path = QPainterPath()
        path.addEllipse(125, 0, 50, 300)

        def advance():
            t = time.process_time()
            cons_item.setPos(path.pointAtPercent(t % 1.0))
            negate_item.setPos(path.pointAtPercent((t + 0.5) % 1.0))

        timer = QTimer(negate_item, interval=5)
        timer.start()
        timer.timeout.connect(advance)
        self.qWait()
        timer.stop()
Ejemplo n.º 17
0
    def test_layout(self):
        file_desc, disc_desc, bayes_desc = self.widget_desc()
        file_item = NodeItem()
        file_item.setWidgetDescription(file_desc)
        file_item.setPos(0, 150)
        self.scene.add_node_item(file_item)

        bayes_item = NodeItem()
        bayes_item.setWidgetDescription(bayes_desc)
        bayes_item.setPos(200, 0)
        self.scene.add_node_item(bayes_item)

        disc_item = NodeItem()
        disc_item.setWidgetDescription(disc_desc)
        disc_item.setPos(200, 300)
        self.scene.add_node_item(disc_item)

        link = LinkItem()
        link.setSourceItem(file_item)
        link.setSinkItem(disc_item)
        self.scene.add_link_item(link)

        link = LinkItem()
        link.setSourceItem(file_item)
        link.setSinkItem(bayes_item)
        self.scene.add_link_item(link)

        layout = AnchorLayout()
        self.scene.addItem(layout)
        self.scene.set_anchor_layout(layout)

        layout.invalidateNode(file_item)
        layout.activate()

        p1, p2 = file_item.outputAnchorItem.anchorPositions()
        self.assertGreater(p1, p2)

        self.scene.node_item_position_changed.connect(layout.invalidateNode)

        path = QPainterPath()
        path.addEllipse(125, 0, 50, 300)

        def advance():
            t = time.clock()
            bayes_item.setPos(path.pointAtPercent(t % 1.0))
            disc_item.setPos(path.pointAtPercent((t + 0.5) % 1.0))

            self.singleShot(20, advance)

        advance()

        self.app.exec_()
Ejemplo n.º 18
0
    def test_graphicspathobject(self):
        obj = GraphicsPathObject()
        path = QPainterPath()
        obj.setFlag(GraphicsPathObject.ItemIsMovable)

        path.addEllipse(20, 20, 50, 50)

        obj.setPath(path)
        self.assertEqual(obj.path(), path)

        self.assertTrue(obj.path() is not path,
                        msg="setPath stores the path not a copy")

        brect = obj.boundingRect()
        self.assertTrue(brect.contains(path.boundingRect()))

        with self.assertRaises(TypeError):
            obj.setPath("This is not a path")

        brush = QBrush(QColor("#ffbb11"))
        obj.setBrush(brush)

        self.assertEqual(obj.brush(), brush)

        self.assertTrue(obj.brush() is not brush,
                        "setBrush stores the brush not a copy")

        pen = QPen(QColor("#FFFFFF"), 1.4)
        obj.setPen(pen)

        self.assertEqual(obj.pen(), pen)

        self.assertTrue(obj.pen() is not pen,
                        "setPen stores the pen not a copy")

        brect = obj.boundingRect()
        self.assertGreaterEqual(area(brect), (50 + 1.4 * 2)**2)

        self.assertIsInstance(obj.shape(), QPainterPath)

        positions = []
        obj.positionChanged[QPointF].connect(positions.append)

        pos = QPointF(10, 10)
        obj.setPos(pos)

        self.assertEqual(positions, [pos])

        self.scene.addItem(obj)
        self.view.show()

        self.qWait()
Ejemplo n.º 19
0
    def test_graphicspathobject(self):
        obj = GraphicsPathObject()
        path = QPainterPath()
        obj.setFlag(GraphicsPathObject.ItemIsMovable)

        path.addEllipse(20, 20, 50, 50)

        obj.setPath(path)
        self.assertEqual(obj.path(), path)

        self.assertTrue(obj.path() is not path,
                        msg="setPath stores the path not a copy")

        brect = obj.boundingRect()
        self.assertTrue(brect.contains(path.boundingRect()))

        with self.assertRaises(TypeError):
            obj.setPath("This is not a path")

        brush = QBrush(QColor("#ffbb11"))
        obj.setBrush(brush)

        self.assertEqual(obj.brush(), brush)

        self.assertTrue(obj.brush() is not brush,
                        "setBrush stores the brush not a copy")

        pen = QPen(QColor("#FFFFFF"), 1.4)
        obj.setPen(pen)

        self.assertEqual(obj.pen(), pen)

        self.assertTrue(obj.pen() is not pen,
                        "setPen stores the pen not a copy")

        brect = obj.boundingRect()
        self.assertGreaterEqual(area(brect), (50 + 1.4 * 2) ** 2)

        self.assertIsInstance(obj.shape(), QPainterPath)

        positions = []
        obj.positionChanged[QPointF].connect(positions.append)

        pos = QPointF(10, 10)
        obj.setPos(pos)

        self.assertEqual(positions, [pos])

        self.scene.addItem(obj)
        self.view.show()

        self.app.exec_()
Ejemplo n.º 20
0
    def setPath(self, path):
        """Set the items `path` (:class:`QPainterPath`).
        """
        if not isinstance(path, QPainterPath):
            raise TypeError("%r, 'QPainterPath' expected" % type(path))

        if self.__path != path:
            self.prepareGeometryChange()
            # Need to store a copy of object so the shape can't be mutated
            # without properly updating the geometry.
            self.__path = QPainterPath(path)
            self.__boundingRect = None
            self.update()
Ejemplo n.º 21
0
    def test_layout(self):
        one_desc, negate_desc, cons_desc = self.widget_desc()
        one_item = NodeItem()
        one_item.setWidgetDescription(one_desc)
        one_item.setPos(0, 150)
        self.scene.add_node_item(one_item)

        cons_item = NodeItem()
        cons_item.setWidgetDescription(cons_desc)
        cons_item.setPos(200, 0)
        self.scene.add_node_item(cons_item)

        negate_item = NodeItem()
        negate_item.setWidgetDescription(negate_desc)
        negate_item.setPos(200, 300)
        self.scene.add_node_item(negate_item)

        link = LinkItem()
        link.setSourceItem(one_item)
        link.setSinkItem(negate_item)
        self.scene.add_link_item(link)

        link = LinkItem()
        link.setSourceItem(one_item)
        link.setSinkItem(cons_item)
        self.scene.add_link_item(link)

        layout = AnchorLayout()
        self.scene.addItem(layout)
        self.scene.set_anchor_layout(layout)

        layout.invalidateNode(one_item)
        layout.activate()

        p1, p2 = one_item.outputAnchorItem.anchorPositions()
        self.assertTrue(p1 > p2)

        self.scene.node_item_position_changed.connect(layout.invalidateNode)

        path = QPainterPath()
        path.addEllipse(125, 0, 50, 300)

        def advance():
            t = time.process_time()
            cons_item.setPos(path.pointAtPercent(t % 1.0))
            negate_item.setPos(path.pointAtPercent((t + 0.5) % 1.0))

        timer = QTimer(negate_item, interval=20)
        timer.start()
        timer.timeout.connect(advance)
        self.app.exec_()
Ejemplo n.º 22
0
    def __init__(self, parent, **kwargs):
        # type: (Optional[QGraphicsItem], Any) -> None
        super().__init__(parent, **kwargs)
        self.__parentNodeItem = None  # type: Optional[NodeItem]
        self.setAcceptHoverEvents(True)
        self.setPen(QPen(Qt.NoPen))
        self.normalBrush = QBrush(QColor("#CDD5D9"))
        self.normalHoverBrush = QBrush(QColor("#9CACB4"))
        self.connectedBrush = self.normalHoverBrush
        self.connectedHoverBrush = QBrush(QColor("#959595"))
        self.setBrush(self.normalBrush)

        self.__animationEnabled = False
        self.__hover = False

        # Does this item have any anchored links.
        self.anchored = False

        if isinstance(parent, NodeItem):
            self.__parentNodeItem = parent
        else:
            self.__parentNodeItem = None

        self.__anchorPath = QPainterPath()
        self.__points = []  # type: List[AnchorPoint]
        self.__pointPositions = []  # type: List[float]

        self.__fullStroke = QPainterPath()
        self.__dottedStroke = QPainterPath()
        self.__shape = None  # type: Optional[QPainterPath]

        self.shadow = QGraphicsDropShadowEffect(
            blurRadius=0,
            color=QColor(SHADOW_COLOR),
            offset=QPointF(0, 0),
        )
        # self.setGraphicsEffect(self.shadow)
        self.shadow.setEnabled(False)

        shadowitem = GraphicsPathObject(self, objectName="shadow-shape-item")
        shadowitem.setPen(Qt.NoPen)
        shadowitem.setBrush(QBrush(QColor(SHADOW_COLOR)))
        shadowitem.setGraphicsEffect(self.shadow)
        shadowitem.setFlag(QGraphicsItem.ItemStacksBehindParent)
        self.__shadow = shadowitem
        self.__blurAnimation = QPropertyAnimation(self.shadow, b"blurRadius",
                                                  self)
        self.__blurAnimation.setDuration(50)
        self.__blurAnimation.finished.connect(self.__on_finished)
Ejemplo n.º 23
0
def ellipse_path(center, a, b, rotation=0):
    if not isinstance(center, QPointF):
        center = QPointF(*center)

    brect = QRectF(-a, -b, 2 * a, 2 * b)

    path = QPainterPath()
    path.addEllipse(brect)

    if rotation != 0:
        transform = QTransform().rotate(rotation)
        path = transform.map(path)

    path.translate(center)
    return path
Ejemplo n.º 24
0
def path_toQtPath(geom):
    p = QPainterPath()
    anchor, points = geom
    if len(points) > 1:
        p.moveTo(*points[0])
        for (x, y) in points[1:]:
            p.lineTo(x, y)
    elif len(points) == 1:
        r = QRectF(0, 0, 1e-0, 1e-9)
        r.moveCenter(*points[0])
        p.addRect(r)
    elif len(points) == 0:
        r = QRectF(0, 0, 1e-16, 1e-16)
        r.moveCenter(QPointF(*anchor))
        p.addRect(r)
    return p
Ejemplo n.º 25
0
    def __init__(self, parent, *args):
        GraphicsPathObject.__init__(self, parent, *args)
        self.setAcceptHoverEvents(True)
        self.setPen(QPen(Qt.NoPen))
        self.normalBrush = QBrush(QColor("#CDD5D9"))
        self.connectedBrush = QBrush(QColor("#9CACB4"))
        self.setBrush(self.normalBrush)

        self.shadow = QGraphicsDropShadowEffect(
            blurRadius=10,
            color=QColor(SHADOW_COLOR),
            offset=QPointF(0, 0)
        )

        self.setGraphicsEffect(self.shadow)
        self.shadow.setEnabled(False)

        # Does this item have any anchored links.
        self.anchored = False

        if isinstance(parent, NodeItem):
            self.__parentNodeItem = parent
        else:
            self.__parentNodeItem = None

        self.__anchorPath = QPainterPath()
        self.__points = []
        self.__pointPositions = []

        self.__fullStroke = None
        self.__dottedStroke = None
        self.__shape = None
Ejemplo n.º 26
0
 def setCurvePath(self, path):
     if path != self.__curvepath:
         self.prepareGeometryChange()
         self.__curvepath = QPainterPath(path)
         self.__curvepath_disabled = None
         self.__shape = None
         self.__update()
Ejemplo n.º 27
0
    def __init__(self, parent=None, anchor=0, **kwargs):
        GraphicsPathObject.__init__(self, parent, **kwargs)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, False)
        self.setAcceptedMouseButtons(Qt.LeftButton)

        self.__constraint = 0
        self.__constraintFunc = None
        self.__anchor = 0
        self.__initialPosition = None
        self.setAnchor(anchor)

        path = QPainterPath()
        path.addEllipse(QRectF(-4, -4, 8, 8))
        self.setPath(path)

        self.setBrush(QBrush(Qt.lightGray, Qt.SolidPattern))
Ejemplo n.º 28
0
    def __init__(self, parent=None, anchor=0, **kwargs):
        GraphicsPathObject.__init__(self, parent, **kwargs)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, False)
        self.setAcceptedMouseButtons(Qt.LeftButton)

        self.__constraint = 0
        self.__constraintFunc = None
        self.__anchor = 0
        self.__initialPosition = None
        self.setAnchor(anchor)

        path = QPainterPath()
        path.addEllipse(QRectF(-4, -4, 8, 8))
        self.setPath(path)

        self.setBrush(QBrush(Qt.lightGray, Qt.SolidPattern))
Ejemplo n.º 29
0
 def anchorPath(self):
     # type: () -> QPainterPath
     """
     Return the anchor path (:class:`QPainterPath`). This is a curve on
     which the anchor points lie.
     """
     return QPainterPath(self.__anchorPath)
Ejemplo n.º 30
0
    def __init__(self, parent):
        # type: (QGraphicsItem) -> None
        super().__init__(parent)
        self.setAcceptedMouseButtons(Qt.NoButton)
        self.setAcceptHoverEvents(True)

        self.__animationEnabled = False
        self.__hover = False
        self.__enabled = True
        self.__selected = False
        self.__shape = None  # type: Optional[QPainterPath]
        self.__curvepath = QPainterPath()
        self.__curvepath_disabled = None  # type: Optional[QPainterPath]
        self.__pen = self.pen()
        self.setPen(QPen(QBrush(QColor("#9CACB4")), 2.0))

        self.shadow = QGraphicsDropShadowEffect(blurRadius=5,
                                                color=QColor(SHADOW_COLOR),
                                                offset=QPointF(0, 0))
        self.setGraphicsEffect(self.shadow)
        self.shadow.setEnabled(False)

        self.__blurAnimation = QPropertyAnimation(self.shadow, b"blurRadius")
        self.__blurAnimation.setDuration(50)
        self.__blurAnimation.finished.connect(self.__on_finished)
Ejemplo n.º 31
0
 def update_properties(self):
     p = self.plot()
     if p is None:
         return
     x_id, y_id = self.axes()
     rect = p.data_rect_for_axes(x_id, y_id)
     path = QPainterPath()
     if self._x_enabled and x_id in p.axes:
         for pos, label, size, _w in p.axes[x_id].ticks():
             path.moveTo(pos, rect.bottom())
             path.lineTo(pos, rect.top())
     if self._y_enabled and y_id in p.axes:
         for pos, label, size, _w in p.axes[y_id].ticks():
             path.moveTo(rect.left(), pos)
             path.lineTo(rect.right(), pos)
     self._path_item.setPath(self.graph_transform().map(path))
Ejemplo n.º 32
0
    def setupGraphics(self):
        """
        Set up the graphics.
        """
        shape_rect = QRectF(-24, -24, 48, 48)

        self.shapeItem = NodeBodyItem(self)
        self.shapeItem.setShapeRect(shape_rect)
        self.shapeItem.setAnimationEnabled(self.__animationEnabled)

        # Rect for widget's 'ears'.
        anchor_rect = QRectF(-31, -31, 62, 62)
        self.inputAnchorItem = SinkAnchorItem(self)
        input_path = QPainterPath()
        start_angle = 180 - self.ANCHOR_SPAN_ANGLE / 2
        input_path.arcMoveTo(anchor_rect, start_angle)
        input_path.arcTo(anchor_rect, start_angle, self.ANCHOR_SPAN_ANGLE)
        self.inputAnchorItem.setAnchorPath(input_path)

        self.outputAnchorItem = SourceAnchorItem(self)
        output_path = QPainterPath()
        start_angle = self.ANCHOR_SPAN_ANGLE / 2
        output_path.arcMoveTo(anchor_rect, start_angle)
        output_path.arcTo(anchor_rect, start_angle, - self.ANCHOR_SPAN_ANGLE)
        self.outputAnchorItem.setAnchorPath(output_path)

        self.inputAnchorItem.hide()
        self.outputAnchorItem.hide()

        # Title caption item
        self.captionTextItem = NameTextItem(self)

        self.captionTextItem.setPlainText("")
        self.captionTextItem.setPos(0, 33)

        def iconItem(standard_pixmap):
            item = GraphicsIconItem(self, icon=standard_icon(standard_pixmap),
                                    iconSize=QSize(16, 16))
            item.hide()
            return item

        self.errorItem = iconItem(QStyle.SP_MessageBoxCritical)
        self.warningItem = iconItem(QStyle.SP_MessageBoxWarning)
        self.infoItem = iconItem(QStyle.SP_MessageBoxInformation)

        self.prepareGeometryChange()
        self.__boundingRect = None
Ejemplo n.º 33
0
    def __init__(self, n_attributes, y_values, color, name=""):
        OWCurve.__init__(self, tooltip=name)
        self._item = QGraphicsPathItem(self)
        self.path = QPainterPath()
        self.fitted = False

        self.n_attributes = n_attributes
        self.n_rows = int(len(y_values) / n_attributes)

        self.set_style(OWCurve.Lines)
        if isinstance(color, tuple):
            self.set_pen(QPen(QColor(*color)))
        else:
            self.set_pen(QPen(QColor(color)))

        x_values = list(range(n_attributes)) * self.n_rows
        self.set_data(x_values, y_values)
    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.setFlag(QGraphicsObject.ItemSendsGeometryChanges)

        self.__path = QPainterPath()
        self.__brush = QBrush(Qt.NoBrush)
        self.__pen = QPen()
        self.__boundingRect = None
Ejemplo n.º 35
0
    def test_anchoritem(self):
        anchoritem = NodeAnchorItem(None)
        self.scene.addItem(anchoritem)

        path = QPainterPath()
        path.addEllipse(0, 0, 100, 100)

        anchoritem.setAnchorPath(path)

        anchor = AnchorPoint()
        anchoritem.addAnchor(anchor)

        ellipse1 = QGraphicsEllipseItem(-3, -3, 6, 6)
        ellipse2 = QGraphicsEllipseItem(-3, -3, 6, 6)
        self.scene.addItem(ellipse1)
        self.scene.addItem(ellipse2)

        anchor.scenePositionChanged.connect(ellipse1.setPos)

        with self.assertRaises(ValueError):
            anchoritem.addAnchor(anchor)

        anchor1 = AnchorPoint()
        anchoritem.addAnchor(anchor1)

        anchor1.scenePositionChanged.connect(ellipse2.setPos)

        self.assertSequenceEqual(anchoritem.anchorPoints(), [anchor, anchor1])

        self.assertSequenceEqual(anchoritem.anchorPositions(), [0.5, 0.5])
        anchoritem.setAnchorPositions([0.5, 0.0])

        self.assertSequenceEqual(anchoritem.anchorPositions(), [0.5, 0.0])

        def advance():
            t = anchoritem.anchorPositions()
            t = [(t + 0.05) % 1.0 for t in t]
            anchoritem.setAnchorPositions(t)

        timer = QTimer(anchoritem, interval=10)
        timer.start()
        timer.timeout.connect(advance)

        self.qWait()
        timer.stop()
Ejemplo n.º 36
0
    def __updateCurve(self):
        self.prepareGeometryChange()
        self.__boundingRect = None
        if self.sourceAnchor and self.sinkAnchor:
            source_pos = self.sourceAnchor.anchorScenePos()
            sink_pos = self.sinkAnchor.anchorScenePos()
            source_pos = self.curveItem.mapFromScene(source_pos)
            sink_pos = self.curveItem.mapFromScene(sink_pos)

            # Adaptive offset for the curve control points to avoid a
            # cusp when the two points have the same y coordinate
            # and are close together
            delta = source_pos - sink_pos
            dist = math.sqrt(delta.x() ** 2 + delta.y() ** 2)
            cp_offset = min(dist / 2.0, 60.0)

            # TODO: make the curve tangent orthogonal to the anchors path.
            path = QPainterPath()
            path.moveTo(source_pos)
            path.cubicTo(source_pos + QPointF(cp_offset, 0),
                         sink_pos - QPointF(cp_offset, 0),
                         sink_pos)

            self.curveItem.setCurvePath(path)
            self.sourceIndicator.setPos(source_pos)
            self.sinkIndicator.setPos(sink_pos)
            self.__updateText()
        else:
            self.setHoverState(False)
            self.curveItem.setPath(QPainterPath())
Ejemplo n.º 37
0
 def redraw_path(self):
     self.path = QPainterPath()
     for segment in self.segment(self.data()):
         if self.fitted:
             self.draw_cubic_path(segment)
         else:
             self.draw_normal_path(segment)
     self._item.setPath(self.graph_transform().map(self.path))
     self._item.setPen(self.pen())
Ejemplo n.º 38
0
    def test_anchoritem(self):
        anchoritem = NodeAnchorItem(None)
        self.scene.addItem(anchoritem)

        path = QPainterPath()
        path.addEllipse(0, 0, 100, 100)

        anchoritem.setAnchorPath(path)

        anchor = AnchorPoint()
        anchoritem.addAnchor(anchor)

        ellipse1 = QGraphicsEllipseItem(-3, -3, 6, 6)
        ellipse2 = QGraphicsEllipseItem(-3, -3, 6, 6)
        self.scene.addItem(ellipse1)
        self.scene.addItem(ellipse2)

        anchor.scenePositionChanged.connect(ellipse1.setPos)

        with self.assertRaises(ValueError):
            anchoritem.addAnchor(anchor)

        anchor1 = AnchorPoint()
        anchoritem.addAnchor(anchor1)

        anchor1.scenePositionChanged.connect(ellipse2.setPos)

        self.assertSequenceEqual(anchoritem.anchorPoints(), [anchor, anchor1])

        self.assertSequenceEqual(anchoritem.anchorPositions(), [0.5, 0.5])
        anchoritem.setAnchorPositions([0.5, 0.0])

        self.assertSequenceEqual(anchoritem.anchorPositions(), [0.5, 0.0])

        def advance():
            t = anchoritem.anchorPositions()
            t = [(t + 0.05) % 1.0 for t in t]
            anchoritem.setAnchorPositions(t)

        timer = QTimer(anchoritem, interval=20)
        timer.start()
        timer.timeout.connect(advance)

        self.app.exec_()
Ejemplo n.º 39
0
 def updateSelectionRect(self, event):
     pos = event.scenePos()
     buttonDownPos = event.buttonDownScenePos(Qt.LeftButton)
     rect = QRectF(pos, buttonDownPos).normalized()
     rect = rect.intersected(self.sceneRect())
     if not self.selectionRect:
         self.selectionRect = QGraphicsRectItem()
         self.selectionRect.setBrush(QColor(10, 10, 10, 20))
         self.selectionRect.setPen(QPen(QColor(200, 200, 200, 200)))
         self.addItem(self.selectionRect)
     self.selectionRect.setRect(rect)
     if event.modifiers() & Qt.ControlModifier or \
                     event.modifiers() & Qt.ShiftModifier:
         path = self.selectionArea()
     else:
         path = QPainterPath()
     path.addRect(rect)
     self.setSelectionArea(path)
     self.selectionRectPointChanged.emit(pos)
Ejemplo n.º 40
0
 def updateSelectionRect(self, event):
     pos = event.scenePos()
     buttonDownPos = event.buttonDownScenePos(Qt.LeftButton)
     rect = QRectF(pos, buttonDownPos).normalized()
     rect = rect.intersected(self.sceneRect())
     if not self.selectionRect:
         self.selectionRect = QGraphicsRectItem()
         self.selectionRect.setBrush(QColor(10, 10, 10, 20))
         self.selectionRect.setPen(QPen(QColor(200, 200, 200, 200)))
         self.addItem(self.selectionRect)
     self.selectionRect.setRect(rect)
     if event.modifiers() & Qt.ControlModifier or \
                     event.modifiers() & Qt.ShiftModifier:
         path = self.selectionArea()
     else:
         path = QPainterPath()
     path.addRect(rect)
     self.setSelectionArea(path)
     self.selectionRectPointChanged.emit(pos)
Ejemplo n.º 41
0
    def paintArc(self, painter, option, widget):
        assert self.source is self.dest
        node = self.source

        def best_angle():
            """...is the one furthest away from all other angles"""
            angles = [
                QLineF(node.pos(), other.pos()).angle() for other in chain((
                    edge.source for edge in node.edges
                    if edge.dest == node and edge.source != node), (
                        edge.dest for edge in node.edges
                        if edge.dest != node and edge.source == node))
            ]
            angles.sort()
            if not angles:  # If this self-constraint is the only edge
                return 225
            deltas = np.array(angles[1:] + [360 + angles[0]]) - angles
            return (angles[deltas.argmax()] + deltas.max() / 2) % 360

        angle = best_angle()
        inf = QPointF(-1e20, -1e20)  # Doesn't work with real -np.inf!
        line0 = QLineF(node.pos(), inf)
        line1 = QLineF(node.pos(), inf)
        line2 = QLineF(node.pos(), inf)
        line0.setAngle(angle)
        line1.setAngle(angle - 13)
        line2.setAngle(angle + 13)

        p0 = shape_line_intersection(node.shape(), node.pos(), line0)
        p1 = shape_line_intersection(node.shape(), node.pos(), line1)
        p2 = shape_line_intersection(node.shape(), node.pos(), line2)
        path = QPainterPath()
        path.moveTo(p1)
        line = QLineF(node.pos(), p0)
        line.setLength(3 * line.length())
        pt = line.p2()
        path.quadTo(pt, p2)

        line = QLineF(node.pos(), pt)
        self.setLine(line)  # This invalidates DeviceCoordinateCache
        painter.drawPath(path)

        # Draw arrow head
        line = QLineF(pt, p2)
        self.arrowHead.clear()
        for point in self._arrowhead_points(line):
            self.arrowHead.append(point)
        painter.setBrush(self.pen().color())
        painter.drawPolygon(self.arrowHead)

        # Update label position
        self.label.setPos(path.pointAtPercent(.5))
        if 90 < angle < 270:  # Right-align the label
            pos = self.label.pos()
            x, y = pos.x(), pos.y()
            self.label.setPos(x - self.label.boundingRect().width(), y)
        self.squares.placeBelow(self.label)
Ejemplo n.º 42
0
    def mouseReleaseEvent(self, event):
        QGraphicsScene.mouseReleaseEvent(self, event)

        if event.button() == Qt.LeftButton:
            modifiers = event.modifiers()
            path = QPainterPath()

            # the mouse was moved
            if self.selectionRect:
                path.addRect(self.selectionRect.rect())
                self.removeItem(self.selectionRect)
                self.selectionRect = None

            # the mouse was only clicked - create a selection area of 1x1 size
            else:
                rect = QRectF(event.buttonDownScenePos(Qt.LeftButton), QSizeF(1., 1.)).intersected(self.sceneRect())
                path.addRect(rect)

            self.setSelectionArea(path)
            self.selectionChanged.emit(set(self.selectedItems()), modifiers)
Ejemplo n.º 43
0
class ParallelCoordinatesCurve(OWCurve):
    def __init__(self, n_attributes, y_values, color, name=""):
        OWCurve.__init__(self, tooltip=name)
        self._item = QGraphicsPathItem(self)
        self.path = QPainterPath()
        self.fitted = False

        self.n_attributes = n_attributes
        self.n_rows = int(len(y_values) / n_attributes)

        self.set_style(OWCurve.Lines)
        if isinstance(color, tuple):
            self.set_pen(QPen(QColor(*color)))
        else:
            self.set_pen(QPen(QColor(color)))

        x_values = list(range(n_attributes)) * self.n_rows
        self.set_data(x_values, y_values)

    def update_properties(self):
        self.redraw_path()

    def redraw_path(self):
        self.path = QPainterPath()
        for segment in self.segment(self.data()):
            if self.fitted:
                self.draw_cubic_path(segment)
            else:
                self.draw_normal_path(segment)
        self._item.setPath(self.graph_transform().map(self.path))
        self._item.setPen(self.pen())

    def segment(self, data):
        for i in range(self.n_rows):
            yield data[i * self.n_attributes:(i + 1) * self.n_attributes]

    def draw_cubic_path(self, segment):
        for (x1, y1), (x2, y2) in zip(segment, segment[1:]):
            self.path.moveTo(x1, y1)
            self.path.cubicTo(QPointF(x1 + 0.5, y1), QPointF(x2 - 0.5, y2),
                              QPointF(x2, y2))

    def draw_normal_path(self, segment):
        if not segment:
            return

        x, y = segment[0]
        self.path.moveTo(x, y)
        for x, y in segment[1:]:
            self.path.lineTo(x, y)
Ejemplo n.º 44
0
class ParallelCoordinatesCurve(OWCurve):
    def __init__(self, n_attributes, y_values, color, name=""):
        OWCurve.__init__(self, tooltip=name)
        self._item = QGraphicsPathItem(self)
        self.path = QPainterPath()
        self.fitted = False

        self.n_attributes = n_attributes
        self.n_rows = int(len(y_values) / n_attributes)

        self.set_style(OWCurve.Lines)
        if isinstance(color, tuple):
            self.set_pen(QPen(QColor(*color)))
        else:
            self.set_pen(QPen(QColor(color)))

        x_values = list(range(n_attributes)) * self.n_rows
        self.set_data(x_values, y_values)

    def update_properties(self):
        self.redraw_path()

    def redraw_path(self):
        self.path = QPainterPath()
        for segment in self.segment(self.data()):
            if self.fitted:
                self.draw_cubic_path(segment)
            else:
                self.draw_normal_path(segment)
        self._item.setPath(self.graph_transform().map(self.path))
        self._item.setPen(self.pen())

    def segment(self, data):
        for i in range(self.n_rows):
            yield data[i * self.n_attributes:(i + 1) * self.n_attributes]

    def draw_cubic_path(self, segment):
        for (x1, y1), (x2, y2) in zip(segment, segment[1:]):
            self.path.moveTo(x1, y1)
            self.path.cubicTo(QPointF(x1 + 0.5, y1),
                              QPointF(x2 - 0.5, y2), QPointF(x2, y2))

    def draw_normal_path(self, segment):
        if not segment:
            return

        x, y = segment[0]
        self.path.moveTo(x, y)
        for x, y in segment[1:]:
            self.path.lineTo(x, y)
Ejemplo n.º 45
0
    def __init__(self,
                 parent=None,
                 anchor=Free,
                 constraint=Qt.Orientation(0),
                 **kwargs):
        # type: (Optional[QGraphicsItem], Anchor, Qt.Orientation, Any) -> None
        super().__init__(parent, **kwargs)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, False)
        self.setAcceptedMouseButtons(Qt.LeftButton)

        self.__constraint = constraint  # type: Qt.Orientation

        self.__constraintFunc = None  # type: Optional[ConstraintFunc]
        self.__anchor = ControlPoint.Free
        self.__initialPosition = None  # type: Optional[QPointF]
        self.setAnchor(anchor)

        path = QPainterPath()
        path.addEllipse(QRectF(-4, -4, 8, 8))
        self.setPath(path)

        self.setBrush(QBrush(Qt.lightGray, Qt.SolidPattern))
    def setPath(self, path):
        """Set the items `path` (:class:`QPainterPath`).
        """
        if not isinstance(path, QPainterPath):
            raise TypeError("%r, 'QPainterPath' expected" % type(path))

        if self.__path != path:
            self.prepareGeometryChange()
            # Need to store a copy of object so the shape can't be mutated
            # without properly updating the geometry.
            self.__path = QPainterPath(path)
            self.__boundingRect = None
            self.update()
Ejemplo n.º 47
0
    def _create_violin(self, data: np.ndarray) -> Tuple[QPainterPath, float]:
        if self.__kde is None:
            x, p, max_density = np.zeros(1), np.zeros(1), 0
        else:
            x = np.linspace(data.min() - self.__bandwidth * 2,
                            data.max() + self.__bandwidth * 2, 1000)
            p = np.exp(self.__kde.score_samples(x.reshape(-1, 1)))
            max_density = p.max()
            p = scale_density(self.__scale, p, len(data), max_density)

        if self.__orientation == Qt.Vertical:
            pts = [QPointF(pi, xi) for xi, pi in zip(x, p)]
            pts += [QPointF(-pi, xi) for xi, pi in reversed(list(zip(x, p)))]
        else:
            pts = [QPointF(xi, pi) for xi, pi in zip(x, p)]
            pts += [QPointF(xi, -pi) for xi, pi in reversed(list(zip(x, p)))]
        pts += pts[:1]

        polygon = QPolygonF(pts)
        path = QPainterPath()
        path.addPolygon(polygon)
        return path, max_density
Ejemplo n.º 48
0
 def _create_path(item, path):
     ppath = QPainterPath()
     if item.node.is_leaf:
         ppath.addRect(path.boundingRect().adjusted(-8, -4, 0, 4))
     else:
         ppath.addPolygon(path)
         ppath = path_outline(ppath, width=-8)
     return ppath
Ejemplo n.º 49
0
def arrow_path_plain(line, width):
    """
    Return an :class:`QPainterPath` of a plain looking arrow.
    """
    path = QPainterPath()
    p1, p2 = line.p1(), line.p2()

    if p1 == p2:
        return path

    baseline = QLineF(line)
    # Require some minimum length.
    baseline.setLength(max(line.length() - width * 3, width * 3))
    path.moveTo(baseline.p1())
    path.lineTo(baseline.p2())

    stroker = QPainterPathStroker()
    stroker.setWidth(width)
    path = stroker.createStroke(path)

    arrow_head_len = width * 4
    arrow_head_angle = 50
    line_angle = line.angle() - 180

    angle_1 = line_angle - arrow_head_angle / 2.0
    angle_2 = line_angle + arrow_head_angle / 2.0

    points = [
        p2,
        p2 + QLineF.fromPolar(arrow_head_len, angle_1).p2(),
        p2 + QLineF.fromPolar(arrow_head_len, angle_2).p2(),
        p2,
    ]

    poly = QPolygonF(points)
    path_head = QPainterPath()
    path_head.addPolygon(poly)
    path = path.united(path_head)
    return path
Ejemplo n.º 50
0
    def paintArc(self, painter, option, widget):
        assert self.source is self.dest
        node = self.source
        def best_angle():
            """...is the one furthest away from all other angles"""
            angles = [QLineF(node.pos(), other.pos()).angle()
                      for other in chain((edge.source for edge in node.edges
                                          if edge.dest == node and edge.source != node),
                                         (edge.dest for edge in node.edges
                                          if edge.dest != node and edge.source == node))]
            angles.sort()
            if not angles:  # If this self-constraint is the only edge
                return 225
            deltas = np.array(angles[1:] + [360 + angles[0]]) - angles
            return (angles[deltas.argmax()] + deltas.max()/2) % 360

        angle = best_angle()
        inf = QPointF(-1e20, -1e20)  # Doesn't work with real -np.inf!
        line0 = QLineF(node.pos(), inf)
        line1 = QLineF(node.pos(), inf)
        line2 = QLineF(node.pos(), inf)
        line0.setAngle(angle)
        line1.setAngle(angle - 13)
        line2.setAngle(angle + 13)

        p0 = shape_line_intersection(node.shape(), node.pos(), line0)
        p1 = shape_line_intersection(node.shape(), node.pos(), line1)
        p2 = shape_line_intersection(node.shape(), node.pos(), line2)
        path = QPainterPath()
        path.moveTo(p1)
        line = QLineF(node.pos(), p0)
        line.setLength(3*line.length())
        pt = line.p2()
        path.quadTo(pt, p2)

        line = QLineF(node.pos(), pt)
        self.setLine(line)  # This invalidates DeviceCoordinateCache
        painter.drawPath(path)

        # Draw arrow head
        line = QLineF(pt, p2)
        self.arrowHead.clear()
        for point in self._arrowhead_points(line):
            self.arrowHead.append(point)
        painter.setBrush(self.pen().color())
        painter.drawPolygon(self.arrowHead)

        # Update label position
        self.label.setPos(path.pointAtPercent(.5))
        if 90 < angle < 270:  # Right-align the label
            pos = self.label.pos()
            x, y = pos.x(), pos.y()
            self.label.setPos(x - self.label.boundingRect().width(), y)
        self.squares.placeBelow(self.label)
Ejemplo n.º 51
0
    def __init__(self, n_attributes, y_values, color, name=""):
        OWCurve.__init__(self, tooltip=name)
        self._item = QGraphicsPathItem(self)
        self.path = QPainterPath()
        self.fitted = False

        self.n_attributes = n_attributes
        self.n_rows = int(len(y_values) / n_attributes)

        self.set_style(OWCurve.Lines)
        if isinstance(color, tuple):
            self.set_pen(QPen(QColor(*color)))
        else:
            self.set_pen(QPen(QColor(color)))

        x_values = list(range(n_attributes)) * self.n_rows
        self.set_data(x_values, y_values)
Ejemplo n.º 52
0
def path_from_graphics(graphics):
    """
    Return a constructed `QPainterPath` for a KEGG pathway graphics element.
    """
    path = QPainterPath()
    x, y, w, h = [int(graphics.get(c, 0)) for c in ["x", "y", "width", "height"]]
    type = graphics.get("type", "rectangle")
    if type == "rectangle":
        path.addRect(QRectF(x - w / 2, y - h / 2, w, h))
    elif type == "roundrectangle":
        path.addRoundedRect(QRectF(x - w / 2, y - h / 2, w, h), 10, 10)
    elif type == "circle":
        path.addEllipse(QRectF(x - w / 2, y - h / 2, w, h))
    else:
        ValueError("Unknown graphics type %r." % type)
    return path
Ejemplo n.º 53
0
 def shape(self):
     path = QPainterPath()
     path.addRect(self.boundingRect())
     return path
Ejemplo n.º 54
0
def _define_symbols():
    """
    Add symbol ? to ScatterPlotItemSymbols,
    reflect the triangle to point upwards
    """
    symbols = pyqtgraph.graphicsItems.ScatterPlotItem.Symbols
    path = QPainterPath()
    path.addEllipse(QRectF(-0.35, -0.35, 0.7, 0.7))
    path.moveTo(-0.5, 0.5)
    path.lineTo(0.5, -0.5)
    path.moveTo(-0.5, -0.5)
    path.lineTo(0.5, 0.5)
    symbols["?"] = path

    tr = QTransform()
    tr.rotate(180)
    symbols['t'] = tr.map(symbols['t'])
class GraphicsPathObject(QGraphicsObject):
    """A QGraphicsObject subclass implementing an interface similar to
    QGraphicsPathItem, and also adding a positionChanged() signal

    """

    positionChanged = Signal([], ["QPointF"])

    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.setFlag(QGraphicsObject.ItemSendsGeometryChanges)

        self.__path = QPainterPath()
        self.__brush = QBrush(Qt.NoBrush)
        self.__pen = QPen()
        self.__boundingRect = None

    def setPath(self, path):
        """Set the items `path` (:class:`QPainterPath`).
        """
        if not isinstance(path, QPainterPath):
            raise TypeError("%r, 'QPainterPath' expected" % type(path))

        if self.__path != path:
            self.prepareGeometryChange()
            # Need to store a copy of object so the shape can't be mutated
            # without properly updating the geometry.
            self.__path = QPainterPath(path)
            self.__boundingRect = None
            self.update()

    def path(self):
        """Return the items path.
        """
        return QPainterPath(self.__path)

    def setBrush(self, brush):
        """Set the items `brush` (:class:`QBrush`)
        """
        if not isinstance(brush, QBrush):
            brush = QBrush(brush)

        if self.__brush != brush:
            self.__brush = QBrush(brush)
            self.update()

    def brush(self):
        """Return the items brush.
        """
        return QBrush(self.__brush)

    def setPen(self, pen):
        """Set the items outline `pen` (:class:`QPen`).
        """
        if not isinstance(pen, QPen):
            pen = QPen(pen)

        if self.__pen != pen:
            self.prepareGeometryChange()
            self.__pen = QPen(pen)
            self.__boundingRect = None
            self.update()

    def pen(self):
        """Return the items pen.
        """
        return QPen(self.__pen)

    def paint(self, painter, option, widget=None):
        if self.__path.isEmpty():
            return

        painter.save()
        painter.setPen(self.__pen)
        painter.setBrush(self.__brush)
        painter.drawPath(self.__path)
        painter.restore()

    def boundingRect(self):
        if self.__boundingRect is None:
            br = self.__path.controlPointRect()
            pen_w = self.__pen.widthF()
            self.__boundingRect = br.adjusted(-pen_w, -pen_w, pen_w, pen_w)

        return self.__boundingRect

    def shape(self):
        return shapeFromPath(self.__path, self.__pen)

    def itemChange(self, change, value):
        if change == QGraphicsObject.ItemPositionHasChanged:
            self.positionChanged.emit()
            self.positionChanged[QPointF].emit(value)

        return super().itemChange(change, value)
Ejemplo n.º 56
0
def qpainterpath_simple_split(path, t):
    """
    Split a QPainterPath defined simple curve.

    The path must be either empty or composed of a single LineToElement or
    CurveToElement.

    Parameters
    ----------
    path : QPainterPath

    t : float
        Point where to split specified as a percentage along the path

    Returns
    -------
    splitpath: Tuple[QPainterPath, QPainterPath]
        A pair of QPainterPaths
    """
    assert path.elementCount() > 0
    el0 = path.elementAt(0)
    assert el0.type == QPainterPath.MoveToElement
    if path.elementCount() == 1:
        p1 = QPainterPath()
        p1.moveTo(el0.x, el0.y)
        return p1, QPainterPath(p1)

    el1 = path.elementAt(1)
    if el1.type == QPainterPath.LineToElement:
        pointat = path.pointAtPercent(t)
        l1 = QLineF(el0.x, el0.y, pointat.x(), pointat.y())
        l2 = QLineF(pointat.x(), pointat.y(), el1.x, el1.y)
        p1 = QPainterPath()
        p2 = QPainterPath()
        p1.addLine(l1)
        p2.addLine(l2)
        return p1, p2
    elif el1.type == QPainterPath.CurveToElement:
        c0, c1, c2, c3 = el0, el1, path.elementAt(2), path.elementAt(3)
        assert all(el.type == QPainterPath.CurveToDataElement
                   for el in [c2, c3])
        cp = [QPointF(el.x, el.y) for el in [c0, c1, c2, c3]]
        first, second = bezier_subdivide(cp, t)
        p1, p2 = QPainterPath(), QPainterPath()
        p1.moveTo(first[0])
        p1.cubicTo(*first[1:])
        p2.moveTo(second[0])
        p2.cubicTo(*second[1:])
        return p1, p2
    else:
        assert False
Ejemplo n.º 57
0
 def __updateFrame(self):
     rect = self.geometry()
     rect.moveTo(0, 0)
     path = QPainterPath()
     path.addRect(rect)
     self.__framePathItem.setPath(path)
Ejemplo n.º 58
0
def arrow_path_concave(line, width):
    """
    Return a :class:`QPainterPath` of a pretty looking arrow.
    """
    path = QPainterPath()
    p1, p2 = line.p1(), line.p2()

    if p1 == p2:
        return path

    baseline = QLineF(line)
    # Require some minimum length.
    baseline.setLength(max(line.length() - width * 3, width * 3))

    start, end = baseline.p1(), baseline.p2()
    mid = (start + end) / 2.0
    normal = QLineF.fromPolar(1.0, baseline.angle() + 90).p2()

    path.moveTo(start)
    path.lineTo(start + (normal * width / 4.0))

    path.quadTo(mid + (normal * width / 4.0),
                end + (normal * width / 1.5))

    path.lineTo(end - (normal * width / 1.5))
    path.quadTo(mid - (normal * width / 4.0),
                start - (normal * width / 4.0))
    path.closeSubpath()

    arrow_head_len = width * 4
    arrow_head_angle = 50
    line_angle = line.angle() - 180

    angle_1 = line_angle - arrow_head_angle / 2.0
    angle_2 = line_angle + arrow_head_angle / 2.0

    points = [p2,
              p2 + QLineF.fromPolar(arrow_head_len, angle_1).p2(),
              baseline.p2(),
              p2 + QLineF.fromPolar(arrow_head_len, angle_2).p2(),
              p2]

    poly = QPolygonF(points)
    path_head = QPainterPath()
    path_head.addPolygon(poly)
    path = path.united(path_head)
    return path
Ejemplo n.º 59
0
class NodeAnchorItem(GraphicsPathObject):
    """
    The left/right widget input/output anchors.
    """

    def __init__(self, parent, *args):
        GraphicsPathObject.__init__(self, parent, *args)
        self.setAcceptHoverEvents(True)
        self.setPen(QPen(Qt.NoPen))
        self.normalBrush = QBrush(QColor("#CDD5D9"))
        self.connectedBrush = QBrush(QColor("#9CACB4"))
        self.setBrush(self.normalBrush)

        self.shadow = QGraphicsDropShadowEffect(
            blurRadius=10,
            color=QColor(SHADOW_COLOR),
            offset=QPointF(0, 0)
        )

        self.setGraphicsEffect(self.shadow)
        self.shadow.setEnabled(False)

        # Does this item have any anchored links.
        self.anchored = False

        if isinstance(parent, NodeItem):
            self.__parentNodeItem = parent
        else:
            self.__parentNodeItem = None

        self.__anchorPath = QPainterPath()
        self.__points = []
        self.__pointPositions = []

        self.__fullStroke = None
        self.__dottedStroke = None
        self.__shape = None

    def parentNodeItem(self):
        """
        Return a parent :class:`NodeItem` or ``None`` if this anchor's
        parent is not a :class:`NodeItem` instance.

        """
        return self.__parentNodeItem

    def setAnchorPath(self, path):
        """
        Set the anchor's curve path as a :class:`QPainterPath`.
        """
        self.prepareGeometryChange()
        self.__boundingRect = None

        self.__anchorPath = path
        # Create a stroke of the path.
        stroke_path = QPainterPathStroker()
        stroke_path.setCapStyle(Qt.RoundCap)

        # Shape is wider (bigger mouse hit area - should be settable)
        stroke_path.setWidth(12)
        self.__shape = stroke_path.createStroke(path)

        # The full stroke
        stroke_path.setWidth(3)
        self.__fullStroke = stroke_path.createStroke(path)

        # The dotted stroke (when not connected to anything)
        stroke_path.setDashPattern(Qt.DotLine)
        self.__dottedStroke = stroke_path.createStroke(path)

        if self.anchored:
            self.setPath(self.__fullStroke)
            self.setBrush(self.connectedBrush)
        else:
            self.setPath(self.__dottedStroke)
            self.setBrush(self.normalBrush)

    def anchorPath(self):
        """
        Return the anchor path (:class:`QPainterPath`). This is a curve on
        which the anchor points lie.

        """
        return self.__anchorPath

    def setAnchored(self, anchored):
        """
        Set the items anchored state. When ``False`` the item draws it self
        with a dotted stroke.

        """
        self.anchored = anchored
        if anchored:
            self.setPath(self.__fullStroke)
            self.setBrush(self.connectedBrush)
        else:
            self.setPath(self.__dottedStroke)
            self.setBrush(self.normalBrush)

    def setConnectionHint(self, hint=None):
        """
        Set the connection hint. This can be used to indicate if
        a connection can be made or not.

        """
        raise NotImplementedError

    def count(self):
        """
        Return the number of anchor points.
        """
        return len(self.__points)

    def addAnchor(self, anchor, position=0.5):
        """
        Add a new :class:`AnchorPoint` to this item and return it's index.

        The `position` specifies where along the `anchorPath` is the new
        point inserted.

        """
        return self.insertAnchor(self.count(), anchor, position)

    def insertAnchor(self, index, anchor, position=0.5):
        """
        Insert a new :class:`AnchorPoint` at `index`.

        See also
        --------
        NodeAnchorItem.addAnchor

        """
        if anchor in self.__points:
            raise ValueError("%s already added." % anchor)

        self.__points.insert(index, anchor)
        self.__pointPositions.insert(index, position)

        anchor.setParentItem(self)
        anchor.setPos(self.__anchorPath.pointAtPercent(position))
        anchor.destroyed.connect(self.__onAnchorDestroyed)

        self.__updatePositions()

        self.setAnchored(bool(self.__points))

        return index

    def removeAnchor(self, anchor):
        """
        Remove and delete the anchor point.
        """
        anchor = self.takeAnchor(anchor)

        anchor.hide()
        anchor.setParentItem(None)
        anchor.deleteLater()

    def takeAnchor(self, anchor):
        """
        Remove the anchor but don't delete it.
        """
        index = self.__points.index(anchor)

        del self.__points[index]
        del self.__pointPositions[index]

        anchor.destroyed.disconnect(self.__onAnchorDestroyed)

        self.__updatePositions()

        self.setAnchored(bool(self.__points))

        return anchor

    def __onAnchorDestroyed(self, anchor):
        try:
            index = self.__points.index(anchor)
        except ValueError:
            return

        del self.__points[index]
        del self.__pointPositions[index]

    def anchorPoints(self):
        """
        Return a list of anchor points.
        """
        return list(self.__points)

    def anchorPoint(self, index):
        """
        Return the anchor point at `index`.
        """
        return self.__points[index]

    def setAnchorPositions(self, positions):
        """
        Set the anchor positions in percentages (0..1) along the path curve.
        """
        if self.__pointPositions != positions:
            self.__pointPositions = list(positions)

            self.__updatePositions()

    def anchorPositions(self):
        """
        Return the positions of anchor points as a list of floats where
        each float is between 0 and 1 and specifies where along the anchor
        path does the point lie (0 is at start 1 is at the end).

        """
        return list(self.__pointPositions)

    def shape(self):
        if self.__shape is not None:
            return self.__shape
        else:
            return GraphicsPathObject.shape(self)

    def hoverEnterEvent(self, event):
        self.shadow.setEnabled(True)
        return GraphicsPathObject.hoverEnterEvent(self, event)

    def hoverLeaveEvent(self, event):
        self.shadow.setEnabled(False)
        return GraphicsPathObject.hoverLeaveEvent(self, event)

    def __updatePositions(self):
        """Update anchor points positions.
        """
        for point, t in zip(self.__points, self.__pointPositions):
            pos = self.__anchorPath.pointAtPercent(t)
            point.setPos(pos)