1# -*- coding: utf-8 -*- 2# 3# Copyright © 2009- The Spyder Development Team 4# Copyright © 2014-2015 Colin Duquesnoy 5# 6# Licensed under the terms of the MIT License 7# (see LICENSE.txt for details) 8 9""" 10**QtPy** is a shim over the various Python Qt bindings. It is used to write 11Qt binding indenpendent libraries or applications. 12 13If one of the APIs has already been imported, then it will be used. 14 15Otherwise, the shim will automatically select the first available API (PyQt5, 16PySide2, PyQt4 and finally PySide); in that case, you can force the use of one 17specific bindings (e.g. if your application is using one specific bindings and 18you need to use library that use QtPy) by setting up the ``QT_API`` environment 19variable. 20 21PyQt5 22===== 23 24For PyQt5, you don't have to set anything as it will be used automatically:: 25 26 >>> from qtpy import QtGui, QtWidgets, QtCore 27 >>> print(QtWidgets.QWidget) 28 29 30PySide2 31====== 32 33Set the QT_API environment variable to 'pyside2' before importing other 34packages:: 35 36 >>> import os 37 >>> os.environ['QT_API'] = 'pyside2' 38 >>> from qtpy import QtGui, QtWidgets, QtCore 39 >>> print(QtWidgets.QWidget) 40 41PyQt4 42===== 43 44Set the ``QT_API`` environment variable to 'pyqt' before importing any python 45package:: 46 47 >>> import os 48 >>> os.environ['QT_API'] = 'pyqt' 49 >>> from qtpy import QtGui, QtWidgets, QtCore 50 >>> print(QtWidgets.QWidget) 51 52PySide 53====== 54 55Set the QT_API environment variable to 'pyside' before importing other 56packages:: 57 58 >>> import os 59 >>> os.environ['QT_API'] = 'pyside' 60 >>> from qtpy import QtGui, QtWidgets, QtCore 61 >>> print(QtWidgets.QWidget) 62 63""" 64 65from distutils.version import LooseVersion 66import os 67import platform 68import sys 69import warnings 70 71# Version of QtPy 72from ._version import __version__ 73 74 75class PythonQtError(RuntimeError): 76 """Error raise if no bindings could be selected.""" 77 pass 78 79 80class PythonQtWarning(Warning): 81 """Warning if some features are not implemented in a binding.""" 82 pass 83 84 85# Qt API environment variable name 86QT_API = 'QT_API' 87 88# Names of the expected PyQt5 api 89PYQT5_API = ['pyqt5'] 90 91# Names of the expected PyQt4 api 92PYQT4_API = [ 93 'pyqt', # name used in IPython.qt 94 'pyqt4' # pyqode.qt original name 95] 96 97# Names of the expected PySide api 98PYSIDE_API = ['pyside'] 99 100# Names of the expected PySide2 api 101PYSIDE2_API = ['pyside2'] 102 103# Detecting if a binding was specified by the user 104binding_specified = QT_API in os.environ 105 106# Setting a default value for QT_API 107os.environ.setdefault(QT_API, 'pyqt5') 108 109API = os.environ[QT_API].lower() 110initial_api = API 111assert API in (PYQT5_API + PYQT4_API + PYSIDE_API + PYSIDE2_API) 112 113is_old_pyqt = is_pyqt46 = False 114PYQT5 = True 115PYQT4 = PYSIDE = PYSIDE2 = False 116 117# When `FORCE_QT_API` is set, we disregard 118# any previously imported python bindings. 119if os.environ.get('FORCE_QT_API') is not None: 120 if 'PyQt5' in sys.modules: 121 API = initial_api if initial_api in PYQT5_API else 'pyqt5' 122 elif 'PySide2' in sys.modules: 123 API = initial_api if initial_api in PYSIDE2_API else 'pyside2' 124 elif 'PyQt4' in sys.modules: 125 API = initial_api if initial_api in PYQT4_API else 'pyqt4' 126 elif 'PySide' in sys.modules: 127 API = initial_api if initial_api in PYSIDE_API else 'pyside' 128 129 130if API in PYQT5_API: 131 try: 132 from PyQt5.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore 133 from PyQt5.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore 134 PYSIDE_VERSION = None 135 136 if sys.platform == 'darwin': 137 macos_version = LooseVersion(platform.mac_ver()[0]) 138 if macos_version < LooseVersion('10.10'): 139 if LooseVersion(QT_VERSION) >= LooseVersion('5.9'): 140 raise PythonQtError("Qt 5.9 or higher only works in " 141 "macOS 10.10 or higher. Your " 142 "program will fail in this " 143 "system.") 144 elif macos_version < LooseVersion('10.11'): 145 if LooseVersion(QT_VERSION) >= LooseVersion('5.11'): 146 raise PythonQtError("Qt 5.11 or higher only works in " 147 "macOS 10.11 or higher. Your " 148 "program will fail in this " 149 "system.") 150 151 del macos_version 152 except ImportError: 153 API = os.environ['QT_API'] = 'pyside2' 154 155if API in PYSIDE2_API: 156 try: 157 from PySide2 import __version__ as PYSIDE_VERSION # analysis:ignore 158 from PySide2.QtCore import __version__ as QT_VERSION # analysis:ignore 159 160 PYQT_VERSION = None 161 PYQT5 = False 162 PYSIDE2 = True 163 164 if sys.platform == 'darwin': 165 macos_version = LooseVersion(platform.mac_ver()[0]) 166 if macos_version < LooseVersion('10.11'): 167 if LooseVersion(QT_VERSION) >= LooseVersion('5.11'): 168 raise PythonQtError("Qt 5.11 or higher only works in " 169 "macOS 10.11 or higher. Your " 170 "program will fail in this " 171 "system.") 172 173 del macos_version 174 except ImportError: 175 API = os.environ['QT_API'] = 'pyqt' 176 177if API in PYQT4_API: 178 try: 179 import sip 180 try: 181 sip.setapi('QString', 2) 182 sip.setapi('QVariant', 2) 183 sip.setapi('QDate', 2) 184 sip.setapi('QDateTime', 2) 185 sip.setapi('QTextStream', 2) 186 sip.setapi('QTime', 2) 187 sip.setapi('QUrl', 2) 188 except (AttributeError, ValueError): 189 # PyQt < v4.6 190 pass 191 from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore 192 from PyQt4.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore 193 PYSIDE_VERSION = None 194 PYQT5 = False 195 PYQT4 = True 196 except ImportError: 197 API = os.environ['QT_API'] = 'pyside' 198 else: 199 is_old_pyqt = PYQT_VERSION.startswith(('4.4', '4.5', '4.6', '4.7')) 200 is_pyqt46 = PYQT_VERSION.startswith('4.6') 201 202if API in PYSIDE_API: 203 try: 204 from PySide import __version__ as PYSIDE_VERSION # analysis:ignore 205 from PySide.QtCore import __version__ as QT_VERSION # analysis:ignore 206 PYQT_VERSION = None 207 PYQT5 = PYSIDE2 = False 208 PYSIDE = True 209 except ImportError: 210 raise PythonQtError('No Qt bindings could be found') 211 212# If a correct API name is passed to QT_API and it could not be found, 213# switches to another and informs through the warning 214if API != initial_api and binding_specified: 215 warnings.warn('Selected binding "{}" could not be found, ' 216 'using "{}"'.format(initial_api, API), RuntimeWarning) 217 218API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4', 219 'pyside': 'PySide', 'pyside2':'PySide2'}[API] 220 221if PYQT4: 222 import sip 223 try: 224 API_NAME += (" (API v{0})".format(sip.getapi('QString'))) 225 except AttributeError: 226 pass 227