def updateArrowHead(self): line = self.line() if not self.isBezier(): if self._reversed: u = line.p1() - line.p2() else: u = line.p2() - line.p1() else: if self._reversed: u = self._points[0].pos() - self._points[1].pos() else: u = self._points[-1].pos() - self._points[-2].pos() mag_u = mag2D(u) if mag_u == 0.0: self._arrowHead = QPainterPath() return u /= mag_u v = QPointF(-u.y(), u.x()) # perp vector path = QPainterPath() if self._reversed: tip = line.p1() else: tip = line.p2() size = self.arrowHeadSize() p = tip - (u + v) * size q = tip + (v - u) * size r = tip - u * size path.moveTo(p) path.quadTo((p + tip + r) / 3, tip) path.quadTo((q + tip + r) / 3, q) self._arrowHead = path
def update(self, eventPos, fase, point=None): # If user is setting the final point if fase == 1: path = QPainterPath() path.moveTo(self.initialPoint.position) path.lineTo(eventPos) self.setPath(path) if point: self.finalPoint = point # If user is setting ctrlPoint1 elif fase == 2: path = QPainterPath() path.moveTo(self.initialPoint.position) path.quadTo(eventPos, self.finalPoint.position) self.setPath(path) if point: self.ctrlPoint1 = point # If user is finishing the path (setting ctrlPoint2) elif fase == 3: path = QPainterPath() path.moveTo(self.initialPoint.position) path.cubicTo(self.ctrlPoint1.position, eventPos, self.finalPoint.position) self.setPath(path) if point: self.ctrlPoint2 = point
def _pointWithinThreshold(x, y, curve, eps): """ See whether *(x, y)* is within *eps* of *curve*. """ path = QPainterPath() path.addEllipse(x - eps, y - eps, 2 * eps, 2 * eps) curvePath = QPainterPath() if curve[-1].segmentType == "curve": p1, p2, p3, p4 = curve curvePath.moveTo(p1.x, p1.y) curvePath.cubicTo(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y) curvePath.cubicTo(p3.x, p3.y, p2.x, p2.y, p1.x, p1.y) else: first = curve[0] curvePath.moveTo(first.x, first.y) # PACK for fontTools pts = [] for pt in curve: pts.append((pt.x, pt.y)) # draw for pt1, pt2 in decomposeQuadraticSegment(pts[1:]): curvePath.quadTo(*pt1+pt2) for pt1, pt2 in decomposeQuadraticSegment(list(reversed(pts[:-1]))): curvePath.quadTo(*pt1+pt2) return path.intersects(curvePath)
def get_path(self, clickedPoints) -> QPainterPath: m_path = QPainterPath() self.clickedPoints = clickedPoints if len(self.clickedPoints) < 3: print("still have 2 points or less.") return m_path pt1 = QPointF() pt2 = QPointF() for i in range(len(self.clickedPoints) - 1): pt1 = self.get_line_start(i) if i == 0: m_path.moveTo(pt1) else: m_path.quadTo(clickedPoints[i], pt1) # pt2 = self.getLineEnd(i) # mPath.lineTo(pt2) # pt1 = self.getLineStart(0) # mPath.quadTo(clickedPoints[0], pt1) return m_path
def drawGlyph(self, scene, glyph, offsetX=0, offsetY=0, color=(255, 255, 255)): path = QPainterPath() path.setFillRule(Qt.WindingFill) for c in self.decomposedPaths(glyph): segs = c.segments path.moveTo(segs[-1].points[-1].x, segs[-1].points[-1].y) for seg in segs: tuples = [(a.x, a.y) for a in seg.points] flattuples = list(sum(tuples, ())) if len(tuples) == 2: path.quadTo(*flattuples) elif len(tuples) == 3: path.cubicTo(*flattuples) else: path.lineTo(*flattuples) line = QGraphicsPathItem() line.setBrush(QColor(*color)) p = QPen() p.setStyle(Qt.NoPen) line.setPen(p) line.setPath(path) reflect = QTransform(1, 0, 0, -1, 0, 0) reflect.translate(offsetX, offsetY) # print(f"Drawing {glyph} at offset {offsetX} {offsetY}") line.setTransform(reflect) scene.addItem(line)
def _pointWithinThreshold(x, y, curve, eps): """ See whether *(x, y)* is within *eps* of *curve*. """ path = QPainterPath() path.addEllipse(x - eps, y - eps, 2 * eps, 2 * eps) curvePath = QPainterPath() if curve[-1].segmentType == "curve": p1, p2, p3, p4 = curve curvePath.moveTo(p1.x, p1.y) curvePath.cubicTo(p2.x, p2.y, p3.x, p3.y, p4.x, p4.y) curvePath.cubicTo(p3.x, p3.y, p2.x, p2.y, p1.x, p1.y) else: first = curve[0] curvePath.moveTo(first.x, first.y) # PACK for fontTools pts = [] for pt in curve: pts.append((pt.x, pt.y)) # draw for pt1, pt2 in decomposeQuadraticSegment(pts[1:]): curvePath.quadTo(*pt1 + pt2) for pt1, pt2 in decomposeQuadraticSegment(list(reversed(pts[:-1]))): curvePath.quadTo(*pt1 + pt2) return path.intersects(curvePath)
def _insertGen( path: QPainterPath, start: QPointF, c1: QPointF, p1: QPointF, c2: QPointF): path.moveTo(start) path.quadTo(c1, p1) path.quadTo(c2, start)
def triangle_pellet(self, painter: QPainter, pellet_size: float): """A pellet that is pointy and triangular (1st in logo)""" pellet_size *= 1.5 pellet = QPainterPath() pellet.setFillRule(Qt.WindingFill) pellet.quadTo(0.9 * pellet_size, 0.5 * pellet_size, 0, pellet_size) pellet.quadTo(-0.5 * pellet_size, 0.4 * pellet_size, 0, 0) painter.drawPath(pellet)
def round_pellet(self, painter: QPainter, pellet_size): """A pellet that is round but not a circle (3rd in the logo).""" pellet_size *= 1.3 pellet = QPainterPath() pellet.setFillRule(Qt.WindingFill) for c in [1, -1]: pellet.quadTo(c * pellet_size * 0.8, pellet_size * 0.9, 0, pellet_size if c != -1 else 0) painter.drawPath(pellet)
def _get_path(self): path = QPainterPath() if not self.line().length(): return path path.moveTo(self.line().p1()) if self.index: path.quadTo(self.quad_center, self.line().p2()) else: path.lineTo(self.line().p2()) return path
def dip_pellet(self, painter: QPainter, pellet_size): """A pellet that has a dip in the middle (4th in the logo).""" pellet_size *= 1.2 pellet = QPainterPath() pellet.setFillRule(Qt.WindingFill) for c in [1, -1]: pellet.quadTo(c * pellet_size, pellet_size * 1.4, 0, pellet_size if c != -1 else 0) painter.drawPath(pellet)
def activateNeighbor(self, active_prexoveritem: 'PreXoverItem', shortcut: str = None): """Draws a quad line starting from the item5p to the item3p. To be called with whatever the active_prexoveritem is for the parts `active_base`. Args: active_prexoveritem: Description shortcut: Default is None """ if self._getActiveTool().methodPrefix() != "selectTool": return if self.is3p and not active_prexoveritem.is3p: item5p = active_prexoveritem item3p = self elif not self.is3p and active_prexoveritem.is3p: item5p = self item3p = active_prexoveritem else: return same_parity = self.is_fwd == active_prexoveritem.is_fwd p1 = item5p._tick_marks.scenePos() + item5p.exit_pos p2 = item3p._tick_marks.scenePos() + item3p.exit_pos c1 = QPointF() # case 1: same parity if same_parity: dy = abs(p2.y() - p1.y()) c1.setX(p1.x() + _X_SCALE * dy) c1.setY(0.5 * (p1.y() + p2.y())) # case 2: different parity else: if item3p.is_fwd: c1.setX(p1.x() - _X_SCALE * abs(p2.y() - p1.y())) else: c1.setX(p1.x() + _X_SCALE * abs(p2.y() - p1.y())) c1.setY(0.5 * (p1.y() + p2.y())) pp = QPainterPath() pp.moveTo(self._tick_marks.mapFromScene(p1)) pp.quadTo(self._tick_marks.mapFromScene(c1), self._tick_marks.mapFromScene(p2)) # pp.cubicTo(c1, c2, self._tick_marks.mapFromScene(p2)) self._bond_item.setPath(pp) self._bond_item.show()
def draw_background(self, painter): self.initCoordinateSystem(painter) glass = QPainterPath() glass.moveTo(12.5, 267.5) glass.quadTo(12.5, 263.0, 7.5, 257.0) glass.lineTo(7.5, 25.0) glass.quadTo(7.5, 12.5, 0, 12.5) glass.quadTo(-7.5, 12.5, -7.5, 25.0) glass.lineTo(-7.5, 257.0) glass.quadTo(-12.5, 263.0, -12.5, 267.5) glass.quadTo(-12.5, 278.0, 0.0, 280.0) glass.quadTo(12.5, 278.0, 12.5, 267.5) linearGrad = QLinearGradient(QPointF(-2.0, 0.0), QPointF(12.5, 0.0)) linearGrad.setSpread(QGradient.ReflectSpread) linearGrad.setColorAt(1.0, QColor(0, 150, 255, 170)) linearGrad.setColorAt(0.0, QColor(255, 255, 255, 0)) painter.setBrush(QBrush(linearGrad)) painter.setPen(Qt.black) painter.drawPath(glass) pen = QPen() for i in range(33): pen.setWidthF(1.0) length = 12 if i % 4: length = 8 pen.setWidthF(0.75) if i % 2: length = 5 pen.setWidthF(0.5) painter.setPen(pen) painter.drawLine(-7, 28 + 7 * i, -7 + length, 28 + 7 * i) painter.setFont(self.mDigitFont) for i in range(9): val = self.mPointer.minimal() + i * (self.mPointer.maximal() - self.mPointer.minimal()) / 8.0 Size = painter.fontMetrics().size(Qt.TextSingleLine, str(val)) painter.drawText(QPointF(10, 252 - i * 28 + Size.width() / 4.0), str(val))
def updateConnector(self, item: QGraphicsPathItem, p1: QPointF, p2: QPointF, select: bool = True): path = QPainterPath(p1) path.quadTo(p1 + QPointF(-15, 0), (p1 + p2) * 0.5) path.quadTo(p2 + QPointF(15, 0), p2) if item is None: item = self.nodesScene.addPath(path) else: item.setPath(path) item.setZValue(-1) if select: item.setFlag(QGraphicsItem.ItemIsSelectable) return item
def shape(self): if self.ctrlPoint: path = QPainterPath() path.moveTo( self.path().pointAtPercent(4 / self.path().length()).x(), self.path().pointAtPercent(4 / self.path().length()).y()) path.quadTo( self.ctrlPoint.position.x(), self.ctrlPoint.position.y(), self.path().pointAtPercent(1 - 4 / self.path().length()).x(), self.path().pointAtPercent(1 - 4 / self.path().length()).y()) path.quadTo( self.ctrlPoint.position.x(), self.ctrlPoint.position.y(), self.path().pointAtPercent(4 / self.path().length()).x(), self.path().pointAtPercent(4 / self.path().length()).y()) return path else: return super(quadraticEdge, self).shape()
def _draw(self, painter: QPainter, width: int, height: int): self.x = self.flower_center_x(width) * smoothen_curve( self.get_age_coefficient()) self.y = self.flower_center_y(height) * smoothen_curve( self.get_age_coefficient()) painter.setPen( QPen(Color.green, self.stem_width * smoothen_curve(self.get_age_coefficient()))) # draw the stem path = QPainterPath() path.quadTo(0, self.y * 0.6, self.x, self.y) painter.drawPath(path) # draw the leaves for position, coefficient, rotation in self.leafs: painter.save() # find the point on the stem and rotate the leaf accordingly painter.translate(path.pointAtPercent(position)) painter.rotate(degrees(rotation)) # rotate according to where the flower is leaning towards if self.y != 0: painter.rotate(-degrees(sin(self.x / self.y))) # make it so both leaves are facing the same direction if rotation < 0: painter.scale(-1, 1) painter.setBrush(QBrush(Color.green)) painter.setPen(QPen(0)) # draw the leaf leaf = QPainterPath() leaf.setFillRule(Qt.WindingFill) ls = self.leaf_size(width) * smoothen_curve( self.get_age_coefficient())**2 * coefficient leaf.quadTo(0.4 * ls, 0.5 * ls, 0, ls) leaf.cubicTo(0, 0.5 * ls, -0.4 * ls, 0.4 * ls, 0, 0) painter.drawPath(leaf) painter.restore()
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 QMarkItem(QGraphicsPathItem): def __init__(self, pt, style, parent=None): super().__init__() self.saveable = True self.pt = pt self.path = QPainterPath() # Draw a ?-mark with barycentre under mouseclick self.path.moveTo(pt.x() - 6, pt.y() - 10) self.path.quadTo(pt.x() - 6, pt.y() - 15, pt.x(), pt.y() - 15) self.path.quadTo(pt.x() + 6, pt.y() - 15, pt.x() + 6, pt.y() - 10) self.path.cubicTo(pt.x() + 6, pt.y() - 1, pt.x(), pt.y() - 7, pt.x(), pt.y() + 2) self.path.moveTo(pt.x(), pt.y() + 12) self.path.lineTo(pt.x(), pt.y() + 10) self.setPath(self.path) self.restyle(style) self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) def restyle(self, style): self.normal_thick = 3 * style["pen_width"] / 2 self.setPen(QPen(style["annot_color"], self.normal_thick)) def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionChange and self.scene(): command = CommandMoveItem(self, value) self.scene().undoStack.push(command) return super().itemChange(change, value) def pickle(self): return ["QMark", self.pt.x() + self.x(), self.pt.y() + self.y()] def paint(self, painter, option, widget): if not self.scene().itemWithinBounds(self): # paint a bounding rectangle out-of-bounds warning painter.setPen(QPen(QColor(255, 165, 0), 8)) painter.setBrush(QBrush(QColor(255, 165, 0, 128))) painter.drawRoundedRect(option.rect, 10, 10) # paint the normal item with the default 'paint' method super().paint(painter, option, widget)
class QMarkItem(QGraphicsPathItem): # Very similar to the arrowitem, but careful drawing the "?" def __init__(self, pt, parent=None): super(QMarkItem, self).__init__() self.saveable = True self.animator = [parent] self.animateFlag = False self.pt = pt self.path = QPainterPath() # Draw a ?-mark with barycentre under mouseclick self.path.moveTo(pt.x() - 6, pt.y() - 10) self.path.quadTo(pt.x() - 6, pt.y() - 15, pt.x(), pt.y() - 15) self.path.quadTo(pt.x() + 6, pt.y() - 15, pt.x() + 6, pt.y() - 10) self.path.cubicTo(pt.x() + 6, pt.y() - 1, pt.x(), pt.y() - 7, pt.x(), pt.y() + 2) self.path.moveTo(pt.x(), pt.y() + 12) self.path.lineTo(pt.x(), pt.y() + 10) self.setPath(self.path) self.setPen(QPen(Qt.red, 3)) self.setFlag(QGraphicsItem.ItemIsMovable) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges) def itemChange(self, change, value): if change == QGraphicsItem.ItemPositionChange and self.scene(): command = CommandMoveItem(self, value) self.scene().undoStack.push(command) return QGraphicsPathItem.itemChange(self, change, value) def pickle(self): return ["QMark", self.pt.x() + self.x(), self.pt.y() + self.y()] def paint(self, painter, option, widget): if not self.collidesWithItem(self.scene().underImage, mode=Qt.ContainsItemShape): # paint a bounding rectangle out-of-bounds warning painter.setPen(QPen(QColor(255, 165, 0), 8)) painter.setBrush(QBrush(QColor(255, 165, 0, 128))) painter.drawRoundedRect(option.rect, 10, 10) # paint the normal item with the default 'paint' method super(QMarkItem, self).paint(painter, option, widget)
def goOn(self): self.anim = QPropertyAnimation(self.mascot, b"pos") self.mascotFlip.hide() self.mascotGif.start() self.mascot.show() path = QPainterPath() path.moveTo(-70, 410) for i in range(self.step): path.quadTo( QPointF( self.xRange / self.step * i - 70 + (self.xRange / self.step / 2), 410), QPointF((self.xRange / self.step) * i - 70 + (self.xRange / self.step), 430)) vals = [p / 100 for p in range(0, 101)] self.anim.setDuration(self.duration) for i in vals: self.anim.setKeyValueAt(i, path.pointAtPercent(i)) self.anim.start() self.anim.finished.connect(self.goFlip)
def to_qpainter_path(paths: Iterable[Path], extrusion: 'Vertex' = Z_AXIS): """ Convert the given `paths` into a :class:`PyQt5.QtGui.QPainterPath` object. The `extrusion` vector is applied to all paths, all vertices are projected onto the plane normal to this extrusion vector. The default extrusion vector is the WCS z-axis. The :class:`QPainterPath` is a 2D object with :ref:`OCS` coordinates and the z-elevation is lost. (requires PyQt5) Args: paths: iterable of :class:`Path` objects extrusion: extrusion vector for all paths Returns: `QPainterPath`_ in OCS! .. versionadded:: 0.16 """ from PyQt5.QtGui import QPainterPath from PyQt5.QtCore import QPointF if not extrusion.isclose(Z_AXIS): paths = tools.transform_paths_to_ocs(paths, OCS(extrusion)) else: paths = list(paths) if len(paths) == 0: raise ValueError('one or more paths required') def qpnt(v: Vec3): return QPointF(v.x, v.y) qpath = QPainterPath() for path in paths: qpath.moveTo(qpnt(path.start)) for cmd in path: if cmd.type == Command.LINE_TO: qpath.lineTo(qpnt(cmd.end)) elif cmd.type == Command.CURVE3_TO: qpath.quadTo(qpnt(cmd.ctrl), qpnt(cmd.end)) elif cmd.type == Command.CURVE4_TO: qpath.cubicTo(qpnt(cmd.ctrl1), qpnt(cmd.ctrl2), qpnt(cmd.end)) return qpath
def paintEvent(self, event): # Initializing QPainter qp = QPainter() qp.begin(self) qp.setRenderHint(QPainter.Antialiasing) sidebar_rect = self.geometry() # Gradient gradient = QLinearGradient(0, 0, sidebar_rect.width(), 0) gradient.setColorAt(0.0, self.color_gradient_left) gradient.setColorAt(1.0, self.color_gradient_right) qp.setBrush(QBrush(gradient)) # qp.setPen(Qt.white) qp.drawRect(0, 0, sidebar_rect.width(), sidebar_rect.height()) # Glass highlight qp.setBrush(QBrush(Qt.white)) qp.setPen(QPen(QBrush(Qt.white), 0.01)) qp.setOpacity(0.1) qppath = QPainterPath() qppath.moveTo(sidebar_rect.width() * 0.2, 0) qppath.quadTo(sidebar_rect.width() * 0.3, sidebar_rect.height() * 0.5, sidebar_rect.width() * 0.2, sidebar_rect.height() - 1) qppath.lineTo(0, sidebar_rect.height()) qppath.lineTo(0, 0) qp.setClipPath(qppath) qp.drawRect(1, 1, sidebar_rect.width() - 1, sidebar.height() - 1) # Left border highlight qp.setOpacity(1.0) gradient = QLinearGradient(0, 0, 2, 0) gradient.setColorAt(0.0, QColor(255, 255, 255, 80)) gradient.setColorAt(1.0, QColor(0, 0, 0, 0)) qp.setBrush(QBrush(gradient)) # qp.setPen(Qt.transparent) qp.drawRect(0, 0, 8, sidebar_rect.height()) qp.end()
def randomWalk(self, image): """ actual calculations :param image: bitmap to squigglify :return: """ self.removeOldGraphicsItems() group = QGraphicsItemGroup() no_of_walks = self.parent.noOfWalksWalkify.value() coordinates = {} self.applyThreshold(image) for w in range(no_of_walks): x, y = self.find_darkest(image) x, y, color = self.find_darkest_neighbor(image, x, y) coordinates[w] = np.array([[x, y]]) no_of_line_segments = self.parent.noOfLineSegmentsWalkify.value() adjustbrightness = self.parent.localBrightnessAdjustmentWalkify.value( ) stroke_width = self.parent.lineWidthWalkify.value() for s in range(0, no_of_line_segments): dx, dy, dc = self.find_darkest_neighbor(image, x, y) self.lighten_area_around(image, adjustbrightness, dx, dy) x, y = dx, dy coordinates[w] = np.append(coordinates[w], [[x, y]], axis=0) for w in range(no_of_walks): coordinates[w] = simplify_coords( coordinates[w], self.parent.polylineSimplificationToleranceWalkify.value()) for w in range(no_of_walks): path = QPainterPath() in_the_middle_of_a_quad = False for idx, c in enumerate(coordinates[w]): quad = self.parent.useSmootherShapesWalkify.checkState( ) == Qt.Checked if not quad: if idx == 0: path.moveTo(coordinates[w][idx][0], coordinates[w][idx][1]) else: path.lineTo(coordinates[w][idx][0], coordinates[w][idx][1]) else: if idx == 0: path.moveTo(coordinates[w][idx][0], coordinates[w][idx][1]) elif idx % 2 == 1: middlex, middley = coordinates[w][idx][0], coordinates[ w][idx][1] in_the_middle_of_a_quad = True else: path.quadTo(middlex, middley, coordinates[w][idx][0], coordinates[w][idx][1]) in_the_middle_of_a_quad = False if in_the_middle_of_a_quad: path.lineTo(middlex, middley) item = QGraphicsPathItem(path) pen = QPen() pen.setWidth(stroke_width) item.setPen(pen) group.addToGroup(item) self.addNewGraphicsItems(group)
def _updatePath(self, strand5p): """ Draws a quad curve from the edge of the fromBase to the top or bottom of the toBase (q5), and finally to the center of the toBase (toBaseEndpoint). If floatPos!=None, this is a floatingXover and floatPos is the destination point (where the mouse is) while toHelix, toIndex are potentially None and represent the base at floatPos. Args: strand5p (TYPE): Description """ group = self.group() self.tempReparent() node3 = self._node3 node5 = self._node5 bw = _BASE_WIDTH parent = self.partItem() vhi5 = self._virtual_helix_item pt5 = vhi5.mapToItem(parent, *node5.point()) n5_is_forward = node5.is_forward vhi3 = node3.virtualHelixItem() pt3 = vhi3.mapToItem(parent, *node3.point()) n3_is_forward = node3.is_forward same_strand = (n5_is_forward == n3_is_forward) and (vhi3 == vhi5) same_parity = n5_is_forward == n3_is_forward # Enter/exit are relative to the direction that the path travels # overall. five_enter_pt = pt5 + QPointF(0 if n5_is_forward else 1, .5) * bw five_center_pt = pt5 + QPointF(.5, .5) * bw # five_exit_pt = pt5 + QPointF(.85 if n5_is_forward else .15, 0 if n5_is_forward else 1)*bw five_exit_pt = pt5 + QPointF(.5 if n5_is_forward else .5, 0 if n5_is_forward else 1) * bw # three_enter_pt = pt3 + QPointF(.15 if n3_is_forward else .85, 0 if n3_is_forward else 1)*bw three_enter_pt = pt3 + QPointF(.5 if n3_is_forward else .5, 0 if n3_is_forward else 1) * bw three_center_pt = pt3 + QPointF(.5, .5) * bw three_exit_pt = pt3 + QPointF(1 if n3_is_forward else 0, .5) * bw c1 = QPointF() # case 1: same strand if same_strand: dx = abs(three_enter_pt.x() - five_exit_pt.x()) c1.setX(0.5 * (five_exit_pt.x() + three_enter_pt.x())) if n5_is_forward: c1.setY(five_exit_pt.y() - _Y_SCALE * dx) else: c1.setY(five_exit_pt.y() + _Y_SCALE * dx) # case 2: same parity elif same_parity: dy = abs(three_enter_pt.y() - five_exit_pt.y()) c1.setX(five_exit_pt.x() + _X_SCALE * dy) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # case 3: different parity else: if n5_is_forward: c1.setX(five_exit_pt.x() - _X_SCALE * abs(three_enter_pt.y() - five_exit_pt.y())) else: c1.setX(five_exit_pt.x() + _X_SCALE * abs(three_enter_pt.y() - five_exit_pt.y())) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # Construct painter path painterpath = QPainterPath() painterpath.moveTo(five_enter_pt) painterpath.lineTo(five_center_pt) painterpath.lineTo(five_exit_pt) # The xover5's non-crossing-over end (3') has a connection painterpath.quadTo(c1, three_enter_pt) painterpath.lineTo(three_center_pt) painterpath.lineTo(three_exit_pt) tempR = painterpath.boundingRect() tempR.adjust(-bw / 2, 0, bw, 0) self._click_area.setRect(tempR) self.setPath(painterpath) node3.updatePositionAndAppearance() node5.updatePositionAndAppearance() if group: group.addToGroup(self) self._updateColor(strand5p)
def updateFloatPath(self, point=None): """ Draws a quad curve from the edge of the fromBase to the top or bottom of the toBase (q5), and finally to the center of the toBase (toBaseEndpoint). If floatPos!=None, this is a floatingXover and floatPos is the destination point (where the mouse is) while toHelix, toIndex are potentially None and represent the base at floatPos. Args: point (None, optional): Description """ node3 = self._node3 node5 = self._node5 bw = _BASE_WIDTH vhi5 = self._virtual_helix_item nucleicacid_part_item = vhi5.partItem() pt5 = vhi5.mapToItem(nucleicacid_part_item, *node5.floatPoint()) n5_is_forward = node5.is_forward # Enter/exit are relative to the direction that the path travels # overall. five_enter_pt = pt5 + QPointF(0 if n5_is_forward else 1, .5)*bw five_center_pt = pt5 + QPointF(.5, .5)*bw five_exit_pt = pt5 + QPointF(.5, 0 if n5_is_forward else 1)*bw vhi3 = node3.virtualHelixItem() if point: pt3 = point n3_is_forward = True same_strand = False same_parity = False three_enter_pt = three_center_pt = three_exit_pt = pt3 else: pt3 = vhi3.mapToItem(nucleicacid_part_item, *node3.point()) n3_is_forward = node3.is_forward same_strand = (n5_is_forward == n3_is_forward) and vhi3 == vhi5 same_parity = n5_is_forward == n3_is_forward three_enter_pt = pt3 + QPointF(.5, 0 if n3_is_forward else 1)*bw three_center_pt = pt3 + QPointF(.5, .5)*bw three_exit_pt = pt3 + QPointF(1 if n3_is_forward else 0, .5)*bw c1 = QPointF() # case 1: same strand if same_strand: dx = abs(three_enter_pt.x() - five_exit_pt.x()) c1.setX(0.5 * (five_exit_pt.x() + three_enter_pt.x())) if n5_is_forward: c1.setY(five_exit_pt.y() - _yScale * dx) else: c1.setY(five_exit_pt.y() + _yScale * dx) # case 2: same parity elif same_parity: dy = abs(three_enter_pt.y() - five_exit_pt.y()) c1.setX(five_exit_pt.x() + _xScale * dy) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # case 3: different parity else: if n5_is_forward: c1.setX(five_exit_pt.x() - _xScale * abs(three_enter_pt.y() - five_exit_pt.y())) else: c1.setX(five_exit_pt.x() + _xScale * abs(three_enter_pt.y() - five_exit_pt.y())) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # Construct painter path painterpath = QPainterPath() painterpath.moveTo(five_enter_pt) painterpath.lineTo(five_center_pt) painterpath.lineTo(five_exit_pt) painterpath.quadTo(c1, three_enter_pt) painterpath.lineTo(three_center_pt) painterpath.lineTo(three_exit_pt) self.setPath(painterpath) self._updateFloatPen()
def _updatePath(self, strand5p): """ Draws a quad curve from the edge of the fromBase to the top or bottom of the toBase (q5), and finally to the center of the toBase (toBaseEndpoint). If floatPos!=None, this is a floatingXover and floatPos is the destination point (where the mouse is) while toHelix, toIndex are potentially None and represent the base at floatPos. """ group = self.group() self.tempReparent() node3 = self._node3 node5 = self._node5 bw = _BASE_WIDTH parent = self.partItem() vhi5 = self._virtual_helix_item pt5 = vhi5.mapToItem(parent, *node5.point()) five_is_top = node5.isOnTop() five_is_5to3 = node5.isDrawn5to3() vhi3 = node3.virtualHelixItem() pt3 = vhi3.mapToItem(parent, *node3.point()) three_is_top = node3.isOnTop() three_is_5to3 = node3.isDrawn5to3() same_strand = (node5.strandType() == node3.strandType()) and vhi3 == vhi5 same_parity = five_is_5to3 == three_is_5to3 # Enter/exit are relative to the direction that the path travels # overall. five_enter_pt = pt5 + QPointF(0 if five_is_5to3 else 1, 0.5) * bw five_center_pt = pt5 + QPointF(0.5, 0.5) * bw five_exit_pt = pt5 + QPointF(0.5, 0 if five_is_top else 1) * bw three_enter_pt = pt3 + QPointF(0.5, 0 if three_is_top else 1) * bw three_center_pt = pt3 + QPointF(0.5, 0.5) * bw three_exit_pt = pt3 + QPointF(1 if three_is_5to3 else 0, 0.5) * bw c1 = QPointF() # case 1: same strand if same_strand: dx = abs(three_enter_pt.x() - five_exit_pt.x()) c1.setX(0.5 * (five_exit_pt.x() + three_enter_pt.x())) if five_is_top: c1.setY(five_exit_pt.y() - _Y_SCALE * dx) else: c1.setY(five_exit_pt.y() + _Y_SCALE * dx) # case 2: same parity elif same_parity: dy = abs(three_enter_pt.y() - five_exit_pt.y()) c1.setX(five_exit_pt.x() + _X_SCALE * dy) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # case 3: different parity else: if five_is_top and five_is_5to3: c1.setX(five_exit_pt.x() - _X_SCALE * abs(three_enter_pt.y() - five_exit_pt.y())) else: c1.setX(five_exit_pt.x() + _X_SCALE * abs(three_enter_pt.y() - five_exit_pt.y())) c1.setY(0.5 * (five_exit_pt.y() + three_enter_pt.y())) # Construct painter path painterpath = QPainterPath() painterpath.moveTo(five_enter_pt) painterpath.lineTo(five_center_pt) painterpath.lineTo(five_exit_pt) # The xover5's non-crossing-over end (3') has a connection painterpath.quadTo(c1, three_enter_pt) painterpath.lineTo(three_center_pt) painterpath.lineTo(three_exit_pt) tempR = painterpath.boundingRect() tempR.adjust(-bw / 2, 0, bw, 0) self._click_area.setRect(tempR) self.setPath(painterpath) node3.updatePositionAndAppearance() node5.updatePositionAndAppearance() if group: group.addToGroup(self) self._updateColor(strand5p)
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 paintEvent(self, e): super().paintEvent(e) # 画鼠标位置处的圆 if 1 in self.parent.painter_tools.values(): painter = QPainter(self) painter.setPen(QPen(self.parent.pencolor, 1, Qt.SolidLine)) rect = QRectF(self.px - self.parent.tool_width // 2, self.py - self.parent.tool_width // 2, self.parent.tool_width, self.parent.tool_width) painter.drawEllipse(rect) painter.end() if self.startpaint: while len(self.parent.eraser_pointlist): # 画橡皮擦 self.pixPainter = QPainter(self.pixmap()) self.pixPainter.setRenderHint(QPainter.Antialiasing) self.pixPainter.setBrush(QColor(0, 0, 0, 0)) self.pixPainter.setPen(Qt.NoPen) self.pixPainter.setCompositionMode( QPainter.CompositionMode_Clear) new_pen_point = self.parent.eraser_pointlist.pop(0) if self.parent.old_pen[0] != -2 and new_pen_point[0] != -2: self.pixPainter.drawEllipse( new_pen_point[0] - self.parent.tool_width / 2, new_pen_point[1] - self.parent.tool_width / 2, self.parent.tool_width, self.parent.tool_width) self.parent.old_pen = new_pen_point self.pixPainter.end() while len(self.parent.pen_pointlist): # 画笔功能 self.pixPainter = QPainter(self.pixmap()) self.pixPainter.setRenderHint(QPainter.Antialiasing) self.pixPainter.setBrush(self.parent.pencolor) self.pixPainter.setPen( QPen(self.parent.pencolor, self.parent.tool_width, Qt.SolidLine)) new_pen_point = self.parent.pen_pointlist.pop(0) if self.parent.old_pen[0] != -2 and new_pen_point[0] != -2: path = QPainterPath( QPoint(self.parent.old_pen[0], self.parent.old_pen[1])) path.quadTo( QPoint( (new_pen_point[0] + self.parent.old_pen[0]) / 2, (new_pen_point[1] + self.parent.old_pen[1]) / 2), QPoint(new_pen_point[0], new_pen_point[1])) self.pixPainter.drawPath(path) self.parent.old_pen = new_pen_point self.pixPainter.end() while len(self.parent.drawpix_pointlist): # 画马赛克/材质贴图功能 self.pixPainter = QPainter(self.pixmap()) self.pixPainter.setRenderHint(QPainter.Antialiasing) brush = QBrush(self.parent.pencolor) brush.setTexture(QPixmap(self.pixpng)) self.pixPainter.setBrush(brush) self.pixPainter.setPen(Qt.NoPen) new_pen_point = self.parent.drawpix_pointlist.pop(0) if self.parent.old_pen[0] != -2 and new_pen_point[0] != -2: self.pixPainter.drawEllipse( new_pen_point[0] - self.parent.tool_width / 2, new_pen_point[1] - self.parent.tool_width / 2, self.parent.tool_width, self.parent.tool_width) self.parent.old_pen = new_pen_point self.pixPainter.end() if self.parent.drawrect_pointlist[0][ 0] != -2 and self.parent.drawrect_pointlist[1][ 0] != -2: # 画方形 self.pixPainter = QPainter(self.pixmap()) self.pixPainter.setRenderHint(QPainter.Antialiasing) temppainter = QPainter(self) temppainter.setPen(QPen(self.parent.pencolor, 3, Qt.SolidLine)) poitlist = self.parent.drawrect_pointlist temppainter.drawRect(min(poitlist[0][0], poitlist[1][0]), min(poitlist[0][1], poitlist[1][1]), abs(poitlist[0][0] - poitlist[1][0]), abs(poitlist[0][1] - poitlist[1][1])) temppainter.end() if self.parent.drawrect_pointlist[2] == 1: self.pixPainter.setPen( QPen(self.parent.pencolor, 3, Qt.SolidLine)) self.pixPainter.drawRect( min(poitlist[0][0], poitlist[1][0]), min(poitlist[0][1], poitlist[1][1]), abs(poitlist[0][0] - poitlist[1][0]), abs(poitlist[0][1] - poitlist[1][1])) self.parent.drawrect_pointlist = [[-2, -2], [-2, -2], 0] self.pixPainter.end() if self.parent.drawcircle_pointlist[0][ 0] != -2 and self.parent.drawcircle_pointlist[1][ 0] != -2: # 画圆 self.pixPainter = QPainter(self.pixmap()) self.pixPainter.setRenderHint(QPainter.Antialiasing) temppainter = QPainter(self) temppainter.setPen(QPen(self.parent.pencolor, 3, Qt.SolidLine)) poitlist = self.parent.drawcircle_pointlist temppainter.drawEllipse(min(poitlist[0][0], poitlist[1][0]), min(poitlist[0][1], poitlist[1][1]), abs(poitlist[0][0] - poitlist[1][0]), abs(poitlist[0][1] - poitlist[1][1])) temppainter.end() if self.parent.drawcircle_pointlist[2] == 1: self.pixPainter.setPen( QPen(self.parent.pencolor, 3, Qt.SolidLine)) self.pixPainter.drawEllipse( min(poitlist[0][0], poitlist[1][0]), min(poitlist[0][1], poitlist[1][1]), abs(poitlist[0][0] - poitlist[1][0]), abs(poitlist[0][1] - poitlist[1][1])) self.parent.drawcircle_pointlist = [[-2, -2], [-2, -2], 0] self.pixPainter.end() if self.parent.drawarrow_pointlist[0][ 0] != -2 and self.parent.drawarrow_pointlist[1][ 0] != -2: # 画箭头 self.pixPainter = QPainter(self.pixmap()) self.pixPainter.setRenderHint(QPainter.Antialiasing) temppainter = QPainter(self) poitlist = self.parent.drawarrow_pointlist temppainter.translate(poitlist[0][0], poitlist[0][1]) degree = math.degrees( math.atan2(poitlist[1][1] - poitlist[0][1], poitlist[1][0] - poitlist[0][0])) temppainter.rotate(degree) dx = math.sqrt((poitlist[1][1] - poitlist[0][1])**2 + (poitlist[1][0] - poitlist[0][0])**2) dy = 30 temppainter.drawPixmap(0, -dy / 2, QPixmap(':/arrow.png').scaled(dx, dy)) temppainter.end() if self.parent.drawarrow_pointlist[2] == 1: self.pixPainter.translate(poitlist[0][0], poitlist[0][1]) degree = math.degrees( math.atan2(poitlist[1][1] - poitlist[0][1], poitlist[1][0] - poitlist[0][0])) self.pixPainter.rotate(degree) dx = math.sqrt((poitlist[1][1] - poitlist[0][1])**2 + (poitlist[1][0] - poitlist[0][0])**2) dy = 30 self.pixPainter.drawPixmap( 0, -dy / 2, QPixmap(':/arrow.png').scaled(dx, dy)) self.parent.drawarrow_pointlist = [[-2, -2], [-2, -2], 0] # self.parent.drawarrow_pointlist[0] = [-2, -2] self.pixPainter.end() if len(self.parent.drawtext_pointlist ) > 1 or self.parent.text_box.paint: # 画文字 self.pixPainter = QPainter(self.pixmap()) self.pixPainter.setRenderHint(QPainter.Antialiasing) self.parent.text_box.paint = False # print(self.parent.drawtext_pointlist) text = self.parent.text_box.toPlainText() self.parent.text_box.clear() pos = self.parent.drawtext_pointlist.pop(0) if text: self.pixPainter.setFont( QFont('微软雅黑', self.parent.tool_width)) self.pixPainter.setPen( QPen(self.parent.pencolor, 3, Qt.SolidLine)) self.pixPainter.drawText( pos[0] + self.parent.text_box.document.size().height() / 8, pos[1] + self.parent.text_box.document.size().height() * 32 / 41, text) self.pixPainter.end() else: self.startpaint = 1
def drawOutline(self, painter): path = QPainterPath() path.moveTo(-7.5, 257) path.quadTo(-12.5, 263, -12.5, 267.5) path.quadTo(-12.5, 278, 0, 280) path.quadTo(12.5, 278, 12.5, 267.5) path.moveTo(12.5, 267.5) path.quadTo(12.5, 263, 7.5, 257) path.lineTo(7.5, 25) path.quadTo(7.5, 12.5, 0, 12.5) path.quadTo(-7.5, 12.5, -7.5, 25) path.lineTo(-7.5, 257) p1 = QPointF(-2.0, 0) p2 = QPointF(12.5, 0) linearGrad = QLinearGradient(p1, p2) linearGrad.setSpread(QGradient.ReflectSpread) linearGrad.setColorAt(1, QColor(0, 150, 255, 170)) linearGrad.setColorAt(0, QColor(255, 255, 255, 0)) painter.setBrush(QBrush(linearGrad)) painter.setPen(Qt.black) painter.drawPath(path) pen = QPen() length = 12 for i in range(33): pen.setWidthF(1.0) length = 12 if i % 4: length = 8 pen.setWidthF(0.8) if i % 2: length = 5 pen.setWidthF(0.6) painter.setPen(pen) painter.drawLine(-7, 28 + i * 7, -7 + length, 28 + i * 7) for i in range(9): num = self.m_min + i * (self.m_max - self.m_min) / 8 val = "{0}".format(num) fm = painter.fontMetrics() size = fm.size(Qt.TextSingleLine, val) point = QPointF(OFFSET, 252 - i * 28 + size.width() / 4.0) painter.drawText(point, val)
def endShape(self, mode, vertices, isCurve, isBezier, isQuadratic, isContour, shapeKind): if len(vertices) == 0: return closeShape = mode == CLOSE if closeShape and not isContour: vertices.append(vertices[0]) if isCurve and (shapeKind == POLYGON or shapeKind == None): s = 1 - self.curveTightness path = QPainterPath() path.moveTo(vertices[1][0], vertices[1][1]) for i in range(1, len(vertices) - 2): v = vertices[i] b = [] b.append([v[0], v[1]]) b.append([ v[0] + (s * vertices[i + 1][0] - s * vertices[i - 1][0]) / 6, v[1] + (s * vertices[i + 1][1] - s * vertices[i - 1][1]) / 6 ]) b.append([ vertices[i + 1][0] + (s * vertices[i][0] - s * vertices[i + 2][0]) / 6, vertices[i + 1][1] + (s * vertices[i][1] - s * vertices[i + 2][1]) / 6 ]) b.append([vertices[i + 1][0], vertices[i + 1][1]]) path.cubicTo( b[1][0], b[1][1], b[2][0], b[2][1], b[3][0], b[3][1] ) if closeShape: path.lineTo(vertices[i + 1][0], vertices[i + 1][1]) self.renderPath(path) elif isBezier and (shapeKind == POLYGON or shapeKind == None): path = QPainterPath() path.moveTo(vertices[0][0], vertices[0][1]) for v in vertices: if len(v) == 2: path.lineTo(v[0], v[1]) else: path.cubicTo( v[0], v[1], v[2], v[3], v[4], v[5], ) self.renderPath(path) elif isQuadratic and (shapeKind == POLYGON or shapeKind == None): path = QPainterPath() path.moveTo(vertices[0][0], vertices[0][1]) for v in vertices: if len(v) == 2: path.lineTo(v[0], v[1]) else: path.quadTo( v[0], v[1], v[2], v[3], ) self.renderPath(path) else: if shapeKind == POINTS: for p in vertices: self.point(p[0], p[1]) elif shapeKind == LINES: for i in range(0, len(vertices) - 1, 2): self.line(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1]) elif shapeKind == TRIANGLES: for i in range(0, len(vertices) - 2, 3): self.triangle(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1], vertices[i + 2][0], vertices[i + 2][1]) elif shapeKind == TRIANGLE_STRIP: for i in range(len(vertices) - 2): self.triangle(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1], vertices[i + 2][0], vertices[i + 2][1]) elif shapeKind == TRIANGLE_FAN: for i in range(1, len(vertices) - 1): self.triangle(vertices[0][0], vertices[0][1], vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1]) elif shapeKind == QUADS: for i in range(0, len(vertices) - 3, 4): self.quad(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1], vertices[i + 2][0], vertices[i + 2][1], vertices[i + 3][0], vertices[i + 3][1]) elif shapeKind == QUAD_STRIP: for i in range(0, len(vertices) - 3, 2): self.quad(vertices[i][0], vertices[i][1], vertices[i + 1][0], vertices[i + 1][1], vertices[i + 2][0], vertices[i + 2][1], vertices[i + 3][0], vertices[i + 3][1]) else: path = QPainterPath() path.moveTo(vertices[0][0], vertices[0][1]) for p in vertices: path.lineTo(p[0], p[1]) self.renderPath(path) return
def drawEdge(self, x1, y1, x2, y2, index, curve, tear=False): """ Draw an edge from x1, y1 to x2, y2. Edges connect two nodes --Args-- index: the edge index curve: distance from center of straight edge to a point on curved edge (can be positive or negitive. Used to keep edges from overlapping. tear: if true draw in tear edge style """ # determine if edge conntects a node to itself. if abs(x1 - x2) < 0.01 and abs(y1 - y2) < 0.01: path = QPainterPath() curve = curve * 2 path.addEllipse(x1, y1 - curve / 2.0, curve, curve) if tear: gi = self.addPath(path, self.edgePen) else: gi = self.addPath(path, self.tearEdgePen) else: # mid point of the edge if it is a straight line xmid = (x1 + x2) / 2.0 ymid = (y1 + y2) / 2.0 # get the angle of the edge and the angle perpendicular ang = math.atan2((y2 - y1), (x2 - x1)) ang_perp = math.atan2((x1 - x2), (y2 - y1)) # calculate the mid point of the curved edge xcurve = xmid + curve * math.cos(ang_perp) ycurve = ymid + curve * math.sin(ang_perp) # calculate control point for drawing quaratic curve xcontrol = 2 * xcurve - xmid ycontrol = 2 * ycurve - ymid # draw Edge path = QPainterPath() path.moveTo(x1, y1) path.quadTo(xcontrol, ycontrol, x2, y2) p2 = QPainterPathStroker() path = p2.createStroke(path) # if edge is selected draw it highlighted if index in self.selectedEdges: self.addPath(path, self.eSelectionPen) # if edge is a tear draw it tear style else draw it normal if tear: gi = self.addPath(path, self.tearEdgePen) else: gi = self.addPath(path, self.edgePen) # Add data to edge so if seleted we can determine that it # is an edge and which edge it is. gi.setData(1, index) gi.setData(2, "edge") # Draw the arrow path = QPainterPath() xs = xcurve + self.edgeArrowSize * math.cos(ang) ys = ycurve + self.edgeArrowSize * math.sin(ang) path.moveTo(xs, ys) path.lineTo( xs - self.edgeArrowSize * math.cos(ang) + self.edgeArrowSize / 2.0 * math.cos(ang_perp), ys - self.edgeArrowSize * math.sin(ang) + self.edgeArrowSize / 2.0 * math.sin(ang_perp), ) path.lineTo( xs - self.edgeArrowSize * math.cos(ang) - self.edgeArrowSize / 2.0 * math.cos(ang_perp), ys - self.edgeArrowSize * math.sin(ang) - self.edgeArrowSize / 2.0 * math.sin(ang_perp), ) path.lineTo(xs, ys) gi = self.addPath(path, self.edgePen, self.edgeArrowBrush) # Add data so selecting the arrow in like selecting the edge gi.setData(1, index) gi.setData(2, "edge")
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 generatePath(self, outerRimPoints, innerRimPoints): # MoveTo, LineTo, QuadTo orp = outerRimPoints irp = innerRimPoints # For debugging purposes: #print("Outer: " + str(orp)) #print("Inner: " + str(irp)) p = QPainterPath() # Set to starting position: top center p.moveTo(orp['topCenter']) # Generate outer rim path p.lineTo(orp['topRight']['topSidePoint']) p.quadTo(orp['topRight']['controlPoint'], orp['topRight']['rightSidePoint']) p.lineTo(orp['bottomRight']['rightSidePoint']) p.quadTo(orp['bottomRight']['controlPoint'], orp['bottomRight']['bottomSidePoint']) p.lineTo(orp['bottomLeft']['bottomSidePoint']) p.quadTo(orp['bottomLeft']['controlPoint'], orp['bottomLeft']['leftSidePoint']) p.lineTo(orp['topLeft']['leftSidePoint']) p.quadTo(orp['topLeft']['controlPoint'], orp['topLeft']['topSidePoint']) p.lineTo(orp['topCenter']) # Connect to inner rim path p.lineTo(irp['topCenter']) # Generate inner rim path p.lineTo(irp['topLeft']['topSidePoint']) p.quadTo(irp['topLeft']['controlPoint'], irp['topLeft']['leftSidePoint']) p.lineTo(irp['bottomLeft']['leftSidePoint']) p.quadTo(irp['bottomLeft']['controlPoint'], irp['bottomLeft']['bottomSidePoint']) p.lineTo(irp['bottomRight']['bottomSidePoint']) p.quadTo(irp['bottomRight']['controlPoint'], irp['bottomRight']['rightSidePoint']) p.lineTo(irp['topRight']['rightSidePoint']) p.quadTo(irp['topRight']['controlPoint'], irp['topRight']['topSidePoint']) p.lineTo(irp['topCenter']) # Path 'should' be closed automatically, if not here is one final lineTo: # p.lineTo(orp['topCenter']) return p