def addArrow(self, p1, p2, color=BLACK): p5.stroke(color) self.addLine(p1, p2) to_int = (int(p1[0]), int(p1[1]), int(p2[0]), int(p2[1])) if to_int in ARROW_CACHE: arrow = ARROW_CACHE[to_int] else: p1 = QPoint(*p1) p2 = QPoint(*p2) path = QPainterPath() path.moveTo(p1) path.lineTo(p2) line = QLineF(p1, p2) end = p2 pathlen = path.length() leng = min(10, pathlen / 4.0) arrowbase = path.pointAtPercent(path.percentAtLength(pathlen - leng)) l1 = QLineF(arrowbase, end) l2 = QLineF(arrowbase, end) l1.setAngle(line.angle() - 150) l2.setAngle(line.angle() + 150) l1.setLength(l1.length() / 2.0) l2.setLength(l2.length() / 2.0) arrow = (arrowbase.toTuple(), l1.p2().toTuple(), end.toTuple(), l2.p2().toTuple()) ARROW_CACHE[to_int] = arrow p5.fill(color) p5.quad(*arrow)
class ImporterExporterAnimation: def __init__(self, item, duration=2000, count=5, percentage_size=0.24, x_shift=0): """Initializes animation stuff. Args: item (QGraphicsItem): The item on top of which the animation should play. """ self._item = item self.cubes = [QGraphicsTextItem("\uf1b2", item) for i in range(count)] self.opacity_at_value_path = QPainterPath(QPointF(0.0, 0.0)) self.opacity_at_value_path.lineTo(QPointF(0.01, 1.0)) self.opacity_at_value_path.lineTo(QPointF(0.5, 1.0)) self.opacity_at_value_path.lineTo(QPointF(1.0, 0.0)) self.time_line = QTimeLine() self.time_line.setLoopCount(0) # loop forever self.time_line.setFrameRange(0, 10) self.time_line.setDuration(duration) self.time_line.setCurveShape(QTimeLine.LinearCurve) self.time_line.valueChanged.connect( self._handle_time_line_value_changed) self.time_line.stateChanged.connect( self._handle_time_line_state_changed) font = QFont('Font Awesome 5 Free Solid') item_rect = item.rect() cube_size = percentage_size * 0.875 * item_rect.height() font.setPixelSize(cube_size) rect = item_rect.translated(-0.5 * cube_size + x_shift, -cube_size) end = rect.center() ctrl = end - QPointF(0, 0.6 * rect.height()) lower, upper = 0.2, 0.8 starts = [lower + i * (upper - lower) / count for i in range(count)] starts = [ rect.topLeft() + QPointF(start * rect.width(), 0) for start in starts ] self.paths = [QPainterPath(start) for start in starts] for path in self.paths: path.quadTo(ctrl, end) self.offsets = [i / count for i in range(count)] for cube in self.cubes: cube.setFont(font) cube.setDefaultTextColor("#003333") cube.setTransformOriginPoint(cube.boundingRect().center()) cube.hide() cube.setOpacity(0) @Slot(float) def _handle_time_line_value_changed(self, value): for cube, offset, path in zip(self.cubes, self.offsets, self.paths): value = (offset + value) % 1.0 opacity = self.opacity_at_value_path.pointAtPercent(value).y() cube.setOpacity(opacity) percent = self.percent(value) point = path.pointAtPercent(percent) angle = percent * 360.0 cube.setPos(point) cube.setRotation(angle) @Slot("QTimeLine::State") def _handle_time_line_state_changed(self, new_state): if new_state == QTimeLine.Running: random.shuffle(self.offsets) for cube in self.cubes: cube.show() elif new_state == QTimeLine.NotRunning: for cube in self.cubes: cube.hide() def start(self): """Starts the animation.""" if self.time_line.state() == QTimeLine.Running: return self.time_line.start() @staticmethod def percent(value): raise NotImplementedError() def stop(self): """Stops the animation""" self.time_line.stop()