Aziziz14
Aziziz14

Reputation: 35

Animating QPushButton PyQt

I'm having an issue trying to animate a QPushButtons. I've a queue of buttons that once popped, I would like to animate from one color to another. I've recreated a minimum working example below:

from PyQt5 import QtCore, QtGui
from PyQt5.QtWidgets import QHBoxLayout, QPushButton, QApplication, QWidget

from main import comboBox

app = QApplication([])
app.setStyle('Fusion')
window = QWidget()

def change_color():
    anim = QtCore.QPropertyAnimation(changeButton, b"color")
    anim.setDuration(1000)
    anim.setStartValue(QtGui.QColor(255, 144, 0))
    anim.setEndValue(QtGui.QColor(255, 255, 255))
    anim.start()


hbox = QHBoxLayout()
hbox.addStretch(1)
changeButton = QPushButton()
changeButton.setText("change Grid")
hbox.addWidget((changeButton))
window.setLayout((hbox))


window.show()
app.exec()

changeButton.clicked.connect(lambda event: change_color())

When I go into the debug mode it shows that it's reaching each of the lines, however, it there is no color change happening. Am I doing something wrong here?

Upvotes: 0

Views: 1409

Answers (1)

musicamante
musicamante

Reputation: 48479

You have two problems.

The first is that you're connecting the signal after the app.exec() call. That call starts the event loop, and, as such, it's blocking (nothing after that line will be processed until it returns), so you must move that before starting the event loop.

Then, as the QPropertyAnimation documentation explains:

QPropertyAnimation interpolates over Qt properties.

color is not a Qt property for QPushButton (nor any of its base classes), in fact, if you look at the debug/terminal output for your program (after moving the signal connection as said above), you'll see the following:

StdErr: QPropertyAnimation: you're trying to animate a non-existing property color of your QObject

A simple solution would be to use QVariantAnimation instead, and update the color in a function connected to the animation's valueChanged signal.

def change_color():
    def updateColor(color):
        # ... change the color

    anim = QtCore.QVariantAnimation(changeButton)
    anim.setDuration(1000)
    anim.setStartValue(QtGui.QColor(255, 144, 0))
    anim.setEndValue(QtGui.QColor(255, 255, 255))
    anim.valueChanged.connect(updateColor)
    anim.start()

# ...

changeButton.clicked.connect(change_color)

The choice becomes the way you change color: if you're not using stylesheets for the button, the more appropriate way would be to use the button palette:

def change_color(button):
    def updateColor(color):
        palette.setColor(role, color)
        button.setPalette(palette)
    palette = button.palette()
    role = button.foregroundRole()
    anim = QtCore.QVariantAnimation(button)
    anim.setDuration(1000)
    anim.setStartValue(QtGui.QColor(255, 144, 0))
    anim.setEndValue(QtGui.QColor(255, 255, 255))
    anim.valueChanged.connect(updateColor)
    anim.start(anim.DeleteWhenStopped)

# ...

changeButton.clicked.connect(lambda: change_color(changeButton))

Note that I added the button argument (you should not use globals anyway), and also the DeleteWhenStopped flag to start() in order to avoid unnecessary stacking of unused animations when they're finished.

If, instead, you're using stylesheets, you need to override the button's stylesheet. Obviously, you have to be careful: the button shouldn't have any stylesheet set directly on it.

def change_color(button):
    def updateColor(color):
        button.setStyleSheet('color: {}'.format(color.name()))
    # ...

Note that a more appropriate approach would be to subclass the button and implement the animation directly on it. By doing that you can use a QPropertyAnimation by using a custom pyqtProperty, but you will still need to manually set the color as above in the property setter. In this case, you can just have a single animation (possibly creating it in the __init__) and just update its start/end values.

Alternatively, you can just call update in the setter, and override the paintEvent() by using QStyle functions to draw the button primitive and label using the property value.

Upvotes: 1

Related Questions