Exemple #1
0
 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)
Exemple #2
0
        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()
Exemple #3
0
    def createScene(self):
        # Root entity
        self.rootEntity = Qt3DCore.QEntity()

        # Material
        self.material = Qt3DExtras.QPhongMaterial(self.rootEntity)

        # Torus
        self.torusEntity = Qt3DCore.QEntity(self.rootEntity)
        self.torusMesh = Qt3DExtras.QTorusMesh()
        self.torusMesh.setRadius(5)
        self.torusMesh.setMinorRadius(1)
        self.torusMesh.setRings(100)
        self.torusMesh.setSlices(20)

        self.torusTransform = Qt3DCore.QTransform()
        self.torusTransform.setScale3D(QVector3D(1.5, 1, 0.5))
        self.torusTransform.setRotation(QQuaternion.fromAxisAndAngle(QVector3D(1, 0, 0), 45))

        self.torusEntity.addComponent(self.torusMesh)
        self.torusEntity.addComponent(self.torusTransform)
        self.torusEntity.addComponent(self.material)

        # Sphere
        self.sphereEntity = Qt3DCore.QEntity(self.rootEntity)
        self.sphereMesh = Qt3DExtras.QSphereMesh()
        self.sphereMesh.setRadius(3)

        self.sphereTransform = Qt3DCore.QTransform()
        self.controller = OrbitTransformController(self.sphereTransform)
        self.controller.setTarget(self.sphereTransform)
        self.controller.setRadius(20)

        self.sphereRotateTransformAnimation = QPropertyAnimation(self.sphereTransform)
        self.sphereRotateTransformAnimation.setTargetObject(self.controller)
        self.sphereRotateTransformAnimation.setPropertyName(b"angle")
        self.sphereRotateTransformAnimation.setStartValue(0)
        self.sphereRotateTransformAnimation.setEndValue(360)
        self.sphereRotateTransformAnimation.setDuration(10000)
        self.sphereRotateTransformAnimation.setLoopCount(-1)
        self.sphereRotateTransformAnimation.start()

        self.sphereEntity.addComponent(self.sphereMesh)
        self.sphereEntity.addComponent(self.sphereTransform)
        self.sphereEntity.addComponent(self.material)
Exemple #4
0
    def close_slide_menu_if_opened(self):
        width = self.ui.left_side_menu.width()
        if width == round(width_display / 10):
            newWidth = round(width_display / 18.82)
            self.ui.homeButton.setText(QCoreApplication.translate("Main", u"", None))
            self.ui.listButton.setText(QCoreApplication.translate("Main", u"", None))
            self.ui.addButton.setText(QCoreApplication.translate("Main", u"", None))
            self.ui.removeButton.setText(QCoreApplication.translate("Main", u"", None))
            self.ui.chooseTablePushButton.setText(QCoreApplication.translate("Main", u"", None))

            # Animate the transition
            self.animation = QPropertyAnimation(self.ui.left_side_menu,
                                                b"minimumWidth")
            self.animation.setDuration(250)
            self.animation.setStartValue(width)  # Start value is the current menu width
            self.animation.setEndValue(newWidth)  # end value is the new menu width
            self.animation.setEasingCurve(QtCore.QEasingCurve.InOutQuart)
            self.animation.start()
Exemple #5
0
 def __init__(self):
     super(HeuristicBar, self).__init__()
     # centring text works, but still do no rotate it
     self.setStyleSheet(
         "QProgressBar { text-align: center; background:#aaa;" +
         " } QProgressBar::chunk {" +
         "background: qlineargradient(x0:0, y0:0, x1:1, y1:0, stop:0 #222, stop:1 #333);"
         + "border-radius: 5px;" + "border-style: solid;" +
         "border-width: 1px;" + "border-color: #845;}")
     self.setOrientation(Qt.Vertical)
     self.setSizePolicy(
         QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
     # self.setMinimumWidth(60)
     self.setMinimum(-100)
     self.setMaximum(100)
     # self.setValue(0)
     self.setFormat("%p")
     self.animator = QPropertyAnimation(self, b"value")
Exemple #6
0
    def __init__(self, q, parent=None, leftText='ON', rightText='OFF'):
        QObject.__init__(self, parent=parent)
        self.leftText = leftText
        self.rightText = rightText
        self.mPointer = q
        self.mPosition = 0.0
        self.mGradient = QLinearGradient()
        self.mGradient.setSpread(QGradient.PadSpread)

        self.animation = QPropertyAnimation(self)
        self.animation.setTargetObject(self)
        self.animation.setPropertyName(b'position')
        self.animation.setStartValue(0.0)
        self.animation.setEndValue(1.0)
        self.animation.setDuration(200)
        self.animation.setEasingCurve(QEasingCurve.InOutExpo)

        self.animation.finished.connect(self.mPointer.update)
Exemple #7
0
 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)
Exemple #8
0
    def mousePressEvent(self, mouseEvent: QMouseEvent):
        closestNode = self.playerNode.closest(mouseEvent.pos() - self.paintOffset)
        direction = closestNode.row - self.playerNode.row, closestNode.column - self.playerNode.column
        crawlNode = self.playerNode.crawl(direction)

        self.animation = QPropertyAnimation(self, b"player", self)
        if len(crawlNode.links) > 2:
            self.animation.setEasingCurve(QEasingCurve.OutBack);
        else:
            self.animation.setEasingCurve(QEasingCurve.OutBounce);
        self.animation.setStartValue(self.player)
        self.animation.setEndValue(crawlNode.point)
        self.animation.setDuration(400)
        self.animation.start()

        self.playerNode = crawlNode
        if self.playerNode == self.finishNode:
            QMessageBox.information(self, self.tr("Victory!"), self.tr("You won :)"), QMessageBox.Ok)
            self.initMaze()
Exemple #9
0
 def opacityAnimation(self, end, duration, callback):
     anims = []
     for fx in self.opacFX:
         ani = QPropertyAnimation(fx, b"opacity")
         ani.setStartValue(fx.opacity())
         ani.setEndValue(end)
         a = max(fx.opacity(), end)
         i = min(fx.opacity(), end)
         ani.setDuration((a - i) * duration)
         ani.stateChanged.connect(callback)
         anims.append(ani)
     return anims
Exemple #10
0
    def animate_hover(self, enter):
        base_color = QColor(244, 84, 67, 100)
        hover_color = QColor(244, 84, 67, 255)

        start_value = base_color if enter else hover_color

        if self.transition:
            start_value = self.transition.currentValue()
            self.transition.stop()

        self.transition = QPropertyAnimation(self, b"palete")

        self.transition.setStartValue(start_value)
        self.transition.setEndValue(hover_color if enter else base_color)
        self.transition.setDuration(100)
        self.transition.setDirection(QPropertyAnimation.Forward)
        self.transition.valueChanged.connect(
            lambda: self.restyle(self.transition.currentValue()))

        self.transition.start()
Exemple #11
0
    def toggle_menu(self, max_width, enable):
        if enable:
            """GET WIDTH"""
            width = self.ui.frame_left_menu.width()
            max_extend = max_width
            standard = 70

            """SET MAX WIDTH"""
            if width == 70:
                width_extended = max_extend
            else:
                width_extended = standard

            """ANIMATION"""
            self.animation = QPropertyAnimation(self.ui.frame_left_menu, b"minimumWidth")
            self.animation.setDuration(300)
            self.animation.setStartValue(width)
            self.animation.setEndValue(width_extended)
            self.animation.setEasingCurve(QtCore.QEasingCurve.InOutQuart)
            self.animation.start()
Exemple #12
0
    def animate_pl_move(self, i, j):
        if not self.is_position_valid(i, j):
            return
        self.raise_()
        self.animator.stop()
        self.animator = QPropertyAnimation(self, b"geometry")
        y = (self.table_width - 20) / 8 * i + 10
        x = (self.table_width - 20) / 8 * j + 10
        old_rect = QRect(self.pos().x(),
                         self.pos().y(), self.width(), self.height())
        rect = QRect(x, y, self.height(), self.width())
        self.animator.setDuration(500)
        self.animator.setStartValue(old_rect)
        self.animator.setEndValue(rect)
        self.animator.finished.connect(self.calc_position)

        self.movable = False
        self.set_cursor_pointing(False)

        self.animator.start()
Exemple #13
0
    def toggleMenu(self, maxWidth, enable):
        if enable:
            # GET WIDTH
            width = self.ui.frame_left_menu.width()
            maxExtend = maxWidth
            standard = 70

            # SET MAX WIDTH
            if width == 70:
                widthExtended = maxExtend
            else:
                widthExtended = standard

            # ANIMATION
            self.animation = QPropertyAnimation(self.ui.frame_left_menu, b"minimumWidth")
            self.animation.setDuration(300)
            self.animation.setStartValue(width)
            self.animation.setEndValue(widthExtended)
            self.animation.setEasingCurve(QtCore.QEasingCurve.InOutQuart)
            self.animation.start()
    def searched(self):
        """
        Provides the color changing animation for recently searched nodes
        """

        self.draw(Cell.SEARCHED)

        if self.node.isStart:
            self.setStart()
        elif self.node.isEnd:
            self.setEnd()
        else:

            self.effect = QGraphicsColorizeEffect(self)
            self.setGraphicsEffect(self.effect)

            self.paAnimation = QPropertyAnimation(self.effect, b"color")
            self.paAnimation.setStartValue(QColor(131, 18, 165))
            self.paAnimation.setEndValue(Cell.SEARCHED)
            self.paAnimation.setDuration(1000)
            self.paAnimation.start()
Exemple #15
0
    def toggle_menu(self, maxWidth, enable):  # Toggle Menu Bar
        if enable:
            # Get Width
            width = self.ui.frame_pages.width()
            maxExtend = maxWidth
            standard = 0

            # Set Max Width
            if width == 0:
                widthExtended = maxExtend
            else:
                widthExtended = standard

            # Animation
            self.animation = QPropertyAnimation(self.ui.frame_pages,
                                                b"minimumWidth")
            self.animation.setDuration(400)
            self.animation.setStartValue(width)
            self.animation.setEndValue(widthExtended)
            self.animation.setEasingCurve(QEasingCurve.InOutQuart)
            self.animation.start()
Exemple #16
0
    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"))
Exemple #17
0
    def __init__(self,
                 widget: QWidget,
                 bg_color: Tuple[int, int, int, int],
                 additional_stylesheet: str = ''):
        """ Animate provided Widget background stylesheet color

        :param widget:
        :param bg_color:
        """
        super(BgrAnimation, self).__init__(widget)
        self.widget = widget
        self.color = QColor()

        self.bg_color = self.widget.palette().color(QPalette.Background)
        self.additional_stylesheet = additional_stylesheet
        if bg_color:
            self.bg_color = QColor(*bg_color)

        self.color_anim = QPropertyAnimation(self, b'backColor')
        self.color_anim.setEasingCurve(QEasingCurve.InOutSine)
        self._setup_blink()

        self.pulsate_anim = QPropertyAnimation(self, b'backColor')
        self.pulsate_anim.setEasingCurve(QEasingCurve.InOutQuint)
        self._setup_pulsate()

        self.fade_anim = QPropertyAnimation(self, b'backColor')
        self.fade_anim.setEasingCurve(QEasingCurve.InCubic)
	def __init__(self):
		QMainWindow.__init__(self)
		self.setWindowFlags(Qt.FramelessWindowHint)

		self.setFixedSize(300,300)
		layout = QVBoxLayout()
		label = QLabel()
		label.setFixedSize(280,280)
		layout.addWidget(label, alignment=Qt.AlignCenter)
		self.setStyleSheet("QLabel{background-color:white}")
		self.blink_animation = QPropertyAnimation(self, b"backcolor")
		self.blink_animation.setStartValue(QColor(255,0,0))
		self.blink_animation.setKeyValueAt(0.5, QColor(0,0,255))
		self.blink_animation.setEndValue(QColor(255,0,0))
		self.blink_animation.setDuration(3000)

		#self.blink_animation.setLoopCount(-1)
		self.blink_animation.start()

		container = QWidget()
		container.setLayout(layout)
		self.setCentralWidget(container)
    def __init__(self, text, Rect, scale, speed, line, color, bold, parent=None):
        super(scrollTextLabel, self).__init__(parent)

        if color == 'red':
            self.color = QColor(231, 0, 18, 255)  # red
        elif color == 'white':
            self.color = QColor(255, 255, 246, 255)  # white
        elif color == 'Grass':
            self.color = QColor(144, 195, 32, 255)  # Grass
        elif color == 'Blue':
            self.color = QColor(0, 160, 234, 255)  # Blue
        if bold:
            self.font = QFont("Helvetica", scale, QFont.Bold)  # 20 25 30 粗體
        else:
            self.font = QFont("Helvetica", scale)
        self.txt = text
        self.speed = speed  # between 50 ~ 120

        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setWindowFlags(
            Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.WindowCloseButtonHint)  # 隱藏 FramelessWindow

        self.metrics = QFontMetrics(self.font)
        self.setFixedWidth(self.metrics.width(self.txt) + 10)
        self.setFixedHeight(self.metrics.height() + 5)

        # self.move(Rect.x() + Rect.width() * 0.97, Rect.y() + 50 * line)
        self.setFocusPolicy(Qt.NoFocus)
        self.hide()
        self.anim = QPropertyAnimation(self, 'pos')
        self.anim.setDuration(self.speed * 100)
        self.anim.setStartValue(QPoint(Rect.x() + Rect.width() * 0.95, Rect.y() + 50 * line))
        self.anim.setEndValue(QPoint(-self.width() + Rect.x(), Rect.y() + 50 * line))
        self.anim.setEasingCurve(QEasingCurve.Linear)

        self.show()
        self.repaint()
        self.anim.start()
        self.anim.finished.connect(self.sendslot)
 def _get_animation_slot(self, window, animation_property):
     # clear expired slots
     self.animations[:] = [x for x in self.animations if x is not None]
     if len(self.animations) > 100:
         raise MemoryError("Animation stack overflow?")
     # finish any move animations that affect the current window:
     for animation in self.animations:
         if animation.targetObject() == window:
             endpoint = animation.endValue()
             animation.stop()
             window.move(endpoint)
     self.animations.append(QPropertyAnimation(window, animation_property))
     return self.animations[len(self.animations) - 1]
Exemple #21
0
class TabBgrAnimation(BgrAnimation):
    def __init__(self, tab_widget):
        """ Animate the background color of the last tab of a tabwidget

        :param tab_widget:
        """
        widget = tab_widget
        if type(tab_widget) is QTabWidget:
            widget = tab_widget.tabBar()
            LOGGER.debug('Tab Bar: %s', widget)

        self.style = '''
            QTabBar::tab:last {{ background-color: {}; 
                                 border: 1px solid rgb(221, 221, 221); padding-left: 15px;}}
            QTabBar::tab:last:selected {{ background-color: rgb(255, 255, 255);}}
            QTabBar::tab:last:hover {{ background-color: rgb(216, 234, 249);}}
        '''

        super(TabBgrAnimation, self).__init__(widget, tuple())

        # -- Overwrite color_anim --
        #    makes sure we do not animate backColor wihich would actually change tabWidgets
        #    background.
        self.color_anim = QPropertyAnimation(self, b'styleAnim')
        self.color_anim.setEasingCurve(QEasingCurve.InOutSine)
        self._setup_blink()

    def _get_back_color(self):
        return self.color

    def _set_back_color(self, color):
        self.color = color
        qss_color = f'rgba({color.red()}, {color.green()}, {color.blue()}, {color.alpha()})'
        try:
            self.widget.setStyleSheet(self.style.format(qss_color))
        except AttributeError as e:
            LOGGER.debug('Error setting widget background color: %s', e)

    styleAnim = Property(QColor, _get_back_color, _set_back_color)
Exemple #22
0
 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)
Exemple #23
0
 def __init__(self, *args, pos=(0, 0), shift=(0, 0), length=30, **kwargs):
     super(FWidget, self).__init__(*args, **kwargs)
     self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowMinimizeButtonHint)
     self.setAttribute(Qt.WA_TranslucentBackground)
     self.setAttribute(Qt.WA_TransparentForMouseEvents, True)
     self.length = length
     self.lengths = dict((i, length * i / 6) for i in range(1, 13))
     self.resize(self.lengths[12], self.lengths[12])
     self.center = length * 10, length * 9
     self.opacity = QGraphicsOpacityEffect(self)
     x, y = pos
     a, b = shift
     self.move(x + a, y + b)
     self.setGraphicsEffect(self.opacity)
     self.opacity.setOpacity(0)
     self.animation = QPropertyAnimation(self.opacity, b'opacity')
     self.animation.setDuration(1000)
     self.animation.setStartValue(0)
     # self.animation.setKeyValueAt(0.25, 0.67)
     self.animation.setKeyValueAt(0.5, 1)
     # self.animation.setKeyValueAt(0.75, 0.67)
     self.animation.setEndValue(0)
     self.animation.setLoopCount(-1)
Exemple #24
0
class UIFunctions(MainWindow):

    def toggle_menu(self, maxWidth, enable):
        if enable:

            # GET WIDTH
            width = self.ui.frame_left_menu.width()
            max_extend = maxWidth
            standard = 110

            # SET MAX WIDTH
            if width == 110:
                width_extended = max_extend
            else:
                width_extended = standard

            # ANIMATION
            self.animation = QPropertyAnimation(self.ui.frame_left_menu, b"minimumWidth")
            self.animation.setDuration(400)
            self.animation.setStartValue(width)
            self.animation.setEndValue(width_extended)
            self.animation.setEasingCurve(QtCore.QEasingCurve.InOutQuart)
            self.animation.start()
Exemple #25
0
 def toggle(self, maxWidth=150, check=True):
     if check:
         width = self.left_menu.width()
         print(width)
         maxExtend = maxWidth
         standart = 70
         if width == 70:
             widthExtended = maxExtend
             self.pb_settings.setText("Settings")
             self.pb_form.setText("Form")
             self.pb_home.setText("Home")
         else:
             widthExtended = standart
             self.pb_settings.setText("")
             self.pb_form.setText("")
             self.pb_home.setText("")
         #Animation#
         self.animation = QPropertyAnimation(self.left_menu,
                                             b"minimumWidth")
         self.animation.setDuration(400)
         self.animation.setStartValue(width)
         self.animation.setEndValue(widthExtended)
         self.animation.start()
Exemple #26
0
        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()
Exemple #27
0
    def createScene(self):
        # Root entity
        self.rootEntity = Qt3DCore.QEntity()

        # Material
        self.material = Qt3DExtras.QPhongMaterial(self.rootEntity)

        # Torus
        self.torusEntity = Qt3DCore.QEntity(self.rootEntity)
        self.torusMesh = Qt3DExtras.QTorusMesh()
        self.torusMesh.setRadius(5)
        self.torusMesh.setMinorRadius(1)
        self.torusMesh.setRings(100)
        self.torusMesh.setSlices(20)

        self.torusTransform = Qt3DCore.QTransform()
        self.torusTransform.setScale3D(QVector3D(1.5, 1, 0.5))
        self.torusTransform.setRotation(QQuaternion.fromAxisAndAngle(QVector3D(1, 0, 0), 45))

        self.torusEntity.addComponent(self.torusMesh)
        self.torusEntity.addComponent(self.torusTransform)
        self.torusEntity.addComponent(self.material)

        # Sphere
        self.sphereEntity = Qt3DCore.QEntity(self.rootEntity)
        self.sphereMesh = Qt3DExtras.QSphereMesh()
        self.sphereMesh.setRadius(3)

        self.sphereTransform = Qt3DCore.QTransform()
        self.controller = OrbitTransformController(self.sphereTransform)
        self.controller.setTarget(self.sphereTransform)
        self.controller.setRadius(20)

        self.sphereRotateTransformAnimation = QPropertyAnimation(self.sphereTransform)
        self.sphereRotateTransformAnimation.setTargetObject(self.controller)
        self.sphereRotateTransformAnimation.setPropertyName(b"angle")
        self.sphereRotateTransformAnimation.setStartValue(0)
        self.sphereRotateTransformAnimation.setEndValue(360)
        self.sphereRotateTransformAnimation.setDuration(10000)
        self.sphereRotateTransformAnimation.setLoopCount(-1)
        self.sphereRotateTransformAnimation.start()

        self.sphereEntity.addComponent(self.sphereMesh)
        self.sphereEntity.addComponent(self.sphereTransform)
        self.sphereEntity.addComponent(self.material)
Exemple #28
0
    def __init__(self, parent: QWidget = None):
        super(ShadowClock, self).__init__(parent)
        self.__radiusWidth: int = 6  # 半径宽度
        self.__shadowWidth: int = 4  # 光晕宽度

        self.__textColor: QColor = QColor("#22A3A9")  # 文本颜色
        self.__shadowColor: QColor = QColor("#22A3A9")  # 光晕颜色
        self.__hourColor: QColor = QColor("#22A3A9")  # 时钟颜色
        self.__minuteColor: QColor = QColor("#22A3A9")  # 分钟颜色
        self.__secondColor: QColor = QColor("#22A3A9")  # 秒钟颜色

        # 采用动画机制,产生过渡效果
        self.animation: QPropertyAnimation = QPropertyAnimation(self, b'')
        self.animation.valueChanged.connect(self.update)
        self.animation.setDuration(1000)
        self.animation.setStartValue(0)
        self.animation.setEndValue(20)
        self.animation.setLoopCount(-1)
        self.animation.start()
Exemple #29
0
class AnimateWindowOpacity:
    def __init__(self,
                 widget: QWidget,
                 duration: int,
                 start_value: float = 0.8,
                 end_value: float = 1.0):
        self.widget = widget
        self.duration = duration
        self.animation = QPropertyAnimation(self.widget, b"windowOpacity")
        self.start_value, self.end_value = start_value, end_value
        self.setup_animation(self.start_value, self.end_value)

    def setup_animation(self,
                        start_value: float = 0.0,
                        end_value: float = 1.0,
                        duration: int = 0):
        if not duration:
            duration = self.duration

        self.animation.setDuration(duration)
        self.animation.setKeyValueAt(0.0, start_value)
        self.animation.setKeyValueAt(1.0, end_value)
        self.animation.setEasingCurve(QEasingCurve.OutCubic)

    def fade_in(self, duration: int = 0):
        if self.widget.windowOpacity() >= self.end_value:
            return False

        self.setup_animation(self.start_value, self.end_value, duration)
        self.play()
        return True

    def fade_out(self, duration: int = 0):
        if self.widget.windowOpacity() <= self.start_value:
            return False

        self.setup_animation(self.end_value, self.start_value, duration)
        self.play()
        return True

    def play(self):
        if self.animation.state() != QAbstractAnimation.Running:
            self.animation.start()
Exemple #30
0
class AnimatedButton:
    def __init__(self, btn, duration):
        self.btn = btn
        self.duration = duration

        self.animation = QPropertyAnimation(self.btn, b"iconSize")

        self.setup_animation()

    def setup_animation(self):
        size = self.btn.iconSize()
        start_value = QSize(round(size.width() * 0.2),
                            round(size.height() * 0.2))
        end_value = size

        self.animation.setDuration(self.duration)
        self.animation.setKeyValueAt(0.0, end_value)
        self.animation.setKeyValueAt(0.5, start_value)
        self.animation.setKeyValueAt(1.0, end_value)
        self.animation.setEasingCurve(QEasingCurve.OutElastic)

    def play_highlight(self):
        self.animation.setDuration(self.duration)
        self.animation.setEasingCurve(QEasingCurve.OutElastic)
        self.play()

    def play_on(self):
        self.animation.setDuration(round(self.duration * 0.3))
        self.animation.setEasingCurve(QEasingCurve.InCirc)
        self.play()

    def play_off(self):
        self.animation.setDuration(round(self.duration * 0.3))
        self.animation.setEasingCurve(QEasingCurve.OutCirc)
        self.play()

    def play(self, event=None):
        if self.animation.state() != QAbstractAnimation.Running:
            self.animation.start()
Exemple #31
0
class KnechtExpandableWidget(QObject):
    expand_height = 250

    def __init__(self,
                 widget: QWidget,
                 expand_btn: QPushButton = None,
                 collapsible_widget: QWidget = None):
        """ Method to add expandable widget functionality to a widget containing an collapsible widget

        :param widget: the widget which size will be changed
        :param expand_btn: the button which will toggle the expanded/collapsed state
        :param collapsible_widget: the widget contained inside >>widget<< whose minimum size height hint will be
                                   set to 0 so it becomes collapsible
        """
        super(KnechtExpandableWidget, self).__init__()

        self.widget = widget

        self.size_animation = QPropertyAnimation(self.widget, b'size')
        self.size_animation.setDuration(150)
        self.size_animation.setEasingCurve(QEasingCurve.OutCurve)
        self.size_animation.finished.connect(self.widget.updateGeometry)

        self.expand_toggle_btn = expand_btn or QPushButton(self)
        self.expand_toggle_btn.setCheckable(True)
        self.expand_toggle_btn.released.connect(self.expand_widget)

        self.collapsible_widget = collapsible_widget or QWidget(self)
        self.collapsible_widget.minimumSizeHint = self._custom_minimum_size_hint

        self.org_resize = self.widget.resizeEvent
        self.widget.resizeEvent = self._custom_resize

        LOGGER.debug('Initialized Expandable Widget with parent: %s',
                     self.parent())

    def _custom_minimum_size_hint(self):
        return QSize(self.collapsible_widget.sizeHint().width(), 0)

    def _custom_resize(self, event):
        self.org_resize(event)
        collapsed_height = self.widget.minimumSizeHint().height()

        if self.size_animation.state() != QAbstractAnimation.Running:
            if event.size().height() > collapsed_height:
                self.expand_toggle_btn.setChecked(True)
            else:
                self.expand_toggle_btn.setChecked(False)

        event.accept()

    def toggle_expand(self, immediate: bool = False):
        if self.expand_toggle_btn.isChecked():
            self.expand_toggle_btn.setChecked(False)
            self.expand_widget(immediate=immediate)
        else:
            self.expand_toggle_btn.setChecked(True)
            self.expand_widget(immediate=immediate)

    def expand_widget(self, immediate: bool = False):
        if self.expand_toggle_btn.isChecked():
            expand_height = self.widget.size().height()
        else:
            expand_height = 0

        expanded_size = QSize(
            self.widget.size().width(),
            self.widget.minimumSizeHint().height() + expand_height)

        self.size_animation.setStartValue(self.widget.size())
        self.size_animation.setEndValue(expanded_size)

        if immediate:
            self.widget.resize(expanded_size)
            self.widget.updateGeometry()
        else:
            self.size_animation.start()
Exemple #32
0
class QMaze(QWidget):

    class QNode:
        def __init__(self, maze: 'QMaze', row: int, column: int):
            self.row, self.column = row, column
            self.maze = maze
            self.neighbors = []
            self.links = []

        @property
        def point(self) -> QPoint:
            return QPoint(self.column * self.maze.paintStep, self.row * self.maze.paintStep)

        def closest(self, point: QPoint) -> 'QMaze.QNode':
            closestNode, closestDistance = None, float("inf")
            for node in self.links:
                delta = point - node.point
                distance = delta.manhattanLength()
                if distance < closestDistance:
                    closestNode, closestDistance = node, distance
            return closestNode

        def crawl(self, direction: tuple, distance: int=1) -> 'QMaze.QNode':
            if len(self.links) > 2 and distance > 1:
                return self
            for link in self.links:
                if direction == (link.row - self.row, link.column - self.column):
                    return link.crawl(direction, distance + 1)
            else:
                return self

    def __init__(self, size: int):
        super(QMaze, self).__init__()
        self.size = size
        self.nodes = None
        self.animation = None
        self.startNode = None
        self.finishNode = None
        self.playerNode = None
        self.paintStep = 0
        self.paintOffset = 0

        self.initMaze()
        self.initUI()
        self.show()

    def getPlayer(self) -> QPoint:
        return self._player

    def setPlayer(self, point: QPoint):
        self._player = point
        self.update()

    player = Property(QPoint, getPlayer, setPlayer)

    def initMaze(self):
        self.nodes = {}
        for row in range(self.size):
            for column in range(self.size):
                self.nodes[row, column] = QMaze.QNode(self, row, column)

        for (row, column), node in self.nodes.items():
            if 0 < row:
                node.neighbors.append(self.nodes[row - 1, column])
            if row < self.size - 1:
                node.neighbors.append(self.nodes[row + 1, column])
            if 0 < column:
                node.neighbors.append(self.nodes[row, column - 1])
            if column < self.size - 1:
                node.neighbors.append(self.nodes[row, column + 1])

        self.startNode = self.nodes[0, 0]
        self.finishNode = self.generateMaze(self.startNode)
        self.playerNode = self.startNode
        self.player = self.playerNode.point

    def generateMaze(self, start: 'QMaze.QNode') -> 'QMaze.QNode':
        generated = set()
        deepestNode, deepestRecursion = start, -1

        def generateNode(node, recursion=0):
            nonlocal generated, deepestNode, deepestRecursion
            if node in generated:
                return
            generated.add(node)
            for neighbor in random.sample(node.neighbors, len(node.neighbors)):
                if neighbor not in generated:
                    node.links.append(neighbor)
                    neighbor.links.append(node)
                    generateNode(neighbor, recursion + 1)
            if recursion > deepestRecursion:
                deepestNode, deepestRecursion = node, recursion

        generateNode(start)
        return deepestNode

    def initUI(self):
        self.setWindowTitle(self.tr("Maze"))

    def mousePressEvent(self, mouseEvent: QMouseEvent):
        closestNode = self.playerNode.closest(mouseEvent.pos() - self.paintOffset)
        direction = closestNode.row - self.playerNode.row, closestNode.column - self.playerNode.column
        crawlNode = self.playerNode.crawl(direction)

        self.animation = QPropertyAnimation(self, b"player", self)
        if len(crawlNode.links) > 2:
            self.animation.setEasingCurve(QEasingCurve.OutBack);
        else:
            self.animation.setEasingCurve(QEasingCurve.OutBounce);
        self.animation.setStartValue(self.player)
        self.animation.setEndValue(crawlNode.point)
        self.animation.setDuration(400)
        self.animation.start()

        self.playerNode = crawlNode
        if self.playerNode == self.finishNode:
            QMessageBox.information(self, self.tr("Victory!"), self.tr("You won :)"), QMessageBox.Ok)
            self.initMaze()

    def paintEvent(self, paintEvent: QPaintEvent):
        pen = QPen()
        pen.setJoinStyle(Qt.RoundJoin)
        pen.setCapStyle(Qt.RoundCap)
        painter = QPainter(self)
        painter.translate(self.paintOffset)
        painter.setBackgroundMode(Qt.TransparentMode)
        painter.setRenderHint(QPainter.Antialiasing)

        if self.nodes is not None:
            painted = set()

            def paintNode(node):
                nonlocal painter, painted
                if node in painted:
                    return
                painted.add(node)
                for link in node.links:
                    if link not in painted:
                        painter.drawLine(node.point, link.point)
                        paintNode(link)

            color = self.palette().color(QPalette.Dark)
            pen.setColor(color)
            pen.setWidth(0.50 * self.paintStep)
            painter.setPen(pen)
            for node in self.nodes.values():
                if paintEvent.region().contains(node.point):
                    paintNode(node)

        if self.startNode is not None:
            color = self.palette().color(QPalette.Dark)
            pen.setColor(color)
            pen.setWidth(0.75 * self.paintStep)
            painter.setPen(pen)
            if paintEvent.region().contains(self.startNode.point):
                painter.drawPoint(self.startNode.point)

        if self.finishNode is not None and paintEvent.region().contains(self.finishNode.point):
            color = self.palette().color(QPalette.Dark).darker(120)
            pen.setColor(color)
            pen.setWidth(0.75 * self.paintStep)
            painter.setPen(pen)
            painter.drawPoint(self.finishNode.point)

        if self.player is not None:
            color = self.palette().color(QPalette.Highlight)
            color.setAlpha(196)
            pen.setColor(color)
            pen.setWidth(0.90 * self.paintStep)
            painter.setPen(pen)
            painter.drawPoint(self.player)

        del painter, pen

    def resizeEvent(self, resizeEvent: QResizeEvent):
        self.paintStep = min(self.width() / self.size, self.height() / self.size)
        self.paintOffset = QPoint((self.paintStep + (self.width() - self.paintStep * self.size)) / 2,
                                  (self.paintStep + (self.height() - self.paintStep * self.size)) / 2)
        self.player = self.playerNode.point

    def sizeHint(self) -> QSize:
        paintStepHint = 40
        return QSize(self.size * paintStepHint, self.size * paintStepHint)
Exemple #33
0
class Window(Qt3DExtras.Qt3DWindow):
    def __init__(self):
        super(Window, self).__init__()

        # Camera
        self.camera().lens().setPerspectiveProjection(45, 16 / 9, 0.1, 1000)
        self.camera().setPosition(QVector3D(0, 0, 40))
        self.camera().setViewCenter(QVector3D(0, 0, 0))

        # For camera controls
        self.createScene()
        self.camController = Qt3DExtras.QOrbitCameraController(self.rootEntity)
        self.camController.setLinearSpeed(50)
        self.camController.setLookSpeed(180)
        self.camController.setCamera(self.camera())

        self.setRootEntity(self.rootEntity)

    def createScene(self):
        # Root entity
        self.rootEntity = Qt3DCore.QEntity()

        # Material
        self.material = Qt3DExtras.QPhongMaterial(self.rootEntity)

        # Torus
        self.torusEntity = Qt3DCore.QEntity(self.rootEntity)
        self.torusMesh = Qt3DExtras.QTorusMesh()
        self.torusMesh.setRadius(5)
        self.torusMesh.setMinorRadius(1)
        self.torusMesh.setRings(100)
        self.torusMesh.setSlices(20)

        self.torusTransform = Qt3DCore.QTransform()
        self.torusTransform.setScale3D(QVector3D(1.5, 1, 0.5))
        self.torusTransform.setRotation(QQuaternion.fromAxisAndAngle(QVector3D(1, 0, 0), 45))

        self.torusEntity.addComponent(self.torusMesh)
        self.torusEntity.addComponent(self.torusTransform)
        self.torusEntity.addComponent(self.material)

        # Sphere
        self.sphereEntity = Qt3DCore.QEntity(self.rootEntity)
        self.sphereMesh = Qt3DExtras.QSphereMesh()
        self.sphereMesh.setRadius(3)

        self.sphereTransform = Qt3DCore.QTransform()
        self.controller = OrbitTransformController(self.sphereTransform)
        self.controller.setTarget(self.sphereTransform)
        self.controller.setRadius(20)

        self.sphereRotateTransformAnimation = QPropertyAnimation(self.sphereTransform)
        self.sphereRotateTransformAnimation.setTargetObject(self.controller)
        self.sphereRotateTransformAnimation.setPropertyName(b"angle")
        self.sphereRotateTransformAnimation.setStartValue(0)
        self.sphereRotateTransformAnimation.setEndValue(360)
        self.sphereRotateTransformAnimation.setDuration(10000)
        self.sphereRotateTransformAnimation.setLoopCount(-1)
        self.sphereRotateTransformAnimation.start()

        self.sphereEntity.addComponent(self.sphereMesh)
        self.sphereEntity.addComponent(self.sphereTransform)
        self.sphereEntity.addComponent(self.material)
class Cell(QLabel):
    """
    The visual representation of a Node
    """

    EMPTY = QColor(0, 0, 0, 120)
    SEARCHED = QColor(0, 55, 165)
    IN_LIST = SEARCHED.lighter(f=150)
    PATH = QColor(255, 255, 0)
    WALL = QColor(255, 255, 255, 150)

    def __init__(self, node, mainWindow):
        # Init superclass
        QLabel.__init__(self)

        self.mainWindow = mainWindow
        self.node = node
        node.setCell(self)

        # -- Getting expected GUI behavior --
        self.setAutoFillBackground(True)  # Allows for automatic color updating
        self.setSizePolicy(
            QSizePolicy.Ignored,
            QSizePolicy.Ignored)  # Allows cells to adapt to window size
        self.setScaledContents(True)  # Makes images scale to cell size
        self.setAttribute(Qt.WA_Hover,
                          True)  # Allows the enterEvent definition to work

        # Animation variables
        self.effect = None
        self.paAnimation = None

        # Initialize as empty cell
        self.draw(Cell.EMPTY)

    def draw(self, color: QColor):
        """
        Draws this cell with the specified color. The class variables will be passed in, but any color can be used
        """
        self.clear()
        self.setGraphicsEffect(None)

        pal = QPalette()
        pal.setColor(QPalette.Background, color)
        self.setPalette(pal)

    def setStart(self):
        """
        Draws this cell as the start node
        """
        self.setPixmap(QPixmap(":/icon/icons/start.png"))

    def setEnd(self):
        """
        Draws this cell as the end node
        """
        self.setPixmap(QPixmap(":/icon/icons/end.png"))

    def setWall(self, wall: bool):
        """
        Draws this cell as a wall and updates the node to reflect it
        """
        if wall:
            self.draw(Cell.WALL)
            self.setPixmap(QPixmap(":/icon/icons/wall.png"))
            self.node.wall = True
        else:
            self.draw(Cell.EMPTY)
            self.node.wall = False

    def inPath(self):
        self.draw(Cell.PATH)

        if self.node.isStart:
            self.setStart()
        elif self.node.isEnd:
            self.setEnd()

    def searched(self):
        """
        Provides the color changing animation for recently searched nodes
        """

        self.draw(Cell.SEARCHED)

        if self.node.isStart:
            self.setStart()
        elif self.node.isEnd:
            self.setEnd()
        else:

            self.effect = QGraphicsColorizeEffect(self)
            self.setGraphicsEffect(self.effect)

            self.paAnimation = QPropertyAnimation(self.effect, b"color")
            self.paAnimation.setStartValue(QColor(131, 18, 165))
            self.paAnimation.setEndValue(Cell.SEARCHED)
            self.paAnimation.setDuration(1000)
            self.paAnimation.start()

    def mousePressEvent(self, event):
        """
        Determines if self is the start or end node or neither, and updates things accordingly
        NOTE: The mouse release event is handled by the window
        """

        if self.node.isStart:
            QApplication.setOverrideCursor(
                QCursor(
                    QPixmap(":/icon/icons/start.png").scaledToHeight(
                        self.height())))
            self.clear()
            self.draw(Cell.EMPTY)
            self.mainWindow.changingStart = True
        elif self.node.isEnd:
            QApplication.setOverrideCursor(
                QPixmap(":/icon/icons/end.png").scaledToHeight(self.height()))
            self.clear()
            self.draw(Cell.EMPTY)
            self.mainWindow.changingEnd = True
        elif self.node.wall:
            self.mainWindow.erasingWall = True
            self.setWall(False)
        else:
            self.mainWindow.drawingWall = True
            self.setWall(True)
Exemple #35
0
class BgrAnimation(QObject):
    def __init__(self,
                 widget: QWidget,
                 bg_color: Tuple[int, int, int, int],
                 additional_stylesheet: str = ''):
        """ Animate provided Widget background stylesheet color

        :param widget:
        :param bg_color:
        """
        super(BgrAnimation, self).__init__(widget)
        self.widget = widget
        self.color = QColor()

        self.bg_color = self.widget.palette().color(QPalette.Background)
        self.additional_stylesheet = additional_stylesheet
        if bg_color:
            self.bg_color = QColor(*bg_color)

        self.color_anim = QPropertyAnimation(self, b'backColor')
        self.color_anim.setEasingCurve(QEasingCurve.InOutSine)
        self._setup_blink()

        self.pulsate_anim = QPropertyAnimation(self, b'backColor')
        self.pulsate_anim.setEasingCurve(QEasingCurve.InOutQuint)
        self._setup_pulsate()

        self.fade_anim = QPropertyAnimation(self, b'backColor')
        self.fade_anim.setEasingCurve(QEasingCurve.InCubic)

    def fade(self, start_color: tuple, end_color: tuple, duration: int):
        self.fade_anim.setStartValue(QColor(*start_color))
        self.fade_anim.setEndValue(QColor(*end_color))
        self.fade_anim.setDuration(duration)

        self.fade_anim.start(QAbstractAnimation.KeepWhenStopped)

    def _setup_blink(self, anim_color: tuple = (26, 118, 255, 255)):
        start_color = self.bg_color
        anim_color = QColor(*anim_color)

        self.color_anim.setStartValue(start_color)
        self.color_anim.setKeyValueAt(0.5, anim_color)
        self.color_anim.setEndValue(start_color)

        self.color_anim.setDuration(600)

    def blink(self, num: int = 1):
        self.pulsate_anim.stop()
        self.color_anim.setLoopCount(num)
        self.color_anim.start()

    def _setup_pulsate(self, anim_color: tuple = (255, 80, 50, 255)):
        start_color = self.bg_color
        anim_color = QColor(*anim_color)

        self.pulsate_anim.setStartValue(start_color)
        self.pulsate_anim.setKeyValueAt(0.5, anim_color)
        self.pulsate_anim.setEndValue(start_color)

        self.pulsate_anim.setDuration(4000)

    def active_pulsate(self, num: int = -1):
        self.pulsate_anim.setLoopCount(num)
        self.pulsate_anim.start()

    def _get_back_color(self):
        return self.color

    def _set_back_color(self, color):
        self.color = color
        qss_color = f'rgba({color.red()}, {color.green()}, {color.blue()}, {color.alpha()})'
        try:
            self.widget.setStyleSheet(
                f'background-color: {qss_color};{self.additional_stylesheet}')
        except AttributeError as e:
            LOGGER.debug('Error setting widget background color: %s', e)

    backColor = Property(QColor, _get_back_color, _set_back_color)
Exemple #36
0
class informationWidget(QWidget):

    def __init__(self, parent):
        super(informationWidget, self).__init__(parent)
        Pwidth = parent.width()
        self.resize(Pwidth, 28)
        _color = QColor(151, 238, 238)
        _color.setAlphaF(0.6)
        _palette = QPalette()
        _palette.setBrush(self.backgroundRole(), _color)
        self.setPalette(_palette)
        self.setAutoFillBackground(True)
        self.setGeometry(QRect(0, -28, Pwidth, 28))

        self.MainHLayout = QHBoxLayout(self)
        self.MainHLayout.setSpacing(3)
        self.MainHLayout.setContentsMargins(0, 0, 0, 0)
        #self.MainHLayout.setStretch(1, 1)

        self.msg_label = QLabel(self)
        self.msg_label.setStyleSheet("background-color: transparent;")
        self.msg_label.setScaledContents(True)
        self.msg_label.setMaximumSize(QSize(30, 28))
        self.MainHLayout.addWidget(self.msg_label)

        Pheight = self.height()
        self.ask_label = QLabel(self)
        self.ask_label.setStyleSheet("background-color: transparent; color: black;")
        self.ask_label.setAlignment(Qt.AlignCenter)
        self.MainHLayout.addWidget(self.ask_label)
        
        close_button = QToolButton(self)
        close_pix = self.style().standardPixmap(QStyle.SP_TitleBarCloseButton)
        close_button.setIcon(close_pix)
        close_button.setStyleSheet("QToolButton{background-color: transparent;}")
        close_button.setCursor(Qt.PointingHandCursor)
        close_button.setMaximumSize(QSize(30, 28))
        self.MainHLayout.addWidget(close_button)

        close_button.clicked.connect(lambda *args: self.hideMine())
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.show()
        
        self.QanimationVis = QPropertyAnimation(self, b"geometry")
        self.QanimationVis.setDuration(300)   #设置动画时间为1秒
        self.QanimationVis.setStartValue(QRect(0, -28, self.width(), self.height()))
        self.QanimationVis.setEndValue(QRect(0, 0, self.width(), self.height()))
        self.QanimationHid = QPropertyAnimation(self, b"geometry")
        self.QanimationHid.setDuration(300)
        self.QanimationHid.setStartValue(QRect(0, 0, self.width(), self.height()))
        self.QanimationHid.setEndValue(QRect(0, -28, self.width(), self.height()))
        
    def setTextPixmap(self, text, mapfile):
        self.ask_label.setText(text)
        self.msg_label.setPixmap(QPixmap(mapfile))
        self.QanimationVis.start()
        
    def hideMine(self):
        self.QanimationHid.start()
Exemple #37
0
    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