diff --git a/README.md b/README.md index d082d7ee..5ab066e2 100644 --- a/README.md +++ b/README.md @@ -37,15 +37,16 @@ Qt Bindings Test Matrix The following table represents the python environments we test in our CI system. Our CI system uses Ubuntu 18.04, Windows Server 2019, and macOS 10.15 base images. -| Qt-Bindings | Python 2.7 | Python 3.6 | Python 3.7 | Python 3.8 | -| :----------- | :----------------: | :----------------: | :----------------: | :----------------: | -| PyQt-4 | :white_check_mark: | :x: | :x: | :x: | -| PySide1 | :white_check_mark: | :x: | :x: | :x: | -| PyQt-5.9 | :x: | :white_check_mark: | :x: | :x: | -| PySide2-5.13 | :x: | :x: | :white_check_mark: | :x: | -| PyQt-5.14 | :x: | :x: | :x: | :white_check_mark: | +| Qt-Bindings | Python 2.7 | Python 3.6 | Python 3.7 | Python 3.8 | +| :------------- | :----------------: | :----------------: | :----------------: | :----------------: | +| PyQt-4 | :white_check_mark: | :x: | :x: | :x: | +| PySide1 | :white_check_mark: | :x: | :x: | :x: | +| PyQt5-5.9 | :x: | :white_check_mark: | :x: | :x: | +| PySide2-5.13 | :x: | :x: | :white_check_mark: | :x: | +| PyQt5-Latest | :x: | :x: | :x: | :white_check_mark: | +| PySide2-Latest | :x: | :x: | :x: | :white_check_mark: | -* pyqtgraph has had some incompatabilities with PySide2-5.6, and we recommend you avoid those bindings if possible +* pyqtgraph has had some incompatibilities with PySide2-5.6, and we recommend you avoid those bindings if possible * on macOS with Python 2.7 and Qt4 bindings (PyQt4 or PySide) the openGL related visualizations do not work Support diff --git a/azure-test-template.yml b/azure-test-template.yml index 5cdae342..902077a6 100644 --- a/azure-test-template.yml +++ b/azure-test-template.yml @@ -18,7 +18,7 @@ jobs: python.version: '2.7' qt.bindings: "pyside" install.method: "conda" - Python36-PyQt-5.9: + Python36-PyQt5-5.9: python.version: "3.6" qt.bindings: "pyqt" install.method: "conda" @@ -26,10 +26,14 @@ jobs: python.version: "3.7" qt.bindings: "pyside2" install.method: "conda" - Python38-PyQt-Latest: + Python38-PyQt5-Latest: python.version: '3.8' qt.bindings: "PyQt5" install.method: "pip" + Python38-PySide2-Latest: + python.version: '3.8' + qt.bindings: "PySide2" + install.method: "pip" steps: - task: DownloadPipelineArtifact@2 diff --git a/examples/PlotWidget.py b/examples/PlotWidget.py index e52a893d..38bbc73c 100644 --- a/examples/PlotWidget.py +++ b/examples/PlotWidget.py @@ -13,7 +13,7 @@ import numpy as np import pyqtgraph as pg #QtGui.QApplication.setGraphicsSystem('raster') -app = QtGui.QApplication([]) +app = pg.mkQApp() mw = QtGui.QMainWindow() mw.setWindowTitle('pyqtgraph example: PlotWidget') mw.resize(800,800) diff --git a/examples/test_examples.py b/examples/test_examples.py index f10fe358..a9fecca2 100644 --- a/examples/test_examples.py +++ b/examples/test_examples.py @@ -150,7 +150,12 @@ conditionalExamples = { ) } -@pytest.mark.skipif(Qt.QT_LIB == "PySide2" and "Qt.QtVersion.startswith('5.14')", reason="new PySide2 doesn't have loadUi functionality") +@pytest.mark.skipif( + Qt.QT_LIB == "PySide2" + and tuple(map(int, Qt.PySide2.__version__.split("."))) >= (5, 14) + and tuple(map(int, Qt.PySide2.__version__.split("."))) < (5, 14, 2, 2), + reason="new PySide2 doesn't have loadUi functionality" +) @pytest.mark.parametrize( "frontend, f", [ diff --git a/pyqtgraph/Qt.py b/pyqtgraph/Qt.py index 702bc2bd..6035d7ac 100644 --- a/pyqtgraph/Qt.py +++ b/pyqtgraph/Qt.py @@ -10,7 +10,7 @@ This module exists to smooth out some of the differences between PySide and PyQt """ -import os, sys, re, time +import os, sys, re, time, subprocess, warnings from .python2_3 import asUnicode @@ -105,24 +105,38 @@ def _loadUiType(uiFile): if QT_LIB == "PYSIDE": import pysideuic else: - import pyside2uic as pysideuic - import xml.etree.ElementTree as xml + try: + import pyside2uic as pysideuic + except ImportError: + # later vserions of pyside2 have dropped pysideuic; use the uic binary instead. + pysideuic = None + # get class names from ui file + import xml.etree.ElementTree as xml parsed = xml.parse(uiFile) widget_class = parsed.find('widget').get('class') form_class = parsed.find('class').text - - with open(uiFile, 'r') as f: + + # convert ui file to python code + if pysideuic is None: + pyside2version = tuple(map(int, PySide2.__version__.split("."))) + if pyside2version >= (5, 14) and pyside2version < (5, 14, 2, 2): + warnings.warn('For UI compilation, it is recommended to upgrade to PySide >= 5.15') + uipy = subprocess.check_output(['pyside2-uic', uiFile]) + else: o = _StringIO() - frame = {} + with open(uiFile, 'r') as f: + pysideuic.compileUi(f, o, indent=0) + uipy = o.getvalue() - pysideuic.compileUi(f, o, indent=0) - pyc = compile(o.getvalue(), '', 'exec') - exec(pyc, frame) + # exceute python code + pyc = compile(uipy, '', 'exec') + frame = {} + 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) + # 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 diff --git a/pyqtgraph/tests/test_qt.py b/pyqtgraph/tests/test_qt.py index 9a4f373b..3ecf9db8 100644 --- a/pyqtgraph/tests/test_qt.py +++ b/pyqtgraph/tests/test_qt.py @@ -15,7 +15,12 @@ def test_isQObjectAlive(): @pytest.mark.skipif(pg.Qt.QT_LIB == 'PySide', reason='pysideuic does not appear to be ' 'packaged with conda') -@pytest.mark.skipif(pg.Qt.QT_LIB == "PySide2" and "pg.Qt.QtVersion.startswith('5.14')", reason="new PySide2 doesn't have loadUi functionality") +@pytest.mark.skipif( + pg.Qt.QT_LIB == "PySide2" + and tuple(map(int, pg.Qt.PySide2.__version__.split("."))) >= (5, 14) + and tuple(map(int, pg.Qt.PySide2.__version__.split("."))) < (5, 14, 2, 2), + reason="new PySide2 doesn't have loadUi functionality" +) def test_loadUiType(): path = os.path.dirname(__file__) formClass, baseClass = pg.Qt.loadUiType(os.path.join(path, 'uictest.ui'))