36. Animation Fremework

The Qt animation framework provides a way to animate GUI elements in Qt Widgets applications. It lets you animate a Qt property value change of a widget or a QObject

We have already seen the animation framework in action in Chapter 21, where we built an animated bar widget from a progress bar and a label. In this chapter we build several versions of a self-dismissing notification panel: we first animate the panel’s position, then its color, geometry and opacity, after which we demonstrate various easing curves, sequential, and parallel animation groups, ending the chapter with a pause animation demonstration.

36.1 QPropertyAnimation Basics

Not all UI transitions in polished desktop applications are instant - panels slide into view, buttons shift on click, and widgets expand to reveal content. QPropertyAnimation drives any Q_PROPERTY value from a start to an end value over a set duration, and is the foundation everything else in this chapter builds on.

An icon of a clipboard-list1

You need to build a QFrame-based panel that slides horizontally into a budget tracker window when a button is clicked.

To use a property animation in your application:

  1. Create a QPropertyAnimation instance. For the purpose of this chapter we build NotificationPanel, a custom panel inherited from QFrame that has a label to display the notification message and a button to dismiss it. Because the panel will be animated, we add a QPropertyAnimation as an instance member. The constructor takes three arguments:

    • The animation target object. In this case, self, the NotificationPanel instance.
    • The target object’s property to animate as a bytes value. pos is a QPoint holding the top-left corner position of the frame within its parent widget. For a QObject’s member to serve as an animation target it must be marked with Qt’s Q_PROPERTY macro in C++, or with the @Property decorator in your PySide6 classes.
    • The animation’s Qt parent. Passing self ties the animation’s lifetime to the panel in Qt’s object hierarchy.

    We also set the animation duration to 500 milliseconds. The helper methods target_x() and target_y() calculate the horizontal and vertical coordinates needed to center the panel within its parent.

  2. Set the animation start and end values. A new NotificationPanel is created for each notification (in show_notification() on the main window). In show_panel() we set the pos start value to a position just off the left edge of the parent (QPoint(-self.width(), self.target_y())), and the end value to the centered position within the parent.

  3. Start the animation. Call show() to make the panel visible, then QPropertyAnimation.start() to begin the animation. The panel’s pos is interpolated from the start value to the end value over 500 milliseconds, ending up centered in the parent. Clicking the Dismiss button calls deleteLater(), which removes the panel from memory once Qt returns to the event loop.

Once you start the application it should look like this:

target_x() and target_y() are evaluated once when show_panel() is called. If the main window is resized while the animation is still running, the panel will land at the position calculated before the resize rather than the new center. For a production application you would recalculate the end value in a resizeEvent() handler. For the examples in this chapter the edge case is ignored to keep the focus on the animation mechanics.

36.2 Animating Color, Geometry, and Opacity

Position is only one of many properties you can animate - any value exposed through Qt’s property system is a valid target. In this section we set up three animations on the same widget: a color transition, a fade-in, and an expanding entrance. Color and opacity animate straightforwad from fixed values, but geometry requires a little more work: because the widget’s position isn’t known until it’s been placed by its parent, the start and end QRect values have to be set at show time rather than in __init__().

An icon of a clipboard-list1

Your task is to turn the sliding frame from the previous exercise into a status panel. You need to allow the users to choose from three separate QPropertyAnimation instances:

  • Color, which animates a custom property to drive a color transition on the message label from yellow to green.
  • Opacity, which fades the panel in from fully transparent to fully opaque.
  • Geometry, which expands the panel from a single point to its full size.

To animate widget color, geometry, and opacity in your application:

  1. Create a color animation. Qt widgets do not expose a color property, so for the notification panel we introduce ColoredLabel, a QLabel subclass that declares a custom color property of type QColor. Its setter applies the color to the label background via QSS. Create an animation targeting this property that interpolates from yellow to green over 1000 milliseconds.
  1. Create an opacity animation. Qt widgets do not expose an opacity property directly - opacity is handled via QGraphicsOpacityEffect. Create an instance, attach it to NotificationPanel with setGraphicsEffect(), then create an animation targeting the effect’s opacity property and interpolate from 0.0 (fully transparent) to 1.0 (fully opaque) over 1000 milliseconds.

  2. Create a geometry animation. A widget’s geometry includes its position within the parent, which is not known at construction time. For this reason the start and end QRect values are calculated in setup_geometry_animations(), called from show_panel() just before the panel is shown. The start rect collapses the panel to a single pixel at the center of its final position. The end rect is the full-size centered rect.

  3. Store the animations in a dictionary keyed by AnimationType. The AnimationType enum is passed into the panel’s constructor so the correct animation is selected up front. The main window provides a combobox for selecting the animation type. A new NotificationPanel is created for each notification with the currently selected type.

When you run the application, select an animation type from the combobox and click the button to show the notification panel playing the selected animation.

36.3 Easing Curves

A linear animation has constant speed (the same distance per frame from start to finish) which can feel mechanical. Easing curves remap elapsed time to progress non-linearly, giving motion a sense of weight, momentum, or snap.

An icon of a clipboard-list1

You are tasked with selecting an appropriate easing curve for the notification panel. You add all available easing curve to a combobox so the user can select which one will be applied before the panel is shown.

To inspect different easing curve types:

  1. Populate the combobox. Iterate over QEasingCurve.Type which exposes all registered curve types and store each enum value as item data. Skip the curve types listed in EXCLUDEC_CURVE_TYPES:

    • Custom - requires a user-supplied callback installed via setCustomType(). Without one, passing it to setEasingCurve() raises an error.
    • NCurveTypes - a sentinel value equal to the total count of curve types in the enum, used internally by Qt for bounds checking. It has no curve implementation.
    • BezierSpline and TCBSpline - user-defined spline types that require control points to be added manually via addCubicBezierSegment() and addTCBSegment() respectively before they have a defined shape.
    • InCurve and OutCurve - Internal values.
    • SineCurve and CosineCurve - implement oscillating waveforms. The animated property does not end up at endValue() when the animation finishes.

    SineCurve and CosineCurve are particularly dangerous: they break the promise of QpropertyAnimation that the animated property will end at endValue() when the animation finishes. InCurve, OutCurve, SineCurve and CosineCUrve are absent from the Qt documentation so you should not use them either.

  2. Set the easing curve. When the user clicks the button, show_notification(s) reads the currently selected curve from the combobox and passes it to NotificationPanel.set_easing_curve(), which forwards it to self.animation.setEasingCurve() before the animation starts.

  1. Show the notification. show_panel() is called with a randomly chosen message from MESSAGES. The animation runs with whichever curve is currently selected in the combobox.

When you select a curve and click the button, the panel covers the same distance every time but with a different animation type.

36.4 Sequential Animation Groups

Some transitions require steps in order - a panel slides in, then its label appears, then the Dismiss button pulses with a glow. Wiring this with signals is brittle. QSequentialAnimationGroup chains animations so each starts only when the previous one finishes, and the whole sequence is controlled through a single object.

An icon of a clipboard-list1

You restructure the notification panel’s entrance into three ordered phases: it slides in from off-screen, its message label fades up, and finally the Dismiss button pulses with an orange glow.

All three animations go into a single QSequentialAnimationGroup started with one call.

To use a sequential animation group in your application:

  1. Create the individual animations. Create three animations:

    • A position animation that moves the panel from off-screen to the center of the main window.
    • An opacity animation that fades the message label in from 0.0 to 1.0 via a QGraphicsOpactyEffect.
    • A glow animation that raises blurRadius of a QgraphicsDropShadowEffect attached to the Dismiss button from 0 to 10.
  2. Create the animation group and add the animations. Create a QSequentialAnimationGroup and add the three animations in the order they will play: position first , then opacity, then glow.

  3. Start the group instead the individual animations. In show_panel(), set the start and end values for the position animation, then call start() on the group. One call drives all three animations in sequence.

When you run the application and click the button, the panel slides to the center of the window, the message label fades in, and finally the Dismiss button gets an orange glow.

36.5 Parallel Animation Groups

Where sequential groups enforce order, QParallelAnimationGroup runs all its children simultaneously, making it the right tool when several properties should change together as one cohesive motion.

An icon of a clipboard-list1

You replace the notification panel’s slide-only entrance with a combined effect: position and opacity animate in parallel, so the panel slides in while fading from transparent to opaque. Dismiss reverses the same group before deleting the panel.

To use a parallel animation group in your application:

  1. Create the individual animations. As in the previous section, create a position animation and an opacity animation. This time both are set to 1000 milliseconds (This doesn’t always have to be the case, and the group finishes only when the longest child finishes). Set OutBounce as the easing curve for the position animation in show_panel().

  2. Create the group and add both animations. Create a QParallelAnimationGroup and add the position and opacity animations to it. Unlike QSequentialAnimationGroup, you can’t use pauses here: both animations start at the same moment.

  3. Start the animation group.

Rather than discarding the panel immediatelly as in the sequential animation group example, hide_panel() calls anim_group.setDirection(Backward) and restarts the group, which plays both animations in reverse - the panel slides back off-screen while fading out. deleteLater() is connected to finished so the panel is cleaned up only after the reverse animation completes.

When a QParallelAnimationGroup contains animations of different durations and is reversed, shorter animations do not start immediately - they are delayed by the difference between their duration and the group’s total duration. A 300ms animation inside a 1000ms group will sit idle for 700ms before playing in reverse.

When you run the application the panel bounces in while fading up. Clicking Dismis fades and slides it back out before it is destroyed.

36.6 Pause Animations

QPauseAnimation is a duration-only animation - it doesn’t have any visual effects. Its purpose is to introduce pauses inside a QSequentialAnimationGroup without having to use a QTimer.

An icon of a clipboard-list1

You complete the notification panel by giving it self-dismissing behaviour: on trigger, the position animation plays, waits via a QPauseAnimation, then the opacity animation plays. You expose the pause duration as a constructor parameter. Clicking Dismiss early interrupts the group cleanly and runs the fade-out immediatelly.

To use a pause animation in your application:

  1. Create the animations. Create the position and the opacity animations.

  2. Create the animation group and add the animations including the pause. Add the animation in order: slide-in, pause, and fade-out. The group drives the entire lifecycle of the panel with a single start() call in show_panel(). It is not necessary to construct a QPauseAnimation yourself. QSequentialAnimationGroup provides the convenience functions addPause() and insertPause() for that.

It is possible that the user dismisses the panel while the animation group is still playing. dismiss() checks whether the opacity animation is already running (which indicates that the auto-dismiss fade is already in progress) and does nothing in that case. Otherwise it stops the group, starts the opacity animation directly, and connect its finished signal to deleteLater().

When you run the applicatio the panel slides in, holds for the specified duration, then fades out on its own. Clicking Dismiss at any point before the fade begins triggers the same fade-out immediatelly.