Resize widget from center when animating the size property

1k Views Asked by At

I'm trying to code a widget that slightly increases in size on mouse-over and decreases when the mouse leaves again.
This is what I have come up with so far:

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

from random import randrange


class MainWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setFixedSize(500, 500)

        cent_widget = QWidget()
        self.setCentralWidget(cent_widget)

        layout = QVBoxLayout()
        cent_widget.setLayout(layout)

        layout.addWidget(MyItem(), Qt.AlignCenter,
                         alignment=Qt.AlignCenter)


class MyItem(QLabel):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setBaseSize(200, 250)
        self.setMinimumSize(self.baseSize())
        self.resize(self.baseSize())

        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        self.setStyleSheet('background: #{:02x}{:02x}{:02x}'.format(
            randrange(255), randrange(255), randrange(255)
        ))

        # Animation
        self._enlarged = False
        self.zoom_factor = 1.2

        self.anim = QPropertyAnimation(self, b'size')
        self.anim.setEasingCurve(QEasingCurve.InOutSine)
        self.anim.setDuration(250)

    def enterEvent(self, event: QEvent) -> None:
        self.resize_anim()
        self._enlarged = True

    def leaveEvent(self, event: QEvent) -> None:
        self.resize_anim()
        self._enlarged = False

    def resize_anim(self):
        if self._enlarged:
            new_size = self.baseSize()
        else:
            new_size = QSize(
                int(self.baseSize().width() * self.zoom_factor),
                int(self.baseSize().height() * self.zoom_factor)
            )
        self.anim.setEndValue(new_size)
        self.anim.start()


if __name__ == '__main__':
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()

Green rectangle that gets resized from the top left corner on mouse-over

It's almost working the way I want, my only problem is that the widget gets resized from the top-left corner instead of from the center.
How can I change that?

1

There are 1 best solutions below

0
eyllanesc On BEST ANSWER

Instead of animating using the size property you should use the geometry property as it is relative to the parent widget, so you can animate its geometry making the center remain invariant.

from PyQt5.QtCore import (
    QAbstractAnimation,
    QEasingCurve,
    QEvent,
    QPropertyAnimation,
    QRect,
    Qt,
)
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QSizePolicy,
    QVBoxLayout,
    QWidget,
)

import random


class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setFixedSize(500, 500)

        cent_widget = QWidget()
        self.setCentralWidget(cent_widget)

        layout = QVBoxLayout(cent_widget)
        layout.addWidget(MyItem(), alignment=Qt.AlignCenter)


class MyItem(QLabel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.setBaseSize(200, 250)
        self.setMinimumSize(self.baseSize())
        self.resize(self.baseSize())

        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)

        self.setStyleSheet(
            "background: {}".format(QColor(*random.sample(range(255), 3)).name())
        )

        # Animation
        self.zoom_factor = 1.2

        self.anim = QPropertyAnimation(self, b"geometry")
        self.anim.setEasingCurve(QEasingCurve.InOutSine)
        self.anim.setDuration(250)

    def enterEvent(self, event: QEvent) -> None:

        initial_rect = self.geometry()
        final_rect = QRect(
            0,
            0,
            int(initial_rect.width() * self.zoom_factor),
            int(initial_rect.height() * self.zoom_factor),
        )
        final_rect.moveCenter(initial_rect.center())

        self.anim.setStartValue(initial_rect)
        self.anim.setEndValue(final_rect)
        self.anim.setDirection(QAbstractAnimation.Forward)
        self.anim.start()

    def leaveEvent(self, event: QEvent) -> None:
        self.anim.setDirection(QAbstractAnimation.Backward)
        self.anim.start()


if __name__ == "__main__":
    app = QApplication([])
    window = MainWindow()
    window.show()
    app.exec()