2 # Copyright © 2009- The Spyder Development Team
3 # Copyright © 2014-2015 Colin Duquesnoy
5 # Licensed under the terms of the MIT License
6 # (see LICENSE.txt for details)
9 **QtPy** is a shim over the various Python Qt bindings. It is used to write
10 Qt binding independent libraries or applications.
12 If one of the APIs has already been imported, then it will be used.
14 Otherwise, the shim will automatically select the first available API (PyQt5, PySide2,
15 PyQt6 and PySide6); in that case, you can force the use of one
16 specific bindings (e.g. if your application is using one specific bindings and
17 you need to use library that use QtPy) by setting up the ``QT_API`` environment
23 For PyQt5, you don't have to set anything as it will be used automatically::
25 >>> from qtpy import QtGui, QtWidgets, QtCore
26 >>> print(QtWidgets.QWidget)
31 Set the QT_API environment variable to 'pyside2' before importing other
35 >>> os.environ['QT_API'] = 'pyside2'
36 >>> from qtpy import QtGui, QtWidgets, QtCore
37 >>> print(QtWidgets.QWidget)
43 >>> os.environ['QT_API'] = 'pyqt6'
44 >>> from qtpy import QtGui, QtWidgets, QtCore
45 >>> print(QtWidgets.QWidget)
51 >>> os.environ['QT_API'] = 'pyside6'
52 >>> from qtpy import QtGui, QtWidgets, QtCore
53 >>> print(QtWidgets.QWidget)
66 class PythonQtError(RuntimeError):
67 """Error raised if no bindings could be selected."""
70 class PythonQtWarning(Warning):
71 """Warning if some features are not implemented in a binding."""
74 class PythonQtValueError(ValueError):
75 """Error raised if an invalid QT_API is specified."""
78 class QtBindingsNotFoundError(PythonQtError
):
79 """Error raised if no bindings could be selected."""
80 _msg
= 'No Qt bindings could be found'
83 super().__init
__(self
._msg
)
86 class QtModuleNotFoundError(ModuleNotFoundError
, PythonQtError
):
87 """Raised when a Python Qt binding submodule is not installed/supported."""
88 _msg
= 'The {name} module was not found.'
89 _msg_binding
= '{binding}'
92 def __init__(self
, *, name
, msg
=None, **msg_kwargs
):
94 binding
= self
._msg
_binding
.format(binding
=API_NAME
)
95 msg
= msg
or f
'{self._msg} {self._msg_extra}'.strip()
96 msg
= msg
.format(name
=name
, binding
=binding
, **msg_kwargs
)
97 super().__init
__(msg
, name
=name
)
100 class QtModuleNotInOSError(QtModuleNotFoundError
):
101 """Raised when a module is not supported on the current operating system."""
102 _msg
= '{name} does not exist on this operating system.'
105 class QtModuleNotInQtVersionError(QtModuleNotFoundError
):
106 """Raised when a module is not implemented in the current Qt version."""
107 _msg
= '{name} does not exist in {version}.'
109 def __init__(self
, *, name
, msg
=None, **msg_kwargs
):
111 version
= 'Qt5' if QT5
else 'Qt6'
112 super().__init
__(name
=name
, version
=version
)
115 class QtBindingMissingModuleError(QtModuleNotFoundError
):
116 """Raised when a module is not supported by a given binding."""
117 _msg_extra
= 'It is not currently implemented in {binding}.'
120 class QtModuleNotInstalledError(QtModuleNotFoundError
):
121 """Raise when a module is supported by the binding, but not installed."""
122 _msg_extra
= 'It must be installed separately'
124 def __init__(self
, *, missing_package
=None, **superclass_kwargs
):
125 self
.missing_package
= missing_package
126 if missing_package
is not None:
127 self
._msg
_extra
+= ' as {missing_package}.'
128 super().__init
__(missing_package
=missing_package
, **superclass_kwargs
)
131 # Qt API environment variable name
134 # Names of the expected PyQt5 api
135 PYQT5_API
= ['pyqt5']
137 PYQT6_API
= ['pyqt6']
139 # Names of the expected PySide2 api
140 PYSIDE2_API
= ['pyside2']
142 # Names of the expected PySide6 api
143 PYSIDE6_API
= ['pyside6']
145 # Minimum supported versions of Qt and the bindings
146 QT5_VERSION_MIN
= PYQT5_VERSION_MIN
= '5.9.0'
147 PYSIDE2_VERSION_MIN
= '5.12.0'
148 QT6_VERSION_MIN
= PYQT6_VERSION_MIN
= PYSIDE6_VERSION_MIN
= '6.2.0'
150 QT_VERSION_MIN
= QT5_VERSION_MIN
151 PYQT_VERSION_MIN
= PYQT5_VERSION_MIN
152 PYSIDE_VERSION_MIN
= PYSIDE2_VERSION_MIN
154 # Detecting if a binding was specified by the user
155 binding_specified
= QT_API
in os
.environ
157 API_NAMES
= {'pyqt5': 'PyQt5', 'pyside2': 'PySide2',
158 'pyqt6': 'PyQt6', 'pyside6': 'PySide6'}
159 API
= os
.environ
.get(QT_API
, 'pyqt5').lower()
161 if API
not in API_NAMES
:
162 raise PythonQtValueError(
163 f
'Specified QT_API={repr(QT_API.lower())} is not in valid options: '
166 is_old_pyqt
= is_pyqt46
= False
168 QT4
= QT6
= PYQT4
= PYQT6
= PYSIDE
= PYSIDE2
= PYSIDE6
= False
171 PYSIDE_VERSION
= None
174 # Unless `FORCE_QT_API` is set, use previously imported Qt Python bindings
175 if not os
.environ
.get('FORCE_QT_API'):
176 if 'PyQt5' in sys
.modules
:
177 API
= initial_api
if initial_api
in PYQT5_API
else 'pyqt5'
178 elif 'PySide2' in sys
.modules
:
179 API
= initial_api
if initial_api
in PYSIDE2_API
else 'pyside2'
180 elif 'PyQt6' in sys
.modules
:
181 API
= initial_api
if initial_api
in PYQT6_API
else 'pyqt6'
182 elif 'PySide6' in sys
.modules
:
183 API
= initial_api
if initial_api
in PYSIDE6_API
else 'pyside6'
187 from PyQt5
.QtCore
import PYQT_VERSION_STR
as PYQT_VERSION
# analysis:ignore
188 from PyQt5
.QtCore
import QT_VERSION_STR
as QT_VERSION
# analysis:ignore
194 os
.environ
[QT_API
] = API
196 if API
in PYSIDE2_API
:
198 from PySide2
import __version__
as PYSIDE_VERSION
# analysis:ignore
199 from PySide2
.QtCore
import __version__
as QT_VERSION
# analysis:ignore
206 os
.environ
[QT_API
] = API
210 from PyQt6
.QtCore
import PYQT_VERSION_STR
as PYQT_VERSION
# analysis:ignore
211 from PyQt6
.QtCore
import QT_VERSION_STR
as QT_VERSION
# analysis:ignore
219 os
.environ
[QT_API
] = API
221 if API
in PYSIDE6_API
:
223 from PySide6
import __version__
as PYSIDE_VERSION
# analysis:ignore
224 from PySide6
.QtCore
import __version__
as QT_VERSION
# analysis:ignore
230 raise QtBindingsNotFoundError()
232 os
.environ
[QT_API
] = API
235 # If a correct API name is passed to QT_API and it could not be found,
236 # switches to another and informs through the warning
237 if API
!= initial_api
and binding_specified
:
238 warnings
.warn('Selected binding "{}" could not be found, '
239 'using "{}"'.format(initial_api
, API
), RuntimeWarning)
242 # Set display name of the Qt API
243 API_NAME
= API_NAMES
[API
]
246 # QtDataVisualization backward compatibility (QtDataVisualization vs. QtDatavisualization)
247 # Only available for Qt5 bindings > 5.9 on Windows
248 from . import QtDataVisualization
as QtDatavisualization
# analysis:ignore
249 except (ImportError, PythonQtError
):