Skip to main content

CSS transition animations for PyQt6/PySide6 — extends static QSS with transition and other properties

Project description

Qt CSS Engine

A CSS animation engine for PyQt6/PySide6 that extends Qt's static stylesheet system (QSS) with dynamic CSS transitions and extra properties like box-shadow, opacity and CSS gradients. Qt's stylesheet engine has no concept of time or interpolation — this project implements an out-of-band animation system that intercepts transition: declarations from a stylesheet, tracks widget pseudo-states (hover, pressed, focus), and drives smooth property animations via Qt's animation framework. Dynamic class change is also supported so .btn -> .btn.active -> .btn.other-state will animate based on the transition property.

All that is required is to install TransitionEngine as an event filter and it will take care of the rest.

CSS Hot reload is supported via TransitionEngine.reload_rules(new_rules)

Subcontrols (e.g ::item, ::handle) are not supported because they are not real QWidgets. They will be just passed through as classic QSS stylesheet blocks.

This engine was made primarily for use in YASB project and this will be the main focus for now, but it can be integrated into any Qt application.

WARNING: This project is still in early development, very experimental and is not ready for production use. There will be bugs and breaking changes.

Python and Qt version

Python 3.14+

PySide6/PyQt6 6.10+

Installation

uv add qt-css-engine

Usage

from qt_css_engine import TransitionEngine, extract_rules

app = QApplication([])
cleaned_qss, rules = extract_rules(stylesheet)
app.setStyleSheet(cleaned_qss)
engine = TransitionEngine(rules)
app.installEventFilter(engine)

Simple transition examples

/* A simple widget with a hover transition */
.btn {
    background-color: steelblue;
    color: white;
    border-radius: 4px;
    transition: background-color 300ms ease;
}

.btn:hover {
    background-color: royalblue;
}

/* A widget with a box-shadow transition */
#btn {
    background-color: steelblue;
    color: white;
    border-radius: 4px;
    box-shadow: 0px 0px 0px black;
    transition: box-shadow 300ms ease;
}

#btn:hover {
    box-shadow: 4px 4px 4px black;
}

More complex examples

Check the ./demo/main.py for hot reloading, dynamic class change and more.

Transition syntax and supported values

Shorthand

transition: <property> <duration> [<easing>] [<delay>];
transition: <property> <duration> [<delay>] [<easing>];

/* multiple */
transition: background-color 300ms ease, border-radius 200ms linear 50ms;

/* all animatable properties */
transition: all 300ms ease-in-out;

Longhands

transition-property: background-color, border-radius;
transition-duration: 300ms, 200ms;
transition-timing-function: ease, linear;
transition-delay: 0ms, 50ms;

Longhands override the shorthand when both are declared in the same block. Values cycle per the CSS spec when list lengths differ.

Time units

Unit Example
Milliseconds 300ms
Seconds 0.3s

Easing curves

Value Description
linear Constant speed
ease Slow in, slow out (default)
ease-in Slow start
ease-out Slow end
ease-in-out Slow start and end
cubic-bezier(x1, y1, x2, y2) Custom curve — values outside [0, 1] produce overshoot
steps(n) / steps(n, jump-end|jump-start|jump-none|jump-both) Discrete stepped animation
step-start / step-end Aliases for steps(1, jump-start) / steps(1, jump-end)

Delay

Positive delay: animation starts after the delay elapses. The property is frozen at its current rendered value during the delay period.

Negative delay: animation starts immediately but offset |delay| ms into the timeline, as if it had already been running that long.

transition: background 400ms ease 100ms;   /* 100ms positive delay */
transition: background 400ms ease -100ms;  /* starts 100ms in */
/* OR */
transition: background 400ms 100ms ease;
transition: background 400ms -100ms ease;

Supported pseudo-classes

Pseudo-class Description Transition Static
:hover Hover in-out
:focus Focus in-out
:pressed Hold-down mouse button
:checked Checked state on-off
:active Window gains/loses focus
:clicked (NEW) Play full animation loop on click

Supported properties

Color values accepted everywhere a color is listed: named (red, steelblue, …), #RRGGBB, #AARRGGBB, rgb(), rgba(), hsl(), hsla().

Numeric values accepted everywhere a length is listed: <n>px, <n>pt, <n>em.

Property Description Supported values Transition Static
background-color / background Background color color values; linear-gradient(), radial-gradient(), conic-gradient() (static only) ✅ solid colors
color Text color color values
border-color Border color shorthand (→ 4 sides) color values
border-top-color, border-right-color, border-bottom-color, border-left-color Per-side border color color values
border-width Border width shorthand (→ 4 sides) length values
border-top-width, border-right-width, border-bottom-width, border-left-width Per-side border width length values
border-radius Border radius shorthand (→ 4 corners) length values
border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius Per-corner border radius length values
padding Padding shorthand (→ 4 sides) length values
padding-top, padding-right, padding-bottom, padding-left Per-side padding length values
margin Margin shorthand (→ 4 sides) length values
margin-top, margin-right, margin-bottom, margin-left Per-side margin length values
width, height Widget size length values
min-width, max-width, min-height, max-height Size constraints length values
font-size Font size length values
font-weight Font weight 100900
letter-spacing Letter spacing length values
word-spacing Word spacing length values
spacing Qt widget item spacing length values
opacity Widget opacity — not native QSS, applied via QGraphicsOpacityEffect 0.01.0
box-shadow Drop shadow — not native QSS, applied via QGraphicsDropShadowEffect. No inset, spread is ignored. First shadow wins when multiple are declared. <x> <y> [blur] [spread] <color>
cursor Mouse cursor — Qt QSS ignores cursor, applied via setCursor() default, pointer, text, crosshair, wait, progress, help, move, grab, grabbing, copy, alias, not-allowed, no-drop, cell, all-scroll, n-resize, s-resize, e-resize, w-resize, ne-resize, nw-resize, se-resize, sw-resize, ns-resize, ew-resize, nesw-resize, nwse-resize, row-resize, col-resize, none

Environment variables

Variable Description Default
CSS_ENGINE_LEFT_CLICK_ONLY Exclude middle and right click from triggering on-click animations False
CSS_ENGINE_EVENT_LOGGING Enable internal event debug logging (qt_css_engine.event logger) False

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

qt_css_engine-0.1.3.tar.gz (97.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

qt_css_engine-0.1.3-py3-none-any.whl (49.2 kB view details)

Uploaded Python 3

File details

Details for the file qt_css_engine-0.1.3.tar.gz.

File metadata

  • Download URL: qt_css_engine-0.1.3.tar.gz
  • Upload date:
  • Size: 97.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for qt_css_engine-0.1.3.tar.gz
Algorithm Hash digest
SHA256 54ef3d4ab8c1353fb146e99d859a9224077dd01701f36cb52c68f6cf6ae3ac0d
MD5 7ba6a8eee47be9442d2525e6534aac8d
BLAKE2b-256 a59e523a7f96221c13cf45976618f795ec97810d5ea7e2b54a8ec4a413ae9faf

See more details on using hashes here.

File details

Details for the file qt_css_engine-0.1.3-py3-none-any.whl.

File metadata

  • Download URL: qt_css_engine-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 49.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.19 {"installer":{"name":"uv","version":"0.11.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for qt_css_engine-0.1.3-py3-none-any.whl
Algorithm Hash digest
SHA256 533b531c9f9b4802f53039356382865fbb9433161119b637adafb6b5160ecc3b
MD5 8c4123d208746d16d9995c059df8e991
BLAKE2b-256 d98bab50e707fcea832fca6566b4a165cae88dd6a38355d1d9a907a3b505c862

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page