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)
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 __updateFrame(self): # type: () -> None rect = self.geometry() rect.moveTo(0, 0) path = QPainterPath() path.addRect(rect) self.__framePathItem.setPath(path)
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())
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)
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)
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
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
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)
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 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
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)
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_()
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()
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()
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_()
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 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_()
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)
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
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
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 setCurvePath(self, path): if path != self.__curvepath: self.prepareGeometryChange() self.__curvepath = QPainterPath(path) self.__curvepath_disabled = None self.__shape = None self.__update()
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))
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)
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)
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))
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
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
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()
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_()
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)
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)
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)
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)
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 _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
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
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
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)
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
def shape(self): path = QPainterPath() path.addRect(self.boundingRect()) return path
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)
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
def __updateFrame(self): rect = self.geometry() rect.moveTo(0, 0) path = QPainterPath() path.addRect(rect) self.__framePathItem.setPath(path)
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
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)