I want to implement a button (already has a custom class) that when clicked, fades out all the widgets on the existing screen before switching to another layout (implemented using QStackedLayout)
I've looked at different documentations and guides on PySide6 on how to animate fading in/out but nothing seems to be working. Not sure what is wrong with the code per se bit I've done the debugging and the animation class is acting on the widget
I assume that to make the Widget fade, I had to create QGraphicsOpacityEffect with the top-level widget being the parent, then adding the QPropertyAnimation for it to work.
main.py
# Required Libraries for PySide6/Qt
from PySide6.QtWidgets import QWidget, QApplication, QPushButton, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QMainWindow, QSystemTrayIcon, QStackedLayout, QGraphicsOpacityEffect
from PySide6.QtGui import QIcon, QPixmap, QFont, QLinearGradient, QPainter, QColor
from PySide6.QtCore import Qt, QPointF, QSize, QVariantAnimation, QAbstractAnimation, QEasingCurve, QPropertyAnimation, QTimer
# For changing the taskbar icon
import ctypes
import platform
# For relative imports
import sys
sys.path.append('../classes')
# Classes and Different Windows
from classes.getStartedButton import getStartedButton
class window(QMainWindow):
# Set up core components of window
def __init__(self,h,w):
# Gets primary parameters of the screen that the window will display in
self.height = h
self.width = w
super().__init__()
# Required to change taskbar icon
if platform.system() == "Windows":
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
"com.random.appID")
# Multiple screens will overlay each other
self.layout = QStackedLayout()
# Init splash screen - this is first
self.splashScreen()
# Function to init other screens
self.initScreens()
# Must create main widget to hold the stacked layout, or another window will appear
main_widget_holder = QWidget()
main_widget_holder.setLayout(self.layout)
self.setCentralWidget(main_widget_holder)
def initScreens(self):
apiScreen = QWidget()
another_button = QPushButton("Test")
another_layout = QVBoxLayout()
another_layout.addWidget(another_button)
apiScreen.setLayout(another_layout)
self.layout.addWidget(apiScreen)
# Window definition for splash screen
def splashScreen(self):
"""Window that displays the splash screen
"""
# Widget that holds all the widgets in the splash screen
self.placeholder = QWidget()
# Logo & Title Component
logo = QLabel("")
logo.setPixmap(QPixmap("image.png"))
# Align logo on right side of the split
logo.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
logo.setStyleSheet(
"border-right:3px solid white;background-color: rgb(22,22,22);")
title = QLabel("Another\nApp")
title.setStyleSheet("padding-left:2px; font-size:36px")
# Header to hold title and logo
header_layout = QHBoxLayout()
header_layout.addWidget(logo)
header_layout.addWidget(title)
header = QWidget()
header.setStyleSheet("margin-bottom:20px")
# Assign header_layout to the header widget
header.setLayout(header_layout)
# Button Component
button = getStartedButton("Get Started ")
# Set max width of the button to cover both text and logo
button.setMaximumWidth(self.placeholder.width())
button.clicked.connect(self.transition_splash)
# Vertical Layout from child widget components
title_scrn = QVBoxLayout()
title_scrn.addWidget(header)
title_scrn.addWidget(button)
# Define alignment to be vertically and horizontal aligned (Prevents button from appearing on the bottom of the app as well)
title_scrn.setAlignment(Qt.AlignCenter)
# Enlarge the default window size of 640*480 to something bigger (add 50px on all sides)
self.placeholder.setLayout(title_scrn)
self.placeholder.setObjectName("self.placeholder")
self.placeholder.setMinimumSize(
self.placeholder.width() 100, self.placeholder.height() 100)
self.setCentralWidget(self.placeholder)
# Grey/Black Background
self.setStyleSheet(
"QMainWindow{background-color: rgb(22,22,22);}QLabel{color:white} #button{padding:25px; border-radius:15px;background: qlineargradient(x1:0, y1:0,x2:1,y2:1,stop: 0 #00dbde, stop:1 #D600ff); border:1px solid white}")
self.setMinimumSize(self.width/3*2,self.height/3*2)
self.layout.addWidget(self.placeholder)
def transition_splash(self):
opacityEffect = QGraphicsOpacityEffect(self.placeholder)
self.placeholder.setGraphicsEffect(opacityEffect)
animationEffect = QPropertyAnimation(opacityEffect, b"opacity")
animationEffect.setStartValue(1)
animationEffect.setEndValue(0)
animationEffect.setDuration(2500)
animationEffect.start()
timer = QTimer()
timer.singleShot(2500,self.change_layout)
def change_layout(self):
self.layout.setCurrentIndex(1)
# Initialise program
if __name__ == "__main__":
app = QApplication([])
page = window(app.primaryScreen().size().height(),app.primaryScreen().size().width())
page.show()
sys.exit(app.exec())
button.py
# Required Libraries for PySide6/Qt
from PySide6.QtWidgets import QWidget, QApplication, QPushButton, QVBoxLayout, QHBoxLayout, QLabel, QSizePolicy, QMainWindow, QSystemTrayIcon, QStackedLayout, QGraphicsOpacityEffect
from PySide6.QtGui import QIcon, QPixmap, QFont, QLinearGradient, QPainter, QColor
from PySide6.QtCore import Qt, QPointF, QSize, QVariantAnimation, QAbstractAnimation, QEasingCurve
# Custom PushButton for splash screen
class getStartedButton(QPushButton):
getStartedButtonColorStart = QColor(0, 219, 222)
getStartedButtonColorInt = QColor(101, 118, 255)
getStartedButtonColorEnd = QColor(214, 0, 255)
def __init__(self, text):
super().__init__()
self.setText(text)
# Setting ID so that it can be used in CSS
self.setObjectName("button")
self.setStyleSheet("font-size:24px")
# Button Animation
self.getStartedButtonAnimation = QVariantAnimation(
self, startValue=0.42, endValue=0.98, duration=300)
self.getStartedButtonAnimation.valueChanged.connect(
self.animate_button)
self.getStartedButtonAnimation.setEasingCurve(QEasingCurve.InOutCubic)
def enterEvent(self, event):
self.getStartedButtonAnimation.setDirection(QAbstractAnimation.Forward)
self.getStartedButtonAnimation.start()
# Suppression of event type error
try:
super().enterEvent(event)
except TypeError:
pass
def leaveEvent(self, event):
self.getStartedButtonAnimation.setDirection(
QAbstractAnimation.Backward)
self.getStartedButtonAnimation.start()
# Suppression of event type error
try:
super().enterEvent(event)
except TypeError:
pass
def animate_button(self, value):
grad = "background-color: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 {startColor}, stop:{value} {intermediateColor}, stop: 1.0 {stopColor});font-size:24px".format(
startColor=self.getStartedButtonColorStart.name(), intermediateColor=self.getStartedButtonColorInt.name(), stopColor=self.getStartedButtonColorEnd.name(), value=value
)
self.setStyleSheet(grad)
For context, I've looked at other questions already on SO and other sites such as
- How to change the opacity of a PyQt5 window
- https://www.pythonguis.com/tutorials/pyside6-animated-widgets/
CodePudding user response:
Not sure what is wrong with the code
The QPropertyAnimation object is destroyed before it gets a chance to start your animation. Your question has already been solved here.
To make it work, you must persist the object:
def transition_splash(self):
opacityEffect = QGraphicsOpacityEffect(self.placeholder)
self.placeholder.setGraphicsEffect(opacityEffect)
self.animationEffect = QPropertyAnimation(opacityEffect, b"opacity")
self.animationEffect.setStartValue(1)
self.animationEffect.setEndValue(0)
self.animationEffect.setDuration(2500)
self.animationEffect.start()
# Use the finished signal instead of the QTimer
self.animationEffect.finished.connect(self.change_layout)