Qt for Python Release: 6.12 is here!
Qt for Python 6.12 is here, alongside the Qt 6.12 LTS release. This is the biggest release we have had in a while. If you haven't read what's new in Qt itself, check out the general announcement.
Three things stand out:
- Reactive properties for QML: The new QtQmlFeatures module removes most of the signal-and-property boilerplate from your Python classes.
- PySide6 on iOS: The long-awaited iOS support is here.
- A security review of our tooling, motivated by the EU Cyber Resilience Act (CRA): We threat-modeled Qt for Python, fixed the issues we found, and added security markers to our source files.
Additionally, this release also contains an extensive list of fixes across Qt for Python.
QtQmlFeatures: reactive properties for Python (Technical Preview)
Exposing a Python value to QML takes more code than it should. A single `price` property needs a `Signal`, a backing field, a getter, and a setter that checks changes and emits the signal. Derived values need even more wiring on top of that.
The new QtQmlFeatures module removes most of it. Here is a shopping cart with `price`, `quantity`, and a derived `total`, written the traditional way:
class Cart(QObject): |
And here is the same cart with the new decorators. It also warns on large price jumps and logs every change. The traditional version has to do that inside each setter.
@auto_properties |
The properties are still real Q_PROPERTYs, so QML can read and write them. When QML changes `price`, the watcher fires, `total` updates, and the bindings refresh. You write no signal connections.
Four decorators do the work:
- `
@auto_properties` turns plain `self.x = ...` assignments into properties with notify signals. - `
@computed` declares a cached, read-only value that refreshes when its dependencies change. - `
@watch` runs after a property changes and gives you the old and new values. - `
@effect` runs whenever any of the listed properties change.
The Reactive Properties tutorial builds this cart step by step. It starts from the traditional version and removes one chunk of boilerplate per step. Additionally, the following examples are updated to use QtQmlFeatures:
- Objectlistmodel example
- ColorPaletteClient example. On a side note, the colorpaletteclient was also updated to use a FastAPI server as can be seen from the example doc.
- Scenegraph geometry example
Two new examples were also added:
- QML component loading example: Demonstrates how to load a QML defined type from Python with
load_qml_component - Quick controls from Python: Demonstrates how to load QtQuick Controls types from Python with
load_qml_componentand drive their functionality from Python
iOS support to boost our mobile compatibility
We mentioned in previous posts that iOS was in progress, and we're happy to say that support for iOS has been added (PYSIDE-2352). We would like to thank everyone who contributed along the way until reaching this milestone.
Getting started takes three steps:
- Get the iOS wheels.
iOS needs its own PySide6 and shiboken6 wheels, you can download them from the Qt for Python downloads page, or fetch them with qtpip:
$ pip install qtpip$ qtpip download PySide6 --ios --arch arm64 --platform deviceUse --platform simulator to get the wheels for the iOS Simulator instead. Each wheel targets one architecture and one platform.
- Generate the Xcode project.
The new pyside6-ios-deploy tool takes your project and the two wheels and generates an Xcode project. It uses the same pysidedeploy.spec file as pyside6-deploy and pyside6-android-deploy.
$ pyside6-ios-deploy /path/to/main.py --name "<AppName>" \--wheel-pyside=<PySide6 wheel> \--wheel-shiboken=<shiboken wheel>The tool needs a macOS host. It downloads
Python.xcframeworkautomatically on first use. - Build and run on Xcode.
Open the generated
.xcodeproj, then run it on the simulator or on a connected device. Signing and provisioning stay in Xcode. For a device build, pass your Apple Developer Team ID with--team-id.
For the full story, see pyside6-ios-deploy documentation and read our dedicated post, Python Mobile App Development: Bringing PySide6 on iOS which has been updated to have the latest features and options.
Deployment: smoother, and on more platforms
Deployment saw many changes in this release.
pyside6-deploy
- It can now be configured through a
[tool.pyside6.deploy]section inpyproject.toml(PYSIDE-1612). pyside6-deploy,pyside6-android-deploy, andpyside6-projectdeploy accept--no-installto skip automatic installation of build dependencies.- ⚠️ Behavior change:
pyside6-deploynow creates standalone packages by default (PYSIDE-3459). Use--onefileto get the previous default.
Android
- The NDK was updated to r28c, and the limited API wheel tag was raised to cp310.
- Host Python versions 3.10 through 3.14 are supported, and the resulting binaries are compatible with devices using 16 KB memory pages (PYSIDE-3425).
- The tool now reports unmet prerequisites instead of hanging, and reliably deploys applications using QtQmlFeatures.
- The prebuilt CPython is now used, replacing manual cross-compilation (PYSIDE-3426).
- We fixed the JDK lookup and the NDK quarantine on macOS.
Security hardening
Now that the Cyber Resilience Act (CRA) reporting obligations apply, security is getting more attention. Security has always been a priority for Qt for Python. For 6.12, we looked at our tools, the way an attacker would and did a complete threat analysis of Qt for Python.
Some of the notable changes here that have some effect on developer experience here are:
- Android deployment: a potential zip slip during wheel extraction was fixed, and downloaded NDK and command-line tools are now verified before being extracted.
- pyside6-qml: it now warns before importing Python files located next to the QML source.
- pyside6-deploy: a potential shell injection on Windows was fixed.
- Serialization: deserialization of non-trivial classes from QDataStream and QSettings is now blocked.
- Qt Widgets Designer: it now asks for a one-time confirmation before loading Python plugins.
- QUiLoader:
loadUiType()no longer searches for the pyside6-uic binary in the path. This now requires an explicit opt-in through a keyword argument and triggers a warning. We generally recommend moving away from this function in favor of running pyside6-uic at build time. - Shiboken6: when generating doxygen documentation, XML is parsed with XML_PARSE_NO_XXE for libxml ≥ 2.13, and entity expansion is disabled for older versions.
⚠️ We will be adding SBOM documents in future releases, because they are not yet ready.
New Qt Web Engine and Qt PDF distribution
If you are a Qt Web Engine user, you probably already read the blog post where a separation of the releases was announced. For that reason, we are following this configuration in order to ensure developers will get the latest releases alongside PySide releases.
Because our wheels contains the binaries of the modules, the schema that we will be using is to have wheel packages with a version that combines Qt full version + chromium version, this means that for Qt WebEngine 6.140 we will have a release in 6.12 that will have the version: 6.12.0.140, which will enable us to release new wheels both for new bug-fix releases in Qt, and new chromium releases for WebEngine.
The natural dependencies of Qt PDF module is also affected for this new structure and will follow the same configuration.
Python: new versions and improvement
- Python 3.15: initial support has been added (PYSIDE-3221). Shiboken generates the new module initialization code within conditionals for 3.15.
- Type hints: QDate and QDateTime now accept Python datetime types (PYSIDE-3038), and the __copy__ return annotation was fixed (PYSIDE-3338).
- New APIs: QBitArray.fromBits(PyBuffer) and QBitArray.bits() (PYSIDE-3335). QMetaObject::className() now returns qualified names for nested classes, such as Outer.Inner (PYSIDE-3294).
- Diagnostics: a warning is now shown when an unknown signal parameter is encountered (PYSIDE-3364).
- Free threaded Python: There is an on-going effort by Quansight to enable free-threaded support in PySide, that hopefully we will have news soon.
Stability fixes
- A memory leak in
QStandardItemModelwhen callingclear()after populating throughinvisibleRootItem()(PYSIDE-3365) - A crash when returning a class implementing the sequence protocol from
QAbstractItemModel.data()(PYSIDE-3423) - A crash retrieving the wrapper for
QtWebEngineCore.ResourceRequestBody(PYSIDE-3409) - A crash caused by stale
QLayoutItempointers due to incorrect parenting (PYSIDE-3469) - Connections to private methods (prefixed by
__) invoked by derived classes (PYSIDE-3376) - CMake build regressions affecting header packaging and include paths (PYSIDE-3306)
⚠️ The copy constructors of QTreeWidgetItem, QTableWidgetItem, and QListWidgetItem have been removed (PYSIDE-3367).
Shiboken6 updates
- A type system attribute for GIL usage in free-threaded builds (PYSIDE-2221)
- Blocking functions, like
QEventLoop.exec(), can now be flagged through a function modification (PYSIDE-3356) - Multiphase module initialization code is now also generated for the deployed case, after a related Nuitka bug was fixed (PYSIDE-3155)
- The
__module__attribute of shiboken6'sObjectType/Objecttypes was fixed (PYSIDE-3336) - shibokenmodule: a recursive version of dump() for inspecting parent/child relationships (PYSIDE-3469)
Getting Qt for Python
As always, you can get the latest version by running:
pip install pyside6
If you use uv, add it to your project and sync the environment:
uv add pyside6
uv sync
or if you are a commercial user:
qtpip install pyside6
Let's stay in touch!
We want to keep trying new experimental support, features, and Python module integration. What should we do next? Drop us a message or open a suggestion on the Qt Bug Tracker 👍.
We hope you enjoy the release. As always, drop by our community platforms and let us know if something is not working properly by opening a bug report.
Blog Topics
Comments
Subscribe to our blog
Try Qt 6.12 Now!
Download the latest release here: www.qt.io/download
Qt 6.12 LTS is now available, with new features and improvements for application developers and device creators.
We're Hiring
Check out all our open positions here and follow us on Instagram to see what it's like to be #QtPeople.