Exemplo n.º 1
0
        def moveEvent(self, tetritile):
            translation = QPropertyAnimation(self, b"pos")
            start, end = self.pos(), QPointF(tetritile.row, tetritile.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(), tetritile.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()
Exemplo n.º 2
0
class SplitterResizer(QObject):
    """An object able to control the size of a widget in a
    QSpliter instance.

    """
    def __init__(self, parent=None):
        QObject.__init__(self, parent)
        self.__splitter = None
        self.__widget = None
        self.__animationEnabled = True
        self.__size = -1
        self.__expanded = False
        self.__animation = QPropertyAnimation(self, b"size_", self)

        self.__action = QAction("toogle-expanded", self, checkable=True)
        self.__action.triggered[bool].connect(self.setExpanded)

    def setSize(self, size):
        """Set the size of the controlled widget (either width or height
        depending on the orientation).

        """
        if self.__size != size:
            self.__size = size
            self.__update()

    def size(self):
        """Return the size of the widget in the splitter (either height of
        width) depending on the splitter orientation.

        """
        if self.__splitter and self.__widget:
            index = self.__splitter.indexOf(self.__widget)
            sizes = self.__splitter.sizes()
            return sizes[index]
        else:
            return -1

    size_ = Property(int, fget=size, fset=setSize)

    def setAnimationEnabled(self, enable):
        """Enable/disable animation.
        """
        self.__animation.setDuration(0 if enable else 200)

    def animationEnabled(self):
        return self.__animation.duration() == 0

    def setSplitterAndWidget(self, splitter, widget):
        """Set the QSplitter and QWidget instance the resizer should control.

        .. note:: the widget must be in the splitter.

        """
        if splitter and widget and not splitter.indexOf(widget) > 0:
            raise ValueError("Widget must be in a spliter.")

        if self.__widget:
            self.__widget.removeEventFilter()
        self.__splitter = splitter
        self.__widget = widget

        if widget:
            widget.installEventFilter(self)

        self.__update()

    def toogleExpandedAction(self):
        """Return a QAction that can be used to toggle expanded state.
        """
        return self.__action

    def open(self):
        """Open the controlled widget (expand it to it sizeHint).
        """
        self.__expanded = True
        self.__action.setChecked(True)

        if not (self.__splitter and self.__widget):
            return

        size = self.size()
        if size > 0:
            # Already has non zero size.
            return

        hint = self.__widget.sizeHint()

        if self.__splitter.orientation() == Qt.Vertical:
            end = hint.height()
        else:
            end = hint.width()

        self.__animation.setStartValue(0)
        self.__animation.setEndValue(end)
        self.__animation.start()

    def close(self):
        """Close the controlled widget (shrink to size 0).
        """
        self.__expanded = False
        self.__action.setChecked(False)

        if not (self.__splitter and self.__widget):
            return

        self.__animation.setStartValue(self.size())
        self.__animation.setEndValue(0)
        self.__animation.start()

    def setExpanded(self, expanded):
        """Set the expanded state.

        """
        if self.__expanded != expanded:
            if expanded:
                self.open()
            else:
                self.close()

    def expanded(self):
        """Return the expanded state.
        """
        return self.__expanded

    def __update(self):
        """Update the splitter sizes.
        """
        if self.__splitter and self.__widget:
            splitter = self.__splitter
            index = splitter.indexOf(self.__widget)
            sizes = splitter.sizes()
            current = sizes[index]
            diff = current - self.__size
            sizes[index] = self.__size
            sizes[index - 1] = sizes[index - 1] + diff

            self.__splitter.setSizes(sizes)

    def eventFilter(self, obj, event):
        if event.type() == QEvent.Resize:
            if self.__splitter.orientation() == Qt.Vertical:
                size = event.size().height()
            else:
                size = event.size().width()

            if self.__expanded and size == 0:
                self.__action.setChecked(False)
                self.__expanded = False
            elif not self.__expanded and size > 0:
                self.__action.setChecked(True)
                self.__expanded = True

        return QObject.eventFilter(self, obj, event)
Exemplo n.º 3
0
class StackedWidgetSlideOverAnimator(AbstractAnimator):
    m_direction: AnimationDirection
    m_coveredWidget: QWidget
    m_decorator: StackedWidgetSlideOverDecorator
    m_animation: QPropertyAnimation

    def __init__(self, _container, _widgetForSlide):
        super(StackedWidgetSlideOverAnimator, self).__init__(_container)

        self.m_direction = AnimationDirection.FromLeftToRight
        self.m_coveredWidget = _container.currentWidget()
        self.m_decorator = StackedWidgetSlideOverDecorator(
            _container, _widgetForGrab=_widgetForSlide)
        self.m_animation = QPropertyAnimation(self.m_decorator, b"pos")

        _container.installEventFilter(self)

        self.m_animation.setDuration(400)

        self.m_decorator.hide()

        def finished():
            self.setAnimatedStopped()

            if self.isAnimatedForward():
                _container.setCurrentWidget(_widgetForSlide)
            self.m_decorator.hide()

        self.m_animation.finished.connect(finished)

    def updateCoveredWidget(self):
        self.m_coveredWidget = self.stackedWidget().currentWidget()

    def setAnimationDirection(self, _direction):
        if self.m_direction != _direction:
            self.m_direction = _direction

    def animationDuration(self):
        return self.m_animation.duration()

    def animateForward(self):
        self.slideOverIn()

    def slideOverIn(self):
        if self.isAnimated() and self.isAnimatedForward():
            return
        self.setAnimatedForward()

        self.m_decorator.grabWidget()

        startPos = QPoint()
        finalPos = QPoint()

        if self.m_direction == AnimationDirection.FromLeftToRight:
            startPos.setX(-1 * self.stackedWidget().width())
        if self.m_direction == AnimationDirection.FromRightToLeft:
            startPos.setX(self.stackedWidget().width())
        if self.m_direction == AnimationDirection.FromTopToBottom:
            startPos.setY(-1 * self.stackedWidget().height())
        if self.m_direction == AnimationDirection.FromBottomToTop:
            startPos.setY(self.stackedWidget().height())

        self.m_decorator.show()
        self.m_decorator.raise_()

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.InOutExpo)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(startPos)
            self.m_animation.setEndValue(finalPos)

            self.m_animation.start()

    def animateBackward(self):
        self.slideOverOut()

    def slideOverOut(self):
        if self.isAnimated() and self.isAnimatedBackward():
            return
        self.setAnimatedBackward()

        self.m_decorator.grabWidget()

        startPos = QPoint()
        finalPos = QPoint()

        if self.m_direction == AnimationDirection.FromLeftToRight:
            finalPos.setX(-1 * self.stackedWidget().width())
        if self.m_direction == AnimationDirection.FromRightToLeft:
            finalPos.setX(self.stackedWidget().width())
        if self.m_direction == AnimationDirection.FromTopToBottom:
            finalPos.setY(-1 * self.stackedWidget().height())
        if self.m_direction == AnimationDirection.FromBottomToTop:
            finalPos.setY(self.stackedWidget().height())

        if isinstance(self.stackedWidget(), type(self)):
            self.stackedWidget().setCurrentWidget(self.m_coveredWidget)

        self.m_decorator.show()
        self.m_decorator.raise_()

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.InOutExpo)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(startPos)
            self.m_animation.setEndValue(finalPos)

            self.m_animation.start()

    def eventFilter(self, _object, _event):
        if _object == self.stackedWidget() and _event.type(
        ) == QEvent.Resize and self.m_decorator.isVisible():
            self.m_decorator.grabWidget()

        return QWidget.eventFilter(self, _object, _event)

    def stackedWidget(self):
        return self.parent()
Exemplo n.º 4
0
class StackedWidgetFadeInAnimator(AbstractAnimator):
    m_decorator: StackedWidgetFadeInDecorator
    m_animation: QPropertyAnimation

    def __init__(self, _container, _fadeWidget):
        super(StackedWidgetFadeInAnimator, self).__init__(_container)

        self.m_decorator = StackedWidgetFadeInDecorator(_container, _fadeWidget=_fadeWidget)
        self.m_animation = QPropertyAnimation(self.m_decorator, b"opacity")

        _container.installEventFilter(self)

        self.m_animation.setDuration(200)

        self.m_decorator.hide()

        def finished():
            self.setAnimatedStopped()

            if self.m_animation.direction() == QPropertyAnimation.Forward:
                _container.setCurrentWidget(_fadeWidget)
            self.m_decorator.hide()

        self.m_animation.finished.connect(finished)

    def animationDuration(self):
        return self.m_animation.duration()

    def animateForward(self):
        self.fadeIn()

    def fadeIn(self):
        if self.isAnimated() and self.isAnimatedForward():
            return
        self.setAnimatedForward()

        self.m_decorator.grabContainer()
        self.m_decorator.grabFadeWidget()

        startOpacity = 0
        finalOpacity = 1

        self.m_decorator.setOpacity(startOpacity)
        self.m_decorator.move(0, 0)
        self.m_decorator.show()
        self.m_decorator.raise_()

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.InQuad)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(startOpacity)
            self.m_animation.setEndValue(finalOpacity)

            self.m_animation.start()

    def eventFilter(self, _object, _event):
        if _object == self.fadeWidget() and _event.type() == QEvent.Resize and self.m_decorator.isVisible():
            self.m_decorator.grabContainer()
            self.m_decorator.grabFadeWidget()

        return QWidget.eventFilter(self, _object, _event)

    def fadeWidget(self):
        return self.parent()
Exemplo n.º 5
0
class CircleFillAnimator(AbstractAnimator):
    m_decorator: CircleFillDecorator
    m_animation: QPropertyAnimation
    m_hideAfterFinish = True

    def __init__(self, _widgetForFill):
        super(CircleFillAnimator, self).__init__(_widgetForFill)

        self.m_decorator = CircleFillDecorator(_widgetForFill)
        self.m_animation = QPropertyAnimation(self.m_decorator, b"_radius")

        _widgetForFill.installEventFilter(self)

        self.m_animation.setDuration(500)

        self.m_decorator.setAttribute(Qt.WA_TransparentForMouseEvents)
        self.m_decorator.hide()

        def finished():
            self.setAnimatedStopped()
            if self.m_hideAfterFinish:
                self.hideDecorator()

        self.m_animation.finished.connect(finished)

    def setStartPoint(self, _point):
        self.m_decorator.setStartPoint(_point)

    def setFillColor(self, _color):
        self.m_decorator.setFillColor(_color)

    def setHideAfterFinish(self, _hide):
        self.m_hideAfterFinish = _hide

    def animationDuration(self):
        return self.m_animation.duration()

    def animateForward(self):
        self.fillIn()

    def fillIn(self):
        if self.isAnimated() and self.isAnimatedForward():
            return
        self.setAnimatedForward()

        startRadius = 0
        finalRadius = math.sqrt(
            self.widgetForFill().height() * self.widgetForFill().height() +
            self.widgetForFill().width() * self.widgetForFill().width())

        self.m_decorator.resize(self.widgetForFill().size())
        self.m_decorator.move(0, 0)
        self.m_decorator.show()
        self.m_decorator.raise_()

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.OutQuart)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(startRadius)
            self.m_animation.setEndValue(finalRadius)
            self.m_animation.start()

    def animateBackward(self):
        self.fillOut()

    def fillOut(self):
        if self.isAnimated() and self.isAnimatedBackward():
            return
        self.setAnimatedBackward()

        startRadius = math.sqrt(
            self.widgetForFill().height() * self.widgetForFill().height() +
            self.widgetForFill().width() * self.widgetForFill().width())
        finalRadius = 0

        self.m_decorator.resize(self.widgetForFill().size())
        self.m_decorator.move(0, 0)
        self.m_decorator.show()
        self.m_decorator.raise_()

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.OutQuart)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(startRadius)
            self.m_animation.setEndValue(finalRadius)
            self.m_animation.start()

    def hideDecorator(self):
        hideEffect = self.m_decorator.graphicsEffect()
        if hideEffect == None:
            hideEffect = QGraphicsOpacityEffect(self.m_decorator)
            self.m_decorator.setGraphicsEffect(hideEffect)
        hideEffect.setOpacity(1)

        hideAnimation = QPropertyAnimation(hideEffect, b"opacity",
                                           self.m_decorator)
        hideAnimation.setDuration(400)
        hideAnimation.setStartValue(1)
        hideAnimation.setEndValue(0)

        def finished():
            self.m_decorator.hide()
            hideEffect.setOpacity(1)

        hideAnimation.finished.connect(finished)

        hideAnimation.start(QAbstractAnimation.DeleteWhenStopped)

    def widgetForFill(self):
        return self.parent()
class ExpandAnimator(AbstractAnimator):
    m_decorator: ExpandDecorator
    m_animation: QPropertyAnimation
    m_expandRect: QRect

    def __init__(self, _widgetForFill):
        super(ExpandAnimator, self).__init__(_widgetForFill)

        self.m_decorator = ExpandDecorator(_widgetForFill)
        self.m_animation = QPropertyAnimation(self.m_decorator, b"_expandRect")

        _widgetForFill.installEventFilter(self)

        self.m_animation.setDuration(400)

        self.m_decorator.hide()

        def finished():
            self.setAnimatedStopped()
            if self.isAnimatedBackward():
                self.m_decorator.hide()

        self.m_animation.finished.connect(finished)

    def setExpandRect(self, _rect):
        self.m_expandRect = _rect
        self.m_decorator.setExpandRect(_rect)

    def setFillColor(self, _color):
        self.m_decorator.setFillColor(_color)

    def animationDuration(self):
        return self.m_animation.duration()

    def animateForward(self):
        self.expandIn()

    def expandIn(self):
        if self.isAnimated() and self.isAnimatedForward():
            return
        self.setAnimatedForward()

        startExpandRect = self.m_expandRect
        finalExpandRect = self.widgetForFill().rect()

        self.m_decorator.resize(self.widgetForFill().size())
        self.m_decorator.move(0, 0)
        self.m_decorator.grabExpandRect()
        self.m_decorator.show()
        self.m_decorator.raise_()

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.InOutQuart)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(startExpandRect)
            self.m_animation.setEndValue(finalExpandRect)
            self.m_animation.start()

    def animateBackward(self):
        self.expandOut()

    def expandOut(self):
        if self.isAnimated() and self.isAnimatedBackward():
            return
        self.setAnimatedBackward()

        startExpandRect = self.widgetForFill().rect()
        finalExpandRect = self.m_expandRect

        self.m_decorator.resize(self.widgetForFill().size())
        self.m_decorator.move(0, 0)
        self.m_decorator.hide()
        self.m_decorator.grabExpandRect()
        self.m_decorator.show()
        self.m_decorator.raise_()

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.InOutQuart)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(startExpandRect)
            self.m_animation.setEndValue(finalExpandRect)
            self.m_animation.start()

    def eventFilter(self, _object, _event):
        if _object == self.widgetForFill() and _event.type(
        ) == QEvent.Resize and self.m_decorator.isVisible():
            self.m_decorator.resize(self.widgetForFill().size())
            self.m_animation.setEndValue(self.widgetForFill().rect())

        return QObject.eventFilter(self, _object, _event)

    def widgetForFill(self):
        return self.parent()
Exemplo n.º 7
0
class SlideAnimator(AbstractAnimator):
    m_direction:AnimationDirection
    m_isFixBackground:bool
    m_isFixStartSize:bool
    m_startMinSize = QSize()
    m_startMaxSize = QSize()
    m_startSize = QSize()
    m_decorator: SlideForegroundDecorator
    m_animation: QPropertyAnimation

    def __init__(self, _widgetForSlide):
        super(SlideAnimator, self).__init__(_widgetForSlide)

        self.m_direction = AnimationDirection.FromLeftToRight
        self.m_isFixBackground = True
        self.m_isFixStartSize = False
        self.m_decorator = SlideForegroundDecorator(_widgetForSlide)
        self.m_animation = QPropertyAnimation(self.m_decorator, b"maximumWidth")

        _widgetForSlide.installEventFilter(self)

        self.m_animation.setDuration(300)

        self.m_decorator.hide()

        def valueChanged(_value):
            if self.isWidth():
                self.widgetForSlide().setMaximumWidth(_value)
            else:
                self.widgetForSlide().setMaximumHeight(_value)
        self.m_animation.valueChanged.connect(valueChanged)

        def finished():
            self.setAnimatedStopped()
            self.m_decorator.hide()
        self.m_animation.finished.connect(finished)

    def setAnimationDirection(self, _direction):
        if self.m_direction != _direction:
            self.m_direction = _direction
        self.m_animation.setPropertyName(b"maximumWidth" if self.isWidth() else b"maximumHeight")

    def setFixBackground(self, _fix):
        if self.m_isFixBackground != _fix:
            self.m_isFixBackground = _fix

    def setFixStartSize(self, _fix):
        if self.m_isFixStartSize != _fix:
            self.m_isFixStartSize = _fix

    def animationDuration(self):
        return self.m_animation.duration()

    def animateForward(self):
        self.slideIn()

    def slideIn(self):
        if not self.m_startMinSize.isValid():
            self.m_startMinSize = self.widgetForSlide().minimumSize()
        if not self.m_startMaxSize.isValid():
            self.m_startMaxSize = self.widgetForSlide().maximumSize()
        if not self.m_startSize.isValid():
            self.m_startSize = self.widgetForSlide().sizeHint()

        if self.isAnimated() and self.isAnimatedForward():
            return
        self.setAnimatedForward()

        if self.isWidth():
            self.widgetForSlide().setMaximumWidth(0)
        else:
            self.widgetForSlide().setMaximumHeight(0)

        self.widgetForSlide().show()
        currentSize = self.widgetForSlide().size()

        finalSize = QSize(currentSize.width(), currentSize.height())
        self.fixSize(self.m_startSize, finalSize)

        self.widgetForSlide().hide()
        self.fixSizeOfWidgetForSlide(finalSize)
        self.m_decorator.grabParent(finalSize)
        self.fixSizeOfWidgetForSlide(currentSize)
        self.widgetForSlide().show()

        if self.m_isFixBackground:
            self.m_decorator.move(0, 0)
            self.m_decorator.show()
            self.m_decorator.raise_()

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.OutQuart)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(self.widgetForSlide().width() if self.isWidth() else self.widgetForSlide().height())
            self.m_animation.setEndValue(finalSize.width() if self.isWidth() else finalSize.height())
            self.m_animation.start()

    def animateBackward(self):
        self.slideOut()

    def slideOut(self):
        if not self.m_startMinSize.isValid():
            self.m_startMinSize = self.widgetForSlide().minimumSize()
        if not self.m_startMaxSize.isValid():
            self.m_startMaxSize = self.widgetForSlide().maximumSize()
        if not self.m_startSize.isValid() or not self.m_isFixStartSize:
            self.m_startSize = self.widgetForSlide().size()

        if self.isAnimated() and self.isAnimatedBackward():
            return
        self.setAnimatedBackward()

        finalSize = self.widgetForSlide().size()
        if self.isWidth():
            finalSize.setWidth(0)
        else:
            finalSize.setHeight(0)

        self.m_decorator.grabParent(self.widgetForSlide().size())

        if self.m_isFixBackground:
            self.m_decorator.move(0, 0)
            self.m_decorator.show()
            self.m_decorator.raise_()

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.InQuart)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(self.widgetForSlide().width() if self.isWidth() else self.widgetForSlide().height())
            self.m_animation.setEndValue(finalSize.width() if self.isWidth() else finalSize.height())
            self.m_animation.start()

    def eventFilter(self, _object, _event):
        if _object == self.widgetForSlide() and _event.type() == QEvent.Resize and self.m_decorator.isVisible():
            if self.m_direction == AnimationDirection.FromLeftToRight:
                self.m_decorator.move(self.widgetForSlide().width() - self.m_decorator.width(), 0)
            if self.m_direction == AnimationDirection.FromTopToBottom:
                self.m_decorator.move(0, self.widgetForSlide().height() - self.m_decorator.height())

        return QObject.eventFilter(self, _object, _event)

    def isWidth(self):
        return self.m_direction == AnimationDirection.FromLeftToRight or self.m_direction == AnimationDirection.FromRightToLeft

    def fixSize(self, _sourceSize, _targetSize):
        if self.isWidth():
            _targetSize.setWidth(_sourceSize.width())
        else:
            _targetSize.setHeight(_sourceSize.height())

    def fixSizeOfWidgetForSlide(self, _sourceSize):
        if self.isWidth():
            self.widgetForSlide().setFixedWidth(_sourceSize.width())
        else:
            self.widgetForSlide().setFixedHeight(_sourceSize.height())


    def widgetForSlide(self):
        return self.parent()
Exemplo n.º 8
0
class SideSlideAnimator(AbstractAnimator):
    m_side: ApplicationSide
    m_decorateBackground: bool
    m_decorator: SideSlideDecorator
    m_animation: QPropertyAnimation

    def __init__(self, _widgetForSlide):
        super(SideSlideAnimator, self).__init__(_widgetForSlide)

        self.m_side = ApplicationSide()
        self.m_decorateBackground = True
        self.m_decorator = SideSlideDecorator(_widgetForSlide.parentWidget())
        self.m_animation = QPropertyAnimation(self.m_decorator, b"_slidePos")

        _widgetForSlide.parentWidget().installEventFilter(self)

        self.m_animation.setEasingCurve(QEasingCurve.InQuad)
        self.m_animation.setDuration(260)

        self.m_decorator.hide()

        def finished():
            self.setAnimatedStopped()
            if self.isAnimatedForward():
                self.widgetForSlide().move(self.m_decorator.slidePos())
            else:
                self.m_decorator.hide()

        self.m_animation.finished.connect(finished)

        self.m_decorator.clicked.connect(self.slideOut)

    def setApplicationSide(self, _side):
        if self.m_side != _side:
            self.m_side = _side

    def setDecorateBackground(self, _decorate):
        if self.m_decorateBackground != _decorate:
            self.m_decorateBackground = _decorate

    def animationDuration(self):
        return self.m_animation.duration()

    def animateForward(self):
        self.slideIn()

    def slideIn(self):
        if self.isAnimated() and self.isAnimatedForward():
            return
        self.setAnimatedForward()

        self.widgetForSlide().lower()
        self.widgetForSlide().move(-self.widgetForSlide().width(),
                                   -self.widgetForSlide().height())
        self.widgetForSlide().show()
        self.widgetForSlide().resize(self.widgetForSlide().sizeHint())

        _topWidget = self.widgetForSlide()
        topWidget = self.widgetForSlide()
        while _topWidget:
            topWidget = _topWidget
            _topWidget = topWidget.parentWidget()

        finalSize = self.widgetForSlide().size()
        startPosition = QPoint()
        finalPosition = QPoint()
        if self.m_side == ApplicationSide.LeftSide:
            finalSize.setHeight(topWidget.height())
            startPosition = QPoint(-finalSize.width(), 0)
            finalPosition = QPoint(0, 0)
        if self.m_side == ApplicationSide.TopSide:
            finalSize.setWidth(topWidget.width())
            startPosition = QPoint(0, -finalSize.height())
            finalPosition = QPoint(0, 0)
        if self.m_side == ApplicationSide.RightSide:
            finalSize.setHeight(topWidget.height())
            startPosition = QPoint(topWidget.width(), 0)
            finalPosition = QPoint(topWidget.width() - finalSize.width(), 0)
        if self.m_side == ApplicationSide.BottomSide:
            finalSize.setWidth(topWidget.width())
            startPosition = QPoint(0, topWidget.height())
            finalPosition = QPoint(0, topWidget.height() - finalSize.height())

        if self.m_decorateBackground:
            self.m_decorator.setParent(topWidget)
            self.m_decorator.move(0, 0)
            self.m_decorator.grabParent()
            self.m_decorator.show()
            self.m_decorator.raise_()

        self.widgetForSlide().move(startPosition)
        self.widgetForSlide().resize(finalSize)
        self.widgetForSlide().raise_()

        self.m_decorator.grabSlideWidget(self.widgetForSlide())

        if self.m_animation.state() == QPropertyAnimation.Running:
            self.m_animation.pause()
            self.m_animation.setDirection(QPropertyAnimation.Backward)
            self.m_animation.resume()
        else:
            self.m_animation.setEasingCurve(QEasingCurve.OutQuart)
            self.m_animation.setDirection(QPropertyAnimation.Forward)
            self.m_animation.setStartValue(startPosition)
            self.m_animation.setEndValue(finalPosition)
            self.m_animation.start()

        if self.m_decorateBackground:
            DARKER = True
            self.m_decorator.decorate(DARKER)

    def animateBackward(self):
        self.slideOut()

    def slideOut(self):
        if self.isAnimated() and self.isAnimatedBackward():
            return
        self.setAnimatedBackward()

        if self.widgetForSlide().isVisible():
            _topWidget = self.widgetForSlide()
            topWidget = self.widgetForSlide()
            while _topWidget:
                topWidget = _topWidget
                _topWidget = topWidget.parentWidget()

            self.widgetForSlide().hide()

            finalSize = self.widgetForSlide().size()
            startPosition = self.widgetForSlide().pos()
            finalPosition = QPoint()
            if self.m_side == ApplicationSide.LeftSide:
                startPosition = QPoint(0, 0)
                finalPosition = QPoint(-finalSize.width(), 0)
            if self.m_side == ApplicationSide.TopSide:
                startPosition = QPoint(0, 0)
                finalPosition = QPoint(0, -finalSize.height())
            if self.m_side == ApplicationSide.RightSide:
                startPosition = QPoint(topWidget.width() - finalSize.width(),
                                       0)
                finalPosition = QPoint(topWidget.width(), 0)
            if self.m_side == ApplicationSide.BottomSide:
                startPosition = QPoint(0,
                                       topWidget.height() - finalSize.height())
                finalPosition = QPoint(0, topWidget.height())

            if self.m_animation.state() == QPropertyAnimation.Running:
                self.m_animation.pause()
                self.m_animation.setDirection(QPropertyAnimation.Backward)
                self.m_animation.resume()
            else:
                self.m_animation.setEasingCurve(QEasingCurve.InQuart)
                self.m_animation.setDirection(QPropertyAnimation.Forward)
                self.m_animation.setStartValue(startPosition)
                self.m_animation.setEndValue(finalPosition)
                self.m_animation.start()

            if self.m_decorateBackground:
                LIGHTER = False
                self.m_decorator.decorate(LIGHTER)

    def eventFilter(self, _object, _event):
        if _object == self.widgetForSlide().parentWidget() and _event.type(
        ) == QEvent.Resize:
            widgetForSlideParent = self.widgetForSlide().parentWidget()
            if self.m_side == ApplicationSide.RightSide:
                self.widgetForSlide().move(
                    widgetForSlideParent.width() -
                    self.widgetForSlide().width(), 0)
            if self.m_side == ApplicationSide.LeftSide:
                self.widgetForSlide().resize(self.widgetForSlide().width(),
                                             widgetForSlideParent.height())
            if self.m_side == ApplicationSide.BottomSide:
                self.widgetForSlide().move(
                    0,
                    widgetForSlideParent.height() -
                    self.widgetForSlide().height())
            if self.m_side == ApplicationSide.TopSide:
                self.widgetForSlide().resize(widgetForSlideParent.width(),
                                             self.widgetForSlide().height())

            self.m_decorator.grabSlideWidget(self.widgetForSlide())

        return QObject.eventFilter(self, _object, _event)

    def widgetForSlide(self):
        return self.parent()
Exemplo n.º 9
0
class Button(QGraphicsObject):
    font: QFont = QFont("monospace", 16)

    clicked = pyqtSignal('PyQt_PyObject')
    hovered_ = pyqtSignal('PyQt_PyObject')

    def __init__(self, btn_id: str, label: str, tooltip: str = "", parent: QGraphicsItem = None):
        QGraphicsObject.__init__(self, parent)
        self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
        self.scaling = 1.0
        self.label = QGraphicsSimpleTextItem(label, self)
        self.label.setFont(Button.font)
        self.text_color_enabled = QColor(255, 255, 255, 255)
        self.text_color_disabled = QColor(200, 200, 200, 255)
        self.fill_color_disabled = QColor(125, 125, 125, 200)
        self.label.setBrush(QBrush(self.text_color_enabled))
        self.btn_id = btn_id
        self.rect = QRectF(0, 0, 0, 0)

        self.tooltip = QGraphicsSimpleTextItem(tooltip, self)
        self.tooltip.setBrush(QColor(255, 255, 255, 200))
        self.tooltip.setFont(Button.font)
        self.tooltip.setVisible(False)
        self.tooltip.setZValue(25)
        self.tooltip_shown = False

        self.base_color = ButtonColor.GRAY

        self.hor_margin = 10
        self.ver_margin = 5
        self.current_timer = 0

        self.logic: Union[CheckbuttonLogic, PushbuttonLogic] = PushbuttonLogic("gray")
        self.fill_color_current = self.logic.idle_color()

        self.color_animation = QPropertyAnimation(self, b"current_fill_color")
        self.color_animation.setEasingCurve(QEasingCurve.Linear)

        self.hovered = False
        self.setAcceptHoverEvents(True)
        self.setZValue(4)
        self.set_height(12)

        self.pixmap: QPixmap = None
        self.max_pixmap_height = 128
        self.disabled = False

        self.mode = ButtonMode.Button

    def set_height(self, h: int):
        self.prepareGeometryChange()
        self.ver_margin = int(0.25 * h)
        font: QFont = self.label.font()
        font.setPointSize(h)
        self.label.setFont(font)
        self.rect.setWidth(self.label.boundingRect().width() + 2 * self.hor_margin)
        self.rect.setHeight(self.label.boundingRect().height() + 2 * self.ver_margin)
        self._reposition_text()

    def set_width(self, w: int):
        if self.pixmap is not None:
            return
        self.prepareGeometryChange()
        self.rect.setWidth(w)
        self.hor_margin = self.ver_margin
        if self.label.boundingRect().width() > self.rect.width():
            w = self.rect.width() - 2 * self.hor_margin
            factor = w / self.label.boundingRect().width()
            h = factor * self.label.boundingRect().height()
            font = self.label.font()
            font.setPixelSize(max(h, 12))
            self.label.setFont(font)
        self._reposition_text()

    def set_button_height(self, h: int):
        self.rect.setHeight(h)
        self._reposition_text()

    def scale_button(self, factor: float):
        factor = 1.0
        self.rect.setHeight(int(factor * self.rect.height()))
        self.rect.setWidth(int(factor * self.rect.width()))
        self.label.setScale(self.scaling)
        self.fit_to_contents()

    def _reposition_text(self):
        x = self.rect.width() / 2 - self.label.boundingRect().width() / 2
        y = self.rect.height() / 2 - self.label.boundingRect().height() / 2
        self.label.setPos(QPointF(x, y))
        self.update()

    def boundingRect(self):
        return self.rect

    def paint(self, painter: QPainter, options, widget=None):
        painter.setBrush(QBrush(self.fill_color_current))
        painter.setPen(QPen(QColor(0, 0, 0, 0)))

        painter.drawRoundedRect(self.rect, 5, 5)

        if self.pixmap is not None:
            painter.drawPixmap(self.hor_margin * self.scaling, self.ver_margin * self.scaling, self.pixmap)
        if self.tooltip_shown:
            painter.setBrush(QBrush(QColor(50, 50, 50, 200)))
            painter.drawRoundedRect(self.tooltip.boundingRect().translated(self.tooltip.pos())
                                    .marginsAdded(QMarginsF(5, 0, 5, 0)), 5, 5)

    def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
        if self.disabled or self.mode == ButtonMode.Label:
            return
        self.fill_color_current = self.logic.press_color()
        self.scene().update()

    def mouseReleaseEvent(self, event: QGraphicsSceneMouseEvent):
        if self.disabled or self.mode == ButtonMode.Label:
            return
        self.click_button()

    def hoverEnterEvent(self, event: QGraphicsSceneHoverEvent):
        if self.disabled or self.mode == ButtonMode.Label:
            return
        self.hovered_.emit({'btn_id': self.btn_id, 'button': self, 'hovered': True})
        self.hovered = True

        if len(self.tooltip.text()) > 0:
            self.tooltip_shown = True
            self.tooltip.setVisible(True)
            view = self.scene().views()[0]
            rect_ = view.mapFromScene(self.tooltip.sceneBoundingRect()).boundingRect()
            pos = self.boundingRect().topRight()
            mouse_pos = view.mapFromScene(event.scenePos())
            if mouse_pos.x() + rect_.width() >= view.viewport().width():
                pos = QPointF(-self.tooltip.boundingRect().width(), 0)

            self.tooltip.setPos(pos)

        self.color_animation.setDuration(200)
        self.color_animation.setStartValue(self.logic.idle_color())
        self.color_animation.setEndValue(self.logic.hover_enter_color())
        self.scene().update()
        if self.current_timer >= 0:
            self.killTimer(self.current_timer)
        self.color_animation.start()
        self.current_timer = self.startTimer(self.color_animation.duration() // 80)

    def hoverLeaveEvent(self, event: QGraphicsSceneHoverEvent):
        if self.disabled or self.mode == ButtonMode.Label:
            return

        self.hovered_.emit({'btn_id': self.btn_id, 'button': self, 'hovered': False})
        self.hovered = False
        self.tooltip_shown = False
        self.tooltip.setVisible(False)

        self.color_animation.setDuration(200)
        self.color_animation.setStartValue(self.logic.hover_enter_color())
        self.color_animation.setEndValue(self.logic.hover_left_color())
        self.scene().update()
        if self.current_timer > 0:
            self.killTimer(self.current_timer)
        self.color_animation.start()
        self.current_timer = self.startTimer(self.color_animation.duration() // 80)

    def hoverMoveEvent(self, event: QGraphicsSceneHoverEvent):
        pass

    @pyqtProperty(QColor)
    def current_fill_color(self):
        return self.fill_color_current

    @current_fill_color.setter
    def current_fill_color(self, color: QColor):
        self.fill_color_current = color

    def timerEvent(self, ev: QTimerEvent):
        self.scene().update()
        if self.color_animation.state() == QPropertyAnimation.Stopped:
            self.killTimer(ev.timerId())
            self.current_timer = 0

    def set_base_color(self, colors: List[ButtonColor]):
        self.logic.set_colors(colors)
        self.fill_color_current = self.logic.idle_color()

    def is_on(self):
        return self.logic.is_down()

    def set_on(self, value: bool):
        if isinstance(self.logic, CheckbuttonLogic):
            self.logic.down = value
            self.fill_color_current = self.logic.press_color() if value else self.logic.idle_color()
            self.update()

    def set_is_check_button(self, value: bool):
        if value:
            self.logic = CheckbuttonLogic()
        else:
            self.logic = PushbuttonLogic("gray")

    def set_pixmap(self, pixmap: QPixmap):
        if pixmap is not None:
            self.pixmap = pixmap.scaledToHeight(self.max_pixmap_height * self.scaling) if pixmap is not None else None
            self.fit_to_contents()

    def fit_to_contents(self):
        self.prepareGeometryChange()
        width = 2 * self.hor_margin * self.label.scale()
        height = 2 * self.ver_margin * self.label.scale() + self.label.boundingRect().height() * self.label.scale()
        if self.pixmap is not None:
            width += max(self.pixmap.width(), self.label.boundingRect().width() * self.label.scale())
            height += self.ver_margin * self.scaling + self.pixmap.height()
        else:
            width += self.label.boundingRect().width() * self.label.scale()
        self.rect.setWidth(width)
        self.rect.setHeight(height)

        self.label.setPos(0.5 * width - 0.5 * self.label.boundingRect().width() * self.label.scale() + 0.0 * self.hor_margin,
                          height - self.label.boundingRect().height() * self.label.scale() - self.ver_margin * self.scaling)
        self.update()

    def adjust_text_to_button(self):
        height_diff = 0.75 * self.rect.height() - self.label.boundingRect().height()
        fac = height_diff / (0.75 * self.rect.height())
        self.label.setTransformOriginPoint(self.label.boundingRect().center())
        self.label.setScale(1.0 + fac)
        self._reposition_text()

    def set_label(self, text: str, direction: str = 'horizontal'):
        if direction == 'vertical':
            text = '\n'.join(list(text))
        self.label.setText(text)
        self._reposition_text()

    def click_button(self, artificial_emit: bool = False):
        if self.disabled:
            return
        self.logic.do_click()
        self.fill_color_current = self.logic.release_color() if not artificial_emit else self.logic.idle_color()
        self.clicked.emit({"btn_id": self.btn_id, "btn_label": self.label, "button": self, 'checked': self.is_on()})
        if artificial_emit:
            self.hovered_.emit({'btn_id': self.btn_id, 'button': self, 'hovered': False})
            self.update()
        if self.scene() is not None:
            self.scene().update()

    def set_opacity(self, opacity: float):
        self.setOpacity(opacity)
        self.update()

    def set_default_state(self):
        self.logic.reset_state()
        self.fill_color_current = self.logic.idle_color()
        self.tooltip.setVisible(False)
        self.tooltip_shown = False
        self.update()

    def set_disabled(self, disabled: bool):
        self.disabled = disabled
        if disabled:
            self.label.setBrush(QBrush(self.text_color_disabled))
            self.fill_color_current = self.fill_color_disabled
        else:
            self.label.setBrush(QBrush(self.text_color_enabled))
            self.fill_color_current = self.logic.idle_color()
        self.update()

    def set_tooltip(self, tooltip: str):
        self.tooltip.setVisible(False)
        self.tooltip_shown = False
        self.tooltip.setText(tooltip)

    def set_custom_color(self, colors: List[QColor]):
        if isinstance(self.logic, CheckbuttonLogic):
            if len(colors) < 2:
                return
            self.logic.set_colors(colors=[
                colors[0],
                colors[0].lighter(120),
                colors[0].darker(120),

                colors[1],
                colors[1].lighter(120),
                colors[1].darker(120)
            ])
        else:
            self.logic.set_colors(colors=[
                colors[0],
                colors[0].lighter(120),
                colors[0].darker(120)
            ])
        self.fill_color_current = self.logic.idle_color()
        self.update()

    def set_mode(self, mode: ButtonMode):
        self.mode = mode