def run_execution_leave_animation(self, skipped): """ Starts the animation associated with execution leaving the icon. Args: skipped (bool): True if project item was not actually executed. """ animation_group = QParallelAnimationGroup(self._toolbox) for link in self.outgoing_links(): animation_group.addAnimation( link.make_execution_animation(skipped)) animation_group.start()
class BubbleLabel(QWidget): BackgroundColor = QColor(195, 195, 195) BorderColor = QColor(150, 150, 150) def __init__(self, *args, **kwargs): text = kwargs.pop("text", "") super(BubbleLabel, self).__init__(*args, **kwargs) # 设置无边框置顶 self.setWindowFlags( Qt.Window | Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.X11BypassWindowManagerHint) # 设置最小宽度和高度 self.setMinimumWidth(200) self.setMinimumHeight(48) self.setAttribute(Qt.WA_TranslucentBackground, True) layout = QVBoxLayout(self) # 左上右下的边距(下方16是因为包括了三角形) layout.setContentsMargins(8, 8, 8, 16) self.label = QLabel(self) layout.addWidget(self.label) self.setText(text) # 获取屏幕高宽 self._desktop = QApplication.instance().desktop() def setText(self, text): self.label.setText(text) def text(self): return self.label.text() def stop(self): self.hide() self.animationGroup.stop() self.close() def show(self): super(BubbleLabel, self).show() # 窗口开始位置 startPos = QPoint( self._desktop.screenGeometry().width() - self.width() - 100, self._desktop.availableGeometry().height() - self.height()) endPos = QPoint( self._desktop.screenGeometry().width() - self.width() - 100, self._desktop.availableGeometry().height() - self.height() * 3 - 5) print(startPos, endPos) self.move(startPos) # 初始化动画 self.initAnimation(startPos, endPos) def initAnimation(self, startPos, endPos): # 透明度动画 opacityAnimation = QPropertyAnimation(self, b"opacity") opacityAnimation.setStartValue(1.0) opacityAnimation.setEndValue(0.0) # 设置动画曲线 opacityAnimation.setEasingCurve(QEasingCurve.InQuad) opacityAnimation.setDuration(4000) # 在4秒的时间内完成 # 往上移动动画 moveAnimation = QPropertyAnimation(self, b"pos") moveAnimation.setStartValue(startPos) moveAnimation.setEndValue(endPos) moveAnimation.setEasingCurve(QEasingCurve.InQuad) moveAnimation.setDuration(5000) # 在5秒的时间内完成 # 并行动画组(目的是让上面的两个动画同时进行) self.animationGroup = QParallelAnimationGroup(self) self.animationGroup.addAnimation(opacityAnimation) self.animationGroup.addAnimation(moveAnimation) self.animationGroup.finished.connect(self.close) # 动画结束时关闭窗口 self.animationGroup.start() def paintEvent(self, event): super(BubbleLabel, self).paintEvent(event) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # 抗锯齿 rectPath = QPainterPath() # 圆角矩形 triPath = QPainterPath() # 底部三角形 height = self.height() - 8 # 往上偏移8 rectPath.addRoundedRect(QRectF(0, 0, self.width(), height), 5, 5) x = self.width() / 5 * 4 triPath.moveTo(x, height) # 移动到底部横线4/5处 # 画三角形 triPath.lineTo(x + 6, height + 8) triPath.lineTo(x + 12, height) rectPath.addPath(triPath) # 添加三角形到之前的矩形上 # 边框画笔 painter.setPen(QPen(self.BorderColor, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) # 背景画刷 painter.setBrush(self.BackgroundColor) # 绘制形状 painter.drawPath(rectPath) # 三角形底边绘制一条线保证颜色与背景一样 painter.setPen(QPen(self.BackgroundColor, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) painter.drawLine(x, height, x + 12, height) def windowOpacity(self): return super(BubbleLabel, self).windowOpacity() def setWindowOpacity(self, opacity): super(BubbleLabel, self).setWindowOpacity(opacity) # 由于opacity属性不在QWidget中需要重新定义一个 opacity = pyqtProperty(float, windowOpacity, setWindowOpacity)
class QTile(QGraphicsObject): colorMap = { Tetris.I: QColor("#53bbf4"), Tetris.J: QColor("#e25fb8"), Tetris.L: QColor("#ffac00"), Tetris.O: QColor("#ecff2e"), Tetris.S: QColor("#97eb00"), Tetris.T: QColor("#ff85cb"), Tetris.Z: QColor("#ff5a48") } def __init__(self, qTetris: 'QTetris', tetrimino: Tetris.Tetrimino, tile: Tetris.Tile): super(QTetris.QTile, self).__init__() tile.delegate = self self.color = self.colorMap[type(tetrimino)] self.qTetris = qTetris self.moveAnimation = QParallelAnimationGroup() self.dropAnimation = QPropertyAnimation(self, b'pos') self.collapseAnimation = QPropertyAnimation(self, b'pos') self.shiftAnimation = QPropertyAnimation(self, b'pos') self.collapseAnimation.finished.connect( lambda tl=tile: tile.delegate.disappeared(tl)) self.qTetris.scene.addItem(self) self.setPos(QPointF(0, 4)) self.moved(tile) def moved(self, tile: Tetris.Tile): translation = QPropertyAnimation(self, b'pos') start, end = self.pos(), QPointF(tile.row, tile.column) curve, speed, delay = QEasingCurve.OutBack, 1 / 50, -1 self.animate(translation, start, end, curve, speed, delay) rotation = QPropertyAnimation(self, b'rotation') start, end = self.rotation(), tile.rotation curve, speed, delay = QEasingCurve.OutBack, 1, -1 self.animate(rotation, start, end, curve, speed, delay) rotation.setDuration(translation.duration()) self.moveAnimation.clear() self.moveAnimation.addAnimation(translation) self.moveAnimation.addAnimation(rotation) self.moveAnimation.start() def dropped(self, tile: Tetris.Tile): start, end = self.pos(), QPointF(tile.row, tile.column) curve, speed, delay = QEasingCurve.OutBounce, 1 / 50, 0 self.animate(self.dropAnimation, start, end, curve, speed, delay) def collapsed(self, tile: Tetris.Tile): start, end = self.pos(), QPointF( tile.row, tile.column + 2 * tile.tetris.num_columns) curve, speed, delay = QEasingCurve.InOutExpo, 1 / 50, 800 if self.dropAnimation.state() == QAbstractAnimation.Running: start = self.dropAnimation.endValue() self.animate(self.collapseAnimation, start, end, curve, speed, delay) def shifted(self, tile: Tetris.Tile): start, end = self.pos(), QPointF(tile.row, tile.column) curve, speed, delay = QEasingCurve.OutBounce, 1 / 100, 1200 if self.dropAnimation.state() == QAbstractAnimation.Running: start = self.dropAnimation.endValue() self.animate(self.shiftAnimation, start, end, curve, speed, delay) def disappeared(self, tile: Tetris.Tile): self.qTetris.scene.removeItem(self) def paint(self, painter: QPainter, styleOption: QStyleOptionGraphicsItem, widget: QWidget = None): pen = QPen() pen.setWidthF(0.05) pen.setColor(Qt.darkGray) painter.setPen(pen) brush = QBrush() brush.setColor(self.color) brush.setStyle(Qt.SolidPattern) painter.setBrush(brush) topLeft = QPointF(0, 0) bottomRight = QPointF(1, 1) rectangle = QRectF(topLeft, bottomRight) rectangle.translate(-0.5, -0.5) painter.drawRect(rectangle) @staticmethod def animate(animation: QPropertyAnimation, start: Union[QPointF, int, float], end: Union[QPointF, int, float], curve: QEasingCurve = QEasingCurve.Linear, speed: float = 1 / 50, delay: int = -1): animation.setStartValue(start) animation.setEndValue(end) animation.setEasingCurve(curve) if type(start) == type(end) == QPointF: distance = (end - start).manhattanLength() else: distance = abs(end - start) animation.setDuration(round(distance / speed)) if delay == 0: animation.start() if delay > 0: QTimer.singleShot(delay, animation.start) def boundingRect(self): topLeft = QPointF(0, 0) bottomRight = QPointF(1, 1) rectangle = QRectF(topLeft, bottomRight) rectangle.translate(-0.5, -0.5) return rectangle
def run_execution_leave_animation(self, skipped): animation_group = QParallelAnimationGroup(self._toolbox) for link in self.outgoing_links(): animation_group.addAnimation( link.make_execution_animation(skipped)) animation_group.start()
class QTile(QGraphicsObject): colorMap = {Tetris.I: QColor("#53bbf4"), Tetris.J: QColor("#e25fb8"), Tetris.L: QColor("#ffac00"), Tetris.O: QColor("#ecff2e"), Tetris.S: QColor("#97eb00"), Tetris.T: QColor("#ff85cb"), Tetris.Z: QColor("#ff5a48")} def __init__(self, qTetris: 'QTetris', tetrimino: Tetris.Tetrimino, tile: Tetris.Tile): super(QTetris.QTile, self).__init__() tile.delegate = self self.color = self.colorMap[type(tetrimino)] self.qTetris = qTetris self.moveAnimation = QParallelAnimationGroup() self.dropAnimation = QPropertyAnimation(self, b'pos') self.collapseAnimation = QPropertyAnimation(self, b'pos') self.shiftAnimation = QPropertyAnimation(self, b'pos') self.collapseAnimation.finished.connect(lambda tl=tile: tile.delegate.disappeared(tl)) self.qTetris.scene.addItem(self) self.setPos(QPointF(0, 4)) self.moved(tile) def moved(self, tile: Tetris.Tile): translation = QPropertyAnimation(self, b'pos') start, end = self.pos(), QPointF(tile.row, tile.column) curve, speed, delay = QEasingCurve.OutBack, 1 / 50, -1 self.animate(translation, start, end, curve, speed, delay) rotation = QPropertyAnimation(self, b'rotation') start, end = self.rotation(), tile.rotation curve, speed, delay = QEasingCurve.OutBack, 1, -1 self.animate(rotation, start, end, curve, speed, delay) rotation.setDuration(translation.duration()) self.moveAnimation.clear() self.moveAnimation.addAnimation(translation) self.moveAnimation.addAnimation(rotation) self.moveAnimation.start() def dropped(self, tile: Tetris.Tile): start, end = self.pos(), QPointF(tile.row, tile.column) curve, speed, delay = QEasingCurve.OutBounce, 1 / 50, 0 self.animate(self.dropAnimation, start, end, curve, speed, delay) def collapsed(self, tile: Tetris.Tile): start, end = self.pos(), QPointF(tile.row, tile.column + 2 * tile.tetris.num_columns) curve, speed, delay = QEasingCurve.InOutExpo, 1 / 50, 800 if self.dropAnimation.state() == QAbstractAnimation.Running: start = self.dropAnimation.endValue() self.animate(self.collapseAnimation, start, end, curve, speed, delay) def shifted(self, tile: Tetris.Tile): start, end = self.pos(), QPointF(tile.row, tile.column) curve, speed, delay = QEasingCurve.OutBounce, 1 / 100, 1200 if self.dropAnimation.state() == QAbstractAnimation.Running: start = self.dropAnimation.endValue() self.animate(self.shiftAnimation, start, end, curve, speed, delay) def disappeared(self, tile: Tetris.Tile): self.qTetris.scene.removeItem(self) def paint(self, painter: QPainter, styleOption: QStyleOptionGraphicsItem, widget: QWidget=None): pen = QPen() pen.setWidthF(0.05) pen.setColor(Qt.darkGray) painter.setPen(pen) brush = QBrush() brush.setColor(self.color) brush.setStyle(Qt.SolidPattern) painter.setBrush(brush) topLeft = QPointF(0, 0) bottomRight = QPointF(1, 1) rectangle = QRectF(topLeft, bottomRight) rectangle.translate(-0.5, -0.5) painter.drawRect(rectangle) @staticmethod def animate(animation: QPropertyAnimation, start: Union[QPointF, int, float], end: Union[QPointF, int, float], curve: QEasingCurve=QEasingCurve.Linear, speed: float=1 / 50, delay: int=-1): animation.setStartValue(start) animation.setEndValue(end) animation.setEasingCurve(curve) if type(start) == type(end) == QPointF: distance = (end - start).manhattanLength() else: distance = abs(end - start) animation.setDuration(round(distance / speed)) if delay == 0: animation.start() if delay > 0: QTimer.singleShot(delay, animation.start) def boundingRect(self): topLeft = QPointF(0, 0) bottomRight = QPointF(1, 1) rectangle = QRectF(topLeft, bottomRight) rectangle.translate(-0.5, -0.5) return rectangle
class Spoiler(QWidget): class Orientation(Enum): HORIZONTAL = 1 VERTICAL = 2 def __init__(self, orientation=Orientation.HORIZONTAL, animationDuration=250, parent=None): QWidget.__init__(self, parent) self.opened = False self.orientation = orientation self.animationDuration = animationDuration self.mainLayout = None self.animator = QParallelAnimationGroup() if orientation is self.Orientation.HORIZONTAL: self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) self.setMaximumWidth(0) self.setMinimumWidth(0) # let the entire widget grow and shrink with its content self.animator.addAnimation(QPropertyAnimation(self)) self.animator.addAnimation( QPropertyAnimation(self, b"minimumWidth")) self.animator.addAnimation( QPropertyAnimation(self, b"maximumWidth")) elif orientation is self.Orientation.VERTICAL: self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred) self.setMaximumHeight(0) self.setMinimumHeight(0) # let the entire widget grow and shrink with its content self.animator.addAnimation(QPropertyAnimation(self)) self.animator.addAnimation( QPropertyAnimation(self, b"minimumHeight")) self.animator.addAnimation( QPropertyAnimation(self, b"maximumHeight")) def open(self): self.animator.setDirection(QAbstractAnimation.Forward) self.animator.start() self.opened = True def close(self): self.animator.setDirection(QAbstractAnimation.Backward) self.animator.start() self.opened = False def isOpened(self): return self.opened def setContentLayout(self, contentLayout): self.setLayout(contentLayout) if self.orientation is self.Orientation.HORIZONTAL: collapsedSize = self.maximumWidth() contentSize = contentLayout.sizeHint().width() elif self.orientation is self.Orientation.VERTICAL: collapsedSize = self.maximumHeight() contentSize = contentLayout.sizeHint().height() i = 0 while i < self.animator.animationCount(): animation = self.animator.animationAt(i) animation.setDuration(self.animationDuration) animation.setStartValue(collapsedSize) animation.setEndValue(collapsedSize + contentSize) i += 1
def transicion_stacked(self, index_final, indexSiguientes=[]): """ Animacion de cambio del StackedWidget """ ''' # PARAMETROS *index_final: Index de pagina al que se dirije. *index_siguientes: Los index que siguen despues de el indicado del "0" le sigue [1,2], de "1" sigue [2] pero del "2" no le sigue nadie [] (esto para tener un efecto avance y retroceso) ''' # obtener index actual /*/*/*/*/*/*/ index_inicio = self.miStack.currentIndex() # condicionamiento /*/*/*/*/*/*/ '1: no debe haber transciciones en ejecucion' '2: el inicio y final no puede ser el mismo (index)' if (self.ctrl_animacion) or (index_final == index_inicio): return # control de transicion (activo) /*/*/*/*/*/*/ self.ctrl_animacion = True # index /*/*/*/*/*/*/ self.index_inicio = index_inicio self.index_final = index_final ''' #-TANSICIONES-*-*-*-*-*-*-*-*-*-*-* ↓ offsetx = 0; offsety = alto_stk ↑ offsetx = 0; offsety = -alto_stk ← offsetx = ancho_stk; offsety = 0 → offsetx = -ancho_stk; offsety = 0 #-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-* ''' # tamaño de widget para transciciones /*/*/*/*/*/*/ ancho_stk = self.miStack.width() alto_stk = self.miStack.height() # efecto de avance y retroceso /*/*/*/*/*/*/ if (self.index_inicio in indexSiguientes): offsetx = -ancho_stk offsety = 0 else: offsetx = ancho_stk offsety = 0 # configuraciones /*/*/*/*/*/*/ velocidad_tansicion = 600 efecto_transicion = QEasingCurve.OutCubic # OutCubic | InOutQuint | OutExpo self.miStack.widget(self.index_final).resize( ancho_stk, alto_stk) # establecer tamaño # posicionamiento del widget /*/*/*/*/*/*/ self.punto_actual = self.miStack.widget(self.index_inicio).pos() self.punto_final = self.miStack.widget(self.index_final).pos() # mostrar el siguiente widged en la transicion /*/*/*/*/*/*/ self.miStack.widget(self.index_final).show() # obtener punto de referencia /*/*/*/*/*/*/ offset = QPoint(offsetx, offsety) # animacion de transcicion de widgets /*/*/*/*/*/*/ animacion_stack = QParallelAnimationGroup( self.miStack, finished=self.realizarAnimacion) animacion_inicio = QPropertyAnimation( self.miStack.widget(self.index_inicio), b"pos", duration=velocidad_tansicion, easingCurve=efecto_transicion, startValue=self.punto_actual, endValue=self.punto_final - offset) animacion_final = QPropertyAnimation( self.miStack.widget(self.index_final), b"pos", duration=velocidad_tansicion, easingCurve=efecto_transicion, startValue=self.punto_actual + offset, endValue=self.punto_final) animacion_stack.addAnimation(animacion_inicio) animacion_stack.addAnimation(animacion_final) # iniciar efecto /*/*/*/*/*/*/ animacion_stack.start(QAbstractAnimation.DeleteWhenStopped)
class QtBubbleLabel(QWidget): BackgroundColor = QColor(195, 195, 195) BorderColor = QColor(150, 150, 150) def __init__(self, *args, **kwargs): super(QtBubbleLabel, self).__init__(*args, **kwargs) self.setWindowFlags(Qt.Window | Qt.Tool | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.X11BypassWindowManagerHint) self.setMinimumWidth(200) self.setMinimumHeight(48) self.setAttribute(Qt.WA_TranslucentBackground, True) layout = QVBoxLayout(self) layout.setContentsMargins(8, 8, 8, 16) self.label = QLabel(self) layout.addWidget(self.label) self._desktop = QApplication.instance().desktop() self.animationGroup = QParallelAnimationGroup(self) def setText(self, text): self.label.setText(text) def text(self): return self.label.text() def stop(self): self.hide() self.animationGroup.stop() self.animationGroup.clear() self.close() def show(self): super(QtBubbleLabel, self).show() x = self.parent().geometry().x() y = self.parent().geometry().y() x2 = self.parent().size().width() y2 = self.parent().size().height() startPos = QPoint(x + int(x2 / 2) - int(self.width() / 2), y + int(y2 / 2)) endPos = QPoint(x + int(x2 / 2) - int(self.width() / 2), y + int(y2 / 2) - self.height() * 3 - 5) self.move(startPos) # 初始化动画 self.initAnimation(startPos, endPos) def initAnimation(self, startPos, endPos): # 透明度动画 opacityAnimation = QPropertyAnimation(self, b"opacity") opacityAnimation.setStartValue(1.0) opacityAnimation.setEndValue(0.0) # 设置动画曲线 opacityAnimation.setEasingCurve(QEasingCurve.InQuad) opacityAnimation.setDuration(3000) # 在4秒的时间内完成 # 往上移动动画 moveAnimation = QPropertyAnimation(self, b"pos") moveAnimation.setStartValue(startPos) moveAnimation.setEndValue(endPos) moveAnimation.setEasingCurve(QEasingCurve.InQuad) moveAnimation.setDuration(4000) # 在5秒的时间内完成 # 并行动画组(目的是让上面的两个动画同时进行) self.animationGroup.addAnimation(opacityAnimation) self.animationGroup.addAnimation(moveAnimation) self.animationGroup.finished.connect(self.close) # 动画结束时关闭窗口 self.animationGroup.start() def paintEvent(self, event): super(QtBubbleLabel, self).paintEvent(event) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # 抗锯齿 rectPath = QPainterPath() # 圆角矩形 height = self.height() - 8 # 往上偏移8 rectPath.addRoundedRect(QRectF(0, 0, self.width(), height), 5, 5) x = self.width() / 5 * 4 # 边框画笔 painter.setPen( QPen(self.BorderColor, 1, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)) # 背景画刷 painter.setBrush(self.BackgroundColor) # 绘制形状 painter.drawPath(rectPath) def windowOpacity(self): return super(QtBubbleLabel, self).windowOpacity() def setWindowOpacity(self, opacity): super(QtBubbleLabel, self).setWindowOpacity(opacity) opacity = Property(float, windowOpacity, setWindowOpacity) def ShowMsg(self, text): self.stop() self.setText(text) self.setStyleSheet("color:black") self.show() @staticmethod def ShowMsgEx(owner, text): data = QtBubbleLabel(owner) data.setText(text) data.setStyleSheet("color:black") data.show() def ShowError(self, text): self.stop() self.setText(text) self.setStyleSheet("color:red") self.show() @staticmethod def ShowErrorEx(owner, text): data = QtBubbleLabel(owner) data.setText(text) data.setStyleSheet("color:red") data.show()