Анимация кнопок работает неправильно

Пытаясь создать анимированные кнопки при наведении в активном окне, воспользовался подсказкой из этого вопроса.
Использовал конструкцию условия чтобы определить какая кнопка, какую анимацию должна проигрывать, но вместо ожидаемого, получил результат, при котором анимацию проигрывает только средняя кнопка.
А если быстро провести курсором по кнопкам, то анимация и вовсе пропадает и появляется лишь тогда, когда окно перезапущено.

Таким образом вырисовывается две проблемы:

  1. Анимация проигрывается только на средней кнопке, остальные лишь тригерят проигрывание анимации на средней кнопке.
  2. Анимация ломается и больше не воспроизводится если по всему ряду кнопок провести курсором не останавливаясь.
import sys
import os
from PyQt5 import QtCore
from PyQt5 import QtGui
from PyQt5.QtCore import *
from PyQt5.QtGui import QIcon, QPixmap
from PyQt5.QtWidgets import *


class Window(QWidget):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)
        self.resize(310, 105)
        self.angle = 0
        self.val = 0
        self.i = 0

# min_button
        self.image_fname_min_button = ('icons/cil-minus.png')
        self.min_button = QPushButton(self)
        self.min_button.resize(100, 100)
        self.min_button.setIcon(
            QIcon(QPixmap(self.image_fname_min_button)))
        # Animation min_button
        self.min_button.installEventFilter(self)
        self.timer_min_button = QtCore.QTimer()
        self.timer_min_button.timeout.connect(
            lambda: self.onRotate('min_button', self.val))

# max_button
        self.image_fname_max_button = ('icons/cil-fullscreen.png')
        self.max_button = QPushButton(self)
        self.max_button.move(105, 0)
        self.max_button.resize(100, 100)
        self.max_button.setIcon(
            QIcon(QPixmap(self.image_fname_max_button)))
        # Animation max_button
        self.max_button.installEventFilter(self)
        self.timer_max_button = QtCore.QTimer()
        self.timer_max_button.timeout.connect(
            lambda: self.onRotate('max_button', self.val))

# close_button
        self.image_fname_close_button = ('icons/cil-power-standby.png')
        self.close_button = QPushButton(self)
        self.close_button.move(210, 0)
        self.close_button.resize(100, 100)
        self.close_button.setIcon(
            QIcon(QPixmap(self.image_fname_close_button)))
        # Animation close_button
        self.close_button.installEventFilter(self)
        self.timer_close_button = QtCore.QTimer()
        self.timer_close_button.timeout.connect(
            lambda: self.onRotate('close_button', self.val))

    def eventFilter(self, obj, event):
        if self.min_button is obj:
            if event.type() == QtCore.QEvent.Enter:
                self.val = 1
                self.timer_min_button.start(60)
            elif event.type() == QtCore.QEvent.Leave:
                self.val = -1
                self.timer_min_button.start(80)
        elif self.max_button is obj:
            if event.type() == QtCore.QEvent.Enter:
                self.val = 1
                self.timer_max_button.start(60)
            elif event.type() == QtCore.QEvent.Leave:
                self.val = -1
                self.timer_max_button.start(80)
        elif self.close_button is obj:
            if event.type() == QtCore.QEvent.Enter:
                self.val = 1
                self.timer_close_button.start(60)
            elif event.type() == QtCore.QEvent.Leave:
                self.val = -1
                self.timer_close_button.start(80)
        return super().eventFilter(obj, event)

    def onRotate(self, name_button, val=0):
        self.i += val
        self.angle += 45 * val
        if self.angle <= 90 and self.angle >= -90:
            t = QtGui.QTransform().rotate(self.angle)
            if name_button == 'min_button':
                print('Ветка min_button')
                pix = QtGui.QPixmap(self.image_fname_min_button).transformed(t)
                image_rotated_min_button = f'{os.path.splitext(self.image_fname_min_button)[0]}_rotated.png'
                pix.save(image_rotated_min_button)
                self.max_button.setIcon(QtGui.QIcon(image_rotated_min_button))
            elif name_button == 'max_button':
                print('Ветка max_button')
                pix = QtGui.QPixmap(self.image_fname_max_button).transformed(t)
                image_rotated_max_button = f'{os.path.splitext(self.image_fname_max_button)[0]}_rotated.png'
                pix.save(image_rotated_max_button)
                self.max_button.setIcon(QtGui.QIcon(image_rotated_max_button))
            elif name_button == 'close_button':
                print('Ветка close_button')
                pix = QtGui.QPixmap(
                    self.image_fname_close_button).transformed(t)
                image_rotated_close_button = f'{os.path.splitext(self.image_fname_close_button)[0]}_rotated.png'
                pix.save(image_rotated_close_button)
                self.max_button.setIcon(
                    QtGui.QIcon(image_rotated_close_button))
        else:
            self.timer_min_button.stop()
            self.timer_max_button.stop()
            self.timer_close_button.stop()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())


Помогите понять что я делаю не так.


Ответы (1 шт):

Автор решения: S. Nick

Попробуйте так:

import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.Qt import *


class PushButton(QPushButton):
    def __init__(self, img, *args, **kwargs):
        super(PushButton, self).__init__(*args, **kwargs)
        
        self.rotation = 0
        self.amount_rotation = 0
        self.angle = 45

        self.pixmap = QPixmap(img)
        self._width = self.pixmap.size().width()
        self._height = self.pixmap.size().height()
        self.pixmap = self.pixmap.scaled(self._width, self._height)
        
        pm = QPixmap(self._width, self._height)
        rectF = QRectF(0, 0, self._width, self._height)  
        painter = QPainter(pm)
        painter.drawPixmap(rectF, self.pixmap, rectF)
        painter.end()

        self.setIcon(QIcon(pm))
        self.setIconSize(QSize(50, 50))
        
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.rotate_item)
        
    def rotate_item(self):
        if (self.angle > 0 and self.amount_rotation >= 7) or \
           (self.angle < 0 and self.amount_rotation <= 0):

            self.timer.stop()
            return
        
        self.rotation = (self.rotation + self.angle) % 360
        
        rectF = QRectF(0, 0, self._width, self._height)   
        pix = QPixmap(self._width, self._height)
        painter = QPainter(pix)
        painter.translate(rectF.center()) 
        painter.rotate(self.rotation)
        painter.translate(-rectF.center())
        painter.drawPixmap(0, 0, self.pixmap)
        painter.end()

        self.setIcon(QIcon(pix))
        
        if self.angle > 0:
            self.amount_rotation += 1
        else:
            self.amount_rotation -= 1

    def enterEvent(self, event):
        self.angle = 45        
        self.timer.stop()
        self.timer.start(100)
        
    def leaveEvent(self, event):
        self.angle = -45        
        self.timer.stop()
        self.timer.start(100)


class Window(QWidget):
    def __init__(self):
        super().__init__()
        
        self.min_button = PushButton("lena50.png", self)
        self.max_button = PushButton("monkey50.png", self)
        self.close_button = PushButton("orig50.jpg", self)

        layout = QHBoxLayout(self)
        layout.addWidget(self.min_button)
        layout.addWidget(self.max_button)
        layout.addWidget(self.close_button)


if __name__=="__main__":
    app = QApplication(sys.argv)
    w = Window()
    w.show()
    sys.exit(app.exec_())

введите сюда описание изображения


lena50.png

введите сюда описание изображения

monkey50.png

введите сюда описание изображения

orig50.jpg

введите сюда описание изображения

→ Ссылка