PyQt5 QStackedWidget widget pop before animation

78 views Asked by At

I'm starting to build a UI with PyQt5. For now, I have a mainwindow in which I put a QStackedWidget. It only contains 2 widgets currently (**welcome_widget **which is the first widget that the user would see after opening the UI / main_widget which is the widget the user would arrive on after having successfully entered its idents).

I would like that, after successfully enter idents (button in welcome_widget, not shown here, no security on it for the moment), the welcome_widget go off with and animation (vertically above the window) and the main_widget arrive with an animation too (vertically from below the window). It works well for the welcome_widget (function "enter_the_app"). For the main_widget (function 'show_main_widget' and 'set_main_widget_visible') it works, but the widget quick pop one time before it reappears with the animation.

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QFrame, QLabel, QVBoxLayout, QHBoxLayout, QPushButton, QSizePolicy, \
    QMainWindow, QLineEdit, QStackedWidget, QGraphicsOpacityEffect, QScrollArea, QGridLayout, QAction
from PyQt5.QtCore import Qt, QPropertyAnimation, QEasingCurve, QPoint, QSize, QSequentialAnimationGroup, \
    QParallelAnimationGroup, QRect
from PyQt5.QtGui import QPixmap, QIcon, QPalette, QBrush, QPainter, QPainterPath


class MainWindow(QMainWindow):
    """

    """
    def __init__(self):
        super().__init__()

        self.background_widget = BackgroundWidget("lightgreen")  # Create the background widget
        self.welcome_widget = WelcomeWidgetObj(self)  # First widget to be shown opening the ui
        self.welcome_anim = QPropertyAnimation(self.welcome_widget, b"geometry")  # Animation of the disappearing of welcome_widget
        self.main_widget = MainWidget(self)  # First widget to appear after having successfully enter idents
        self.stacked_widget = QStackedWidget()  # Will contain the different pages of the ui
        self.stacked_widget.addWidget(self.welcome_widget)  # Add welcome_widget page to the stacked widget
        self.stacked_widget.addWidget(self.main_widget)  # Add main_widget page to the stacked widget
        self.init_ui()
        self.center()

    def init_ui(self):
        """
        Initialize main window.

        :return: None
        """
        self.setCentralWidget(self.background_widget)  # To have a background image that takes all the space provided by the mainwindow
        background_widget_layout = QVBoxLayout(self.background_widget)
        background_widget_layout.addWidget(self.stacked_widget)  # Add the stacked widget that contains all the pages of the ui, in 'background_widget'
        # This means that if the pages don't have background, it will be the same as 'background_widget'
        self.setWindowIcon(QIcon(''))  # UI's logo
        self.setWindowTitle('.....')  # UI's name
        self.setGeometry(100, 100, 1400, 850)  # (x, y, width, height) -> initial main window's size

    def center(self):
        """
        This function lets display the window in center of the screen.

        :return: None
        """
        # Get the screen geometry
        screen = QApplication.primaryScreen()
        screen_geometry = screen.geometry()

        # Get the center point of the screen
        center_point = screen_geometry.center()

        # Calculate the center position for the window
        window_geometry = self.frameGeometry()
        window_geometry.moveCenter(center_point)

        # Move the window to the center position
        self.move(window_geometry.topLeft())

    def enter_the_app(self):
        """
        This function applies an animation on the 'welcome_widget' after the user would have successfully provided its idents

        :return: None
        """
        self.welcome_anim.setEndValue(QRect(0, -self.height(), self.width(), self.height()))  # Indicate where the widget has to end
        self.welcome_anim.setDuration(800)  # Animation's speed
        self.welcome_anim.start()  # Start animations
        self.welcome_anim.finished.connect(self.show_main_widget)  # Connect the end of the animation with the apparition of 'main_widget'

    def show_main_widget(self):
        """
        This function performs animation on main_widget and then makes it visible.

        :return: None
        """
        self.stacked_widget.setCurrentWidget(self.main_widget)  # Set 'main_widget' as the current widget of 'stacked_widget'
        self.main_widget.background_widget.setGeometry(QRect(0, self.height(), self.width(), self.height()))
        # self.main_widget.background_widget.show()  # Sees show method of 'main_widget'
        self.anim_main_widget = QPropertyAnimation(self.main_widget.background_widget, b"geometry")  # main_widget appearance animation
        self.anim_main_widget.setStartValue(QRect(0, self.height(), self.width(), self.height()))
        self.anim_main_widget.setEndValue(QRect(0, 0, self.width(), self.height()))
        self.anim_main_widget.setDuration(800)
        self.anim_main_widget.start()


class BackgroundWidget(QWidget):
    def __init__(self, color, parent=None):
        super().__init__(parent)
        self.setStyleSheet(f"background-color : {color}")


class WelcomeWidgetObj(QWidget):
    """
    Widget corresponding to the welcome page (in which the user will have to enter idents to access the ui).
    """
    def __init__(self, parent):
        super().__init__(parent)

        self.main_layout = QVBoxLayout(self)
        self.main_layout.setContentsMargins(0, 0, 0, 0)  # First widget inside 'main_layout' will not have margin with it
        self.main_layout.setSpacing(0)  # No spacing between widgets put inside 'main_layout'

        self.init_widget()

    def get_main_layout(self):
        return self.main_layout

    def init_widget(self):
        """
        Builds self, first widget seen by the user after opening the ui

        :return: None
        """
        main_frame = QFrame()
        main_frame_layout = QVBoxLayout(main_frame)
        main_frame_layout.setAlignment(Qt.AlignCenter)
        main_frame_layout.setSpacing(20)

        self.get_main_layout().addWidget(main_frame)

        enter_app_button = QPushButton("VALIDER")
        enter_app_button.setFixedHeight(50)
        enter_app_button.setFixedWidth(180)
        enter_app_button.setStyleSheet("background-color : purple")
        enter_app_button.clicked.connect(self.parent().enter_the_app)
        main_frame_layout.addWidget(enter_app_button)


class MainWidget(QWidget):
    def __init__(self, parent):
        super().__init__(parent)

        self.main_layout = QVBoxLayout(self)  # Set up the widget's main layout
        self.main_layout.setContentsMargins(0, 0, 0, 0)  # Widgets inside 'main_layout' will have no margins

        self.main_window = parent
        self.background_widget = BackgroundWidget("pink")
        self.parent_geometry = parent.geometry()

        self.init_widget()

    def get_main_window(self):
        return self.main_window

    def get_main_layout(self):
        return self.main_layout

    def get_background_widget(self):
        return self.background_widget

    def init_widget(self):
        """

        :return:
        """
        background_widget_layout = QVBoxLayout(self.get_background_widget())
        background_widget_layout.setContentsMargins(40, 20, 40, 20)
        self.get_main_layout().addWidget(self.get_background_widget())
        # self.get_background_widget().hide()  # Initially hide the widget

        title_frame = QFrame()
        title_frame.setStyleSheet('background-color : red')
        background_widget_layout.addWidget(title_frame)
        title_frame.setMaximumHeight(110)
        title_frame_layout = QHBoxLayout(title_frame)
        title_frame_layout.setAlignment(Qt.AlignCenter)

        back_home_button = QPushButton("BUTTON 1")
        back_home_button.setFixedSize(75, 75)
        title_frame_layout.addWidget(back_home_button)
        back_home_button.setStyleSheet("background-color : blue")

        bottom_frame = QFrame()
        bottom_frame.setStyleSheet("background-color : yellow")
        background_widget_layout.addWidget(bottom_frame)


if __name__ == '__main__':
    # Create the application
    app = QApplication(sys.argv)
    # Create the main window
    window = MainWindow()
    # Show the main window
    window.show()
    # Start the application event loop
    sys.exit(app.exec_())

The above example reproduces the problem (mostly if you quickly clik on the button, sometimes later too, I don't really understand when it happens). You can try several times if it doesn't reproduce the problem on first tries.

0

There are 0 answers