def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) # INIT THE LINE p1 = QPointF(((kwargs['w'] - 54) / 2), kwargs['h'] / 2) p2 = QPointF(((kwargs['w'] - 54) / 2) + 54 - 2, kwargs['h'] / 2) line = QLineF(p1, p2) # CLACULATE HEAD COORDS angle = line.angle() p1 = QPointF(line.p2().x() + 2, line.p2().y()) p2 = p1 - QPointF(sin(angle + M_PI / 3.0) * 8, cos(angle + M_PI / 3.0) * 8) p3 = p1 - QPointF(sin(angle + M_PI - M_PI / 3.0) * 8, cos(angle + M_PI - M_PI / 3.0) * 8) # INITIALIZE HEAD head = QPolygonF([p1, p2, p3]) # DRAW EDGE LINE painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(line) # DRAW EDGE HEAD painter.setPen(QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.setBrush(QColor(0, 0, 0)) painter.drawPolygon(head) return pixmap
def plot_vert_line_graph(self, qp, x_line, color, c, arrow_up=False, arrow_down=False): if x_line < self._start_date or x_line > self._end_date: return x_line -= self._start_date qp.save() qp.setPen(color) qp.setBrush(color) qp.setRenderHint(QPainter.Antialiasing) arrowSize = 2.0 x, y = self.origGraph(c) line = QLineF(x + self.convX(x_line), y + 10, x + self.convX(x_line), y + 50) qp.drawLine(line) if arrow_up: arrowP1 = line.p1() + QPointF(arrowSize, arrowSize * 3) arrowP2 = line.p1() + QPointF(-arrowSize, arrowSize * 3) qp.drawLine(line.p1(), arrowP1) qp.drawLine(line.p1(), arrowP2) if arrow_down: arrowP1 = line.p2() + QPointF(arrowSize, - arrowSize * 3) arrowP2 = line.p2() + QPointF(-arrowSize, - arrowSize * 3) qp.drawLine(line.p2(), arrowP1) qp.drawLine(line.p2(), arrowP2) qp.restore()
def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) # INITIALIZE EDGE LINE pp1 = QPointF(((kwargs['w'] - 52) / 2), kwargs['h'] / 2) pp2 = QPointF(((kwargs['w'] - 52) / 2) + 52 - 2, kwargs['h'] / 2) line = QLineF(pp1, pp2) # CALCULATE HEAD COORDINATES angle = radians(line.angle()) p1 = QPointF(line.p2().x() + 2, line.p2().y()) p2 = p1 - QPointF(sin(angle + M_PI / 3.0) * 8, cos(angle + M_PI / 3.0) * 8) p3 = p1 - QPointF(sin(angle + M_PI - M_PI / 3.0) * 8, cos(angle + M_PI - M_PI / 3.0) * 8) # INITIALIZE EDGE HEAD head = QPolygonF([p1, p2, p3]) # DRAW THE POLYGON painter.setRenderHint(QPainter.Antialiasing) painter.setPen(QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(line) # DRAW HEAD painter.setPen(QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.setBrush(QColor(0, 0, 0)) painter.drawPolygon(head) # DRAW THE TEXT ON TOP OF THE EDGE space = 2 if Platform.identify() is Platform.Darwin else 0 painter.setFont(Font('Arial', 9, Font.Light)) painter.drawText(pp1.x() + space, (kwargs['h'] / 2) - 4, 'instanceOf') return pixmap
class GuideLine(Guide): def __init__(self, line_or_point, follows=None): super(GuideLine, self).__init__(follows) if isinstance(line_or_point, QLineF): self.line = line_or_point elif follows is not None: self.line = QLineF(self.prevGuide.endPos(), line_or_point) else: self.line = QLineF(QPointF(0, 0), line_or_point) def length(self): return self.line.length() def startPos(self): return QPointF(self.line.p1().x() * self.scaleX, self.line.p1().y() * self.scaleY) def endPos(self): return QPointF(self.line.p2().x() * self.scaleX, self.line.p2().y() * self.scaleY) def guide(self, item, moveSpeed): frame = item.guideFrame - self.startLength endX = (self.line.p1().x() + (frame * self.line.dx() / self.length())) * self.scaleX endY = (self.line.p1().y() + (frame * self.line.dy() / self.length())) * self.scaleY pos = QPointF(endX, endY) self.move(item, pos, moveSpeed)
def paint(self, painter, option, widget): color = Qt.red if self.isSelected() else Qt.black painter.setPen(QPen(color, 2, Qt.SolidLine)) path = QPainterPath(self.startPoint) # start path # iterating over all points of line for i in range(len(self.points) - 1): x1, y1 = self.points[i].x(), self.points[i].y() x2, y2 = self.points[i + 1].x(), self.points[i + 1].y() for point in sorted(self.commonPathsCenters, key=lambda x: x.x() + x.y(), reverse=x2 < x1 or y2 < y1): x, y = point.x(), point.y() if x == x1 == x2: # vertical if min(y1, y2) + 8 <= y < max(y1, y2) - 8: if y2 > y1: path.lineTo(point - QPointF(0, 8)) path.arcTo(QRectF(x - 8, y - 8, 16, 16), 90, -180) path.moveTo(point + QPointF(0, 8)) else: path.lineTo(point + QPointF(0, 8)) path.arcTo(QRectF(x - 8, y - 8, 16, 16), -90, 180) path.moveTo(point - QPointF(0, 8)) elif y == y1 == y2: # horizontal if min(x1, x2) + 8 <= x < max(x1, x2) - 8: if x2 > x1: path.lineTo(point - QPointF(8, 0)) path.arcTo(QRectF(x - 8, y - 8, 16, 16), 180, 180) path.moveTo(point + QPointF(8, 0)) else: path.lineTo(point + QPointF(8, 0)) path.arcTo(QRectF(x - 8, y - 8, 16, 16), 0, -180) path.lineTo(point - QPointF(8, 0)) path.lineTo(self.points[i + 1]) # draw arrow in last segment if i == len(self.points) - 2: arrow_size = 20.0 line = QLineF(self.points[i], self.points[i + 1]) if line.length() < 20: continue angle = math.acos(line.dx() / line.length()) if line.dy() >= 0: angle = (math.pi * 2) - angle arrow_p1 = line.p2() - QPointF(math.sin(angle + math.pi / 2.5) * arrow_size, math.cos(angle + math.pi / 2.5) * arrow_size) arrow_p2 = line.p2() - QPointF(math.sin(angle + math.pi - math.pi / 2.5) * arrow_size, math.cos(angle + math.pi - math.pi / 2.5) * arrow_size) arrowHead = QPolygonF() arrowHead.append(line.p2()) arrowHead.append(arrow_p1) arrowHead.append(arrow_p2) painter.save() painter.setBrush(Qt.black) painter.drawPolygon(arrowHead) painter.restore() painter.drawPath(path) # draw final path
def plot_vert_line_graph(self, qp, x_line, color, c, arrow_up=False, arrow_down=False): if x_line < self._start_date or x_line > self._end_date: return x_line -= self._start_date qp.save() qp.setPen(color) qp.setBrush(color) qp.setRenderHint(QPainter.Antialiasing) arrowSize = 2.0 x, y = self.origGraph(c) line = QLineF(x + self.convX(x_line), y + 10, x + self.convX(x_line), y + 50) qp.drawLine(line) if arrow_up: arrowP1 = line.p1() + QPointF(arrowSize, arrowSize * 3) arrowP2 = line.p1() + QPointF(-arrowSize, arrowSize * 3) qp.drawLine(line.p1(), arrowP1) qp.drawLine(line.p1(), arrowP2) if arrow_down: arrowP1 = line.p2() + QPointF(arrowSize, -arrowSize * 3) arrowP2 = line.p2() + QPointF(-arrowSize, -arrowSize * 3) qp.drawLine(line.p2(), arrowP1) qp.drawLine(line.p2(), arrowP2) qp.restore()
def calc_angle_from_l1_to_l2(a0: QLineF, a1: QLineF, degree: bool): """ Return the angle vector line a0 needs rotated from current pos to a1, Rotate direction: clockwise Return value unit: in radians or degrees(degree = True) """ angle_a0 = calc_angle_from_p1_to_p2(a0.p1(), a0.p2(), False) angle_a1 = calc_angle_from_p1_to_p2(a1.p1(), a1.p2(), False) angle = angle_a1 - angle_a0 if degree is True: return degrees(angle) return angle
def updatePosition(self, force=False): #if self.toNode() is None and self.fromNode() is None: #if not force: #self.updateArrowHead() #self.updateTextPosition() #return if self.dest() and self.source(): self._updatingPos = True #self.saveTextPosition() self.prepareGeometryChange() if len(self._points) == 2: a = self.source().closestBoundaryPosToItem(self.dest()) b = self.dest().closestBoundaryPosToItem(self.source()) a = self.mapFromItem(self.source(), a) b = self.mapFromItem(self.dest(), b) else: a = self.source().closestBoundaryPosToItem(self._points[1]) b = self.dest().closestBoundaryPosToItem(self._points[-2]) a = self.mapFromItem(self.source(), a) b = self.mapFromItem(self.dest(), b) line = QLineF(a, b) length = line.length() # BUGFIX if abs(length) == 0: line = QLineF(a, QPointF(a.x() + 1, a.y())) length = 1 dx = line.dx() dx /= length if dx > 1: dx = 1 elif dx < -1: dx = -1 angle = acos(dx) size = self.arrowHeadSize() head = self._arrowHead if line.dy() >= 0: angle = pi * 2 - angle p2 = line.p2() u = p2 + QPointF( sin(angle + pi + pi / 3) * size, cos(angle + pi + pi / 3) * size) v = p2 + QPointF( sin(angle - pi / 3) * size, cos(angle - pi / 3) * size) if self.dest() != self.toPoint(): self.toPoint().setPos(line.p2()) if self.source() != self.fromPoint(): self.fromPoint().setPos(line.p1()) self.updateArrowHead() #self.updateTextPosition() self._updatingPos = False else: self.updateArrowHead() self.updateTextPosition()
def paint(self, painter, option, widget): if not self.source or not self.dest: return # Draw the line itself. line = QLineF(self.sourcePoint, self.destPoint) if line.length() == 0.0: return painter.setPen(QPen(Qt.black, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(line) # Draw the arrows if there's enough room. angle = math.acos(line.dx() / line.length()) if line.dy() >= 0: angle = Edge.TwoPi - angle sourceArrowP1 = self.sourcePoint + QPointF(math.sin(angle + Edge.Pi / 3) * self.arrowSize, math.cos(angle + Edge.Pi / 3) * self.arrowSize) sourceArrowP2 = self.sourcePoint + QPointF(math.sin(angle + Edge.Pi - Edge.Pi / 3) * self.arrowSize, math.cos(angle + Edge.Pi - Edge.Pi / 3) * self.arrowSize); destArrowP1 = self.destPoint + QPointF(math.sin(angle - Edge.Pi / 3) * self.arrowSize, math.cos(angle - Edge.Pi / 3) * self.arrowSize) destArrowP2 = self.destPoint + QPointF(math.sin(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize, math.cos(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize) painter.setBrush(Qt.black) painter.drawPolygon(QPolygonF([line.p1(), sourceArrowP1, sourceArrowP2])) painter.drawPolygon(QPolygonF([line.p2(), destArrowP1, destArrowP2]))
def paint(self, painter, option, widget): assert self.fromSquare is not None assert self.toSquare is not None line = QLineF(self.sourcePoint, self.destPoint) assert(line.length() != 0.0) # Draw the arrows if there's enough room. angle = math.acos(line.dx() / line.length()) if line.dy() >= 0: angle = (math.pi*2.0) - angle destArrowP1 = self.destPoint + QPointF( math.sin(angle - math.pi / 3) * self.arrowSize, math.cos(angle - math.pi / 3) * self.arrowSize ) destArrowP2 = self.destPoint + QPointF( math.sin(angle - math.pi + math.pi / 3) * self.arrowSize, math.cos(angle - math.pi + math.pi / 3) * self.arrowSize ) painter.setPen(self.pen) painter.setBrush(self.brush) # arrowhead1 = QPolygonF([line.p1(), sourceArrowP1, sourceArrowP2]) arrowhead2 = QPolygonF([line.p2(), destArrowP1, destArrowP2]) painter.drawPolygon(arrowhead2) painter.setPen(self.pen) painter.drawLine(line)
def adjust(self): if not self.source or not self.dest: return line = QLineF( self.mapFromItem(self.source, self.source.getDrawSize() * -0.5, self.source.getDrawSize() * -0.5), self.mapFromItem(self.dest, self.dest.getDrawSize() * -0.5, self.dest.getDrawSize() * -0.5)) length = line.length() self.prepareGeometryChange() if length > self.source.getDrawSize(): edgeOffset = QPointF( (line.dx() * self.source.getDrawSize() / 2) / length, (line.dy() * self.source.getDrawSize() / 2) / length) self.sourcePoint = line.p1() + edgeOffset self.destPoint = line.p2() - edgeOffset else: self.sourcePoint = line.p1() self.destPoint = line.p1()
def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget=None): if not self.source or not self.dest: return line = QLineF(self.source_point, self.dest_point) if line.length() == 0.0: return color = self.get_color() width = SupremeSettings()['edge_width'] if option.state & QStyle.State_Sunken: color, width = Qt.red, 2 painter.setPen(QPen(Qt.black, 2, Qt.DashLine)) painter.drawPolygon(self.getSelectionPolygon()) elif option.state & QStyle.State_MouseOver: color, width = Qt.blue, 2 painter.setPen( QPen(color, width, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(line) angle = math.acos(line.dx() / line.length()) if line.dy() >= 0: angle = math.pi * 2 - angle if self.arrow_size > 0: dest_arrow_p1 = self.dest_point + QPointF( math.sin(angle - math.pi / 3) * self.arrow_size, math.cos(angle - math.pi / 3) * self.arrow_size) dest_arrow_p2 = self.dest_point + QPointF( math.sin(angle - math.pi + math.pi / 3) * self.arrow_size, math.cos(angle - math.pi + math.pi / 3) * self.arrow_size) painter.setBrush(color) painter.drawPolygon( QPolygonF([line.p2(), dest_arrow_p1, dest_arrow_p2])) self.draw_animated_objects()
def __init__(self, position, angle, arrow_size, value, unit): super(StressRepresentation, self).__init__() arrow_line = QLineF() arrow_line.setP1(position) arrow_line.setLength(arrow_size) arrow_line.setAngle(angle) arrow = QGraphicsLineItem() arrow.setLine(arrow_line) self.addToGroup(arrow) arrow_head1_line = QLineF() arrow_head1_line.setP1(arrow_line.p1()) arrow_head1_line.setLength(arrow_size / 4) arrow_head1_line.setAngle(arrow_line.angle() + 45) arrow_head1 = QGraphicsLineItem() arrow_head1.setLine(arrow_head1_line) self.addToGroup(arrow_head1) arrow_head2_line = QLineF() arrow_head2_line.setP1(arrow_line.p1()) arrow_head2_line.setLength(arrow_size / 4) arrow_head2_line.setAngle(arrow_line.angle() - 45) arrow_head2 = QGraphicsLineItem() arrow_head2.setLine(arrow_head2_line) self.addToGroup(arrow_head2) text = QGraphicsTextItem() text.setPlainText(f'{str(value)}{unit}') text.setPos(arrow_line.p2()) self.addToGroup(text) self._set_color()
def setLine(self, line): """ Set the arrow base line (a `QLineF` in object coordinates). """ if self.__line != line: self.__line = line # local item coordinate system geom = self.geometry().translated(-self.pos()) if geom.isNull() and not line.isNull(): geom = QRectF(0, 0, 1, 1) arrow_shape = arrow_path_concave(line, self.lineWidth()) arrow_rect = arrow_shape.boundingRect() if not (geom.contains(arrow_rect)): geom = geom.united(arrow_rect) if self.__autoAdjustGeometry: # Shrink the geometry if required. geom = geom.intersected(arrow_rect) # topLeft can move changing the local coordinates. diff = geom.topLeft() line = QLineF(line.p1() - diff, line.p2() - diff) self.__arrowItem.setLine(line) self.__line = line # parent item coordinate system geom.translate(self.pos()) self.setGeometry(geom)
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
def paint(self, painter, index): widget = self.parent() if index != widget.activeIndex(): return scale = widget.inverseScale() # metrics if self._rulerObject is not None: line, a = self._rulerObject origin = line.p1() cursor = line.p2() size = 8 * scale halfSize = 4 * scale color = QColor(255, 85, 127, 170) # line painter.save() painter.setPen(color) drawing.drawLine(painter, origin.x(), origin.y(), cursor.x(), cursor.y(), scale) # ellipses ellipses = [ (origin.x(), origin.y()), (cursor.x(), cursor.y()), ] path = QPainterPath() path.setFillRule(Qt.WindingFill) for x, y in itertools.chain(self._rulerPts.values(), ellipses): x -= halfSize y -= halfSize path.addEllipse(x, y, size, size) painter.fillPath(path, color) painter.restore() # text line = QLineF(line) xAlign = yAlign = "center" ellipses.pop(0) rp = self._rulerPts # XXX: sort shouldn't be performed in paintEvent for pt in itertools.chain((rp[k] for k in sorted(rp)), ellipses): p = QPointF(*pt) line.setP2(p) if line.length(): d = str(round(line.length(), 1)) pos = (line.p1() + line.p2()) / 2 drawing.drawTextAtPoint(painter, d, pos.x(), pos.y(), scale, xAlign, yAlign) line.setP1(p) xAlign, yAlign = "left", "top" dx = cursor.x() - origin.x() px = size if dx < 0: xAlign = "right" px = -px drawing.drawTextAtPoint(painter, a, cursor.x() + px, cursor.y() + size, scale, xAlign, yAlign)
def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) # INITIALIZE EDGE LINE p1 = QPointF(((kwargs['w'] - 54) / 2), kwargs['h'] / 2) p2 = QPointF(((kwargs['w'] - 54) / 2) + 54 - 2, kwargs['h'] / 2) line = QLineF(p1, p2) # CALCULATE HEAD COORDS angle = radians(line.angle()) p1 = QPointF(line.p2().x() + 2, line.p2().y()) p2 = p1 - QPointF( sin(angle + M_PI / 4.0) * 8, cos(angle + M_PI / 4.0) * 8) p3 = p2 - QPointF( sin(angle + 3.0 / 4.0 * M_PI) * 8, cos(angle + 3.0 / 4.0 * M_PI) * 8) p4 = p3 - QPointF( sin(angle - 3.0 / 4.0 * M_PI) * 8, cos(angle - 3.0 / 4.0 * M_PI) * 8) # INITIALIZE HEAD head = QPolygonF([p1, p2, p3, p4]) # INITIALIZE EDGE PEN linePen = QPen(QColor(0, 0, 0), 1.1, Qt.CustomDashLine, Qt.RoundCap, Qt.RoundJoin) linePen.setDashPattern([3, 3]) # DRAW EDGE POLYGON painter.setRenderHint(QPainter.Antialiasing) painter.setPen(linePen) painter.drawLine(line) # DRAW EDGE HEAD painter.setPen( QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.setBrush(QColor(252, 252, 252)) painter.drawPolygon(head) return pixmap
def moveUIPoint(contour, point, delta): if point.segmentType is None: # point is an offCurve. Get its sibling onCurve and the other # offCurve. onCurve, otherPoint = _getOffCurveSiblingPoints(contour, point) # if the onCurve is selected, the offCurve will move along with it if onCurve.selected: return point.move(delta) if not onCurve.smooth: contour.dirty = True return # if the onCurve is smooth, we need to either... if otherPoint.segmentType is None and not otherPoint.selected: # keep the other offCurve inline line = QLineF(point.x, point.y, onCurve.x, onCurve.y) otherLine = QLineF(onCurve.x, onCurve.y, otherPoint.x, otherPoint.y) line.setLength(line.length() + otherLine.length()) otherPoint.x = line.x2() otherPoint.y = line.y2() else: # keep point in tangency with onCurve -> otherPoint segment, # ie. do an orthogonal projection line = QLineF(otherPoint.x, otherPoint.y, onCurve.x, onCurve.y) n = line.normalVector() n.translate(QPointF(point.x, point.y) - n.p1()) targetPoint = QPointF() n.intersect(line, targetPoint) # check that targetPoint is beyond its neighbor onCurve # we do this by calculating position of the offCurve and second # onCurve relative to the first onCurve. If there is no symmetry # in at least one of the axis, then we need to clamp onCurvePoint = line.p2() onDistance = line.p1() - onCurvePoint newDistance = targetPoint - onCurvePoint if (onDistance.x() >= 0) != (newDistance.x() <= 0) or \ (onDistance.y() >= 0) != (newDistance.y() <= 0): targetPoint = onCurvePoint # ok, now set pos point.x, point.y = targetPoint.x(), targetPoint.y() else: # point is an onCurve. Move its offCurves along with it. index = contour.index(point) point.move(delta) for d in (-1, 1): # edge-case: contour open, trailing offCurve and moving first # onCurve in contour if contour.open and index == 0 and d == -1: continue pt = contour.getPoint(index + d) if pt.segmentType is None: pt.move(delta) contour.dirty = True
def moveUIPoint(contour, point, delta): if point.segmentType is None: # point is an offCurve. Get its sibling onCurve and the other # offCurve. onCurve, otherPoint = _getOffCurveSiblingPoints(contour, point) # if the onCurve is selected, the offCurve will move along with it if onCurve.selected: return point.move(delta) if not onCurve.smooth: contour.dirty = True return # if the onCurve is smooth, we need to either... if otherPoint.segmentType is None and not otherPoint.selected: # keep the other offCurve inline line = QLineF(point.x, point.y, onCurve.x, onCurve.y) otherLine = QLineF( onCurve.x, onCurve.y, otherPoint.x, otherPoint.y) line.setLength(line.length() + otherLine.length()) otherPoint.x = line.x2() otherPoint.y = line.y2() else: # keep point in tangency with onCurve -> otherPoint segment, # ie. do an orthogonal projection line = QLineF(otherPoint.x, otherPoint.y, onCurve.x, onCurve.y) n = line.normalVector() n.translate(QPointF(point.x, point.y) - n.p1()) targetPoint = QPointF() n.intersect(line, targetPoint) # check that targetPoint is beyond its neighbor onCurve # we do this by calculating position of the offCurve and second # onCurve relative to the first onCurve. If there is no symmetry # in at least one of the axis, then we need to clamp onCurvePoint = line.p2() onDistance = line.p1() - onCurvePoint newDistance = targetPoint - onCurvePoint if (onDistance.x() >= 0) != (newDistance.x() <= 0) or \ (onDistance.y() >= 0) != (newDistance.y() <= 0): targetPoint = onCurvePoint # ok, now set pos point.x, point.y = targetPoint.x(), targetPoint.y() else: # point is an onCurve. Move its offCurves along with it. index = contour.index(point) point.move(delta) for d in (-1, 1): # edge-case: contour open, trailing offCurve and moving first # onCurve in contour if contour.open and index == 0 and d == -1: continue pt = contour.getPoint(index + d) if pt.segmentType is None: pt.move(delta) contour.dirty = True
def adjust(self): if not self.source or not self.dest: return line = QLineF(self.mapFromItem(self.source, 0, 0), self.mapFromItem(self.dest, 0, 0)) length = line.length() self.prepareGeometryChange() self.sourcePoint = line.p1() self.destPoint = line.p2()
def adjust(self): if not self._source or not self._dest: return line = QLineF(self.mapFromItem(self._source, 0, 0), self.mapFromItem(self._dest, 0, 0)) length = line.length() self.prepareGeometryChange() min_len = self._source.radius() + self._dest.radius( ) + self._source.pen().widthF() + self._dest.pen().widthF() if length > min_len: offset = QPointF( (line.dx() * (self._source.radius() + self._source.pen().widthF() + 1)) / length, (line.dy() * (self._source.radius() + self._source.pen().widthF() + 1)) / length) self.source_point = line.p1() + offset offset = QPointF( (line.dx() * (self._dest.radius() + self._dest.pen().widthF() + 1)) / length, (line.dy() * (self._dest.radius() + self._dest.pen().widthF() + 1)) / length) self.dest_point = line.p2() - offset else: self.source_point = self.dest_point = line.p1() path = QPainterPath() if self.sourceNode() == self.destNode(): # Draw self-loops self.__is_self_loop = True radius = self._source.radius() path.moveTo( self.source_point.x() - radius - 2 * self._source.pen().widthF(), self.source_point.y()) path.cubicTo( QPointF(self.source_point.x() - 4 * radius, self.source_point.y()), QPointF(self.source_point.x(), self.source_point.y() - 4 * radius), QPointF( self.dest_point.x(), self.dest_point.y() - radius - 2 * self._source.pen().widthF())) else: self.__is_self_loop = False path.moveTo(self.source_point) path.lineTo(self.dest_point) self.setPath(path)
def image(cls, **kwargs): """ Returns an image suitable for the palette. :rtype: QPixmap """ # INITIALIZATION pixmap = QPixmap(kwargs['w'], kwargs['h']) pixmap.fill(Qt.transparent) painter = QPainter(pixmap) # INIT THE LINE p1 = QPointF(((kwargs['w'] - 54) / 2), kwargs['h'] / 2) p2 = QPointF(((kwargs['w'] - 54) / 2) + 54 - 2, kwargs['h'] / 2) line = QLineF(p1, p2) # CLACULATE HEAD COORDS angle = line.angle() p1 = QPointF(line.p2().x() + 2, line.p2().y()) p2 = p1 - QPointF( sin(angle + M_PI / 3.0) * 8, cos(angle + M_PI / 3.0) * 8) p3 = p1 - QPointF( sin(angle + M_PI - M_PI / 3.0) * 8, cos(angle + M_PI - M_PI / 3.0) * 8) # INITIALIZE HEAD head = QPolygonF([p1, p2, p3]) # DRAW EDGE LINE painter.setRenderHint(QPainter.Antialiasing) painter.setPen( QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(line) # DRAW EDGE HEAD painter.setPen( QPen(QColor(0, 0, 0), 1.1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.setBrush(QColor(0, 0, 0)) painter.drawPolygon(head) return pixmap
def adjust(self): if not self.source or not self.dest: return line = QLineF(self.mapFromItem(self.source, 0, 0), self.mapFromItem(self.dest, 0, 0)) length = line.length() self.prepareGeometryChange() if length > 20: edge_offset = QPointF( (line.dx() * self.source.size.width() / 2) / length, (line.dy() * self.source.size.height() / 2) / length) self.source_point = line.p1() + edge_offset self.dest_point = line.p2() - edge_offset else: self.source_point = self.dest_point = line.p1()
def getSelectionPolygon(self): line = QLineF(self.source_point, self.dest_point) angle = line.angle() * np.pi / 180 dx = self.selection_offset * np.sin(angle) dy = self.selection_offset * np.cos(angle) offset1 = QPointF(dx, dy) offset2 = QPointF(-dx, -dy) points = [ line.p1() + offset1, line.p1() + offset2, line.p2() + offset2, line.p2() + offset1 ] polygon = QPolygonF(points) return polygon
def adjust(self): if not self.source or not self.dest: return line = QLineF(self.mapFromItem(self.source, 0, 0), self.mapFromItem(self.dest, 0, 0)) length = line.length() self.prepareGeometryChange() if length > 20.0: edgeOffset = QPointF((line.dx() * 10) / length, (line.dy() * 10) / length) self.sourcePoint = line.p1() + edgeOffset self.destPoint = line.p2() - edgeOffset else: self.sourcePoint = line.p1() self.destPoint = line.p1()
def paint(self, painter, option, widget=None): """ Public method to paint the item in local coordinates. @param painter reference to the painter object (QPainter) @param option style options (QStyleOptionGraphicsItem) @param widget optional reference to the widget painted on (QWidget) """ if (option.state & QStyle.State_Selected) == \ QStyle.State(QStyle.State_Selected): width = 2 else: width = 1 # draw the line first line = QLineF(self._origin, self._end) painter.setPen( QPen(Qt.black, width, Qt.SolidLine, Qt.FlatCap, Qt.MiterJoin)) painter.drawLine(line) # draw the arrow head arrowAngle = self._type * ArrowheadAngleFactor slope = math.atan2(line.dy(), line.dx()) # Calculate left arrow point arrowSlope = slope + arrowAngle a1 = QPointF(self._end.x() - self._halfLength * math.cos(arrowSlope), self._end.y() - self._halfLength * math.sin(arrowSlope)) # Calculate right arrow point arrowSlope = slope - arrowAngle a2 = QPointF(self._end.x() - self._halfLength * math.cos(arrowSlope), self._end.y() - self._halfLength * math.sin(arrowSlope)) if self._filled: painter.setBrush(Qt.black) else: painter.setBrush(Qt.white) polygon = QPolygonF() polygon.append(line.p2()) polygon.append(a1) polygon.append(a2) painter.drawPolygon(polygon)
def adjust(self): """ Draw the arc line """ if not self.source or not self.destination: return line = QLineF( self.mapFromItem( self.source, self.source.boundingRect().width() - (self.source.boundingRect().width() / 2.0), self.source.boundingRect().height() / 2.0), self.mapFromItem(self.destination, self.destination.boundingRect().width() / 2.0, self.destination.boundingRect().height() / 2.0)) self.prepareGeometryChange() self.source_point = line.p1() self.destination_point = line.p2() # mouse over on line only self.setLine(line)
def paint(self, painter, option, widget): if not self.source or not self.dest: return # Draw the line itself. line = QLineF(self.sourcePoint, self.destPoint) if line.length() == 0.0: return painter.setPen( QPen(Qt.black, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(line) # Draw the arrows if there's enough room. angle = math.acos(line.dx() / line.length()) if line.dy() >= 0: angle = Edge.TwoPi - angle destArrowP1 = self.destPoint + QPointF( math.sin(angle - Edge.Pi / 3) * self.arrowSize, math.cos(angle - Edge.Pi / 3) * self.arrowSize) destArrowP2 = self.destPoint + QPointF( math.sin(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize, math.cos(angle - Edge.Pi + Edge.Pi / 3) * self.arrowSize) conditionPoint = self.sourcePoint + QPointF( math.sin(angle + Edge.Pi / 3) * self.arrowSize * 2, math.cos(angle + Edge.Pi / 3) * self.arrowSize * 2) textToPrint = self.condition for path in self.source.edges(): if path.destNode( ).name == self.dest.name and path.condition != self.condition: textToPrint += ", %c" % (path.condition) if path.condition > self.condition: return painter.setBrush(Qt.black) painter.drawPolygon(QPolygonF([line.p2(), destArrowP1, destArrowP2])) painter.drawText(conditionPoint, textToPrint)
def paint(self, painter, options, widget=None): painter.setPen(self.pen) if self.head is not None: painter.drawLine(self.head) if self.tail is not None: painter.drawLine(self.tail) painter.drawLine(self.line()) if self.isSelected(): painter.setPen(self.dashed_pen) painter.drawPolygon(self.selection_polygon) # draw the arrow tip if self.tail is not None: tail_line = self.tail else: tail_line = self.line() intersection_point = QPointF(0, 0) for line in self.to_port.lines(): line = QLineF(self.mapFromScene(line.p1()), self.mapFromScene(line.p2())) intersection_type = tail_line.intersect(line, intersection_point) if intersection_type == QLineF.BoundedIntersection: break angle = math.acos(tail_line.dx() / tail_line.length()) if tail_line.dy() >= 0: angle = (math.pi * 2) - angle arrow_p1 = intersection_point - QPointF( math.sin(angle + math.pi / 3) * self.arrow_size, math.cos(angle + math.pi / 3) * self.arrow_size) arrow_p2 = intersection_point - QPointF( math.sin(angle + math.pi - math.pi / 3) * self.arrow_size, math.cos(angle + math.pi - math.pi / 3) * self.arrow_size) self.arrow_head.clear() for p in [intersection_point, arrow_p1, arrow_p2]: self.arrow_head.append(p) path = QPainterPath() path.addPolygon(self.arrow_head) painter.fillPath(path, self.brush)
def adjust(self): if not self._handle1 or not self._handle2: return line = QLineF(self.mapFromItem(self._handle1, 0, 0), self.mapFromItem(self._handle2, 0, 0)) length = line.length() self.prepareGeometryChange() if length > self.width() * 0.9: offset = QPointF((line.dx() * self.width() / 2) / length, (line.dy() * self.width() / 2) / length) p1 = line.p1() + offset p2 = line.p2() - offset else: p1 = p2 = line.p1() path = QPainterPath() path.moveTo(p1) path.lineTo(p2) self.setPath(path)
def paint(self, painter): line = QLineF(self.sourcePoint, self.destPoint) if line.length() == 0.0: return painter.setPen( QPen(Qt.darkGray, 2, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(line) angle = acos(line.dx() / line.length()) if line.dy() >= 0: angle = (2 * pi) - angle destArrowP1 = self.destPoint + QPointF( sin(angle - pi / 3) * self.arrowSize, cos(angle - pi / 3) * self.arrowSize) destArrowP2 = self.destPoint + QPointF( sin(angle - pi + pi / 3) * self.arrowSize, cos(angle - pi + pi / 3) * self.arrowSize) painter.setBrush(Qt.darkGray) painter.drawPolygon(QPolygonF([line.p2(), destArrowP1, destArrowP2]))
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 adjust(self): """ Draw the arc line """ if not self.source or not self.destination: return line = QLineF( self.mapFromItem( self.source, self.source.boundingRect().width() - (self.source.boundingRect().width() / 2.0), self.source.boundingRect().height() / 2.0 ), self.mapFromItem( self.destination, self.destination.boundingRect().width() / 2.0, self.destination.boundingRect().height() / 2.0 ) ) self.prepareGeometryChange() self.source_point = line.p1() self.destination_point = line.p2() # mouse over on line only self.setLine(line)
def adjustGeometry(self): """ Adjust the widget geometry to exactly fit the arrow inside while preserving the arrow path scene geometry. """ # local system coordinate geom = self.geometry().translated(-self.pos()) line = self.__line arrow_rect = self.__arrowItem.shape().boundingRect() if geom.isNull() and not line.isNull(): geom = QRectF(0, 0, 1, 1) if not (geom.contains(arrow_rect)): geom = geom.united(arrow_rect) geom = geom.intersected(arrow_rect) diff = geom.topLeft() line = QLineF(line.p1() - diff, line.p2() - diff) geom.translate(self.pos()) self.setGeometry(geom) self.setLine(line)
def showWedge(self, angle, color, extended=False, rev_gradient=False, outline_only=False): """Summary Args: angle (TYPE): Description color (TYPE): Description extended (bool, optional): Description rev_gradient (bool, optional): Description outline_only (bool, optional): Description """ # Hack to keep wedge in front # self.setRotation(self.pre_xover_item_group.rotation()) self._last_params = (angle, color, extended, rev_gradient, outline_only) radius = self._radius span = self.pre_xover_item_group.partCrossoverSpanAngle() / 2 radius_adjusted = radius + (_WEDGE_RECT_GAIN / 2) tip = QPointF(radius_adjusted, radius_adjusted) EXT = 1.35 if extended else 1.0 # print("wtf", tip, pos) base_p2 = QPointF(1, 1) line0 = QLineF(tip, QPointF(base_p2)) line1 = QLineF(tip, QPointF(base_p2)) line2 = QLineF(tip, QPointF(base_p2)) quad_scale = 1 + (.22 * (span - 5) / 55) # lo+(hi-lo)*(val-min)/(max-min) line0.setLength(radius_adjusted * EXT * quad_scale) # for quadTo control point line1.setLength(radius_adjusted * EXT) line2.setLength(radius_adjusted * EXT) line0.setAngle(angle) line1.setAngle(angle - span) line2.setAngle(angle + span) path = QPainterPath() if outline_only: self.setPen(getPenObj(color, 0.5, alpha=128, capstyle=Qt.RoundCap)) path.moveTo(line1.p2()) path.quadTo(line0.p2(), line2.p2()) else: gradient = QRadialGradient(tip, radius_adjusted * EXT) color1 = getColorObj(color, alpha=80) color2 = getColorObj(color, alpha=0) if rev_gradient: color1, color2 = color2, color1 if extended: gradient.setColorAt(0, color1) gradient.setColorAt(radius_adjusted / (radius_adjusted * EXT), color1) gradient.setColorAt( radius_adjusted / (radius_adjusted * EXT) + 0.01, color2) gradient.setColorAt(1, color2) else: gradient.setColorAt(0, getColorObj(color, alpha=50)) brush = QBrush(gradient) self.setBrush(brush) path.moveTo(line1.p1()) path.lineTo(line1.p2()) path.quadTo(line0.p2(), line2.p2()) path.lineTo(line2.p1()) self.setPath(path) self.show()
def _drawGuidelines(painter, glyph, scale, rect, guidelines, drawLines=True, drawText=True, drawSelection=True, color=None): if not (drawLines or drawText): return xMin, yMin, width, height = rect xMax = xMin + width yMax = yMin + height fontSize = painter.font().pointSize() for line in guidelines: color_ = color if color_ is None: if line.color: color_ = colorToQColor(line.color) else: color_ = defaultColor("glyphGuideline") painter.save() painter.setPen(color) line1 = None if None not in (line.x, line.y): if line.angle is not None: # make an infinite line that intersects *(line.x, line.y)* # 1. make horizontal line from *(line.x, line.y)* of length # *diagonal* diagonal = math.sqrt(width**2 + height**2) line1 = QLineF(line.x, line.y, line.x + diagonal, line.y) # 2. set the angle # defcon guidelines are clockwise line1.setAngle(line.angle) # 3. reverse the line and set length to 2 * *diagonal* line1.setPoints(line1.p2(), line1.p1()) line1.setLength(2 * diagonal) else: line1 = QLineF(xMin, line.y, xMax, line.y) textX = 0 textY = 0 if drawLines: if line1 is not None: # line drawLine( painter, line1.x1(), line1.y1(), line1.x2(), line1.y2()) # point x, y = line.x, line.y smoothWidth = 8 * scale smoothHalf = smoothWidth / 2.0 painter.save() pointPath = QPainterPath() x -= smoothHalf y -= smoothHalf pointPath.addEllipse(x, y, smoothWidth, smoothWidth) pen = QPen(color_) pen.setWidthF(1 * scale) painter.setPen(pen) if drawSelection and line.selected: painter.fillPath(pointPath, color_) painter.drawPath(pointPath) painter.restore() else: if line.y is not None: drawLine(painter, xMin, line.y, xMax, line.y) elif line.x is not None: drawLine(painter, line.x, yMin, line.x, yMax) if drawText and line.name: if line1 is not None: textX = line.x textY = line.y - 6 * scale xAlign = "center" else: if line.y is not None: textX = glyph.width + 6 * scale textY = line.y - (fontSize / 3.5) * scale elif line.x is not None: textX = line.x + 6 * scale textY = 0 xAlign = "left" drawTextAtPoint( painter, line.name, textX, textY, scale, xAlign=xAlign) painter.restore()
def drawToolButtonMenuIndicator(self, option, painter, widget=None): arrow_rect = self.proxy().subControlRect(QStyle.CC_ToolButton, option, QStyle.SC_ToolButtonMenu, widget) text_color = option.palette.color( QPalette.WindowText if option.state & QStyle.State_AutoRaise else QPalette.ButtonText ) button_color = option.palette.color(QPalette.Button) background_color = self.background_color(button_color, 0.5) painter.save() # draw separating vertical line if option.state & (QStyle.State_On | QStyle.State_Sunken): top_offset, bottom_offset = 4, 3 else: top_offset, bottom_offset = 2, 2 if option.direction == Qt.LeftToRight: separator_line = QLineF( arrow_rect.x() - 3, arrow_rect.top() + top_offset, arrow_rect.x() - 3, arrow_rect.bottom() - bottom_offset, ) else: separator_line = QLineF( arrow_rect.right() + 3, arrow_rect.top() + top_offset, arrow_rect.right() + 3, arrow_rect.bottom() - bottom_offset, ) light_gradient = QLinearGradient(separator_line.p1(), separator_line.p2()) light_gradient.setColorAt( 0.0, ColorScheme.shade(self.background_top_color(button_color), ColorScheme.LightShade, 0.0) ) light_gradient.setColorAt( 1.0, ColorScheme.shade(self.background_bottom_color(button_color), ColorScheme.MidlightShade, 0.5) ) separator_color = ColorScheme.shade(self.background_bottom_color(button_color), ColorScheme.MidShade, 0.0) painter.setRenderHint(QPainter.Antialiasing, False) painter.setPen(QPen(light_gradient, 1)) painter.drawLine(separator_line.translated(-1, 0)) painter.drawLine(separator_line.translated(+1, 0)) painter.setPen(QPen(separator_color, 1)) painter.drawLine(separator_line) # draw arrow arrow = QPolygonF([QPointF(-3, -1.5), QPointF(0.5, 2.5), QPointF(4, -1.5)]) if option.direction == Qt.LeftToRight: arrow.translate(-2, 1) else: arrow.translate(+2, 1) pen_thickness = 1.6 painter.setRenderHint(QPainter.Antialiasing, True) painter.translate(arrow_rect.center()) painter.translate(0, +1) painter.setPen( QPen(self.calc_light_color(background_color), pen_thickness, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) ) painter.drawPolyline(arrow) painter.translate(0, -1) painter.setPen( QPen(self.deco_color(background_color, text_color), pen_thickness, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin) ) painter.drawPolyline(arrow) painter.restore()
def showWedge(self, angle, color, extended=False, rev_gradient=False, outline_only=False): """Summary Args: angle (TYPE): Description color (TYPE): Description extended (bool, optional): Description rev_gradient (bool, optional): Description outline_only (bool, optional): Description """ # Hack to keep wedge in front # self.setRotation(self.pre_xover_item_group.rotation()) self._last_params = (angle, color, extended, rev_gradient, outline_only) radius = self._radius span = self.pre_xover_item_group.partCrossoverSpanAngle() / 2 radius_adjusted = radius + (_WEDGE_RECT_GAIN / 2) tip = QPointF(radius_adjusted, radius_adjusted) EXT = 1.35 if extended else 1.0 # print("wtf", tip, pos) base_p2 = QPointF(1, 1) line0 = QLineF(tip, QPointF(base_p2)) line1 = QLineF(tip, QPointF(base_p2)) line2 = QLineF(tip, QPointF(base_p2)) quad_scale = 1 + (.22*(span - 5) / 55) # lo+(hi-lo)*(val-min)/(max-min) line0.setLength(radius_adjusted * EXT*quad_scale) # for quadTo control point line1.setLength(radius_adjusted * EXT) line2.setLength(radius_adjusted * EXT) line0.setAngle(angle) line1.setAngle(angle - span) line2.setAngle(angle + span) path = QPainterPath() if outline_only: self.setPen(getPenObj(color, 0.5, alpha=128, capstyle=Qt.RoundCap)) path.moveTo(line1.p2()) path.quadTo(line0.p2(), line2.p2()) else: gradient = QRadialGradient(tip, radius_adjusted * EXT) color1 = getColorObj(color, alpha=80) color2 = getColorObj(color, alpha=0) if rev_gradient: color1, color2 = color2, color1 if extended: gradient.setColorAt(0, color1) gradient.setColorAt(radius_adjusted / (radius_adjusted * EXT), color1) gradient.setColorAt(radius_adjusted / (radius_adjusted * EXT) + 0.01, color2) gradient.setColorAt(1, color2) else: gradient.setColorAt(0, getColorObj(color, alpha=50)) brush = QBrush(gradient) self.setBrush(brush) path.moveTo(line1.p1()) path.lineTo(line1.p2()) path.quadTo(line0.p2(), line2.p2()) path.lineTo(line2.p1()) self.setPath(path) self.show()