5

I'm currently trying to fade a specific QLabel in and out. My first try was to use the setAlphaChannel, however this just didn't work. My current approach is to use a for-loop and set the stylesheet of the QLabel. Sadly this makes a unverifiable bug, sometimes the fading works properly, sometimes the QLabel doesn't fade out but is fading in and more random stuff. For me the problem is untraceable.

Here is my current code:

def fade_greeting(self, foo, bar):
    for i in range(255, -1, -5):
        print(i)
        string = "font : 45px; font : bold; color : rgba(220, 220, 220, " + str (i) + "); font-family : HelveticaNeue-UltraLight"
        time.sleep(0.2)
        self.greeting_text.setStyleSheet(string)


    time.sleep(2)
    self.greeting_text.setText(greeting())
    time.sleep(2)

    for i in range(0, 256, 5):
        print(i)
        string = "font : 45px; font : bold; color : rgba(220, 220, 220, " + str (i) + "); font-family : HelveticaNeue-UltraLight"
        time.sleep(0.2)
        self.greeting_text.setStyleSheet(string)

Is there something I missed? Or is there maybe a different approach to this problem?

Already thanks for your help!

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
pxBn
  • 53
  • 1
  • 4
  • 2
    Qt has some specifici tools for that, have a look at this post: https://stackoverflow.com/questions/6952852/qpropertyanimation-doesnt-work-with-a-child-widget – Marco Jan 10 '18 at 16:07

2 Answers2

8

After some trial and error, I found a working recipe:

def fade(self, widget):
    self.effect = QGraphicsOpacityEffect()
    widget.setGraphicsEffect(self.effect)

    self.animation = QtCore.QPropertyAnimation(self.effect, b"opacity")
    self.animation.setDuration(1000)
    self.animation.setStartValue(1)
    self.animation.setEndValue(0)
    self.animation.start()

def unfade(self, widget):
    self.effect = QGraphicsOpacityEffect()
    widget.setGraphicsEffect(self.effect)

    self.animation = QtCore.QPropertyAnimation(self.effect, b"opacity")
    self.animation.setDuration(1000)
    self.animation.setStartValue(0)
    self.animation.setEndValue(1)
    self.animation.start()

I guess you can call it on any widget. I call it on QLabel. For example:

self.fade(self._your_widget_here_)
# or
self.unfade(self._your_widget_here_)

It will fadein or fadeout your widget.

lenooh
  • 10,364
  • 5
  • 58
  • 49
4

sleep() is a blocking function that is not suitable to use in the main GUI thread, Qt provides tools to handle this type of tasks as QVariantAnimation, it provides the colors in an appropriate way for the required animation.

To change the colors you can use QPalette as I show below:

class AnimationLabel(QLabel):
    def __init__(self, *args, **kwargs):
        QLabel.__init__(self, *args, **kwargs)
        self.animation = QVariantAnimation()
        self.animation.valueChanged.connect(self.changeColor)

    @pyqtSlot(QVariant)
    def changeColor(self, color):
        palette = self.palette()
        palette.setColor(QPalette.WindowText, color)
        self.setPalette(palette)

    def startFadeIn(self):
        self.animation.stop()
        self.animation.setStartValue(QColor(0, 0, 0, 0))
        self.animation.setEndValue(QColor(0, 0, 0, 255))
        self.animation.setDuration(2000)
        self.animation.setEasingCurve(QEasingCurve.InBack)
        self.animation.start()

    def startFadeOut(self):
        self.animation.stop()
        self.animation.setStartValue(QColor(0, 0, 0, 255))
        self.animation.setEndValue(QColor(0, 0, 0, 0))
        self.animation.setDuration(2000)
        self.animation.setEasingCurve(QEasingCurve.OutBack)
        self.animation.start()

    def startAnimation(self):
        self.startFadeIn()
        loop = QEventLoop()
        self.animation.finished.connect(loop.quit)
        loop.exec_()
        QTimer.singleShot(2000, self.startFadeOut)

class Widget(QWidget):
    def __init__(self):
        super().__init__()
        lay = QVBoxLayout(self)
        self.greeting_text = AnimationLabel("greeting_text")
        self.greeting_text.setStyleSheet("font : 45px; font : bold; font-family : HelveticaNeue-UltraLight")
        lay.addWidget(self.greeting_text)
        btnFadeIn = QPushButton("fade in")
        btnFadeOut = QPushButton("fade out")
        btnAnimation = QPushButton("animation")
        lay.addWidget(btnFadeIn)
        lay.addWidget(btnFadeOut)
        lay.addWidget(btnAnimation)
        btnFadeIn.clicked.connect(self.greeting_text.startFadeIn)
        btnFadeOut.clicked.connect(self.greeting_text.startFadeOut)
        btnAnimation.clicked.connect(self.greeting_text.startAnimation)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Widget()
    ex.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Yeah I started the fading function in a different thread so the main GUI thread wasn't affected. However, thank you! This solved my problem! – pxBn Jan 11 '18 at 13:58