""" This module exists to smooth out some of the differences between PySide and PyQt4: * Automatically import either PyQt4 or PySide depending on availability * Allow to import QtCore/QtGui pyqtgraph.Qt without specifying which Qt wrapper you want to use. * Declare QtCore.Signal, .Slot in PyQt4 * Declare loadUiType function for Pyside """ import os, sys, re, time from .python2_3 import asUnicode PYSIDE = 'PySide' PYQT4 = 'PyQt4' PYQT5 = 'PyQt5' QT_LIB = os.getenv('PYQTGRAPH_QT_LIB') ## Automatically determine whether to use PyQt or PySide (unless specified by ## environment variable). ## This is done by first checking to see whether one of the libraries ## is already imported. If not, then attempt to import PyQt4, then PySide. if QT_LIB is None: libOrder = [PYQT4, PYSIDE, PYQT5] for lib in libOrder: if lib in sys.modules: QT_LIB = lib break if QT_LIB is None: for lib in libOrder: try: __import__(lib) QT_LIB = lib break except ImportError: pass if QT_LIB is None: raise Exception("PyQtGraph requires one of PyQt4, PyQt5 or PySide; none of these packages could be imported.") if QT_LIB == PYSIDE: from PySide import QtGui, QtCore, QtOpenGL, QtSvg try: from PySide import QtTest if not hasattr(QtTest.QTest, 'qWait'): @staticmethod def qWait(msec): start = time.time() QtGui.QApplication.processEvents() while time.time() < start + msec * 0.001: QtGui.QApplication.processEvents() QtTest.QTest.qWait = qWait except ImportError: pass import PySide try: from PySide import shiboken isQObjectAlive = shiboken.isValid except ImportError: def isQObjectAlive(obj): try: if hasattr(obj, 'parent'): obj.parent() elif hasattr(obj, 'parentItem'): obj.parentItem() else: raise Exception("Cannot determine whether Qt object %s is still alive." % obj) except RuntimeError: return False else: return True VERSION_INFO = 'PySide ' + PySide.__version__ # Make a loadUiType function like PyQt has # Credit: # http://stackoverflow.com/questions/4442286/python-code-genration-with-pyside-uic/14195313#14195313 class StringIO(object): """Alternative to built-in StringIO needed to circumvent unicode/ascii issues""" def __init__(self): self.data = [] def write(self, data): self.data.append(data) def getvalue(self): return ''.join(map(asUnicode, self.data)).encode('utf8') def loadUiType(uiFile): """ Pyside "loadUiType" command like PyQt4 has one, so we have to convert the ui file to py code in-memory first and then execute it in a special frame to retrieve the form_class. from stackoverflow: http://stackoverflow.com/a/14195313/3781327 seems like this might also be a legitimate solution, but I'm not sure how to make PyQt4 and pyside look the same... http://stackoverflow.com/a/8717832 """ import pysideuic import xml.etree.ElementTree as xml #from io import StringIO parsed = xml.parse(uiFile) widget_class = parsed.find('widget').get('class') form_class = parsed.find('class').text with open(uiFile, 'r') as f: o = StringIO() frame = {} pysideuic.compileUi(f, o, indent=0) pyc = compile(o.getvalue(), '', 'exec') exec(pyc, frame) #Fetch the base_class and form class based on their type in the xml from designer form_class = frame['Ui_%s'%form_class] base_class = eval('QtGui.%s'%widget_class) return form_class, base_class elif QT_LIB == PYQT4: from PyQt4 import QtGui, QtCore, uic try: from PyQt4 import QtSvg except ImportError: pass try: from PyQt4 import QtOpenGL except ImportError: pass try: from PyQt4 import QtTest except ImportError: pass VERSION_INFO = 'PyQt4 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR elif QT_LIB == PYQT5: # We're using PyQt5 which has a different structure so we're going to use a shim to # recreate the Qt4 structure for Qt5 from PyQt5 import QtGui, QtCore, QtWidgets, uic try: from PyQt5 import QtSvg except ImportError: pass try: from PyQt5 import QtOpenGL except ImportError: pass try: from PyQt5 import QtTest QtTest.QTest.qWaitForWindowShown = QtTest.QTest.qWaitForWindowExposed except ImportError: pass # Re-implement deprecated APIs __QGraphicsItem_scale = QtWidgets.QGraphicsItem.scale def scale(self, *args): if args: sx, sy = args tr = self.transform() tr.scale(sx, sy) self.setTransform(tr) else: return __QGraphicsItem_scale(self) QtWidgets.QGraphicsItem.scale = scale def rotate(self, angle): tr = self.transform() tr.rotate(angle) self.setTransform(tr) QtWidgets.QGraphicsItem.rotate = rotate def translate(self, dx, dy): tr = self.transform() tr.translate(dx, dy) self.setTransform(tr) QtWidgets.QGraphicsItem.translate = translate def setMargin(self, i): self.setContentsMargins(i, i, i, i) QtWidgets.QGridLayout.setMargin = setMargin def setResizeMode(self, *args): self.setSectionResizeMode(*args) QtWidgets.QHeaderView.setResizeMode = setResizeMode QtGui.QApplication = QtWidgets.QApplication QtGui.QGraphicsScene = QtWidgets.QGraphicsScene QtGui.QGraphicsObject = QtWidgets.QGraphicsObject QtGui.QGraphicsWidget = QtWidgets.QGraphicsWidget QtGui.QApplication.setGraphicsSystem = None # Import all QtWidgets objects into QtGui for o in dir(QtWidgets): if o.startswith('Q'): setattr(QtGui, o, getattr(QtWidgets,o) ) VERSION_INFO = 'PyQt5 ' + QtCore.PYQT_VERSION_STR + ' Qt ' + QtCore.QT_VERSION_STR else: raise ValueError("Invalid Qt lib '%s'" % QT_LIB) # Common to PyQt4 and 5 if QT_LIB.startswith('PyQt'): import sip def isQObjectAlive(obj): return not sip.isdeleted(obj) loadUiType = uic.loadUiType QtCore.Signal = QtCore.pyqtSignal ## Make sure we have Qt >= 4.7 versionReq = [4, 7] USE_PYSIDE = QT_LIB == PYSIDE USE_PYQT4 = QT_LIB == PYQT4 USE_PYQT5 = QT_LIB == PYQT5 QtVersion = PySide.QtCore.__version__ if QT_LIB == PYSIDE else QtCore.QT_VERSION_STR m = re.match(r'(\d+)\.(\d+).*', QtVersion) if m is not None and list(map(int, m.groups())) < versionReq: print(list(map(int, m.groups()))) raise Exception('pyqtgraph requires Qt version >= %d.%d (your version is %s)' % (versionReq[0], versionReq[1], QtVersion))