Skip to content

spyder-ide/qtpy

Repository files navigation

QtPy: Abstraction layer for PyQt5/PySide2/PyQt6/PySide6

license pypi version conda version download count OpenCollective Backers Join the chat at https://gitter.im/spyder-ide/public
PyPI status Github build status Coverage Status

Copyright © 2009– The Spyder Development Team

Description

QtPy provides a uniform interface to support PyQt5, PySide2, PyQt6, and PySide6 through a single codebase. It abstracts the differences between bindings and versions, allowing software to remain portable and easier to maintain.

Import from qtpy instead of PySide/PyQt:

from qtpy import QtCore, QtWidgets

app = QtWidgets.QApplication()
widget = QtWidgets.QWidget()
widget.show()
app.exec()

Installation

pip install qtpy

or

conda install qtpy

Requirements

The installation requires one of the supported packages (PyQt5, PyQt6, PySide2, PySide6) as QtPy does not install any binding by itself. If multiple of these libraries are found, PyQt5 is used by default. To set a specific binding, see Bindings.

Features

  • Supports multiple Qt bindings (PyQt5/6, PySide2/6) without requiring conditional imports or logic branching.
  • Detects and loads the available Qt binding automatically based on what is installed or already imported.
  • Normalizes the module structure to follow the Qt5 layout (QtGui / QtWidgets).
  • Simplifies the process of porting applications between Qt5 and Qt6 by handling API incompatibilities internally.
  • Enables incremental updates to project modules rather than requiring a full-scale rewrite when changing Qt providers.

Bindings

To set a specific binding, set the QT_API environment variable to one of the following values:

Value Binding
pyside6 PySide6
pyside2 PySide2
pyqt6 PyQt6
pyqt5 PyQt5

For example, to use PyQt6:

import os
os.environ['QT_API'] = 'pyqt6'
from qtpy import QtCore, QtGui, QtWidgets
print(QtWidgets.QWidget)

Module Aliases

QtPy provides aliases following the Qt5 module layout:

PyQt5/6 Alias
QtCore.pyqtSignal QtCore.Signal
QtCore.pyqtSlot QtCore.Slot
QtCore.pyqtProperty QtCore.Property
  • For PyQt6 enums, unscoped enum access was added by promoting the enums of the QtCore, QtGui, QtTest and QtWidgets modules.

  • Compatibility is added between the QtGui and QtOpenGL modules for the QOpenGL* classes.

For example:

from qtpy import QtCore, QtWidgets

class Widget(QtWidgets.QWidget):
    value_changed = QtCore.Signal(int)

Module Constants

Constant Value
QtCore.__version__ The current Qt version.
qtpy.QT_VERSION The current Qt version.
qtpy.PYSIDE_VERSION The current binding version for PySide. None if not used.
qtpy.PYQT_VERSION The current binding version for PyQt. None if not used.
qtpy.API_NAME The current selected binding.
qtpy.PYSIDE2 True/False if PySide2 is currently used.
qtpy.PYSIDE6 True/False if PySide6 is currently used.
qtpy.PYQT5 True/False if PyQt5 is currently used.
qtpy.PYQT6 True/False if PyQt6 is currently used.
qtpy.QT5 True/False if Qt5 is currently used.
qtpy.QT6 True/False if Qt6 is currently used.

Compat Module

The qtpy.compat module provides wrappers for QFileDialog static methods and SIP/Shiboken functions.

Source Wrappers
QFileDialog.getExistingDirectory qtpy.compat.getexistingdirectory
QFileDialog.getOpenFileName qtpy.compat.getopenfilename
QFileDialog.getOpenFileNames qtpy.compat.getopenfilenames
QFileDialog.getSaveFileName qtpy.compat.getsavefilename
sip.isdeleted / shiboken.isValid qtpy.compat.isalive

Type Checker Integration

Type checkers have no knowledge of installed packages, so these tools require additional configuration.

A Command Line Interface (CLI) is offered to help with usage of QtPy (to get MyPy and Pyright/Pylance args/configurations).

Mypy

The mypy-args command generates command line arguments for Mypy that will enable it to process the QtPy source files with the same API as QtPy itself would have selected.

To output a string of Mypy CLI args that will reflect the currently selected Qt API run:

qtpy mypy-args


For example, in an environment where PyQt5 is installed and selected (or the default fallback, if no binding can be found in the environment), this would output the following:

```text
--always-true=PYQT5 --always-false=PYSIDE2 --always-false=PYQT6 --always-false=PYSIDE6

Using Bash or a similar shell, this can be injected into the Mypy command line invocation:

mypy --package mypackage $(qtpy mypy-args)

Pyright/Pylance

In the case of Pyright, instead of runtime arguments you need to create a pyrightconfig.json config file for the project or a pyright section in pyproject.toml. Refer to the Pyright Configuration for a full reference. In order to set this configuration, QtPy offers the pyright-config command for guidance.

To print the necessary configuration for your pyrightconfig.json or pyproject.toml, run:

qtpy pyright-config

If you don't have either of these, you should create them.

For example, in an environment where PyQt5 is installed and selected (or the default fallback, if no binding can be found in the environment), qtpy pyright-config would output the following:

pyrightconfig.json

{
  "defineConstant": {
    "PYQT5": true,
    "PYSIDE2": false,
    "PYQT6": false,
    "PYSIDE6": false
  }
}

pyproject.toml

[tool.pyright.defineConstant]
PYQT5 = true
PYSIDE2 = false
PYQT6 = false
PYSIDE6 = false

Note: This configuration is necessary for the correct usage of the default VSCode type checking feature when using QtPy in your source code.

Testing Matrix

Currently, QtPy runs tests for different bindings on Linux, Windows and macOS, using Python 3.9, 3.11 and 3.13, and installed via conda and pip. For the PyQt bindings, the installation of extra packages is also checked via pip.

The current test matrix looks something like this:

Python 3.9 3.11 3.13
OS Binding / manager conda pip conda pip conda pip
Linux PyQt5 5.12 5.15 5.15 5.15 (with extras) 5.15 5.15
PyQt6 skip (unavailable) 6.5 skip (unavailable) 6.8 (with extras) skip (unavailable) 6.8
PySide2 5.13 5.12 5.15 skip (no wheels available) skip (unavailable) skip (no wheels available)
PySide6 6.5 6.5 6.8 6.8 6.8 6.8
Windows PyQt5 5.12 5.15 5.15 5.15 (with extras) 5.15 5.15
PyQt6 skip (unavailable) 6.2 skip (unavailable) 6.8 (with extras) skip (unavailable) 6.8
PySide2 5.13 5.15 5.15 skip (no wheels available) skip (unavailable) skip (no wheels available)
PySide6 6.5 6.2 6.8 6.8 6.8 6.8
MacOS PyQt5 5.12 5.15 skip 5.15 (with extras) 5.15 5.15
PyQt6 skip (unavailable) 6.2 skip 6.8 (with extras) skip (unavailable) 6.8
PySide2 5.13 5.12 skip skip (no wheels available) skip (unavailable) skip (no wheels available)
PySide6 6.8 6.2 skip 6.8 6.8 6.8

Note: The mentioned extra packages for the PyQt bindings are the following:

  • PyQt3D and PyQt6-3D
  • PyQtChart and PyQt6-Charts
  • PyQtDataVisualization and PyQt6-DataVisualization
  • PyQtNetworkAuth and PyQt6-NetworkAuth
  • PyQtPurchasing
  • PyQtWebEngine and PyQt6-WebEngine
  • QScintilla and PyQt6-QScintilla

Attribution and Acknowledgments

This project is based on the pyqode.qt project and the spyderlib.qt module from the Spyder project, and also includes contributions adapted from qt-helpers, developed as part of the glue project.

Unlike pyqode.qt this is not a namespace package, so it is not tied to a particular project or namespace.

Success Stories

You can check out examples of how QtPy adds support for multiple bindings and allows for incremental updates to new binding versions here:

License

This project is released under the MIT license.

Contributing

Everyone is welcome to contribute! See our Contributing guide for more details.

Sponsors

QtPy is funded thanks to the generous support of our users from around the world through Open Collective and NumFOCUS:

Sponsors

About

Provides an uniform layer to support PyQt5, PySide2, PyQt6, PySide6 with a single codebase

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Sponsor this project

Contributors

Languages