[SOLVED] Animating a frame to pop out of the bottom of the app without shrinking the height of other elements


I’m working on a desktop application for windows using PyQt and Qt creator.

What I want

I want to display messages to the user only when the user gave an input. I also wanted the message to draw the eye, so I’m going for the following animated solution:

A frame that’s hidden when not required (with height = 0 and width = the app’s width), ‘grows’ from the bottom of the app when needed, stays visible for 5-6 seconds, then retracts back to the bottom.

The app kind of looks like this without the message:

looks without the message

And kind of like this when the message IS displayed (note how the bottom gray element is ‘covered’ by the message):

looks when message is displayed

What I tried

So the way I did this was to create what I called "footer frame", which contains another frame that I call "message frame". The message frame contains a label that will hold, in time, the message for the user. Everything has pre-determined height, so to hide the whole thing I set the message frame to have a maximum height of 0.

So for the ‘growing’ animation I animated the message frame’s maximumHeight property.

The current problem

THING IS – since I wanted the app to be responsive I put everything in layouts… and because of that, whenever the message is displayed, the rest of the components are ‘compressed’ in height.
kind of like this (note how the bottom gray element is not covered by the message, but all the elements’ heights shrink a little):

compressed when displaying message

Instead, I wanted the messsage to ‘cover’ whatever is located under the message’s coordinates.

I tried to animate the geometry of the message frame, but nothing really happened – probably because the minimum height is still 0. So I tried to change the minimum height right before the animation begins; But that led to that compression again.
Tried to do the same with the footer frame, with the same results.

My question is : What is the best / preferred way of achieving the result I intend with Qt?


Layout managers always try to show all widgets they’re managing. If you want a widget to overlap others, you cannot put it inside a layout, you just create the widget with a parent, and that parent will probably be the widget containing the layout above or the top level window.

This cannot be done in Designer/Creator, as it’s assumed that once a layout has been set for a parent widget, all child widgets will be managed by that layout. The only solution is to do this programmatically.

In the following example I’m assuming a QMainWindow is used, so the reference parent widget is actually the central widget, not the QMainWindow: that’s because the alert should not cover other widgets that are part of a main window’s layout, like the status bar or a bottom placed tool bar or dock).

The animation is actually a QSequentialAnimationGroup that shows the rectangle, waits a few seconds, and hides it again. Since the window could be resized while the animation is running, a helper function is used to properly update the start and end values of the warning and eventually update the geometry when in the "paused" state (which is actually a QPauseAnimation); in order to do so, an event filter is installed on the central widget.

from random import randrange
from PyQt5 import QtCore, QtWidgets, uic

class MyWindow(QtWidgets.QMainWindow):
    def __init__(self):
        uic.loadUi('overlay.ui', self)

        self.alerts = []


        QtCore.QTimer.singleShot(2000, self.showAlert)

    def showAlert(self, message=None, timeout=250):
        # create an alert that is a child of the central widget
        alert = QtWidgets.QLabel(message or 'Some message to the user', 
            self.centralWidget(), wordWrap=True, 
            styleSheet='background: rgb({}, {}, {});'.format(
                randrange(192, 255), randrange(192, 255), randrange(192, 255)))
        alert.animation = QtCore.QSequentialAnimationGroup(alert)
            alert, b'geometry', duration=timeout))
            alert, b'geometry', duration=timeout))

        # delete the alert when the animation finishes
        def deleteLater():

        # update all animations, including the new one; this is not very
        # performant, as it also updates all existing alerts; it is 
        # just done for simplicity;
        # set the start geometry of the alert, show it, and start 
        # the new animation

    def updateAnimations(self):
        width = self.centralWidget().width() - 20
        y = self.centralWidget().height()
        margin = self.fontMetrics().height() * 2
        for alert in self.alerts:
            height = alert.heightForWidth(width) + margin
            startRect = QtCore.QRect(10, y, width, height)
            endRect = startRect.translated(0, -height)

    def eventFilter(self, obj, event):
        if obj == self.centralWidget() and event.type() == event.Resize and self.alerts:
            for alert in self.alerts:
                ani = alert.animation
                # if the animation is "paused", update the geometry
                if isinstance(ani.currentAnimation(), QtCore.QPauseAnimation):
        return super().eventFilter(obj, event)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MyWindow()

Answered By – musicamante

Answer Checked By – Terry (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *