PyQt Fading a QLabel
Asked Answered
G

2

7

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!

Glorygloryofthesnow answered 10/1, 2018 at 16:0 Comment(1)
Qt has some specifici tools for that, have a look at this post: #6953352Garton
R
5

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_())
Rolanda answered 10/1, 2018 at 17:11 Comment(1)
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!Glorygloryofthesnow
C
9

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.

Countermeasure answered 1/3, 2018 at 20:54 Comment(0)
R
5

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_())
Rolanda answered 10/1, 2018 at 17:11 Comment(1)
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!Glorygloryofthesnow

© 2022 - 2024 — McMap. All rights reserved.