# Qt.py keeps one PySide6-shaped import working across four Qt bindings

> Qt.py is a single file that sits between your code and whichever Qt binding is installed, PySide6, PySide2, PyQt6 or PyQt5, and adds a QtCompat layer for the API differences that matter. It was born in the film and visual effects industry and is still released as a package, at version 2.0.7.

**mottosso/Qt.py** — Minimal Python 2 & 3 shim around all Qt bindings - PySide2, PySide6, PyQt5 and PyQt6.

- Repository: https://github.com/mottosso/Qt.py
- Stars: 997 · Forks: 259
- Language: Python
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/mottosso-qt-py

## One import line in front of four Qt bindings

There are several Python bindings for Qt, and they are close enough to be interchangeable in most code and different enough to break in the rest. Qt.py is a shim that sits between your source and whichever binding happens to be installed, covering PySide6, PySide2, PyQt6 and PyQt5.

The project exists because of the film and visual effects industry. The README says it was born there to address the need for software that could run with more than one flavour of the Qt bindings for Python. In a studio pipeline that is not a nicety: the same tool has to open on a workstation with one binding and on a render farm with another, and code that imports PySide6 directly simply fails on the second machine.

The goal is stated in a single line: write once, run in any binding. Usage looks exactly like PySide6, because the members of Qt stem directly from those available via PySide6, with a few additions of its own. For a team that has to support a mixed estate, this is the whole value proposition.

## Three goals: co-existence, one build, visible differences

The project lists three goals, and each one is a design constraint rather than a slogan.

The first is co-existence. Qt.py should not affect other bindings running in the same interpreter session. That matters for hosts such as Maya, where a plugin may have loaded one binding while your own code loads another, and a module that reshaped the global import state would break everything around it.

The second is building for one and running with all: code written with Qt.py should run on any binding.

The third is the most interesting, because it argues against a common instinct. Explicit is better than implicit, and the goal is that differences between bindings should be visible to you. So Qt.py is not trying to erase the differences between PySide6 and PyQt5 and pretend they do not exist. It aims at a common surface and pushes the awkward parts into a compat layer where you can see them. That choice is why the project ships a caveats system and a known problems section rather than only a shim.

## Installing it: one file, pip, conda or vendored into your tree

Qt.py is a single file, and the README lists several ways to get it: copy and paste it into your project, download it as-is, clone it as-is, or install it from PyPI or Anaconda.

```bash
# From PyPI
$ pip install Qt.py
```

The conda route adds the conda-forge channel first:

```bash
# From Anaconda
$ conda config --add channels conda-forge
$ conda install qt.py
```

There is a third path that suits embedded and studio setups: vendoring, where the file is copied into the project tree so that the version travels with the code and nothing depends on the host's site-packages.

The installation advice that matters most is a warning. The README says never to use the latest commit for production, and to use the latest release instead, because otherwise you cannot match a bug report to a version and you cannot know which fixes apply to you. The pip and conda installs give you the latest stable release; unstable ones carry a .b suffix, for example 1.1.0.b3.

## QtCompat is where the awkward differences are parked

Everything in Qt comes from PySide6, plus a small set of members of its own: __version__ for the project version, __binding__ for a string naming the binding in use, __qt_version__ for the Qt version such as Qt 5.6.1, and __binding_version__ for the binding version such as PySide 1.2.6. Those four attributes are enough for a program to report which stack it ended up on, which is the first thing worth doing in a pipeline that has to support several.

The compat layer is the QtCompat submodule. It provides wrappers for functionality that differs critically across bindings, among them loadUi, a minimal wrapper of the PyQt6 and PySide6 loadUi equivalents, and translate, a compatibility wrapper around QCoreApplication.translate. The wrappers table also lists wrapInstance, which the project changelog ties to the 1.3.0 release, described as stability improvements and a greater ability for wrapInstance to do its job.

The pattern for a caller is consistent: import the usual modules from Qt, and when a call differs between bindings, reach for the QtCompat version of it rather than branching on __binding__ yourself.

## QtSiteConfig.py and the environment decide which binding wins

Selection is a documented mechanism, not a coin toss. The README has dedicated sections for environment variables, for the subset of members to expose, for branching on binding-specific code, for overriding the preferred choice, and for a QtSiteConfig.py file. The examples directory in the repository mirrors that structure: it contains a QtSiteConfig folder, a QtSiteConfig_platforms folder, a loadUi folder and a README of its own.

So there are at least three levers. A site config file can record the preferred binding for a machine or a site, the environment can override the choice, and your own code can branch when a binding genuinely behaves differently. The project is explicit that the differences should be visible, which is the reason it bothers with a site config file and a platform specific variant at all.

The examples folder is the place to look for the mechanics. Because the repository ships working examples rather than only prose, a reader can see the shape of a site config and of a loadUi wrapper before writing either.

## Compared with committing to one binding directly

The alternative to Qt.py is to import PySide6, or PyQt5, and accept that your program runs only where that binding exists. That is a perfectly reasonable choice for an application you ship and control, and it has a real advantage: you get the full API of one binding, including everything Qt.py deliberately leaves alone.

Qt.py takes the opposite approach. It defines a common surface shaped after PySide6, adds four introspection attributes, and moves the differences into a compat submodule. The cost is that anything outside that common surface still needs binding-specific handling, so the abstraction is partial by design rather than by accident.

The README itself acknowledges the neighbourhood with a table of contents entry titled Projects similar to Qt.py, alongside entries for who is using it and for projects built on it. The related tools referenced elsewhere in the documentation are a transition guide for moving to Qt 6 and a guide to developing with Qt.py, which is where a team facing a binding migration would start.

## Ten years of releases, a 2.x line, and a beta classification

The history in the README is worth reading because it explains the current shape. The first release was 0.2.6 in June 2016, 0.5.0 was the alpha of the convert command in September 2016, and 0.6.9 was marked a stable release in the same month. Version 1.0.0 arrived in March 2017 with increased safety and a backwards incompatible change, 1.1.0 added a new test suite, 1.4.1 in May 2024 added support for Qt 6, and 2.0.1 in January 2025 dropped support for Qt 4 and for Python older than 3.7.

The current line is 2.x, with releases 2.0.4 on 2026-03-28, 2.0.5 on 2026-06-03 and 2.0.7 on 2026-09-22, and the last push to the master branch was on 2026-09-22.

The project metadata marks it as Development Status :: 4 - Beta, requires Python 3.7 or newer, and lists types-PySide6 as a dependency, which is how the type information for the PySide6 shaped API reaches editors and checkers. Development tooling is pinned in the dev extra: mypy, ruff and tox, and the repository keeps a tox.ini, a tests.py, and a separate caveats system built from CAVEATS.md, caveats.py and build_caveats.py. The licence is MIT.

## Conclusion

Take Qt.py when your code has to run inside environments where you do not control which Qt binding is installed, which is the situation it was built for, and use the released version rather than the latest commit. Do not use it as a way to avoid learning the bindings, because its own goal is to keep their differences visible. Before you commit, check the known problems section and the CAVEATS.md file in the repository, and confirm the Qt version your target hosts ship, since Qt 4 support was dropped in 2.0.1.

## FAQ

### How do I install Qt.py for Python?

Qt.py is a single file that can be copied into your project, downloaded, cloned, or installed with pip install Qt.py or, after adding the conda-forge channel, conda install qt.py. The README advises using the latest release rather than the latest commit for production use.

### Which Qt bindings does Qt.py support?

The supported bindings are PySide6, PySide2, PyQt6 and PyQt5, and all members of Qt come directly from PySide6 plus a few additions of its own. Support for Qt 4 and for Python older than 3.7 was dropped in version 2.0.1.

### How do I find out which Qt binding my code is actually running on?

Qt adds four attributes: __version__ for the project version, __binding__ for a string naming the binding in use, __qt_version__ and __binding_version__. The QtCompat submodule also wraps functionality that differs across bindings, such as loadUi and translate.

### Is Qt.py still being maintained?

The repository is not archived and the last push to the master branch was on 2026-09-22, the same day as release 2.0.7, with 2.0.5 on 2026-06-03 and 2.0.4 on 2026-03-28 before it. The project metadata classifies it as Development Status :: 4 - Beta and the licence is MIT.

## Sources

- [Issues](https://github.com/mottosso/Qt.py/issues)
- [License: MIT](https://github.com/mottosso/Qt.py/blob/master/LICENSE)
- [mottosso/Qt.py on GitHub](https://github.com/mottosso/Qt.py)
- [README](https://github.com/mottosso/Qt.py/blob/master/README.md)
- [Releases](https://github.com/mottosso/Qt.py/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mottosso-qt-py
