From 31dbc9eb01329af3d7d67cbfd6ee76a632aa84c2 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 19 Sep 2016 12:18:14 +0200 Subject: [PATCH 001/703] Use relative imports so its vendored more easily --- qtpy/QtCore.py | 2 +- qtpy/QtDesigner.py | 2 +- qtpy/QtGui.py | 2 +- qtpy/QtMultimedia.py | 6 +++--- qtpy/QtNetwork.py | 2 +- qtpy/QtPrintSupport.py | 2 +- qtpy/QtSvg.py | 2 +- qtpy/QtTest.py | 2 +- qtpy/QtWebEngineWidgets.py | 2 +- qtpy/QtWidgets.py | 6 +++--- qtpy/__init__.py | 2 +- qtpy/_patch/qcombobox.py | 4 ++-- qtpy/compat.py | 8 ++++---- qtpy/uic.py | 4 ++-- 14 files changed, 23 insertions(+), 23 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 07adafb4..bde48a77 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -10,7 +10,7 @@ Provides QtCore classes and functions. """ -from qtpy import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT4, PYSIDE, PythonQtError if PYQT5: diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index 8b2ddedb..4aaafc81 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -9,7 +9,7 @@ Provides QtDesigner classes and functions. """ -from qtpy import PYQT5, PYQT4, PythonQtError +from . import PYQT5, PYQT4, PythonQtError if PYQT5: diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 84049e57..99477c00 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -13,7 +13,7 @@ the ``PyQt5.QtGui`` module. """ -from qtpy import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT4, PYSIDE, PythonQtError if PYQT5: diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index ce3c929e..7ed307e8 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -1,6 +1,6 @@ -from qtpy import PYQT5 -from qtpy import PYQT4 -from qtpy import PYSIDE +from . import PYQT5 +from . import PYQT4 +from . import PYSIDE if PYQT5: diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index aca335bd..de4ff473 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -10,7 +10,7 @@ Provides QtNetwork classes and functions. """ -from qtpy import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT4, PYSIDE, PythonQtError if PYQT5: diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index b272174b..8959fad9 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -9,7 +9,7 @@ Provides QtPrintSupport classes and functions. """ -from qtpy import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT4, PYSIDE, PythonQtError if PYQT5: diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index 2f210ca8..3cf76113 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -8,7 +8,7 @@ """Provides QtSvg classes and functions.""" # Local imports -from qtpy import PYQT4, PYQT5, PYSIDE, PythonQtError +from . import PYQT4, PYQT5, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtSvg import (QGraphicsSvgItem, QSvgGenerator, QSvgRenderer, diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 6d59c765..28067d8b 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -10,7 +10,7 @@ Provides QtTest and functions """ -from qtpy import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT4, PYSIDE, PythonQtError if PYQT5: diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index bd073691..18b5e58f 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -10,7 +10,7 @@ Provides QtWebEngineWidgets classes and functions. """ -from qtpy import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT4, PYSIDE, PythonQtError # To test if we are using WebEngine or WebKit diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 267d16b7..96134648 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -13,9 +13,9 @@ were the ``PyQt5.QtWidgets`` module. """ -from qtpy import PYQT5, PYQT4, PYSIDE, PythonQtError -from qtpy._patch.qcombobox import patch_qcombobox -from qtpy._patch.qheaderview import introduce_renamed_methods_qheaderview +from . import PYQT5, PYQT4, PYSIDE, PythonQtError +from ._patch.qcombobox import patch_qcombobox +from ._patch.qheaderview import introduce_renamed_methods_qheaderview if PYQT5: diff --git a/qtpy/__init__.py b/qtpy/__init__.py index b8373439..273df1d3 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -53,7 +53,7 @@ import os # Version of QtPy -from qtpy._version import __version__ +from ._version import __version__ #: Qt API environment variable name QT_API = 'QT_API' diff --git a/qtpy/_patch/qcombobox.py b/qtpy/_patch/qcombobox.py index ec7c01d1..1017763e 100644 --- a/qtpy/_patch/qcombobox.py +++ b/qtpy/_patch/qcombobox.py @@ -42,8 +42,8 @@ def patch_qcombobox(QComboBox): using PyQt4 and PySide to avoid issues. """ - from qtpy.QtGui import QIcon - from qtpy.QtCore import Qt, QObject + from .QtGui import QIcon + from .QtCore import Qt, QObject class userDataWrapper(): """ diff --git a/qtpy/compat.py b/qtpy/compat.py index ae38090e..e6bdd1b4 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -11,9 +11,9 @@ import sys import collections -from qtpy import PYQT4 -from qtpy.QtWidgets import QFileDialog -from qtpy.py3compat import is_text_string, to_text_string, TEXT_TYPES +from . import PYQT4 +from .QtWidgets import QFileDialog +from .py3compat import is_text_string, to_text_string, TEXT_TYPES # ============================================================================= @@ -105,7 +105,7 @@ def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', options = QFileDialog.Options(0) try: # PyQt =v4.6 QString = None # analysis:ignore diff --git a/qtpy/uic.py b/qtpy/uic.py index ff049ff7..abca4911 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -1,7 +1,7 @@ import os -from qtpy import PYSIDE, PYQT4, PYQT5 -from qtpy.QtWidgets import QComboBox +from . import PYSIDE, PYQT4, PYQT5 +from .QtWidgets import QComboBox __all__ = ['loadUi'] From 3b79dddbdb003528237e0614e14ad91f5ce32c70 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 19 Sep 2016 12:24:06 +0200 Subject: [PATCH 002/703] Missed a level --- qtpy/_patch/qcombobox.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/_patch/qcombobox.py b/qtpy/_patch/qcombobox.py index 1017763e..d3e98bed 100644 --- a/qtpy/_patch/qcombobox.py +++ b/qtpy/_patch/qcombobox.py @@ -42,8 +42,8 @@ def patch_qcombobox(QComboBox): using PyQt4 and PySide to avoid issues. """ - from .QtGui import QIcon - from .QtCore import Qt, QObject + from ..QtGui import QIcon + from ..QtCore import Qt, QObject class userDataWrapper(): """ From 189f819867cbf15511b5fd4954ab475f8fa0aad4 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Mon, 26 Sep 2016 14:08:49 +0200 Subject: [PATCH 003/703] QStringListModel -> QtCore --- qtpy/QtCore.py | 6 ++++-- qtpy/QtWidgets.py | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index bde48a77..6794cb45 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -32,7 +32,8 @@ from PyQt4.QtCore import pyqtSlot as Slot from PyQt4.QtCore import pyqtProperty as Property from PyQt4.QtGui import (QItemSelection, QItemSelectionModel, - QItemSelectionRange, QSortFilterProxyModel) + QItemSelectionRange, QSortFilterProxyModel, + QStringListModel) from PyQt4.QtCore import QT_VERSION_STR as __version__ # Those are imported from `import *` @@ -40,7 +41,8 @@ elif PYSIDE: from PySide.QtCore import * from PySide.QtGui import (QItemSelection, QItemSelectionModel, - QItemSelectionRange, QSortFilterProxyModel) + QItemSelectionRange, QSortFilterProxyModel, + QStringListModel) import PySide.QtCore __version__ = PySide.QtCore.__version__ else: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 96134648..62fa44ec 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -53,7 +53,8 @@ QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform, QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent, QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qFuzzyCompare, - qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator) + qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator, + QStringListModel) # These objects belong to QtPrintSupport del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, @@ -102,7 +103,7 @@ QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform, QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent, QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qGray, qGreen, - qIsGray, qRed, qRgb, qRgba, QIntValidator) + qIsGray, qRed, qRgb, qRgba, QIntValidator, QStringListModel) # These objects belong to QtPrintSupport del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, From 579e1045beb8fae8607b8cea69a4a705f9505e4a Mon Sep 17 00:00:00 2001 From: Dhruv Govil Date: Wed, 26 Oct 2016 12:09:00 -0700 Subject: [PATCH 004/703] Initial setup for PySide2 support, pretesting --- .gitignore | 1 + qtpy/QtCore.py | 4 +++- qtpy/QtGui.py | 4 +++- qtpy/QtMultimedia.py | 3 +++ qtpy/QtNetwork.py | 4 +++- qtpy/QtPrintSupport.py | 4 +++- qtpy/QtSvg.py | 5 ++++- qtpy/QtTest.py | 4 +++- qtpy/QtWebEngineWidgets.py | 12 +++++++++++- qtpy/QtWidgets.py | 4 +++- qtpy/__init__.py | 30 +++++++++++++++++++++++++++--- qtpy/uic.py | 12 ++++++++---- 12 files changed, 72 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 6def0a77..b678ca89 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ lib64 # Other files toread.md .chache +.idea/ \ No newline at end of file diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index bde48a77..39def4fd 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -10,7 +10,7 @@ Provides QtCore classes and functions. """ -from . import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError if PYQT5: @@ -22,6 +22,8 @@ # Those are imported from `import *` del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR +elif PYSIDE2: + from PySide2.QtCore import * elif PYQT4: from PyQt4.QtCore import * # Those are things we inherited from Spyder that fix crazy crashes under diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 99477c00..34a9c67a 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -13,11 +13,13 @@ the ``PyQt5.QtGui`` module. """ -from . import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT4, PYSIDE, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtGui import * +elif PYSIDE2: + from PySide2.QtGui import * elif PYQT4: from PyQt4.Qt import QKeySequence, QTextCursor from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index 7ed307e8..0c8f341d 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -1,10 +1,13 @@ from . import PYQT5 from . import PYQT4 from . import PYSIDE +from . import PYSIDE2 if PYQT5: from PyQt5.QtMultimedia import * +elif PYSIDE2: + from PySide2.QtMultimedia import * elif PYQT4: from PyQt4.QtMultimedia import * from PyQt4.QtGui import QSound diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index de4ff473..49faded7 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -10,11 +10,13 @@ Provides QtNetwork classes and functions. """ -from . import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtNetwork import * +elif PYSIDE2: + from PySide2.QtNetwork import * elif PYQT4: from PyQt4.QtNetwork import * elif PYSIDE: diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 8959fad9..b821d411 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -9,11 +9,13 @@ Provides QtPrintSupport classes and functions. """ -from . import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT4,PYSIDE2, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtPrintSupport import * +elif PYSIDE2: + from PySide2.QtPrintSupport import * elif PYQT4: from PyQt4.QtGui import (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, QPrintPreviewDialog, diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index 3cf76113..17355c50 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -8,11 +8,14 @@ """Provides QtSvg classes and functions.""" # Local imports -from . import PYQT4, PYQT5, PYSIDE, PythonQtError +from . import PYQT4, PYSIDE2, PYQT5, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtSvg import (QGraphicsSvgItem, QSvgGenerator, QSvgRenderer, QSvgWidget) +elif PYSIDE2: + from PySide2.QtSvg import (QGraphicsSvgItem, QSvgGenerator, QSvgRenderer, + QSvgWidget) elif PYQT4: from PyQt4.QtSvg import (QGraphicsSvgItem, QSvgGenerator, QSvgRenderer, QSvgWidget) diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 28067d8b..cca5e192 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -10,11 +10,13 @@ Provides QtTest and functions """ -from . import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5,PYSIDE2, PYQT4, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtTest import QTest +elif PYSIDE2: + from PySide2.QtTest import QTest elif PYQT4: from PyQt4.QtTest import QTest as OldQTest diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 18b5e58f..a7728505 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -10,7 +10,7 @@ Provides QtWebEngineWidgets classes and functions. """ -from . import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5,PYSIDE2, PYQT4, PYSIDE, PythonQtError # To test if we are using WebEngine or WebKit @@ -27,6 +27,16 @@ from PyQt5.QtWebKitWidgets import QWebView as QWebEngineView from PyQt5.QtWebKit import QWebSettings as QWebEngineSettings WEBENGINE = False +elif PYSIDE2: + try: + from PySide2.QtWebEngineWidgets import QWebEnginePage + from PySide2.QtWebEngineWidgets import QWebEngineView + from PySide2.QtWebEngineWidgets import QWebEngineSettings + except ImportError: + from PySide2.QtWebKitWidgets import QWebPage as QWebEnginePage + from PySide2.QtWebKitWidgets import QWebView as QWebEngineView + from PySide2.QtWebKit import QWebSettings as QWebEngineSettings + WEBENGINE = False elif PYQT4: from PyQt4.QtWebKit import QWebPage as QWebEnginePage from PyQt4.QtWebKit import QWebView as QWebEngineView diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 96134648..a3388b9b 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -13,13 +13,15 @@ were the ``PyQt5.QtWidgets`` module. """ -from . import PYQT5, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError from ._patch.qcombobox import patch_qcombobox from ._patch.qheaderview import introduce_renamed_methods_qheaderview if PYQT5: from PyQt5.QtWidgets import * +elif PYSIDE2: + from PySide2.QtWidgets import * elif PYQT4: from PyQt4.QtGui import * QStyleOptionViewItem = QStyleOptionViewItemV4 diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 273df1d3..d753e14b 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -26,6 +26,17 @@ >>> print(QtWidgets.QWidget) +PySide2 +====== + +Set the QT_API environment variable to 'pyside2' before importing other +packages:: + + >>> import os + >>> os.environ['QT_API'] = 'pyside2' + >>> from qtpy import QtGui, QtWidgets, QtCore + >>> print(QtWidgets.QWidget) + PyQt4 ===== @@ -66,14 +77,16 @@ ] #: names of the expected PySide api PYSIDE_API = ['pyside'] +#: names of the expected PySide2 api +PYSIDE2_API = ['pyside2'] os.environ.setdefault(QT_API, 'pyqt5') API = os.environ[QT_API].lower() -assert API in (PYQT5_API + PYQT4_API + PYSIDE_API) +assert API in (PYQT5_API + PYQT4_API + PYSIDE_API+PYSIDE2_API) is_old_pyqt = is_pyqt46 = False PYQT5 = True -PYQT4 = PYSIDE = False +PYQT4 = PYSIDE = PYSIDE2 = False class PythonQtError(Exception): @@ -89,6 +102,17 @@ class PythonQtError(Exception): except ImportError: API = os.environ['QT_API'] = 'pyqt' +if API in PYSIDE2_API: + try: + from PySide2 import __version__ as PYSIDE_VERSION # analysis:ignore + from PySide2.QtCore import __version__ as QT_VERSION # analysis:ignore + + PYQT_VERSION = None + PYQT5 = False + PYSIDE2 = True + except ImportError: + API = os.environ['QT_API'] = 'pyqt4' + if API in PYQT4_API: try: import sip @@ -119,7 +143,7 @@ class PythonQtError(Exception): from PySide import __version__ as PYSIDE_VERSION # analysis:ignore from PySide.QtCore import __version__ as QT_VERSION # analysis:ignore PYQT_VERSION = None - PYQT5 = False + PYQT5 = PYSIDE2 = False PYSIDE = True except ImportError: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/uic.py b/qtpy/uic.py index abca4911..061d5bf0 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -1,6 +1,6 @@ import os -from . import PYSIDE, PYQT4, PYQT5 +from . import PYSIDE, PYSIDE2, PYQT4, PYQT5 from .QtWidgets import QComboBox __all__ = ['loadUi'] @@ -13,7 +13,7 @@ from PyQt4.uic import loadUi -elif PYSIDE: +else: # In PySide, loadUi does not exist, so we define it using QUiLoader, and # then make sure we expose that function. This is adapted from qt-helpers @@ -77,8 +77,12 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. - from PySide.QtCore import QMetaObject - from PySide.QtUiTools import QUiLoader + if PYSIDE: + from PySide.QtCore import QMetaObject + from PySide.QtUiTools import QUiLoader + elif PYSIDE2: + from PySide2.QtCore import QMetaObject + from PySide2.QtUiTools import QUiLoader class UiLoader(QUiLoader): """ From 9a9b66ad0e37e91989ed518fe0f7d1f37732fba4 Mon Sep 17 00:00:00 2001 From: Dhruv Govil Date: Wed, 26 Oct 2016 12:54:49 -0700 Subject: [PATCH 005/703] Updated __init__ dict and added PySide2 del to QtSvg.py --- qtpy/QtSvg.py | 2 +- qtpy/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index 17355c50..1f1f07a3 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -25,4 +25,4 @@ else: raise PythonQtError('No Qt bindings could be found') -del PYQT4, PYQT5, PYSIDE +del PYQT4, PYQT5, PYSIDE, PYSIDE2 diff --git a/qtpy/__init__.py b/qtpy/__init__.py index d753e14b..c2fc2ff8 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -149,7 +149,7 @@ class PythonQtError(Exception): raise PythonQtError('No Qt bindings could be found') API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4', - 'pyside': 'PySide'}[API] + 'pyside': 'PySide', 'pyside2':'PySide2'}[API] if PYQT4: import sip try: From bfefe753d158b9111661e399628f86f15d2fa849 Mon Sep 17 00:00:00 2001 From: Dhruv Govil Date: Wed, 26 Oct 2016 12:59:42 -0700 Subject: [PATCH 006/703] Added __init__ to tests to make them easy to load individually --- qtpy/tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 qtpy/tests/__init__.py diff --git a/qtpy/tests/__init__.py b/qtpy/tests/__init__.py new file mode 100644 index 00000000..e69de29b From 50d2a81754ca1c21f674817f95e4ed4a7b576a52 Mon Sep 17 00:00:00 2001 From: Dhruv Govil Date: Wed, 26 Oct 2016 13:00:50 -0700 Subject: [PATCH 007/703] Added comment to QtWebEngineWidgets for PySide2 --- qtpy/QtWebEngineWidgets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index a7728505..003332cf 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -35,6 +35,8 @@ except ImportError: from PySide2.QtWebKitWidgets import QWebPage as QWebEnginePage from PySide2.QtWebKitWidgets import QWebView as QWebEngineView + #: Current PySide2 builds seem to be missing this. + #: I'll leave it in for now because the final builds should have it from PySide2.QtWebKit import QWebSettings as QWebEngineSettings WEBENGINE = False elif PYQT4: From 135b49bc99603c63b7bc89485ece9c9da684cfb5 Mon Sep 17 00:00:00 2001 From: Dhruv Govil Date: Wed, 26 Oct 2016 13:05:31 -0700 Subject: [PATCH 008/703] Updated tests to have PySide2 tests --- qtpy/tests/conftest.py | 12 ++++++++++++ qtpy/tests/test_main.py | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index c31a78aa..c631886f 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -54,6 +54,18 @@ def pytest_report_header(config): except AttributeError: versions += 'unknown version' + versions += os.linesep + versions += 'PySide2: ' + + try: + import PySide2 + from PySide2 import QtCore + versions += "PySide: {0} - Qt: {1}".format(PySide2.__version__, QtCore.__version__) + except ImportError: + versions += 'not installed' + except AttributeError: + versions += 'unknown version' + versions += os.linesep return versions diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index 63d1439e..b4cd38db 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -13,6 +13,16 @@ def assert_pyside(): assert QtWidgets.QWidget is PySide.QtGui.QWidget assert QtWebEngineWidgets.QWebEnginePage is PySide.QtWebKit.QWebPage +def assert_pyside2(): + """ + Make sure that we are using PySide + """ + import PySide2 + assert QtCore.QEvent is PySide2.QtCore.QEvent + assert QtGui.QPainter is PySide2.QtGui.QPainter + assert QtWidgets.QWidget is PySide2.QtGui.QWidget + # The current builds of PySide2 don't seem to have this yet + # assert QtWebEngineWidgets.QWebEnginePage is PySide2.QtWebKit.QWebPage def assert_pyqt4(): """ @@ -52,6 +62,8 @@ def test_qt_api(): assert_pyqt4() elif QT_API == 'pyqt5': assert_pyqt5() + elif QT_API == 'pyside2': + assert_pyside2() else: # If the tests are run locally, USE_QT_API and QT_API may not be # defined, but we still want to make sure qtpy is behaving sensibly. From ac95dddf8b2dfdc07c5e82ddf91be7cff2ccac22 Mon Sep 17 00:00:00 2001 From: Gonzalo Pena-Castellanos Date: Fri, 28 Oct 2016 09:35:55 -0400 Subject: [PATCH 009/703] Circle ci expansion --- circle.yml | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/circle.yml b/circle.yml index ef97c02c..05034809 100644 --- a/circle.yml +++ b/circle.yml @@ -1,34 +1,42 @@ # https://circleci.com/gh/spyder-ide/qtpy/ - machine: environment: # Used by test scripts TEST_CI: "True" - PYTHON_TEST: "$HOME/miniconda/envs/test/bin/python" - PYTEST: "$HOME/miniconda/envs/test/bin/py.test" - PATH: "$HOME/miniconda/bin:$PATH" # To avoid prepending this to the commands on circle-ci # Python versions to test (Maximum of 4 different versions for now) PY_VERSIONS: "2.7 3.4 3.5" # Used by astropy-ci helpers TRAVIS_OS_NAME: "linux" - CONDA_CHANNELS: "qttesting" - CONDA_DEPENDENCIES: "pyqt pytest pytest-cov qt" PIP_DEPENDENCIES: "coveralls" dependencies: override: - # First convert PY_VERSIONS to an array and then select the Python version based on the - # CIRCLE_NODE_INDEX + # First convert PY_VERSIONS to an array and then select the Python version + # based on the CIRCLE_NODE_INDEX - PY_VERSIONS=($PY_VERSIONS) && TRAVIS_PYTHON_VERSION=${PY_VERSIONS[$CIRCLE_NODE_INDEX]} && echo -e "PYTHON = $TRAVIS_PYTHON_VERSION \n============" && git clone git://github.com/astropy/ci-helpers.git && source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh && - "$PYTHON_TEST" setup.py install; + export PATH="$HOME/miniconda/bin:$PATH" && + source activate test && + conda install ciocheck -c conda-forge -c continuumcrew && + python setup.py develop; test: override: - - conda info --json: # note the colon + # Check style + - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && ciocheck qtpy: # note the colon + parallel: true + # Check PyQt5 + - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install qt=5.* pyqt=5.* && python qtpy/tests/runtests.py: # note the colon + parallel: true + # Check PySide2 +# - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install qt=5.* pyside2 && python qtpy/tests/runtests.py: # note the colon +# parallel: true + # Check PyQt4 + - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install qt=4.* pyqt=4.* && python qtpy/tests/runtests.py: # note the colon parallel: true - - "$PYTHON_TEST qtpy/tests/runtests.py": # note the colon + # Check Pyside + - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install qt=4.* pyside && python qtpy/tests/runtests.py; fi: # note the colon parallel: true From 22807930a05ff28dd5fc5d4d25728a435ba9465e Mon Sep 17 00:00:00 2001 From: Gonzalo Pena-Castellanos Date: Fri, 28 Oct 2016 10:28:15 -0400 Subject: [PATCH 010/703] Add ciocheck config --- .ciocheck | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 .ciocheck diff --git a/.ciocheck b/.ciocheck new file mode 100644 index 00000000..0ed0f223 --- /dev/null +++ b/.ciocheck @@ -0,0 +1,106 @@ +# ----------------------------------------------------------------------------- +# ciocheck +# https://github.com/ContinuumIO/ciocheck +# ----------------------------------------------------------------------------- +[ciocheck] +branch = origin/master +diff_mode = commited +file_mode = all +check = pep8,pydocstyle,flake8,pylint + +# Python (pyformat) +add_copyright = true +add_header = true +add_init = true + +# ----------------------------------------------------------------------------- +# pep8 +# http://pep8.readthedocs.io/en/latest/intro.html#configuration +# ----------------------------------------------------------------------------- +[pep8] +exclude = */tests/* +ignore = E126, +max-line-length = 79 + +# ----------------------------------------------------------------------------- +# pydocstyle +# http://www.pydocstyle.org/en/latest/usage.html#example +# ----------------------------------------------------------------------------- +[pydocstyle] +add-ignore = D203 +inherit = false + +# ----------------------------------------------------------------------------- +# Flake 8 +# http://flake8.readthedocs.io/en/latest/config.html +# ----------------------------------------------------------------------------- +[flake8] +exclude = */tests/* +ignore = E126, +max-line-length = 79 +max-complexity = 64 + +# ----------------------------------------------------------------------------- +# pylint +# https://pylint.readthedocs.io/en/latest/ +# ----------------------------------------------------------------------------- +#[pylint:messages] + +# ----------------------------------------------------------------------------- +# isort +# https://github.com/timothycrosley/isort/wiki/isort-Settings +# ----------------------------------------------------------------------------- +[isort] +from_first = true +import_heading_stdlib = Standard library imports +import_heading_firstparty = Local imports +import_heading_thirdparty = Third party imports +indent = ' ' +known_first_party = qtpy +known_third_party = PySide, PyQt4, PyQt5 +line_length = 79 +sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER + +# ----------------------------------------------------------------------------- +# yapf +# https://github.com/google/yapf#formatting-style +# ----------------------------------------------------------------------------- +[yapf:style] +based_on_style = pep8 +column_limit = 79 +spaces_before_comment = 2 + +# ----------------------------------------------------------------------------- +# autopep8 +# http://pep8.readthedocs.io/en/latest/intro.html#configuration +# ----------------------------------------------------------------------------- +[autopep8] +exclude = */tests/* +ignore = E126, +max-line-length = 79 + +# ----------------------------------------------------------------------------- +# Coverage +# http://coverage.readthedocs.io/en/latest/config.html +# ----------------------------------------------------------------------------- +[coverage:run] +omit = + */tests/* + */build/* + +[coverage:report] +fail_under = 0 +show_missing = true +skip_covered = true +exclude_lines = + pragma: no cover + def test(): + if __name__ == .__main__.: + +# ----------------------------------------------------------------------------- +# pytest +# http://doc.pytest.org/en/latest/usage.html +# ----------------------------------------------------------------------------- +[pytest] +addopts = -rfew --durations=10 +python_functions = test_* \ No newline at end of file From 6a4530e551e0b4158025eb285eeeaa7a738c4bc1 Mon Sep 17 00:00:00 2001 From: Gonzalo Pena-Castellanos Date: Fri, 28 Oct 2016 10:35:52 -0400 Subject: [PATCH 011/703] Update ciocheck config --- .ciocheck | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.ciocheck b/.ciocheck index 0ed0f223..6c06a602 100644 --- a/.ciocheck +++ b/.ciocheck @@ -19,7 +19,7 @@ add_init = true # ----------------------------------------------------------------------------- [pep8] exclude = */tests/* -ignore = E126, +ignore = E126 max-line-length = 79 # ----------------------------------------------------------------------------- @@ -36,7 +36,7 @@ inherit = false # ----------------------------------------------------------------------------- [flake8] exclude = */tests/* -ignore = E126, +ignore = E126,F401 max-line-length = 79 max-complexity = 64 @@ -44,7 +44,8 @@ max-complexity = 64 # pylint # https://pylint.readthedocs.io/en/latest/ # ----------------------------------------------------------------------------- -#[pylint:messages] +[pylint:messages] +disable=unused-import # ----------------------------------------------------------------------------- # isort From a665f40c06f4cf7adf63fe027d9fd849301c7c96 Mon Sep 17 00:00:00 2001 From: Gonzalo Pena-Castellanos Date: Fri, 28 Oct 2016 12:44:45 -0400 Subject: [PATCH 012/703] Update travis to do osx only --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bc3c5323..2bb319ed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,6 @@ branches: - master os: - - linux - osx env: @@ -50,4 +49,3 @@ script: after_success: - coveralls - From cfc210e2382206f25378da58f2615d53990cf53f Mon Sep 17 00:00:00 2001 From: Gonzalo Pena-Castellanos Date: Fri, 28 Oct 2016 12:45:35 -0400 Subject: [PATCH 013/703] Update travis to do osx only --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2bb319ed..0ce79c62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ before_install: install: - git clone git://github.com/astropy/ci-helpers.git - source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh - - python setup.py install + - python setup.py develop script: - python qtpy/tests/runtests.py From b7a4cb98a009b117196fed4599deae74d8294fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Pe=C3=B1a-Castellanos?= Date: Fri, 28 Oct 2016 16:43:47 -0400 Subject: [PATCH 014/703] Update circle.yml --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 05034809..1772720f 100644 --- a/circle.yml +++ b/circle.yml @@ -4,7 +4,7 @@ machine: # Used by test scripts TEST_CI: "True" # Python versions to test (Maximum of 4 different versions for now) - PY_VERSIONS: "2.7 3.4 3.5" + PY_VERSIONS: "3.5 3.4 2.7" # Used by astropy-ci helpers TRAVIS_OS_NAME: "linux" PIP_DEPENDENCIES: "coveralls" From a4655d12118d0d98150b40b9e7c561127df39a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Pe=C3=B1a-Castellanos?= Date: Fri, 28 Oct 2016 17:04:01 -0400 Subject: [PATCH 015/703] Update LICENSE.txt --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index 4152f523..4a791817 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2015 Gonzalo Peña-Castellanos +Copyright (c) The Spyder Development Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 2483ec2fe89f7075e9d5da939ed9e2b2caa80bab Mon Sep 17 00:00:00 2001 From: Gonzalo Pena-Castellanos Date: Fri, 28 Oct 2016 17:26:58 -0400 Subject: [PATCH 016/703] Add manifest --- MANIFEST.in | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 MANIFEST.in diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 00000000..f7532292 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include AUTHORS.md +include CHANGELOG.md +include LICENSE.txt +include README.md \ No newline at end of file From 6ad01127d0a9dcd2b09989b2f079a5513d588765 Mon Sep 17 00:00:00 2001 From: Dhruv Govil Date: Fri, 4 Nov 2016 15:59:44 -0700 Subject: [PATCH 017/703] Addressing notes by @Nodd --- .gitignore | 2 +- qtpy/QtWebEngineWidgets.py | 3 +-- qtpy/__init__.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index b678ca89..034f84d9 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,4 @@ lib64 # Other files toread.md .chache -.idea/ \ No newline at end of file +.idea/ diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 003332cf..d1626934 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -36,8 +36,7 @@ from PySide2.QtWebKitWidgets import QWebPage as QWebEnginePage from PySide2.QtWebKitWidgets import QWebView as QWebEngineView #: Current PySide2 builds seem to be missing this. - #: I'll leave it in for now because the final builds should have it - from PySide2.QtWebKit import QWebSettings as QWebEngineSettings + #: from PySide2.QtWebKit import QWebSettings as QWebEngineSettings WEBENGINE = False elif PYQT4: from PyQt4.QtWebKit import QWebPage as QWebEnginePage diff --git a/qtpy/__init__.py b/qtpy/__init__.py index c2fc2ff8..2ee07e76 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -82,7 +82,7 @@ os.environ.setdefault(QT_API, 'pyqt5') API = os.environ[QT_API].lower() -assert API in (PYQT5_API + PYQT4_API + PYSIDE_API+PYSIDE2_API) +assert API in (PYQT5_API + PYQT4_API + PYSIDE_API + PYSIDE2_API) is_old_pyqt = is_pyqt46 = False PYQT5 = True From 10327bdc1e035fd77a589e47ab68d221d795ae09 Mon Sep 17 00:00:00 2001 From: Dhruv Govil Date: Fri, 4 Nov 2016 16:05:13 -0700 Subject: [PATCH 018/703] Addressing notes by @Nodd --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 034f84d9..ee1415e6 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,5 @@ lib64 toread.md .chache .idea/ + +# End of File \ No newline at end of file From 98c7f3afb2276012f22ad50e77fef60d7d71ee5f Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 2 Jan 2017 13:45:19 -0500 Subject: [PATCH 019/703] QtSvG: Use star imports again instead of direct ones --- qtpy/QtSvg.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index 3cf76113..0bc73205 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -11,14 +11,11 @@ from . import PYQT4, PYQT5, PYSIDE, PythonQtError if PYQT5: - from PyQt5.QtSvg import (QGraphicsSvgItem, QSvgGenerator, QSvgRenderer, - QSvgWidget) + from PyQt5.QtSvg import * elif PYQT4: - from PyQt4.QtSvg import (QGraphicsSvgItem, QSvgGenerator, QSvgRenderer, - QSvgWidget) + from PyQt4.QtSvg import * elif PYSIDE: - from PySide.QtSvg import (QGraphicsSvgItem, QSvgGenerator, QSvgRenderer, - QSvgWidget) + from PySide.QtSvg import * else: raise PythonQtError('No Qt bindings could be found') From ffa771ef3606c5f73377000e3a77639bb40f4825 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 2 Jan 2017 14:20:03 -0500 Subject: [PATCH 020/703] Testing: Try to fix PySide on Travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0ce79c62..15573370 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ os: env: global: - SETUP_XVFB=True - - CONDA_CHANNELS="qttesting" + - CONDA_CHANNELS="conda-forge" - CONDA_DEPENDENCIES="pytest pytest-cov" - PIP_DEPENDENCIES="coveralls" From 32fc58244bb82e27ae3e03e0c0f16ed8a5561957 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 2 Jan 2017 14:34:08 -0500 Subject: [PATCH 021/703] Testing: Cancel queued builds in the same branch in Appveyor --- appveyor.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 52ce588e..be164b79 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,6 +34,15 @@ platform: -x64 install: + # If there is a newer build queued for the same PR, cancel this one. + # The AppVeyor 'rollout builds' option is supposed to serve the same + # purpose but it is problematic because it tends to cancel builds pushed + # directly to master instead of just PR builds (or the converse). + # credits: JuliaLang developers. + - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` + https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` + Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` + throw "There are newer queued builds for this pull request, failing early." } - "git clone git://github.com/astropy/ci-helpers.git" - "powershell ci-helpers/appveyor/install-miniconda.ps1" - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" @@ -45,4 +54,3 @@ build: false test_script: - "%CMD_IN_ENV% python qtpy/tests/runtests.py" - From 8fce1c14c83a156537a63395f57ff29a2cc1c0e1 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 2 Jan 2017 14:35:53 -0500 Subject: [PATCH 022/703] Testing: Do quite conda installs in CircleCI --- circle.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/circle.yml b/circle.yml index 1772720f..15a5eaeb 100644 --- a/circle.yml +++ b/circle.yml @@ -20,7 +20,7 @@ dependencies: source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh && export PATH="$HOME/miniconda/bin:$PATH" && source activate test && - conda install ciocheck -c conda-forge -c continuumcrew && + conda install -q ciocheck -c spyder-ide && python setup.py develop; test: @@ -29,14 +29,14 @@ test: - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && ciocheck qtpy: # note the colon parallel: true # Check PyQt5 - - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install qt=5.* pyqt=5.* && python qtpy/tests/runtests.py: # note the colon + - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=5.* pyqt=5.* && python qtpy/tests/runtests.py: # note the colon parallel: true # Check PySide2 -# - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install qt=5.* pyside2 && python qtpy/tests/runtests.py: # note the colon +# - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=5.* pyside2 && python qtpy/tests/runtests.py: # note the colon # parallel: true # Check PyQt4 - - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install qt=4.* pyqt=4.* && python qtpy/tests/runtests.py: # note the colon + - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=4.* pyqt=4.* && python qtpy/tests/runtests.py: # note the colon parallel: true # Check Pyside - - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install qt=4.* pyside && python qtpy/tests/runtests.py; fi: # note the colon + - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=4.* pyside && python qtpy/tests/runtests.py; fi: # note the colon parallel: true From d4fd0fd434e02065cbf10a77383b5e7b353ad516 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 2 Jan 2017 14:56:05 -0500 Subject: [PATCH 023/703] Testing: Skip PySide on CircleCI because it's segfaulting --- circle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index 15a5eaeb..16332260 100644 --- a/circle.yml +++ b/circle.yml @@ -38,5 +38,5 @@ test: - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=4.* pyqt=4.* && python qtpy/tests/runtests.py: # note the colon parallel: true # Check Pyside - - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=4.* pyside && python qtpy/tests/runtests.py; fi: # note the colon - parallel: true + #- export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda remove -q qt pyqt && conda install -c conda-forge qt=4.* pyside && python qtpy/tests/runtests.py: # note the colon + # parallel: true From 8b9e9bb97a8862518d22ed239c1d7a5a66cf8c3b Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 2 Jan 2017 14:01:39 -0500 Subject: [PATCH 024/703] Add missing copyright header in _patch/qheaderview.py --- qtpy/_patch/qheaderview.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qtpy/_patch/qheaderview.py b/qtpy/_patch/qheaderview.py index 8bef8395..99cd20a7 100644 --- a/qtpy/_patch/qheaderview.py +++ b/qtpy/_patch/qheaderview.py @@ -1,3 +1,10 @@ +# -*- coding: utf-8 -*- +# +# Copyright © The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + def introduce_renamed_methods_qheaderview(QHeaderView): _isClickable = QHeaderView.isClickable From c3a17688f8026fd5a96bbd66fff60c2c5bb68d66 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 5 Jan 2017 18:23:43 -0500 Subject: [PATCH 025/703] uic: Restore full namespace for PyQt5 and PyQt4 --- qtpy/uic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/uic.py b/qtpy/uic.py index abca4911..f1169edd 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -7,11 +7,11 @@ if PYQT5: - from PyQt5.uic import loadUi + from PyQt5.uic import * elif PYQT4: - from PyQt4.uic import loadUi + from PyQt4.uic import * elif PYSIDE: From 0eb6ea133f3def63c0315c129c94b2110b465036 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 5 Jan 2017 18:30:02 -0500 Subject: [PATCH 026/703] uic: Make loadUi the only exported symbol in PySide --- qtpy/uic.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qtpy/uic.py b/qtpy/uic.py index f1169edd..b3d9324d 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -3,7 +3,6 @@ from . import PYSIDE, PYQT4, PYQT5 from .QtWidgets import QComboBox -__all__ = ['loadUi'] if PYQT5: @@ -15,6 +14,8 @@ elif PYSIDE: + __all__ = ['loadUi'] + # In PySide, loadUi does not exist, so we define it using QUiLoader, and # then make sure we expose that function. This is adapted from qt-helpers # which was released under a 3-clause BSD license: From cb7d1f67f384089fea96a25db32b1747a20ce043 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 5 Jan 2017 18:42:53 -0500 Subject: [PATCH 027/703] Test that we're loading all uic objects for PyQt5 and PyQt4 --- qtpy/tests/test_uic.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 07e3f776..4291d271 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -4,6 +4,7 @@ from qtpy import QtWidgets from qtpy.QtWidgets import QComboBox +from qtpy import uic from qtpy.uic import loadUi @@ -66,3 +67,17 @@ def test_load_ui_custom_auto(tmpdir): assert isinstance(ui.pushButton, QtWidgets.QPushButton) assert isinstance(ui.comboBox, _QComboBoxSubclass) + + +def test_load_full_uic(): + """Test that we load the full uic objects for PyQt5 and PyQt4.""" + QT_API = os.environ.get('QT_API', '').lower() + if QT_API == 'pyside': + assert hasattr(uic, 'loadUi') + assert not hasattr(uic, 'loadUiType') + assert not hasattr(uic, 'UiLoader') + else: + objects = ['compileUi', 'compileUiDir', 'compiler', 'exceptions', 'icon_cache', + 'indenter', 'loadUi', 'loadUiType', 'objcreator', 'port_v3', + 'properties', 'uiparser', 'widgetPluginPath'] + assert all([hasattr(uic, o) for o in objects]) From a18d8c69e7cfe24e61b6251a47fbf177b6a99908 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 5 Jan 2017 18:50:13 -0500 Subject: [PATCH 028/703] Testing: Add less objects to test_load_full_uic --- qtpy/tests/test_uic.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 4291d271..08f80fc2 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -77,7 +77,6 @@ def test_load_full_uic(): assert not hasattr(uic, 'loadUiType') assert not hasattr(uic, 'UiLoader') else: - objects = ['compileUi', 'compileUiDir', 'compiler', 'exceptions', 'icon_cache', - 'indenter', 'loadUi', 'loadUiType', 'objcreator', 'port_v3', - 'properties', 'uiparser', 'widgetPluginPath'] + objects = ['compileUi', 'compileUiDir', 'loadUi', 'loadUiType', + 'uiparser', 'widgetPluginPath'] assert all([hasattr(uic, o) for o in objects]) From 457aba9f3b16d09c764f525f2e49218ac6797239 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 5 Jan 2017 18:54:45 -0500 Subject: [PATCH 029/703] Testing: Fix test_load_full_uic --- qtpy/tests/test_uic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 08f80fc2..d549fea8 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -78,5 +78,5 @@ def test_load_full_uic(): assert not hasattr(uic, 'UiLoader') else: objects = ['compileUi', 'compileUiDir', 'loadUi', 'loadUiType', - 'uiparser', 'widgetPluginPath'] + 'widgetPluginPath'] assert all([hasattr(uic, o) for o in objects]) From b59a9f685acb07120517e82ef78438194c47095e Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 5 Jan 2017 19:12:29 -0500 Subject: [PATCH 030/703] Testing: Fix test_load_full_uic in AppVeyor --- qtpy/tests/test_uic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index d549fea8..6e732bb2 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -75,7 +75,6 @@ def test_load_full_uic(): if QT_API == 'pyside': assert hasattr(uic, 'loadUi') assert not hasattr(uic, 'loadUiType') - assert not hasattr(uic, 'UiLoader') else: objects = ['compileUi', 'compileUiDir', 'loadUi', 'loadUiType', 'widgetPluginPath'] From e40bb1cb27dbefd1ce71c91ded1993991eae14da Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 8 Jan 2017 16:46:15 -0500 Subject: [PATCH 031/703] Update Changelog --- CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dc0cef2b..1d6c00e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,45 @@ # History of changes +## Version 1.2 (2017/01/08) + +### New features + +* Add support for QtMultimedia +* Use relative imports so its vendored more easily + +### Bugs fixed + +**Issues** + +* [Issue 83](https://github.com/spyder-ide/qtpy/issues/83) - Include core doc files in PyPi releases +* [Issue 78](https://github.com/spyder-ide/qtpy/issues/78) - Request for a new bugfix release +* [Issue 75](https://github.com/spyder-ide/qtpy/issues/75) - Missing copyright headers +* [Issue 67](https://github.com/spyder-ide/qtpy/issues/67) - uic.loadUiType is missing +* [Issue 64](https://github.com/spyder-ide/qtpy/issues/64) - QHeaderView.setSectionResizeMode +* [Issue 49](https://github.com/spyder-ide/qtpy/issues/49) - QtMultimedia support + +In this release 6 issues were closed + +**Pull requests** + +* [PR 93](https://github.com/spyder-ide/qtpy/pull/93) - Restore uic full namespace for PyQt5 and PyQt4 +* [PR 92](https://github.com/spyder-ide/qtpy/pull/92) - Add missing copyright header in _patch/qheaderview.py +* [PR 91](https://github.com/spyder-ide/qtpy/pull/91) - Use star imports in QtSvg again instead of direct ones (reverts PR #55) +* [PR 88](https://github.com/spyder-ide/qtpy/pull/88) - PR: Add manifest +* [PR 74](https://github.com/spyder-ide/qtpy/pull/74) - Move QStringListModel to QtCore +* [PR 71](https://github.com/spyder-ide/qtpy/pull/71) - PR: Use relative imports so its vendored more easily +* [PR 65](https://github.com/spyder-ide/qtpy/pull/65) - Introduce renamed methods of QHeaderView in PyQt4 and PySide +* [PR 59](https://github.com/spyder-ide/qtpy/pull/59) - Don't install qtpy as a conda package in CircleCI +* [PR 58](https://github.com/spyder-ide/qtpy/pull/58) - Remove reference to how qtpy is pronounced in README +* [PR 55](https://github.com/spyder-ide/qtpy/pull/55) - PR: Add explicit imports to QtSvg module +* [PR 50](https://github.com/spyder-ide/qtpy/pull/50) - Add support for QtMultimedia + +In this release 11 pull requests were merged + + +---- + + ## Version 1.1.2 (2016-08-08) ### Bugfixes From 8bf1c5dbf31f2434f7bc1eadb8e1be52c032c4c3 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 8 Jan 2017 16:47:11 -0500 Subject: [PATCH 032/703] Release 1.2.0 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index a89112b0..d1115f84 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 2, 0, 'dev0') +version_info = (1, 2, 0) __version__ = '.'.join(map(str, version_info)) From f345c96d74c7d56ac45972730c310894ee71a0cd Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 8 Jan 2017 16:48:49 -0500 Subject: [PATCH 033/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index d1115f84..d631eaf6 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 2, 0) +version_info = (1, 3, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 19953fed75680e83a65b00f8ea9367ec9149765e Mon Sep 17 00:00:00 2001 From: Ghislain Antony Vaillant Date: Mon, 9 Jan 2017 14:38:32 +0000 Subject: [PATCH 034/703] Include test suite in sdist Debian, and other Linux distributions, recommends to source upstream Python code from PyPI. However, current releases of `QtPy` are missing the test suite. The latter is desirable for both the package build and CI process in Debian. This PR includes the test suite to the output of `sdist`. Following the merge of this PR, please consider submitting a new `1.2.0.1` or `1.2.1` release with the included test suite. Cheers, Ghis --- MANIFEST.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index f7532292..5d615b99 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,5 @@ include AUTHORS.md include CHANGELOG.md include LICENSE.txt -include README.md \ No newline at end of file +include README.md +recursive-include qtpy/tests * From 092a4d864cd2afd015dad2d015e31d9249154415 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 15 Jan 2017 13:42:10 -0500 Subject: [PATCH 035/703] Readme: Update Appveyor badge because of moving to an org account --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fefb604c..3947047d 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Copyright © 2009- The Spyder Development Team. ## Build status [![Travis status](https://travis-ci.org/spyder-ide/qtpy.svg?branch=master)](https://travis-ci.org/spyder-ide/qtpy) -[![Build status](https://ci.appveyor.com/api/projects/status/ab01a09cbx3m0ao9?svg=true)](https://ci.appveyor.com/project/goanpeca/qtpy) +[![Build status](https://ci.appveyor.com/api/projects/status/62y6i02vhn4hefg0/branch/master?svg=true)](https://ci.appveyor.com/project/spyder-ide/qtpy/branch/master) [![CircleCI](https://circleci.com/gh/spyder-ide/qtpy.svg?style=shield)](https://circleci.com/gh/spyder-ide/qtpy) [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) [![Code Issues](https://www.quantifiedcode.com/api/v1/project/c769241c7d7f4463b1e6f67863dabace/badge.svg)](https://www.quantifiedcode.com/app/project/c769241c7d7f4463b1e6f67863dabace) From d4f631fabf4bf65070551b465e086149e3671700 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 21 Jan 2017 20:15:33 -0500 Subject: [PATCH 036/703] Testing: Don't use Travis to test macOS because it slows down the entire spyder-ide organization I'm sorry to do this but we don't have much of a choice. Travis has very few macOS workers and I'm afraid they are now mostly taken by conda-forge. --- .travis.yml | 51 --------------------------------------------------- 1 file changed, 51 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 15573370..00000000 --- a/.travis.yml +++ /dev/null @@ -1,51 +0,0 @@ -# https://travis-ci.org/spyder-ide/qtpy/ - -# We set the language to c because python isn't supported on the MacOS X nodes -# on Travis. However, the language ends up being irrelevant anyway, since we -# install Python ourselves using conda. -language: c - -sudo: false - -branches: - only: - - master - -os: - - osx - -env: - global: - - SETUP_XVFB=True - - CONDA_CHANNELS="conda-forge" - - CONDA_DEPENDENCIES="pytest pytest-cov" - - PIP_DEPENDENCIES="coveralls" - - matrix: - - PYTHON_VERSION=2.7 USE_QT_API=PyQt5 - - PYTHON_VERSION=2.7 USE_QT_API=PyQt4 - - PYTHON_VERSION=2.7 USE_QT_API=PySide - - PYTHON_VERSION=3.4 USE_QT_API=PyQt4 - - PYTHON_VERSION=3.5 USE_QT_API=PyQt4 - - PYTHON_VERSION=3.5 USE_QT_API=PyQt5 - -before_install: - # Test environments for different Qt bindings - - if [[ "$USE_QT_API" == "PyQt5" ]]; then - export CONDA_DEPENDENCIES='qt=5.* pyqt=5.* '$CONDA_DEPENDENCIES; - elif [[ "$USE_QT_API" == "PyQt4" ]]; then - export CONDA_DEPENDENCIES='qt=4.* pyqt=4.* '$CONDA_DEPENDENCIES; - elif [[ "$USE_QT_API" == "PySide" ]]; then - export CONDA_DEPENDENCIES='qt=4.* pyside '$CONDA_DEPENDENCIES; - fi - -install: - - git clone git://github.com/astropy/ci-helpers.git - - source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh - - python setup.py develop - -script: - - python qtpy/tests/runtests.py - -after_success: - - coveralls From c90e5e884c2e17dfa031c3c66c4e3beae956dc10 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 21 Jan 2017 20:33:20 -0500 Subject: [PATCH 037/703] Testing: Run coveralls on CircleCI --- circle.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/circle.yml b/circle.yml index 16332260..441f3f3f 100644 --- a/circle.yml +++ b/circle.yml @@ -5,6 +5,8 @@ machine: TEST_CI: "True" # Python versions to test (Maximum of 4 different versions for now) PY_VERSIONS: "3.5 3.4 2.7" + # For Coveralls + COVERALLS_REPO_TOKEN: xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5 # Used by astropy-ci helpers TRAVIS_OS_NAME: "linux" PIP_DEPENDENCIES: "coveralls" @@ -37,6 +39,8 @@ test: # Check PyQt4 - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=4.* pyqt=4.* && python qtpy/tests/runtests.py: # note the colon parallel: true + - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && coveralls: # note the colon + parallel: true # Check Pyside #- export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda remove -q qt pyqt && conda install -c conda-forge qt=4.* pyside && python qtpy/tests/runtests.py: # note the colon # parallel: true From db7dbb2a0d2e429fd497e286c9281319ea388909 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 21 Jan 2017 21:07:51 -0500 Subject: [PATCH 038/703] Readme: Remove Travis badge --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 3947047d..731d3b97 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ Copyright © 2009- The Spyder Development Team. [![Join the chat at https://gitter.im/spyder-ide/public](https://badges.gitter.im/spyder-ide/spyder.svg)](https://gitter.im/spyder-ide/public) ## Build status -[![Travis status](https://travis-ci.org/spyder-ide/qtpy.svg?branch=master)](https://travis-ci.org/spyder-ide/qtpy) [![Build status](https://ci.appveyor.com/api/projects/status/62y6i02vhn4hefg0/branch/master?svg=true)](https://ci.appveyor.com/project/spyder-ide/qtpy/branch/master) [![CircleCI](https://circleci.com/gh/spyder-ide/qtpy.svg?style=shield)](https://circleci.com/gh/spyder-ide/qtpy) [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) From 6981791d5cdede9eba5d104f14c697cc05707862 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 21 Jan 2017 21:09:32 -0500 Subject: [PATCH 039/703] Update Changelog --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d6c00e3..5dea58c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # History of changes +## Version 1.2.1 (2017/01/21) + +### Bugs fixed + +**Pull requests** + +* [PR 98](https://github.com/spyder-ide/qtpy/pull/98) - PR: Don't use Travis to test macOS because it slows down the entire spyder-ide organization +* [PR 97](https://github.com/spyder-ide/qtpy/pull/97) - PR: Update Appveyor badge in Readme because of moving to an org account +* [PR 94](https://github.com/spyder-ide/qtpy/pull/94) - PR: Include test suite in sdist + +In this release 3 pull requests were merged + + +---- + + ## Version 1.2 (2017/01/08) ### New features From da56c3536a2d35f32d7d2486710124a9332b741d Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 21 Jan 2017 21:10:09 -0500 Subject: [PATCH 040/703] Release 1.2.1 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index d631eaf6..13489dcd 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 3, 0, 'dev0') +version_info = (1, 2, 1) __version__ = '.'.join(map(str, version_info)) From 7e4d85caf1aac48e0a718d6375bd7512daf93d75 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 21 Jan 2017 21:11:42 -0500 Subject: [PATCH 041/703] Back to work [ci skip] --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 13489dcd..d631eaf6 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 2, 1) +version_info = (1, 3, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 73bc177ade48823688c1a4ff073481c0af6c78f6 Mon Sep 17 00:00:00 2001 From: Ulrich Eck Date: Mon, 27 Feb 2017 12:56:53 +0100 Subject: [PATCH 042/703] add QtOpenGL wrapper for qtpy --- qtpy/QtOpenGL.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 qtpy/QtOpenGL.py diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py new file mode 100644 index 00000000..ef62171a --- /dev/null +++ b/qtpy/QtOpenGL.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtOpenGL classes and functions.""" + +# Local imports +from . import PYQT4, PYQT5, PYSIDE, PythonQtError + +if PYQT5: + from PyQt5.QtOpenGL import * +elif PYQT4: + from PyQt4.QtOpenGL import * +elif PYSIDE: + from PySide.QtOpenGL import * +else: + raise PythonQtError('No Qt bindings could be found') + +del PYQT4, PYQT5, PYSIDE From fcbcf2cd3b7a8df831ad9f4b47457720dfc4a566 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Thu, 4 May 2017 00:55:49 -0700 Subject: [PATCH 043/703] qtpy: add support for PyQt 4.6 PyQt4 4.6 on Centos 6.8 reports itself as: PyQt4.x86_64 4.6.2-9.el6 @base PyQt4-devel.x86_64 4.6.2-9.el6 @base This version is missing symbols from QtGui that causes qtpy to fail. Allow `import qtpy` to work in this environment. Signed-off-by: David Aguilar --- qtpy/QtGui.py | 29 ++++++++++++++++++----------- qtpy/QtWidgets.py | 30 +++++++++++++++++++----------- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 99477c00..515c081c 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -19,6 +19,17 @@ if PYQT5: from PyQt5.QtGui import * elif PYQT4: + try: + # Older versions of PyQt4 do not provide these + from PyQt4.QtGui import (QGlyphRun, QMatrix2x2, QMatrix2x3, + QMatrix2x4, QMatrix3x2, QMatrix3x3, + QMatrix3x4, QMatrix4x2, QMatrix4x3, + QMatrix4x4, QTouchEvent, QQuaternion, + QRadialGradient, QRawFont, QStaticText, + QVector2D, QVector3D, QVector4D, + qFuzzyCompare) + except ImportError: + pass from PyQt4.Qt import QKeySequence, QTextCursor from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, @@ -27,22 +38,19 @@ QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, - QFontMetricsF, QGlyphRun, QGradient, QHelpEvent, + QFontMetricsF, QGradient, QHelpEvent, QHideEvent, QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, QImageIOHandler, QImageReader, QImageWriter, QInputEvent, QInputMethodEvent, QKeyEvent, QLinearGradient, - QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, - QMatrix3x3, QMatrix3x4, QMatrix4x2, QMatrix4x3, - QMatrix4x4, QMouseEvent, QMoveEvent, QMovie, + QMouseEvent, QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState, QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette, QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon, - QPolygonF, QQuaternion, QRadialGradient, QRawFont, - QRegExpValidator, QRegion, QResizeEvent, + QPolygonF, QRegExpValidator, QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, - QStandardItem, QStandardItemModel, QStaticText, + QStandardItem, QStandardItemModel, QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, QTextBlockGroup, QTextBlockUserData, QTextCharFormat, @@ -53,11 +61,10 @@ QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, QTextTableCellFormat, - QTextTableFormat, QTouchEvent, QTransform, - QValidator, QVector2D, QVector3D, QVector4D, - QWhatsThisClickedEvent, QWheelEvent, + QTextTableFormat, QTransform, + QValidator, QWhatsThisClickedEvent, QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, - qFuzzyCompare, qGray, qGreen, qIsGray, qRed, qRgb, + qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator) elif PYSIDE: from PySide.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 62fa44ec..67885168 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -26,23 +26,31 @@ del QStyleOptionViewItemV4 # These objects belong to QtGui + try: + # Older versions of PyQt4 do not provide these + del (QGlyphRun, + QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, + QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, + QQuaternion, QRadialGradient, QRawFont, QRegExpValidator, + QStaticText, QTouchEvent, QVector2D, QVector3D, QVector4D, + qFuzzyCompare) + except NameError: + pass del (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, - QFontMetricsF, QGlyphRun, QGradient, QHelpEvent, QHideEvent, + QFontMetricsF, QGradient, QHelpEvent, QHideEvent, QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, QImageIOHandler, QImageReader, QImageWriter, QInputEvent, QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient, - QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, - QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, QMouseEvent, - QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState, - QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette, - QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon, - QPolygonF, QQuaternion, QRadialGradient, QRawFont, QRegExpValidator, + QMouseEvent, QMoveEvent, QMovie, QPaintDevice, QPaintEngine, + QPaintEngineState, QPaintEvent, QPainter, QPainterPath, + QPainterPathStroker, QPalette, QPen, QPicture, QPictureIO, QPixmap, + QPixmapCache, QPolygon, QPolygonF, QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, - QStandardItem, QStandardItemModel, QStaticText, QStatusTipEvent, + QStandardItem, QStandardItemModel, QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor, QTextDocument, QTextDocumentFragment, QTextDocumentWriter, @@ -50,9 +58,9 @@ QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout, QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, - QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform, - QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent, - QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qFuzzyCompare, + QTextTableCellFormat, QTextTableFormat, QTransform, + QValidator, QWhatsThisClickedEvent, + QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator, QStringListModel) From 9c1a62efe0fad43ed29c2ff9ed30c688fd9cdafd Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 5 May 2017 15:21:09 -0500 Subject: [PATCH 044/703] Add missing newline in gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index ee1415e6..9f30b4e8 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,4 @@ toread.md .chache .idea/ -# End of File \ No newline at end of file +# End of File From 5b34639563b4fcd3105c3b9e7c40e5cac06a571e Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 5 May 2017 16:22:20 -0500 Subject: [PATCH 045/703] Activate support for Pyside again --- circle.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 16332260..217e3bc1 100644 --- a/circle.yml +++ b/circle.yml @@ -4,7 +4,7 @@ machine: # Used by test scripts TEST_CI: "True" # Python versions to test (Maximum of 4 different versions for now) - PY_VERSIONS: "3.5 3.4 2.7" + PY_VERSIONS: "2.7 3.5 3.6" # Used by astropy-ci helpers TRAVIS_OS_NAME: "linux" PIP_DEPENDENCIES: "coveralls" @@ -21,7 +21,7 @@ dependencies: export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q ciocheck -c spyder-ide && - python setup.py develop; + python setup.py install; test: override: @@ -38,5 +38,5 @@ test: - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=4.* pyqt=4.* && python qtpy/tests/runtests.py: # note the colon parallel: true # Check Pyside - #- export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda remove -q qt pyqt && conda install -c conda-forge qt=4.* pyside && python qtpy/tests/runtests.py: # note the colon + - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda remove -q qt pyqt && conda install -c conda-forge qt=4.* pyside && python qtpy/tests/runtests.py: # note the colon # parallel: true From 123e2d3d47050fe95c2143fc2657a010d075a238 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 5 May 2017 16:34:39 -0500 Subject: [PATCH 046/703] Testing: Fix error in Appveyor and don't run ciocheck in CircleCI --- appveyor.yml | 2 +- circle.yml | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index be164b79..7a878839 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -47,7 +47,7 @@ install: - "powershell ci-helpers/appveyor/install-miniconda.ps1" - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - "activate test" - - "python setup.py install" + - "python setup.py develop" # Not a .NET project, we build in the install step instead build: false diff --git a/circle.yml b/circle.yml index 7c8e60bd..bbe20233 100644 --- a/circle.yml +++ b/circle.yml @@ -22,14 +22,10 @@ dependencies: source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh && export PATH="$HOME/miniconda/bin:$PATH" && source activate test && - conda install -q ciocheck -c spyder-ide && - python setup.py install; + python setup.py develop; test: override: - # Check style - - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && ciocheck qtpy: # note the colon - parallel: true # Check PyQt5 - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=5.* pyqt=5.* && python qtpy/tests/runtests.py: # note the colon parallel: true @@ -43,4 +39,4 @@ test: parallel: true # Check Pyside - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda remove -q qt pyqt && conda install -c conda-forge qt=4.* pyside && python qtpy/tests/runtests.py: # note the colon - # parallel: true + parallel: true From 1865ddd8a93b48a24a597b700c1bc4cdd03108c7 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 5 May 2017 16:49:21 -0500 Subject: [PATCH 047/703] Testing: Add missing pytest dependencies on CircleCI --- circle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/circle.yml b/circle.yml index bbe20233..9e746707 100644 --- a/circle.yml +++ b/circle.yml @@ -9,6 +9,7 @@ machine: COVERALLS_REPO_TOKEN: xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5 # Used by astropy-ci helpers TRAVIS_OS_NAME: "linux" + CONDA_DEPENDENCIES: "pytest pytest-cov" PIP_DEPENDENCIES: "coveralls" dependencies: From b7f34ebf7ab6888e698e40ac2c32cdd313127210 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 5 May 2017 17:05:57 -0500 Subject: [PATCH 048/703] Testing: Simplify testing on CircleCI --- ci/test-pyqt4.sh | 19 +++++++++++++++++++ ci/test-pyqt5.sh | 18 ++++++++++++++++++ ci/test-pyside.sh | 19 +++++++++++++++++++ circle.yml | 18 +++++++----------- 4 files changed, 63 insertions(+), 11 deletions(-) create mode 100755 ci/test-pyqt4.sh create mode 100755 ci/test-pyqt5.sh create mode 100755 ci/test-pyside.sh diff --git a/ci/test-pyqt4.sh b/ci/test-pyqt4.sh new file mode 100755 index 00000000..72f732d0 --- /dev/null +++ b/ci/test-pyqt4.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +export PATH="$HOME/miniconda/bin:$PATH" +source activate test + +# We use container 3 to test with pip and pyqt5 +if [ "$CIRCLE_NODE_INDEX" = "3" ]; then + exit 0 +else + conda remove -q qt pyqt + conda install -q qt=4.* pyqt=4.* +fi + +python qtpy/tests/runtests.py + +# Force quitting if exit status of runtests.py was not 0 +if [ $? -ne 0 ]; then + exit 1 +fi diff --git a/ci/test-pyqt5.sh b/ci/test-pyqt5.sh new file mode 100755 index 00000000..8f3cad3f --- /dev/null +++ b/ci/test-pyqt5.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +export PATH="$HOME/miniconda/bin:$PATH" +source activate test + +# We use container 3 to test with pip +if [ "$CIRCLE_NODE_INDEX" != "3" ]; then + conda install -q qt=5.* pyqt=5.* +else + pip install -q pyqt5 +fi + +python qtpy/tests/runtests.py + +# Force quitting if exit status of runtests.py was not 0 +if [ $? -ne 0 ]; then + exit 1 +fi diff --git a/ci/test-pyside.sh b/ci/test-pyside.sh new file mode 100755 index 00000000..5aefe1bf --- /dev/null +++ b/ci/test-pyside.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +export PATH="$HOME/miniconda/bin:$PATH" +source activate test + +# We use container 3 to test with pip and pyqt5 +if [ "$CIRCLE_NODE_INDEX" = "3" ]; then + exit 0 +else + conda remove -q qt pyqt + conda install -q -c conda-forge qt=4.* pyside +fi + +python qtpy/tests/runtests.py + +# Force quitting if exit status of runtests.py was not 0 +if [ $? -ne 0 ]; then + exit 1 +fi diff --git a/circle.yml b/circle.yml index 9e746707..7dafe605 100644 --- a/circle.yml +++ b/circle.yml @@ -4,7 +4,7 @@ machine: # Used by test scripts TEST_CI: "True" # Python versions to test (Maximum of 4 different versions for now) - PY_VERSIONS: "2.7 3.5 3.6" + PY_VERSIONS: "2.7 3.5 3.6 3.5" # For Coveralls COVERALLS_REPO_TOKEN: xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5 # Used by astropy-ci helpers @@ -27,17 +27,13 @@ dependencies: test: override: - # Check PyQt5 - - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=5.* pyqt=5.* && python qtpy/tests/runtests.py: # note the colon + - ./ci/test-pyqt5.sh: # note the colon parallel: true - # Check PySide2 -# - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=5.* pyside2 && python qtpy/tests/runtests.py: # note the colon -# parallel: true - # Check PyQt4 - - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda install -q qt=4.* pyqt=4.* && python qtpy/tests/runtests.py: # note the colon + # - ./ci/test-pyside2.sh: # note the colon + # parallel: true + - ./ci/test-pyqt4.sh: # note the colon parallel: true - - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && coveralls: # note the colon + - ./ci/test-pyside.sh: # note the colon parallel: true - # Check Pyside - - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && conda remove -q qt pyqt && conda install -c conda-forge qt=4.* pyside && python qtpy/tests/runtests.py: # note the colon + - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && coveralls: # note the colon parallel: true From cd289f15eb0adf2a3d068eab42cd9888b64c8179 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 5 May 2017 17:34:00 -0500 Subject: [PATCH 049/703] Testing: Skip a test that it's segfaulting in Python 3 --- qtpy/tests/test_patch_qcombobox.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qtpy/tests/test_patch_qcombobox.py b/qtpy/tests/test_patch_qcombobox.py index 04a83370..f9f4ba2d 100644 --- a/qtpy/tests/test_patch_qcombobox.py +++ b/qtpy/tests/test_patch_qcombobox.py @@ -1,8 +1,14 @@ from __future__ import absolute_import +import sys + +import pytest from qtpy import QtGui, QtWidgets +PY3 = sys.version[0] == "3" + + def get_qapp(icon_path=None): qapp = QtWidgets.QApplication.instance() if qapp is None: @@ -19,6 +25,7 @@ def __getitem__(self, item): raise ValueError("Failing") +@pytest.mark.skipif(PY3, reason="It segfaults in Python 3") def test_patched_qcombobox(): """ In PySide, using Python objects as userData in QComboBox causes From d8d8ac1a4336dcdfdbed22d5ab1ee0ae64965bb4 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 5 May 2017 17:34:40 -0500 Subject: [PATCH 050/703] Testing: Add a package needed for PyQt 5.8 --- circle.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/circle.yml b/circle.yml index 7dafe605..5245305b 100644 --- a/circle.yml +++ b/circle.yml @@ -13,6 +13,9 @@ machine: PIP_DEPENDENCIES: "coveralls" dependencies: + pre: + # We need this for QtMultimedia in 5.8 + - sudo apt-get install libpulse-dev override: # First convert PY_VERSIONS to an array and then select the Python version # based on the CIRCLE_NODE_INDEX From 4070af4abf3dc23f3d3060f172c45eea7f3467c5 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 5 May 2017 17:35:59 -0500 Subject: [PATCH 051/703] Testing: Fix missing pyqt 4 conda package in Python 3.6 --- ci/test-pyqt4.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/test-pyqt4.sh b/ci/test-pyqt4.sh index 72f732d0..cc68554c 100755 --- a/ci/test-pyqt4.sh +++ b/ci/test-pyqt4.sh @@ -8,7 +8,7 @@ if [ "$CIRCLE_NODE_INDEX" = "3" ]; then exit 0 else conda remove -q qt pyqt - conda install -q qt=4.* pyqt=4.* + conda install -q -c conda-forge qt=4.* pyqt=4.* fi python qtpy/tests/runtests.py From 458bef8b862f2522a02565674d3d74fead2423f2 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 6 May 2017 10:26:55 -0500 Subject: [PATCH 052/703] Testing: Skip another test on Python 3 because it's failing --- qtpy/tests/test_patch_qheaderview.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index 6f30c337..43dae0de 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -1,12 +1,17 @@ from __future__ import absolute_import +import sys + +import pytest from qtpy import PYSIDE, PYQT4 from qtpy.QtWidgets import QApplication from qtpy.QtWidgets import QHeaderView from qtpy.QtCore import Qt from qtpy.QtCore import QAbstractListModel -import pytest + +PY3 = sys.version[0] == "3" + def get_qapp(icon_path=None): qapp = QApplication.instance() @@ -14,6 +19,8 @@ def get_qapp(icon_path=None): qapp = QApplication(['']) return qapp + +@pytest.mark.skipif(PY3, reason="It fails on Python 3") def test_patched_qheaderview(): """ This will test whether QHeaderView has the new methods introduced in Qt5. From 7881f5c8a94aad5572fb6cbbc388a03c18a44663 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 7 May 2017 19:09:08 -0500 Subject: [PATCH 053/703] Testing: Add a script to test PySide2 --- ci/test-pyside2.sh | 33 +++++++++++++++++++++++++++++++++ circle.yml | 6 ++++-- 2 files changed, 37 insertions(+), 2 deletions(-) create mode 100755 ci/test-pyside2.sh diff --git a/ci/test-pyside2.sh b/ci/test-pyside2.sh new file mode 100755 index 00000000..85760d4d --- /dev/null +++ b/ci/test-pyside2.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +export PATH="$HOME/miniconda/bin:$PATH" +source activate test + +# Download PySide2 wheel +wget -q https://bintray.com/fredrikaverpil/pyside2-wheels/download_file?file_path=ubuntu14.04%2FPySide2-2.0.0.dev0-cp27-none-linux_x86_64.whl -O PySide2-2.0.0.dev0-cp27-none-linux_x86_64.whl + +# We only use container 0 for PySide2 +if [ "$CIRCLE_NODE_INDEX" = "0" ]; then + conda remove -q qt pyqt + pip install PySide2-2.0.0.dev0-cp27-none-linux_x86_64.whl +else + exit 0 +fi + +# Make symlinks for Qt libraries (else imports fail) +pushd "$HOME/miniconda/envs/test/lib/python2.7/site-packages/PySide2/" + +for file in `ls Qt*x86_64-linux-gnu.so` +do + symlink=${file%.x86_64-linux-gnu.so}.so + ln -s $file $symlink +done + +popd + +python qtpy/tests/runtests.py + +# Force quitting if exit status of runtests.py was not 0 +if [ $? -ne 0 ]; then + exit 1 +fi diff --git a/circle.yml b/circle.yml index 5245305b..b7c76c05 100644 --- a/circle.yml +++ b/circle.yml @@ -16,6 +16,8 @@ dependencies: pre: # We need this for QtMultimedia in 5.8 - sudo apt-get install libpulse-dev + # For PySide2 + - sudo apt-get install libqt5core5a libqt5gui5 libqt5multimedia5 override: # First convert PY_VERSIONS to an array and then select the Python version # based on the CIRCLE_NODE_INDEX @@ -32,8 +34,8 @@ test: override: - ./ci/test-pyqt5.sh: # note the colon parallel: true - # - ./ci/test-pyside2.sh: # note the colon - # parallel: true + - ./ci/test-pyside2.sh: # note the colon + parallel: true - ./ci/test-pyqt4.sh: # note the colon parallel: true - ./ci/test-pyside.sh: # note the colon From 2066fb9bfeae76e6273de60d5111b182151ce89b Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 7 May 2017 19:51:00 -0500 Subject: [PATCH 054/703] Fix setting QT_API for PySide2 --- qtpy/__init__.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 2ee07e76..1ade9a8c 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -66,20 +66,25 @@ # Version of QtPy from ._version import __version__ -#: Qt API environment variable name +# Qt API environment variable name QT_API = 'QT_API' -#: names of the expected PyQt5 api + +# Names of the expected PyQt5 api PYQT5_API = ['pyqt5'] -#: names of the expected PyQt4 api + +# Names of the expected PyQt4 api PYQT4_API = [ 'pyqt', # name used in IPython.qt 'pyqt4' # pyqode.qt original name ] -#: names of the expected PySide api + +# Names of the expected PySide api PYSIDE_API = ['pyside'] -#: names of the expected PySide2 api + +# Names of the expected PySide2 api PYSIDE2_API = ['pyside2'] +# Setting a default value for QT_API os.environ.setdefault(QT_API, 'pyqt5') API = os.environ[QT_API].lower() assert API in (PYQT5_API + PYQT4_API + PYSIDE_API + PYSIDE2_API) @@ -100,7 +105,7 @@ class PythonQtError(Exception): from PyQt5.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore PYSIDE_VERSION = None except ImportError: - API = os.environ['QT_API'] = 'pyqt' + API = os.environ['QT_API'] = 'pyside2' if API in PYSIDE2_API: try: @@ -111,7 +116,7 @@ class PythonQtError(Exception): PYQT5 = False PYSIDE2 = True except ImportError: - API = os.environ['QT_API'] = 'pyqt4' + API = os.environ['QT_API'] = 'pyqt' if API in PYQT4_API: try: @@ -150,6 +155,7 @@ class PythonQtError(Exception): API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4', 'pyside': 'PySide', 'pyside2':'PySide2'}[API] + if PYQT4: import sip try: From d5dad478674222f3f71dca7b123bd444b688325c Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 7 May 2017 19:54:56 -0500 Subject: [PATCH 055/703] Testing: Make tests work with PySide2 --- ci/test-pyside2.sh | 2 ++ circle.yml | 8 +++++++- qtpy/QtMultimedia.py | 4 +++- qtpy/QtWebEngineWidgets.py | 7 ++++--- qtpy/tests/test_main.py | 5 ++--- qtpy/tests/test_patch_qcombobox.py | 4 ++-- qtpy/tests/test_patch_qheaderview.py | 4 ++-- qtpy/tests/test_qtmultimedia.py | 4 +++- qtpy/tests/test_uic.py | 4 +++- 9 files changed, 28 insertions(+), 14 deletions(-) diff --git a/ci/test-pyside2.sh b/ci/test-pyside2.sh index 85760d4d..82886aef 100755 --- a/ci/test-pyside2.sh +++ b/ci/test-pyside2.sh @@ -31,3 +31,5 @@ python qtpy/tests/runtests.py if [ $? -ne 0 ]; then exit 1 fi + +pip uninstall -y -q pyside2 diff --git a/circle.yml b/circle.yml index b7c76c05..1889d8e9 100644 --- a/circle.yml +++ b/circle.yml @@ -17,7 +17,13 @@ dependencies: # We need this for QtMultimedia in 5.8 - sudo apt-get install libpulse-dev # For PySide2 - - sudo apt-get install libqt5core5a libqt5gui5 libqt5multimedia5 + - sudo apt-get autoremove libqt5* + - sudo apt-get install apt -y + - sudo add-apt-repository -y ppa:beineri/opt-qt562-trusty + - sudo apt-get update -y; true + - sudo apt-get install -q qt56webengine + - sudo rsync -a /opt/qt56/ /usr/ + override: # First convert PY_VERSIONS to an array and then select the Python version # based on the CIRCLE_NODE_INDEX diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index 0c8f341d..a20cc3bd 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -7,7 +7,9 @@ if PYQT5: from PyQt5.QtMultimedia import * elif PYSIDE2: - from PySide2.QtMultimedia import * + # Current wheels don't have this module + # from PySide2.QtMultimedia import * + pass elif PYQT4: from PyQt4.QtMultimedia import * from PyQt4.QtGui import QSound diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index d1626934..c5577a22 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -31,12 +31,13 @@ try: from PySide2.QtWebEngineWidgets import QWebEnginePage from PySide2.QtWebEngineWidgets import QWebEngineView - from PySide2.QtWebEngineWidgets import QWebEngineSettings + # Current PySide2 wheels seem to be missing this. + # from PySide2.QtWebEngineWidgets import QWebEngineSettings except ImportError: from PySide2.QtWebKitWidgets import QWebPage as QWebEnginePage from PySide2.QtWebKitWidgets import QWebView as QWebEngineView - #: Current PySide2 builds seem to be missing this. - #: from PySide2.QtWebKit import QWebSettings as QWebEngineSettings + # Current PySide2 wheels seem to be missing this. + # from PySide2.QtWebKit import QWebSettings as QWebEngineSettings WEBENGINE = False elif PYQT4: from PyQt4.QtWebKit import QWebPage as QWebEnginePage diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index b4cd38db..2449249c 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -20,9 +20,8 @@ def assert_pyside2(): import PySide2 assert QtCore.QEvent is PySide2.QtCore.QEvent assert QtGui.QPainter is PySide2.QtGui.QPainter - assert QtWidgets.QWidget is PySide2.QtGui.QWidget - # The current builds of PySide2 don't seem to have this yet - # assert QtWebEngineWidgets.QWebEnginePage is PySide2.QtWebKit.QWebPage + assert QtWidgets.QWidget is PySide2.QtWidgets.QWidget + assert QtWebEngineWidgets.QWebEnginePage is PySide2.QtWebEngineWidgets.QWebEnginePage def assert_pyqt4(): """ diff --git a/qtpy/tests/test_patch_qcombobox.py b/qtpy/tests/test_patch_qcombobox.py index f9f4ba2d..2e5e6fe3 100644 --- a/qtpy/tests/test_patch_qcombobox.py +++ b/qtpy/tests/test_patch_qcombobox.py @@ -3,7 +3,7 @@ import sys import pytest -from qtpy import QtGui, QtWidgets +from qtpy import PYSIDE2, QtGui, QtWidgets PY3 = sys.version[0] == "3" @@ -25,7 +25,7 @@ def __getitem__(self, item): raise ValueError("Failing") -@pytest.mark.skipif(PY3, reason="It segfaults in Python 3") +@pytest.mark.skipif(PY3 or PYSIDE2, reason="It segfaults in Python 3 and PYSIDE2") def test_patched_qcombobox(): """ In PySide, using Python objects as userData in QComboBox causes diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index 43dae0de..6c9e6ded 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -3,7 +3,7 @@ import sys import pytest -from qtpy import PYSIDE, PYQT4 +from qtpy import PYSIDE, PYSIDE2, PYQT4 from qtpy.QtWidgets import QApplication from qtpy.QtWidgets import QHeaderView from qtpy.QtCore import Qt @@ -20,7 +20,7 @@ def get_qapp(icon_path=None): return qapp -@pytest.mark.skipif(PY3, reason="It fails on Python 3") +@pytest.mark.skipif(PY3 or PYSIDE2, reason="It fails on Python 3 and PySide2") def test_patched_qheaderview(): """ This will test whether QHeaderView has the new methods introduced in Qt5. diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index a718d188..5561e77b 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -1,8 +1,10 @@ from __future__ import absolute_import -from qtpy import QtMultimedia +import pytest +from qtpy import PYSIDE2, QtMultimedia +@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" assert QtMultimedia.QAbstractVideoBuffer is not None diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 6e732bb2..1c50e9fe 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -2,7 +2,8 @@ import sys import contextlib -from qtpy import QtWidgets +import pytest +from qtpy import PYSIDE2, QtWidgets from qtpy.QtWidgets import QComboBox from qtpy import uic from qtpy.uic import loadUi @@ -69,6 +70,7 @@ def test_load_ui_custom_auto(tmpdir): assert isinstance(ui.comboBox, _QComboBoxSubclass) +@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_load_full_uic(): """Test that we load the full uic objects for PyQt5 and PyQt4.""" QT_API = os.environ.get('QT_API', '').lower() From 197c5894df6bd2627105c07f2d2c0e227636bbc7 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 7 May 2017 22:37:25 -0500 Subject: [PATCH 056/703] Add a coveragerc file --- .coveragerc | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .coveragerc diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..8cdd5267 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,8 @@ +[run] +omit = + # Omit tests + */tests/* + # Omit other files + __init__.py + py3compat.py + compat.py From ef6c42763ad8bbe06f5b1424a3f8350a3b446928 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 7 May 2017 22:38:02 -0500 Subject: [PATCH 057/703] Remove .coveralls.yml --- .coveralls.yml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index eab06c44..00000000 --- a/.coveralls.yml +++ /dev/null @@ -1,3 +0,0 @@ -service_name: travis-ci -parallel: true - From 68d41963e5fb6559b3cab7362712bca334df46ca Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 7 May 2017 22:52:49 -0500 Subject: [PATCH 058/703] Fix coveragerc --- .coveragerc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.coveragerc b/.coveragerc index 8cdd5267..105878e9 100644 --- a/.coveragerc +++ b/.coveragerc @@ -3,6 +3,7 @@ omit = # Omit tests */tests/* # Omit other files - __init__.py - py3compat.py - compat.py + */__init__.py + */py3compat.py + */compat.py + */_version.py From 05679b3fc9f3a86a1a45dd58c6aa37b45f27108d Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Mon, 8 May 2017 10:59:59 -0500 Subject: [PATCH 059/703] Allow import QDesktopServices as QStandardPaths in qt4. The QDesktopServices from Qt4 has been split into two namespaces, QDesktopServices and QStandardPaths, this allows a pyqt5 import of QStandardPaths to work with pyqt4. --- qtpy/QtCore.py | 20 ++++++++++++++++++++ qtpy/QtGui.py | 14 +++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 25702420..0c521f79 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -38,6 +38,26 @@ QStringListModel) from PyQt4.QtCore import QT_VERSION_STR as __version__ + # QDesktopServices has has been split into (QDesktopServices and + # QStandardPaths) in Qt5 + # This creates a dummy class that emulates QStandardPaths + from PyQt4.QtGui import QDesktopServices as _QDesktopServices + + class QStandardPaths(): + StandardLocation = _QDesktopServices.StandardLocation + displayName = _QDesktopServices.displayName + DesktopLocation = _QDesktopServices.DesktopLocation + DocumentsLocation = _QDesktopServices.DocumentsLocation + FontsLocation = _QDesktopServices.FontsLocation + ApplicationsLocation = _QDesktopServices.ApplicationsLocation + MusicLocation = _QDesktopServices.MusicLocation + MoviesLocation = _QDesktopServices.MoviesLocation + PicturesLocation = _QDesktopServices.PicturesLocation + TempLocation = _QDesktopServices.TempLocation + HomeLocation = _QDesktopServices.HomeLocation + DataLocation = _QDesktopServices.DataLocation + CacheLocation = _QDesktopServices.CacheLocation + # Those are imported from `import *` del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR elif PYSIDE: diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 99ea6f37..798aa5fe 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -36,7 +36,7 @@ from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, - QDesktopServices, QDoubleValidator, QDrag, + QDoubleValidator, QDrag, QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, @@ -68,6 +68,18 @@ QWindowStateChangeEvent, qAlpha, qBlue, qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator) + + # QDesktopServices has has been split into (QDesktopServices and + # QStandardPaths) in Qt5 + # It only exposes QDesktopServices that are still in pyqt5 + from PyQt4.QtGui import QDesktopServices as _QDesktopServices + + class QDesktopServices(): + openUrl = _QDesktopServices.openUrl + setUrlHandler = _QDesktopServices.setUrlHandler + unsetUrlHandler = _QDesktopServices.unsetUrlHandler + + elif PYSIDE: from PySide.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, From add67e63dd30a633bcd89db5c7908a6c9eefc059 Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Mon, 8 May 2017 12:35:55 -0500 Subject: [PATCH 060/703] Add test for QDesktopServices split. --- qtpy/tests/test_qdesktopservice_split.py | 25 ++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 qtpy/tests/test_qdesktopservice_split.py diff --git a/qtpy/tests/test_qdesktopservice_split.py b/qtpy/tests/test_qdesktopservice_split.py new file mode 100644 index 00000000..861341c9 --- /dev/null +++ b/qtpy/tests/test_qdesktopservice_split.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import + +import pytest +from qtpy.QtCore import QStandardPaths +from qtpy.QtGui import QDesktopServices + +"""Test QDesktopServices split in Qt5.""" + +def test_qstandarpath(): + """Test the qtpy.QStandardPaths namespace""" + + assert QStandardPaths.StandardLocation is not None + + # Attributes from QDesktopServices shouldn't be in QStandardPaths + with pytest.raises(AttributeError) as excinfo: + QStandardPaths.setUrlHandler + +def test_qdesktopservice(): + """Test the qtpy.QDesktopServices namespace""" + + assert QDesktopServices.setUrlHandler is not None + + # Attributes from QStandardPaths shouldn't be in QDesktopServices + with pytest.raises(AttributeError) as excinfo: + QDesktopServices.StandardLocation \ No newline at end of file From 14e0ff74b138920cb9a4273aa3a845efb32a855e Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Tue, 9 May 2017 11:42:08 -0500 Subject: [PATCH 061/703] Implement the dummy class for QStandardPaths pyside. --- qtpy/QtCore.py | 21 +++++++++++++++++++++ qtpy/QtGui.py | 11 ++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 0c521f79..4490a291 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -65,6 +65,27 @@ class QStandardPaths(): from PySide.QtGui import (QItemSelection, QItemSelectionModel, QItemSelectionRange, QSortFilterProxyModel, QStringListModel) + + # QDesktopServices has has been split into (QDesktopServices and + # QStandardPaths) in Qt5 + # This creates a dummy class that emulates QStandardPaths + from PySide.QtGui import QDesktopServices as _QDesktopServices + + class QStandardPaths(): + StandardLocation = _QDesktopServices.StandardLocation + displayName = _QDesktopServices.displayName + DesktopLocation = _QDesktopServices.DesktopLocation + DocumentsLocation = _QDesktopServices.DocumentsLocation + FontsLocation = _QDesktopServices.FontsLocation + ApplicationsLocation = _QDesktopServices.ApplicationsLocation + MusicLocation = _QDesktopServices.MusicLocation + MoviesLocation = _QDesktopServices.MoviesLocation + PicturesLocation = _QDesktopServices.PicturesLocation + TempLocation = _QDesktopServices.TempLocation + HomeLocation = _QDesktopServices.HomeLocation + DataLocation = _QDesktopServices.DataLocation + CacheLocation = _QDesktopServices.CacheLocation + import PySide.QtCore __version__ = PySide.QtCore.__version__ else: diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 798aa5fe..3dfaff07 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -84,7 +84,7 @@ class QDesktopServices(): from PySide.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, - QDesktopServices, QDoubleValidator, QDrag, + QDoubleValidator, QDrag, QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, @@ -120,5 +120,14 @@ class QDesktopServices(): QWindowStateChangeEvent, qAlpha, qBlue, qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator) + # QDesktopServices has has been split into (QDesktopServices and + # QStandardPaths) in Qt5 + # It only exposes QDesktopServices that are still in pyqt5 + from PySide.QtGui import QDesktopServices as _QDesktopServices + + class QDesktopServices(): + openUrl = _QDesktopServices.openUrl + setUrlHandler = _QDesktopServices.setUrlHandler + unsetUrlHandler = _QDesktopServices.unsetUrlHandler else: raise PythonQtError('No Qt bindings could be found') From 7d3df185687a5d4bbf20a5a75fa0eaf681c31759 Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Tue, 9 May 2017 11:42:55 -0500 Subject: [PATCH 062/703] Skip QStandardPaths test in pyside2. --- qtpy/tests/test_qdesktopservice_split.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qdesktopservice_split.py b/qtpy/tests/test_qdesktopservice_split.py index 861341c9..bf12927d 100644 --- a/qtpy/tests/test_qdesktopservice_split.py +++ b/qtpy/tests/test_qdesktopservice_split.py @@ -1,13 +1,14 @@ from __future__ import absolute_import import pytest -from qtpy.QtCore import QStandardPaths -from qtpy.QtGui import QDesktopServices +from qtpy import PYSIDE2 """Test QDesktopServices split in Qt5.""" +@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_qstandarpath(): """Test the qtpy.QStandardPaths namespace""" + from qtpy.QtCore import QStandardPaths assert QStandardPaths.StandardLocation is not None @@ -15,8 +16,10 @@ def test_qstandarpath(): with pytest.raises(AttributeError) as excinfo: QStandardPaths.setUrlHandler +@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_qdesktopservice(): """Test the qtpy.QDesktopServices namespace""" + from qtpy.QtGui import QDesktopServices assert QDesktopServices.setUrlHandler is not None From e695829036e99d352f490d01f425dee6d1ca6e2b Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Tue, 9 May 2017 12:28:15 -0500 Subject: [PATCH 063/703] Add test for Qtdesigner, QtNetwork, QtPrintSupport, QtSvg and QtTest. --- qtpy/tests/test_qtdesigner.py | 29 +++++++++++++++++ qtpy/tests/test_qtnetwork.py | 54 +++++++++++++++++++++++++++++++ qtpy/tests/test_qtprintsupport.py | 18 +++++++++++ qtpy/tests/test_qtsvg.py | 12 +++++++ qtpy/tests/test_qttest.py | 12 +++++++ 5 files changed, 125 insertions(+) create mode 100644 qtpy/tests/test_qtdesigner.py create mode 100644 qtpy/tests/test_qtnetwork.py create mode 100644 qtpy/tests/test_qtprintsupport.py create mode 100644 qtpy/tests/test_qtsvg.py create mode 100644 qtpy/tests/test_qttest.py diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py new file mode 100644 index 00000000..9ca05384 --- /dev/null +++ b/qtpy/tests/test_qtdesigner.py @@ -0,0 +1,29 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2, QtDesigner + + +@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") +def test_qtdesigner(): + """Test the qtpy.QtDesigner namespace""" + assert QtDesigner.QAbstractExtensionFactory is not None + assert QtDesigner.QAbstractExtensionManager is not None + assert QtDesigner.QDesignerActionEditorInterface is not None + assert QtDesigner.QDesignerContainerExtension is not None + assert QtDesigner.QDesignerCustomWidgetCollectionInterface is not None + assert QtDesigner.QDesignerCustomWidgetInterface is not None + assert QtDesigner.QDesignerDynamicPropertySheetExtension is not None + assert QtDesigner.QDesignerFormEditorInterface is not None + assert QtDesigner.QDesignerFormWindowCursorInterface is not None + assert QtDesigner.QDesignerFormWindowInterface is not None + assert QtDesigner.QDesignerFormWindowManagerInterface is not None + assert QtDesigner.QDesignerMemberSheetExtension is not None + assert QtDesigner.QDesignerObjectInspectorInterface is not None + assert QtDesigner.QDesignerPropertyEditorInterface is not None + assert QtDesigner.QDesignerPropertySheetExtension is not None + assert QtDesigner.QDesignerTaskMenuExtension is not None + assert QtDesigner.QDesignerWidgetBoxInterface is not None + assert QtDesigner.QExtensionFactory is not None + assert QtDesigner.QExtensionManager is not None + assert QtDesigner.QFormBuilder is not None \ No newline at end of file diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py new file mode 100644 index 00000000..81759a07 --- /dev/null +++ b/qtpy/tests/test_qtnetwork.py @@ -0,0 +1,54 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2, QtNetwork + + +def test_qtnetwork(): + """Test the qtpy.QtNetwork namespace""" + assert QtNetwork.QAbstractNetworkCache is not None + assert QtNetwork.QNetworkCacheMetaData is not None + assert QtNetwork.QHttpMultiPart is not None + assert QtNetwork.QHttpPart is not None + assert QtNetwork.QNetworkAccessManager is not None + assert QtNetwork.QNetworkCookie is not None + assert QtNetwork.QNetworkCookieJar is not None + assert QtNetwork.QNetworkDiskCache is not None + assert QtNetwork.QNetworkReply is not None + assert QtNetwork.QNetworkRequest is not None + assert QtNetwork.QNetworkConfigurationManager is not None + assert QtNetwork.QNetworkConfiguration is not None + assert QtNetwork.QNetworkSession is not None + assert QtNetwork.QAuthenticator is not None + assert QtNetwork.QDnsDomainNameRecord is not None + assert QtNetwork.QDnsHostAddressRecord is not None + assert QtNetwork.QDnsLookup is not None + assert QtNetwork.QDnsMailExchangeRecord is not None + assert QtNetwork.QDnsServiceRecord is not None + assert QtNetwork.QDnsTextRecord is not None + assert QtNetwork.QHostAddress is not None + assert QtNetwork.QHostInfo is not None + assert QtNetwork.QNetworkDatagram is not None + assert QtNetwork.QNetworkAddressEntry is not None + assert QtNetwork.QNetworkInterface is not None + assert QtNetwork.QNetworkProxy is not None + assert QtNetwork.QNetworkProxyFactory is not None + assert QtNetwork.QNetworkProxyQuery is not None + assert QtNetwork.QAbstractSocket is not None + assert QtNetwork.QLocalServer is not None + assert QtNetwork.QLocalSocket is not None + assert QtNetwork.QSctpServer is not None + assert QtNetwork.QSctpSocket is not None + assert QtNetwork.QTcpServer is not None + assert QtNetwork.QTcpSocket is not None + assert QtNetwork.QUdpSocket is not None + assert QtNetwork.QSslCertificate is not None + assert QtNetwork.QSslCertificateExtension is not None + assert QtNetwork.QSslCipher is not None + assert QtNetwork.QSslConfiguration is not None + assert QtNetwork.QSslDiffieHellmanParameters is not None + assert QtNetwork.QSslEllipticCurve is not None + assert QtNetwork.QSslError is not None + assert QtNetwork.QSslKey is not None + assert QtNetwork.QSslPreSharedKeyAuthenticator is not None + assert QtNetwork.QSslSocket is not None diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py new file mode 100644 index 00000000..6221286d --- /dev/null +++ b/qtpy/tests/test_qtprintsupport.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2, QtPrintSupport + + +def test_qtprintsupport(): + """Test the qtpy.QtPrintSupport namespace""" + assert QtPrintSupport.QAbstractPrintDialog is not None + assert QtPrintSupport.QPageSetupDialog is not None + assert QtPrintSupport.QPrintDialog is not None + assert QtPrintSupport.QPrintPreviewDialog is not None + assert QtPrintSupport.QPrintEngine is not None + assert QtPrintSupport.QPrinter is not None + assert QtPrintSupport.QPrinterInfo is not None + assert QtPrintSupport.QPrintPreviewWidget is not None + + diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py new file mode 100644 index 00000000..518ab822 --- /dev/null +++ b/qtpy/tests/test_qtsvg.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2, QtSvg + + +def test_qtsvg(): + """Test the qtpy.QtSvg namespace""" + assert QtSvg.QGraphicsSvgItem is not None + assert QtSvg.QSvgGenerator is not None + assert QtSvg.QSvgRenderer is not None + assert QtSvg.QSvgWidget is not None \ No newline at end of file diff --git a/qtpy/tests/test_qttest.py b/qtpy/tests/test_qttest.py new file mode 100644 index 00000000..d87cecc3 --- /dev/null +++ b/qtpy/tests/test_qttest.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2, QtTest + + +@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") +def test_qttest(): + """Test the qtpy.QtTest namespace""" + assert QtTest.QTest is not None + assert QtTest.QSignalSpy is not None + assert QtTest.QTestEventList is not None \ No newline at end of file From 0388b6974f812c3557230138ddb5223930e96847 Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Tue, 9 May 2017 12:36:15 -0500 Subject: [PATCH 064/703] Remove failing asserts in pyqt4. --- qtpy/tests/test_qtdesigner.py | 1 - qtpy/tests/test_qtnetwork.py | 13 ------------- qtpy/tests/test_qttest.py | 2 -- 3 files changed, 16 deletions(-) diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index 9ca05384..cc90f5f9 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -13,7 +13,6 @@ def test_qtdesigner(): assert QtDesigner.QDesignerContainerExtension is not None assert QtDesigner.QDesignerCustomWidgetCollectionInterface is not None assert QtDesigner.QDesignerCustomWidgetInterface is not None - assert QtDesigner.QDesignerDynamicPropertySheetExtension is not None assert QtDesigner.QDesignerFormEditorInterface is not None assert QtDesigner.QDesignerFormWindowCursorInterface is not None assert QtDesigner.QDesignerFormWindowInterface is not None diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index 81759a07..eb0c9f69 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -20,15 +20,8 @@ def test_qtnetwork(): assert QtNetwork.QNetworkConfiguration is not None assert QtNetwork.QNetworkSession is not None assert QtNetwork.QAuthenticator is not None - assert QtNetwork.QDnsDomainNameRecord is not None - assert QtNetwork.QDnsHostAddressRecord is not None - assert QtNetwork.QDnsLookup is not None - assert QtNetwork.QDnsMailExchangeRecord is not None - assert QtNetwork.QDnsServiceRecord is not None - assert QtNetwork.QDnsTextRecord is not None assert QtNetwork.QHostAddress is not None assert QtNetwork.QHostInfo is not None - assert QtNetwork.QNetworkDatagram is not None assert QtNetwork.QNetworkAddressEntry is not None assert QtNetwork.QNetworkInterface is not None assert QtNetwork.QNetworkProxy is not None @@ -37,18 +30,12 @@ def test_qtnetwork(): assert QtNetwork.QAbstractSocket is not None assert QtNetwork.QLocalServer is not None assert QtNetwork.QLocalSocket is not None - assert QtNetwork.QSctpServer is not None - assert QtNetwork.QSctpSocket is not None assert QtNetwork.QTcpServer is not None assert QtNetwork.QTcpSocket is not None assert QtNetwork.QUdpSocket is not None assert QtNetwork.QSslCertificate is not None - assert QtNetwork.QSslCertificateExtension is not None assert QtNetwork.QSslCipher is not None assert QtNetwork.QSslConfiguration is not None - assert QtNetwork.QSslDiffieHellmanParameters is not None - assert QtNetwork.QSslEllipticCurve is not None assert QtNetwork.QSslError is not None assert QtNetwork.QSslKey is not None - assert QtNetwork.QSslPreSharedKeyAuthenticator is not None assert QtNetwork.QSslSocket is not None diff --git a/qtpy/tests/test_qttest.py b/qtpy/tests/test_qttest.py index d87cecc3..4110e274 100644 --- a/qtpy/tests/test_qttest.py +++ b/qtpy/tests/test_qttest.py @@ -8,5 +8,3 @@ def test_qttest(): """Test the qtpy.QtTest namespace""" assert QtTest.QTest is not None - assert QtTest.QSignalSpy is not None - assert QtTest.QTestEventList is not None \ No newline at end of file From 49f9c23be3cee55463196dbf7f0d785c4627fe82 Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Tue, 9 May 2017 14:57:17 -0500 Subject: [PATCH 065/703] Update Readme to mention that we now support PySide2. --- README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 731d3b97..4986c6a0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# QtPy: Abtraction layer for PyQt5/PyQt4/PySide +# QtPy: Abtraction layer for PyQt5/PyQt4/PySide2/PySide Copyright © 2009- The Spyder Development Team. @@ -19,8 +19,8 @@ Copyright © 2009- The Spyder Development Team. **QtPy** is a small abstraction layer that lets you write applications using a single API call to either PyQt or PySide. -It provides support for PyQt5, PyQt4 and PySide using the PyQt5 layout (where -the QtGui module has been split into QtGui and QtWidgets). +It provides support for PyQt5, PyQt4, PySide2 and PySide using the PyQt5 layout +(where the QtGui module has been split into QtGui and QtWidgets). Basically, you write your code as if you were using PyQt5 but import Qt modules from `qtpy` instead of `PyQt5`. @@ -46,7 +46,7 @@ This project is licensed under the MIT license. ### Requirements -You need PyQt5, PyQt4 or PySide installed in your system to make use +You need PyQt5, PyQt4, PySide2 or PySide installed in your system to make use of QtPy. If several of these packages are found, PyQt5 is used by default unless you set the `QT_API` environment variable. @@ -54,6 +54,7 @@ default unless you set the `QT_API` environment variable. * `pyqt5` (to use PyQt5). * `pyqt` or `pyqt4` (to use PyQt4). +* `pyside2` (to use PySide2) * `pyside` (to use PySide). From 152ab896f866de4c1221c4a39c4886ef1ddfdede Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Tue, 9 May 2017 15:04:43 -0500 Subject: [PATCH 066/703] Change PyQt5 layout to Qt5 layout in the Readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4986c6a0..f4612c2c 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Copyright © 2009- The Spyder Development Team. **QtPy** is a small abstraction layer that lets you write applications using a single API call to either PyQt or PySide. -It provides support for PyQt5, PyQt4, PySide2 and PySide using the PyQt5 layout +It provides support for PyQt5, PyQt4, PySide2 and PySide using the Qt5 layout (where the QtGui module has been split into QtGui and QtWidgets). Basically, you write your code as if you were using PyQt5 but import Qt modules From 455af3046313fe34eb0220faee2ae76dfdab9224 Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Tue, 9 May 2017 13:05:39 -0500 Subject: [PATCH 067/703] Fix failing test on pyside, and remove some unused imports. --- qtpy/tests/test_qtdesigner.py | 6 +++--- qtpy/tests/test_qtnetwork.py | 3 ++- qtpy/tests/test_qtprintsupport.py | 2 +- qtpy/tests/test_qtsvg.py | 6 ++++-- qtpy/tests/test_qttest.py | 3 +-- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index cc90f5f9..0327c6f7 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -1,11 +1,11 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2, QtDesigner +from qtpy import PYSIDE2, PYSIDE - -@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") +@pytest.mark.skipif(PYSIDE2 or PYSIDE, reason="QtDesigner is not avalaible in PySide/PySide2") def test_qtdesigner(): + from qtpy import QtDesigner """Test the qtpy.QtDesigner namespace""" assert QtDesigner.QAbstractExtensionFactory is not None assert QtDesigner.QAbstractExtensionManager is not None diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index eb0c9f69..8f4b71f4 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -1,9 +1,10 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2, QtNetwork +from qtpy import PYSIDE, PYSIDE2, QtNetwork +@pytest.mark.skipif(PYSIDE2 or PYSIDE, reason="It fails on PySide/PySide2") def test_qtnetwork(): """Test the qtpy.QtNetwork namespace""" assert QtNetwork.QAbstractNetworkCache is not None diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py index 6221286d..2e8f7861 100644 --- a/qtpy/tests/test_qtprintsupport.py +++ b/qtpy/tests/test_qtprintsupport.py @@ -1,7 +1,7 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2, QtPrintSupport +from qtpy import QtPrintSupport def test_qtprintsupport(): diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py index 518ab822..51b743bb 100644 --- a/qtpy/tests/test_qtsvg.py +++ b/qtpy/tests/test_qtsvg.py @@ -1,11 +1,13 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2, QtSvg - +from qtpy import PYSIDE2 +@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_qtsvg(): """Test the qtpy.QtSvg namespace""" + from qtpy import QtSvg + assert QtSvg.QGraphicsSvgItem is not None assert QtSvg.QSvgGenerator is not None assert QtSvg.QSvgRenderer is not None diff --git a/qtpy/tests/test_qttest.py b/qtpy/tests/test_qttest.py index 4110e274..5d2ab9e1 100644 --- a/qtpy/tests/test_qttest.py +++ b/qtpy/tests/test_qttest.py @@ -1,10 +1,9 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2, QtTest +from qtpy import QtTest -@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_qttest(): """Test the qtpy.QtTest namespace""" assert QtTest.QTest is not None From 749d5ee2a6f348db9970158d917a7e5bf808ec84 Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Tue, 9 May 2017 16:19:19 -0500 Subject: [PATCH 068/703] Update Readme, to reflect that we actually use the pyside2 structure --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f4612c2c..12f504d5 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,8 @@ write applications using a single API call to either PyQt or PySide. It provides support for PyQt5, PyQt4, PySide2 and PySide using the Qt5 layout (where the QtGui module has been split into QtGui and QtWidgets). -Basically, you write your code as if you were using PyQt5 but import Qt modules -from `qtpy` instead of `PyQt5`. +Basically, you write your code as if you were using PySide2 but import Qt modules +from `qtpy` instead of `PySide2` (or `PyQt5`) ### Attribution and acknowledgements From 0f089e035ec664f35d53952b62c0394a88cbd362 Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Mon, 15 May 2017 10:48:57 -0500 Subject: [PATCH 069/703] Add compatibility for rename of qInstallMsgHandler --> qInstallMessageHandler. --- qtpy/QtCore.py | 5 ++++- qtpy/tests/test_qtcore.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 qtpy/tests/test_qtcore.py diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 4490a291..c1d81e8f 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -37,6 +37,7 @@ QItemSelectionRange, QSortFilterProxyModel, QStringListModel) from PyQt4.QtCore import QT_VERSION_STR as __version__ + from PyQt4.QtCore import qInstallMsgHandler as qInstallMessageHandler # QDesktopServices has has been split into (QDesktopServices and # QStandardPaths) in Qt5 @@ -59,12 +60,14 @@ class QStandardPaths(): CacheLocation = _QDesktopServices.CacheLocation # Those are imported from `import *` - del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR + del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR, qInstallMsgHandler elif PYSIDE: from PySide.QtCore import * from PySide.QtGui import (QItemSelection, QItemSelectionModel, QItemSelectionRange, QSortFilterProxyModel, QStringListModel) + from PySide.QtCore import qInstallMsgHandler as qInstallMessageHandler + del qInstallMsgHandler # QDesktopServices has has been split into (QDesktopServices and # QStandardPaths) in Qt5 diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py new file mode 100644 index 00000000..8dc8f74a --- /dev/null +++ b/qtpy/tests/test_qtcore.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +import pytest +from qtpy import QtCore + +"""Test QtCore.""" + +def test_qtmsghandler(): + """Test the qtpy.QtMsgHandler""" + assert QtCore.qInstallMessageHandler is not None From dbc6c6059c5752ba92b33aef14a07170b6ce62c1 Mon Sep 17 00:00:00 2001 From: cpascual Date: Thu, 27 Jul 2017 16:43:45 +0200 Subject: [PATCH 070/703] Warn if QHeaderView deprecated methods are used Warn instead of raising an exception if QHeaderView deprecated methods are used. Also adapt unit tests accordingly. Fixes #119 --- qtpy/_patch/qheaderview.py | 31 +++++++++++++++++----------- qtpy/tests/test_patch_qheaderview.py | 14 ++++++------- 2 files changed, 26 insertions(+), 19 deletions(-) diff --git a/qtpy/_patch/qheaderview.py b/qtpy/_patch/qheaderview.py index 99cd20a7..b6baddbb 100644 --- a/qtpy/_patch/qheaderview.py +++ b/qtpy/_patch/qheaderview.py @@ -4,6 +4,7 @@ # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +import warnings def introduce_renamed_methods_qheaderview(QHeaderView): @@ -15,8 +16,9 @@ def sectionsClickable(self): return _isClickable(self) QHeaderView.sectionsClickable = sectionsClickable def isClickable(self): - raise Exception('isClickable is only available in Qt4. Use ' - 'sectionsClickable instead.') + warnings.warn('isClickable is only available in Qt4. Use ' + 'sectionsClickable instead.', stacklevel=2) + return _isClickable(self) QHeaderView.isClickable = isClickable @@ -28,8 +30,9 @@ def sectionsMovable(self): return _isMovable(self) QHeaderView.sectionsMovable = sectionsMovable def isMovable(self): - raise Exception('isMovable is only available in Qt4. Use ' - 'sectionsMovable instead.') + warnings.warn('isMovable is only available in Qt4. Use ' + 'sectionsMovable instead.', stacklevel=2) + return _isMovable(self) QHeaderView.isMovable = isMovable @@ -41,8 +44,9 @@ def sectionResizeMode(self, logicalIndex): return _resizeMode(self, logicalIndex) QHeaderView.sectionResizeMode = sectionResizeMode def resizeMode(self, logicalIndex): - raise Exception('resizeMode is only available in Qt4. Use ' - 'sectionResizeMode instead.') + warnings.warn('resizeMode is only available in Qt4. Use ' + 'sectionResizeMode instead.', stacklevel=2) + return _resizeMode(self, logicalIndex) QHeaderView.resizeMode = resizeMode _setClickable = QHeaderView.setClickable @@ -53,8 +57,9 @@ def setSectionsClickable(self, clickable): return _setClickable(self, clickable) QHeaderView.setSectionsClickable = setSectionsClickable def setClickable(self, clickable): - raise Exception('setClickable is only available in Qt4. Use ' - 'setSectionsClickable instead.') + warnings.warn('setClickable is only available in Qt4. Use ' + 'setSectionsClickable instead.', stacklevel=2) + return _setClickable(self, clickable) QHeaderView.setClickable = setClickable @@ -66,8 +71,9 @@ def setSectionsMovable(self, movable): return _setMovable(self, movable) QHeaderView.setSectionsMovable = setSectionsMovable def setMovable(self, movable): - raise Exception('setMovable is only available in Qt4. Use ' - 'setSectionsMovable instead.') + warnings.warn('setMovable is only available in Qt4. Use ' + 'setSectionsMovable instead.', stacklevel=2) + return _setMovable(self, movable) QHeaderView.setMovable = setMovable @@ -80,8 +86,9 @@ def setSectionResizeMode(self, *args): _setResizeMode(self, *args) QHeaderView.setSectionResizeMode = setSectionResizeMode def setResizeMode(self, *args): - raise Exception('setResizeMode is only available in Qt4. Use ' - 'setSectionResizeMode instead.') + warnings.warn('setResizeMode is only available in Qt4. Use ' + 'setSectionResizeMode instead.', stacklevel=2) + _setResizeMode(self, *args) QHeaderView.setResizeMode = setResizeMode diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index 6c9e6ded..17037f34 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -82,17 +82,17 @@ class Model(QAbstractListModel): # test that the old methods in Qt4 raise exceptions if PYQT4 or PYSIDE: - with pytest.raises(Exception): + with pytest.warns(UserWarning): headerview.isClickable() - with pytest.raises(Exception): + with pytest.warns(UserWarning): headerview.isMovable() - with pytest.raises(Exception): + with pytest.warns(UserWarning): headerview.resizeMode(0) - with pytest.raises(Exception): + with pytest.warns(UserWarning): headerview.setClickable(True) - with pytest.raises(Exception): - headerview.setMovableClickable(True) - with pytest.raises(Exception): + with pytest.warns(UserWarning): + headerview.setMovable(True) + with pytest.warns(UserWarning): headerview.setResizeMode(0, QHeaderView.Interactive) From c1611de1548a87ae2c4634099071c1cd3148ddcc Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Wed, 9 Aug 2017 15:16:06 -0500 Subject: [PATCH 071/703] Fix typo in Readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12f504d5..53f318cf 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# QtPy: Abtraction layer for PyQt5/PyQt4/PySide2/PySide +# QtPy: Abstraction layer for PyQt5/PyQt4/PySide2/PySide Copyright © 2009- The Spyder Development Team. From c8b582131dfdc4b394badcdd83d97224ea38de61 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 12 Aug 2017 10:23:36 -0500 Subject: [PATCH 072/703] Update Changelog --- CHANGELOG.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dea58c9..56129cb9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,44 @@ # History of changes +### New features + +* Add support for PySide2 +* Add support for QtMultimedia +* Add support for PyQt 4.6 + +### Bugs fixed + +**Issues** + +* [Issue 124](https://github.com/spyder-ide/qtpy/issues/124) - Typo in readme title +* [Issue 111](https://github.com/spyder-ide/qtpy/issues/111) - Update Readme for 1.3 release +* [Issue 110](https://github.com/spyder-ide/qtpy/issues/110) - Add tests for untested modules +* [Issue 101](https://github.com/spyder-ide/qtpy/issues/101) - Missing: QtOpenGL Module +* [Issue 89](https://github.com/spyder-ide/qtpy/issues/89) - QDesktopServices split into QDesktopServices and QStandardPaths +* [Issue 57](https://github.com/spyder-ide/qtpy/issues/57) - qInstallMessageHandler <-> qInstallMsgHandler +* [Issue 15](https://github.com/spyder-ide/qtpy/issues/15) - Feature Request: PySide2 support + +In this release 7 issues were closed + +**Pull requests** + +* [PR 125](https://github.com/spyder-ide/qtpy/pull/125) - PR: Fix typo in Readme. +* [PR 117](https://github.com/spyder-ide/qtpy/pull/117) - PR: Add compatibility for the rename of qInstallMsgHandler to qInstallMessageHandler +* [PR 115](https://github.com/spyder-ide/qtpy/pull/115) - PR: Update Readme to reflect that we actually use the PySide2 layout +* [PR 114](https://github.com/spyder-ide/qtpy/pull/114) - PR: Update Readme to mention that we now support PySide2. +* [PR 113](https://github.com/spyder-ide/qtpy/pull/113) - PR: Add tests for Qtdesigner, QtNetwork, QtPrintSupport, QtSvg and QtTest. +* [PR 112](https://github.com/spyder-ide/qtpy/pull/112) - PR: Follow QStandardPaths location in Qt5 for PyQt4/PySide +* [PR 109](https://github.com/spyder-ide/qtpy/pull/109) - PR: Add a coveragerc file +* [PR 106](https://github.com/spyder-ide/qtpy/pull/106) - PR: Add support for PyQt 4.6 +* [PR 102](https://github.com/spyder-ide/qtpy/pull/102) - PR: Add a new QtOpenGL module +* [PR 84](https://github.com/spyder-ide/qtpy/pull/84) - PR: Add PySide2 support + +In this release 10 pull requests were merged + + +---- + + ## Version 1.2.1 (2017/01/21) ### Bugs fixed From 05f49ca04f89611ae07898a576a1928619f5dc3b Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 12 Aug 2017 10:24:50 -0500 Subject: [PATCH 073/703] Release 1.3.0 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index d631eaf6..0465a17f 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 3, 0, 'dev0') +version_info = (1, 3, 0) __version__ = '.'.join(map(str, version_info)) From 18510588ef413f5699d33699276383dc4276984a Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 12 Aug 2017 10:28:53 -0500 Subject: [PATCH 074/703] Back to work --- CHANGELOG.md | 2 ++ qtpy/_version.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56129cb9..23069717 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # History of changes +## Version 1.3 (2017-08-12) + ### New features * Add support for PySide2 diff --git a/qtpy/_version.py b/qtpy/_version.py index 0465a17f..d48e8a4b 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 3, 0) +version_info = (1, 4, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 95977be21bdf219e8082f02a655f39e0f3d1df05 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 12 Aug 2017 10:43:37 -0500 Subject: [PATCH 075/703] Remove Quantified Code badge because the service doesn't exist anymore [ci skip] --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 53f318cf..8f7aa7e3 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,6 @@ Copyright © 2009- The Spyder Development Team. [![Build status](https://ci.appveyor.com/api/projects/status/62y6i02vhn4hefg0/branch/master?svg=true)](https://ci.appveyor.com/project/spyder-ide/qtpy/branch/master) [![CircleCI](https://circleci.com/gh/spyder-ide/qtpy.svg?style=shield)](https://circleci.com/gh/spyder-ide/qtpy) [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) -[![Code Issues](https://www.quantifiedcode.com/api/v1/project/c769241c7d7f4463b1e6f67863dabace/badge.svg)](https://www.quantifiedcode.com/app/project/c769241c7d7f4463b1e6f67863dabace) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/spyder-ide/qtpy/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/spyder-ide/qtpy/?branch=master) ## Description From 77407aaa372893c77b5d0c2180f40c7fd1a19228 Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Thu, 17 Aug 2017 18:38:50 -0500 Subject: [PATCH 076/703] Add QtHelp Wrapper. --- qtpy/QtHelp.py | 24 ++++++++++++++++++++++++ qtpy/tests/test_qthelp.py | 22 ++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 qtpy/QtHelp.py create mode 100644 qtpy/tests/test_qthelp.py diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py new file mode 100644 index 00000000..7830d412 --- /dev/null +++ b/qtpy/QtHelp.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +"""QtHelp Wrapper.""" + +from . import PYQT5 +from . import PYQT4 +from . import PYSIDE +from . import PYSIDE2 + +if PYQT5: + from PyQt5.QtHelp import * +elif PYSIDE2: + # Current wheels don't have this module + # from PySide2.QtHelp + pass +elif PYQT4: + from PyQt4.QtHelp import * +elif PYSIDE: + from PySide.QtHelp import * diff --git a/qtpy/tests/test_qthelp.py b/qtpy/tests/test_qthelp.py new file mode 100644 index 00000000..c548ebc0 --- /dev/null +++ b/qtpy/tests/test_qthelp.py @@ -0,0 +1,22 @@ +"""Test for QtHelp namespace.""" + +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2, QtHelp + + +@pytest.mark.skipif(PYSIDE2, reason="QtHelp bind is missing in PySide2") +def test_qthelp(): + """Test the qtpy.QtHelp namespace.""" + assert QtHelp.QHelpContentItem is not None + assert QtHelp.QHelpContentModel is not None + assert QtHelp.QHelpContentWidget is not None + assert QtHelp.QHelpEngine is not None + assert QtHelp.QHelpEngineCore is not None + assert QtHelp.QHelpIndexModel is not None + assert QtHelp.QHelpIndexWidget is not None + assert QtHelp.QHelpSearchEngine is not None + assert QtHelp.QHelpSearchQuery is not None + assert QtHelp.QHelpSearchQueryWidget is not None + assert QtHelp.QHelpSearchResultWidget is not None From 8c562d1a923c6326358b4d7d2b49034fd89c262c Mon Sep 17 00:00:00 2001 From: Ghislain Antony Vaillant Date: Sat, 19 Aug 2017 19:04:21 +0100 Subject: [PATCH 077/703] No cache files included in the release tarball Closes #129 --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 5d615b99..2019e338 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,4 +2,4 @@ include AUTHORS.md include CHANGELOG.md include LICENSE.txt include README.md -recursive-include qtpy/tests * +recursive-include qtpy/tests *.py *.ui From 971f8d377573025e039b5cd81de7442c594f3338 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 21 Aug 2017 11:42:28 -0500 Subject: [PATCH 078/703] Release: Add instruction to remove all non-tracked files --- RELEASE.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index 0aa7e98f..8dedf615 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,6 +1,8 @@ To release a new version of qtpy on PyPI: -* git pull +* git fetch upstream && git merge upstream/master + +* git clean -xfdi * Update CHANGELOG.md From 0f79d83599a14cc1a31cee0b62c3b69bd3be9676 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 21 Aug 2017 11:46:01 -0500 Subject: [PATCH 079/703] Update Changelog --- CHANGELOG.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23069717..177aed02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # History of changes +## Version 1.3.1 (2017-08-21) + +### Bugs fixed + +**Issues** + +* [Issue 129](https://github.com/spyder-ide/qtpy/issues/129) - Spurious cache files in PyPI tarball +* [Issue 119](https://github.com/spyder-ide/qtpy/issues/119) - Importing qtpy should not raise exceptions + +In this release 2 issues were closed + +**Pull requests** + +* [PR 130](https://github.com/spyder-ide/qtpy/pull/130) - PR: No cache files included in the release tarball +* [PR 126](https://github.com/spyder-ide/qtpy/pull/126) - PR: Remove Quantified Code badge because the service doesn't exist anymore +* [PR 121](https://github.com/spyder-ide/qtpy/pull/121) - PR: Warn if QHeaderView deprecated methods are used + +In this release 3 pull requests were merged + + +---- + + ## Version 1.3 (2017-08-12) ### New features From 38e2430e3cd426302e134ae354afd834dc5fc0a7 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 21 Aug 2017 11:46:45 -0500 Subject: [PATCH 080/703] Release 1.3.1 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index d48e8a4b..24706b18 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 4, 0, 'dev0') +version_info = (1, 3, 1) __version__ = '.'.join(map(str, version_info)) From f4f66e58e342909433a74f0c590fd9c5515041a3 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 21 Aug 2017 11:49:00 -0500 Subject: [PATCH 081/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 24706b18..d48e8a4b 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 3, 1) +version_info = (1, 4, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 2ece399552cf4eb6b0b5e67127efbcabca8e410f Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Sat, 19 Aug 2017 12:00:45 -0500 Subject: [PATCH 082/703] Raise warnings when importing QtHelp or QtMultimedia with PySide2 --- qtpy/QtHelp.py | 5 ++++- qtpy/QtMultimedia.py | 6 ++++-- qtpy/__init__.py | 3 +++ qtpy/tests/test_qthelp.py | 20 ++++++++++++++++++-- qtpy/tests/test_qtmultimedia.py | 18 +++++++++++++++++- 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index 7830d412..abe0aae9 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -7,17 +7,20 @@ """QtHelp Wrapper.""" +import warnings + from . import PYQT5 from . import PYQT4 from . import PYSIDE from . import PYSIDE2 +from . import PythonQtWarning if PYQT5: from PyQt5.QtHelp import * elif PYSIDE2: # Current wheels don't have this module # from PySide2.QtHelp - pass + warnings.warn("QtHelp binding is missing in PySide2", PythonQtWarning) elif PYQT4: from PyQt4.QtHelp import * elif PYSIDE: diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index a20cc3bd..47dd090f 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -1,15 +1,17 @@ +import warnings + from . import PYQT5 from . import PYQT4 from . import PYSIDE from . import PYSIDE2 - +from . import PythonQtWarning if PYQT5: from PyQt5.QtMultimedia import * elif PYSIDE2: # Current wheels don't have this module # from PySide2.QtMultimedia import * - pass + warnings.warn("QtMultimedia binding is missing in PySide2", PythonQtWarning) elif PYQT4: from PyQt4.QtMultimedia import * from PyQt4.QtGui import QSound diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 1ade9a8c..5aea6b31 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -98,6 +98,9 @@ class PythonQtError(Exception): """Error raise if no bindings could be selected""" pass +class PythonQtWarning(Warning): + """Warning if some features are not implemented in a binding.""" + pass if API in PYQT5_API: try: diff --git a/qtpy/tests/test_qthelp.py b/qtpy/tests/test_qthelp.py index c548ebc0..07ee1e0f 100644 --- a/qtpy/tests/test_qthelp.py +++ b/qtpy/tests/test_qthelp.py @@ -1,14 +1,17 @@ """Test for QtHelp namespace.""" from __future__ import absolute_import +import warnings import pytest -from qtpy import PYSIDE2, QtHelp +from qtpy import PYSIDE2, PythonQtWarning -@pytest.mark.skipif(PYSIDE2, reason="QtHelp bind is missing in PySide2") +@pytest.mark.skipif(PYSIDE2, reason="QtHelp binding is missing in PySide2") def test_qthelp(): """Test the qtpy.QtHelp namespace.""" + from qtpy import QtHelp + assert QtHelp.QHelpContentItem is not None assert QtHelp.QHelpContentModel is not None assert QtHelp.QHelpContentWidget is not None @@ -20,3 +23,16 @@ def test_qthelp(): assert QtHelp.QHelpSearchQuery is not None assert QtHelp.QHelpSearchQueryWidget is not None assert QtHelp.QHelpSearchResultWidget is not None + + +@pytest.mark.skipif(not PYSIDE2, reason="Only runs in not implemented bindings") +def test_qthelp_not_implemented(): + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Try to import QtHelp. + from qtpy import QtHelp + + assert len(w) == 1 + assert issubclass(w[-1].category, PythonQtWarning) + assert "missing" in str(w[-1].message) diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 5561e77b..fcebed9c 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -1,14 +1,30 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2, QtMultimedia +import warnings +from qtpy import PYSIDE2, PythonQtWarning @pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" + from qtpy import QtMultimedia + assert QtMultimedia.QAbstractVideoBuffer is not None assert QtMultimedia.QAudio is not None assert QtMultimedia.QAudioDeviceInfo is not None assert QtMultimedia.QAudioInput is not None assert QtMultimedia.QSound is not None + + +@pytest.mark.skipif(not PYSIDE2, reason="Only runs in not implemented bindings") +def test_qtmultimedia_not_implemented(): + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Try to import QtMultimedia. + from qtpy import QtMultimedia + + assert len(w) == 1 + assert issubclass(w[-1].category, PythonQtWarning) + assert "missing" in str(w[-1].message) From a7ea5afc7bbed547358ce0206fc221841f0076f4 Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Thu, 24 Aug 2017 18:29:27 -0500 Subject: [PATCH 083/703] Add missing atributte to QStandardPaths --- qtpy/QtCore.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index c1d81e8f..f605bec4 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -58,6 +58,7 @@ class QStandardPaths(): HomeLocation = _QDesktopServices.HomeLocation DataLocation = _QDesktopServices.DataLocation CacheLocation = _QDesktopServices.CacheLocation + writableLocation = _QDesktopServices.storageLocation # Those are imported from `import *` del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR, qInstallMsgHandler @@ -88,6 +89,7 @@ class QStandardPaths(): HomeLocation = _QDesktopServices.HomeLocation DataLocation = _QDesktopServices.DataLocation CacheLocation = _QDesktopServices.CacheLocation + writableLocation = _QDesktopServices.storageLocation import PySide.QtCore __version__ = PySide.QtCore.__version__ From b355784108a3321696c6bcf2573bd6f656f9814a Mon Sep 17 00:00:00 2001 From: Rafael Laverde Date: Thu, 24 Aug 2017 18:30:28 -0500 Subject: [PATCH 084/703] Don't remove deprecated attributes from QDesktopServices, raise a warning instead. --- qtpy/QtGui.py | 36 ++++++++++++++++++++---- qtpy/tests/test_qdesktopservice_split.py | 21 +++++++++++--- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 3dfaff07..071be132 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -12,6 +12,7 @@ exposed here. Therefore, you need to treat/use this package as if it were the ``PyQt5.QtGui`` module. """ +import warnings from . import PYQT5, PYQT4, PYSIDE, PYSIDE2, PythonQtError @@ -75,10 +76,21 @@ from PyQt4.QtGui import QDesktopServices as _QDesktopServices class QDesktopServices(): - openUrl = _QDesktopServices.openUrl - setUrlHandler = _QDesktopServices.setUrlHandler - unsetUrlHandler = _QDesktopServices.unsetUrlHandler + openUrl = _QDesktopServices.openUrl + setUrlHandler = _QDesktopServices.setUrlHandler + unsetUrlHandler = _QDesktopServices.unsetUrlHandler + def __getattr__(self, name): + attr = getattr(_QDesktopServices, name) + + new_name = name + if name == 'storageLocation': + new_name = 'writableLocation' + warnings.warn(("Warning QDesktopServices.{} is deprecated in Qt5" + "we recommend you use QDesktopServices.{} instead").format(name, new_name), + DeprecationWarning) + return attr + QDesktopServices = QDesktopServices() elif PYSIDE: from PySide.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, @@ -126,8 +138,20 @@ class QDesktopServices(): from PySide.QtGui import QDesktopServices as _QDesktopServices class QDesktopServices(): - openUrl = _QDesktopServices.openUrl - setUrlHandler = _QDesktopServices.setUrlHandler - unsetUrlHandler = _QDesktopServices.unsetUrlHandler + openUrl = _QDesktopServices.openUrl + setUrlHandler = _QDesktopServices.setUrlHandler + unsetUrlHandler = _QDesktopServices.unsetUrlHandler + + def __getattr__(self, name): + attr = getattr(_QDesktopServices, name) + + new_name = name + if name == 'storageLocation': + new_name = 'writableLocation' + warnings.warn(("Warning QDesktopServices.{} is deprecated in Qt5" + "we recommend you use QDesktopServices.{} instead").format(name, new_name), + DeprecationWarning) + return attr + QDesktopServices = QDesktopServices() else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qdesktopservice_split.py b/qtpy/tests/test_qdesktopservice_split.py index bf12927d..6883e5c8 100644 --- a/qtpy/tests/test_qdesktopservice_split.py +++ b/qtpy/tests/test_qdesktopservice_split.py @@ -1,7 +1,8 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2 +import warnings +from qtpy import PYQT4, PYSIDE, PYSIDE2 """Test QDesktopServices split in Qt5.""" @@ -23,6 +24,18 @@ def test_qdesktopservice(): assert QDesktopServices.setUrlHandler is not None - # Attributes from QStandardPaths shouldn't be in QDesktopServices - with pytest.raises(AttributeError) as excinfo: - QDesktopServices.StandardLocation \ No newline at end of file + +@pytest.mark.skipif(not (PYQT4 or PYSIDE), reason="Warning is only raised in old bindings") +def test_qdesktopservice_qt4_pyside(): + from qtpy.QtGui import QDesktopServices + # Attributes from QStandardPaths should raise a warning when imported + # from QDesktopServices + with warnings.catch_warnings(record=True) as w: + # Cause all warnings to always be triggered. + warnings.simplefilter("always") + # Try to import QtHelp. + QDesktopServices.StandardLocation + + assert len(w) == 1 + assert issubclass(w[-1].category, DeprecationWarning) + assert "deprecated" in str(w[-1].message) From 5483723f3bc14009f03145affea30c523aec875f Mon Sep 17 00:00:00 2001 From: Daniel Edler Date: Wed, 15 Nov 2017 23:12:06 +0100 Subject: [PATCH 085/703] Add QtSql wrapper (incl. test) --- qtpy/QtSql.py | 28 ++++++++++++++++++++++++++++ qtpy/tests/test_qtsql.py | 26 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 qtpy/QtSql.py create mode 100644 qtpy/tests/test_qtsql.py diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py new file mode 100644 index 00000000..ee6cb0f5 --- /dev/null +++ b/qtpy/QtSql.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtSvg classes and functions.""" + +# Local imports +from . import PYQT4 +from . import PYSIDE2 +from . import PYQT5 +from . import PYSIDE +from . import PythonQtError + +if PYQT5: + from PyQt5.QtSql import * +elif PYSIDE2: + from PySide2.QtSql import * +elif PYQT4: + from PyQt4.QtSql import * +elif PYSIDE: + from PySide.QtSql import * +else: + raise PythonQtError('No Qt bindings could be found') + +del PYQT4, PYQT5, PYSIDE, PYSIDE2 diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py new file mode 100644 index 00000000..6df7f5ae --- /dev/null +++ b/qtpy/tests/test_qtsql.py @@ -0,0 +1,26 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2 + +@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") +def test_qtsvg(): + """Test the qtpy.QtSql namespace""" + from qtpy import QtSql + + assert QtSql.QSqlDatabase is not None + assert QtSql.QSqlDriverCreator is not None + assert QtSql.QSqlDriverCreatorBase is not None + assert QtSql.QSqlDriver is not None + assert QtSql.QSqlDriverPlugin is not None + assert QtSql.QSqlError is not None + assert QtSql.QSqlField is not None + assert QtSql.QSqlIndex is not None + assert QtSql.QSqlQuery is not None + assert QtSql.QSqlRecord is not None + assert QtSql.QSqlResult is not None + assert QtSql.QSqlQueryModel is not None + assert QtSql.QSqlRelationalDelegate is not None + assert QtSql.QSqlRelation is not None + assert QtSql.QSqlRelationalTableModel is not None + assert QtSql.QSqlTableModel is not None From 5004558ed29f39d4baecb6b2f5223fe9d101f2ef Mon Sep 17 00:00:00 2001 From: Daniel Edler Date: Thu, 16 Nov 2017 00:02:56 +0100 Subject: [PATCH 086/703] Remove QtSql.QSqlDriverCreator from test (it's a C++ template) --- qtpy/QtSql.py | 2 +- qtpy/tests/test_qtsql.py | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index ee6cb0f5..010a60dc 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -5,7 +5,7 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtSvg classes and functions.""" +"""Provides QtSql classes and functions.""" # Local imports from . import PYQT4 diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index 6df7f5ae..4e091181 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -1,15 +1,12 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2 +from qtpy import QtSql -@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_qtsvg(): """Test the qtpy.QtSql namespace""" - from qtpy import QtSql - assert QtSql.QSqlDatabase is not None - assert QtSql.QSqlDriverCreator is not None + # assert QtSql.QSqlDriverCreator is not None assert QtSql.QSqlDriverCreatorBase is not None assert QtSql.QSqlDriver is not None assert QtSql.QSqlDriverPlugin is not None From 14c7f4295701ffbb5c7cd14062a43cb8c86d57de Mon Sep 17 00:00:00 2001 From: Daniel Edler Date: Thu, 16 Nov 2017 00:24:26 +0100 Subject: [PATCH 087/703] Remove another class (QSqlDriverPlugin) from test --- qtpy/tests/test_qtsql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index 4e091181..f807e1f4 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -9,7 +9,7 @@ def test_qtsvg(): # assert QtSql.QSqlDriverCreator is not None assert QtSql.QSqlDriverCreatorBase is not None assert QtSql.QSqlDriver is not None - assert QtSql.QSqlDriverPlugin is not None + #assert QtSql.QSqlDriverPlugin is not None assert QtSql.QSqlError is not None assert QtSql.QSqlField is not None assert QtSql.QSqlIndex is not None From ce24651b2d0935bff0f3fdb9192244b40af20bdf Mon Sep 17 00:00:00 2001 From: Daniel Edler Date: Thu, 16 Nov 2017 00:42:25 +0100 Subject: [PATCH 088/703] Rename test_qtsvg() to test_qtsql() in test_qtsql.py --- qtpy/tests/test_qtsql.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index f807e1f4..75af94a0 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -3,7 +3,7 @@ import pytest from qtpy import QtSql -def test_qtsvg(): +def test_qtsql(): """Test the qtpy.QtSql namespace""" assert QtSql.QSqlDatabase is not None # assert QtSql.QSqlDriverCreator is not None From 87f805dfa5f8f8051b1465c737b14b65032aea7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Pe=C3=B1a-Castellanos?= Date: Wed, 22 Nov 2017 18:18:24 -0500 Subject: [PATCH 089/703] Update README.md --- README.md | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/README.md b/README.md index 8f7aa7e3..4783d3d9 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Copyright © 2009- The Spyder Development Team. [![license](https://img.shields.io/pypi/l/qtpy.svg)](./LICENSE) [![pypi version](https://img.shields.io/pypi/v/qtpy.svg)](https://pypi.python.org/pypi/qtpy) [![Join the chat at https://gitter.im/spyder-ide/public](https://badges.gitter.im/spyder-ide/spyder.svg)](https://gitter.im/spyder-ide/public) +[![OpenCollective Backers](https://opencollective.com/spyder/backers/badge.svg?color=blue)](#backers) +[![OpenCollective Sponsors](https://opencollective.com/spyder/sponsors/badge.svg?color=blue)](#sponsors) ## Build status [![Build status](https://ci.appveyor.com/api/projects/status/62y6i02vhn4hefg0/branch/master?svg=true)](https://ci.appveyor.com/project/spyder-ide/qtpy/branch/master) @@ -13,6 +15,27 @@ Copyright © 2009- The Spyder Development Team. [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/spyder-ide/qtpy/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/spyder-ide/qtpy/?branch=master) +---- + +## Important Announcement: Spyder is unfunded! + +Since mid November/2017, [Anaconda, Inc](https://www.anaconda.com/) has +stopped funding Spyder development, after doing it for the past 18 +months. Because of that, development will focus from now on maintaining +Spyder 3 at a much slower pace than before. + +If you want to contribute to maintain Spyder, please consider donating at + +https://opencollective.com/spyder + +We appreciate all the help you can provide us and can't thank you enough for +supporting the work of Spyder devs and Spyder development. + +If you want to know more about this, please read this +[page](https://github.com/spyder-ide/spyder/wiki/Anaconda-stopped-funding-Spyder). + +---- + ## Description **QtPy** is a small abstraction layer that lets you @@ -68,3 +91,20 @@ or ```bash conda install qtpy ``` + +## Contributing + +Everyone is welcome to contribute! + +## Backers + +Support us with a monthly donation and help us continue our activities. + +[![Backers](https://opencollective.com/spyder/backers.svg)](https://opencollective.com/spyder#support) + + +## Sponsors + +Become a sponsor to get your logo on our README on Github. + +[![Sponsors](https://opencollective.com/spyder/sponsors.svg)](https://opencollective.com/spyder#support) From f500e4a198fca66c616477b0700a4d4916b1498b Mon Sep 17 00:00:00 2001 From: Daniel Edler Date: Mon, 27 Nov 2017 20:14:04 +0100 Subject: [PATCH 090/703] Follow style guidelines; Explain the commented lines of code --- qtpy/QtSql.py | 6 +----- qtpy/tests/test_qtsql.py | 5 +++-- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 010a60dc..98520bef 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -8,11 +8,7 @@ """Provides QtSql classes and functions.""" # Local imports -from . import PYQT4 -from . import PYSIDE2 -from . import PYQT5 -from . import PYSIDE -from . import PythonQtError +from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtSql import * diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index 75af94a0..1e7404ff 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -6,10 +6,8 @@ def test_qtsql(): """Test the qtpy.QtSql namespace""" assert QtSql.QSqlDatabase is not None - # assert QtSql.QSqlDriverCreator is not None assert QtSql.QSqlDriverCreatorBase is not None assert QtSql.QSqlDriver is not None - #assert QtSql.QSqlDriverPlugin is not None assert QtSql.QSqlError is not None assert QtSql.QSqlField is not None assert QtSql.QSqlIndex is not None @@ -21,3 +19,6 @@ def test_qtsql(): assert QtSql.QSqlRelation is not None assert QtSql.QSqlRelationalTableModel is not None assert QtSql.QSqlTableModel is not None + + # Following modules are not (yet) part of any wrapper: + # QSqlDriverCreator, QSqlDriverPlugin From 49782e2f4ba3aca0d31e01eafb83a513d14a99a7 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 13 Feb 2018 14:13:45 -0500 Subject: [PATCH 091/703] Testing: Pin PyQt5 to 5.9.2 because 5.10 is generating segfaults --- ci/test-pyqt5.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/test-pyqt5.sh b/ci/test-pyqt5.sh index 8f3cad3f..5fb0bd84 100755 --- a/ci/test-pyqt5.sh +++ b/ci/test-pyqt5.sh @@ -7,7 +7,8 @@ source activate test if [ "$CIRCLE_NODE_INDEX" != "3" ]; then conda install -q qt=5.* pyqt=5.* else - pip install -q pyqt5 + # We are getting segfaults in 5.10 + pip install -q pyqt5==5.9.2 fi python qtpy/tests/runtests.py From 9c67d53a731f8fa6a59804b981e204cb659a56b8 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 13 Feb 2018 17:24:21 +0100 Subject: [PATCH 092/703] If a qt binding is already imported, then use it. --- qtpy/__init__.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 1ade9a8c..a0a336d3 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -10,12 +10,13 @@ **QtPy** is a shim over the various Python Qt bindings. It is used to write Qt binding indenpendent libraries or applications. -The shim will automatically select the first available API (PyQt5, PyQt4 and -finally PySide). +If one of the APIs has already been imported, then it will be used. -You can force the use of one specific bindings (e.g. if your application is -using one specific bindings and you need to use library that use QtPy) by -setting up the ``QT_API`` environment variable. +Otherwise, the shim will automatically select the first available API (PyQt5, +PySide2, PyQt4 and finally PySide); in that case, you can force the use of one +specific bindings (e.g. if your application is using one specific bindings and +you need to use library that use QtPy) by setting up the ``QT_API`` environment +variable. PyQt5 ===== @@ -62,6 +63,7 @@ """ import os +import sys # Version of QtPy from ._version import __version__ @@ -99,6 +101,16 @@ class PythonQtError(Exception): pass +if 'PyQt5' in sys.modules: + API = 'pyqt5' +elif 'PySide2' in sys.modules: + API = 'pyside2' +elif 'PyQt4' in sys.modules: + API = 'pyqt4' +elif 'PySide' in sys.modules: + API = 'pyside' + + if API in PYQT5_API: try: from PyQt5.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore From edb6bfc07556b3ba4b839c0c468427bf186d72cc Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 11 Mar 2018 16:46:10 -0500 Subject: [PATCH 093/703] Update Changelog --- CHANGELOG.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 177aed02..947cc298 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,36 @@ # History of changes +## Version 1.4 (2018-03-11) + +### New features + +* Add support for QtHelp +* Add support for QtSql +* Use already imported bindings + +### Issues Closed + +* [Issue 138](https://github.com/spyder-ide/qtpy/issues/138) - If one binding has already been imported, then qtpy should just use it ([PR 139](https://github.com/spyder-ide/qtpy/pull/139)) +* [Issue 135](https://github.com/spyder-ide/qtpy/issues/135) - Add Wrapper for QtSql [feature request] ([PR 136](https://github.com/spyder-ide/qtpy/pull/136)) +* [Issue 131](https://github.com/spyder-ide/qtpy/issues/131) - Methods missing from QStandardPaths when QT_API=pyqt4 +* [Issue 127](https://github.com/spyder-ide/qtpy/issues/127) - Add Wrapper for QtHelp [feature request] ([PR 128](https://github.com/spyder-ide/qtpy/pull/128)) + +In this release 4 issues were closed. + +### Pull Requests Merged + +* [PR 140](https://github.com/spyder-ide/qtpy/pull/140) - PR: Pin PyQt5 to 5.9.2 in CircleCI because 5.10 is generating segfaults +* [PR 139](https://github.com/spyder-ide/qtpy/pull/139) - PR: If a Qt binding is already imported, then use it. ([138](https://github.com/spyder-ide/qtpy/issues/138)) +* [PR 136](https://github.com/spyder-ide/qtpy/pull/136) - PR: Add QtSql wrapper (incl. test) ([135](https://github.com/spyder-ide/qtpy/issues/135)) +* [PR 132](https://github.com/spyder-ide/qtpy/pull/132) - PR: Changes to QDesktop split +* [PR 128](https://github.com/spyder-ide/qtpy/pull/128) - PR: Add QtHelp Wrapper ([127](https://github.com/spyder-ide/qtpy/issues/127)) + +In this release 5 pull requests were closed. + + +---- + + ## Version 1.3.1 (2017-08-21) ### Bugs fixed From cfa82d514da4a771841fb6a26f445cc655b152e3 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 11 Mar 2018 16:47:15 -0500 Subject: [PATCH 094/703] Release 1.4.0 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index d48e8a4b..801136b4 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 4, 0, 'dev0') +version_info = (1, 4, 0) __version__ = '.'.join(map(str, version_info)) From b112fdce5d94c6234a33527b2910251c349d9042 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 11 Mar 2018 16:49:22 -0500 Subject: [PATCH 095/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 801136b4..7907ee4e 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 4, 0) +version_info = (1, 5, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 714845a575c9b8cf7417f2b20172201fa14938ea Mon Sep 17 00:00:00 2001 From: Gonzalo Pena-Castellanos Date: Tue, 17 Apr 2018 11:13:53 -0500 Subject: [PATCH 096/703] Avoid using PyQt5.Qt, which imports unneeded stuff and forces discrete GPU on OSX --- qtpy/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 9367c2ac..b22366fc 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -116,8 +116,8 @@ class PythonQtWarning(Warning): if API in PYQT5_API: try: - from PyQt5.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore - from PyQt5.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore + from PyQt5.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore + from PyQt5.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore PYSIDE_VERSION = None except ImportError: API = os.environ['QT_API'] = 'pyside2' From a7b04c6015eb63c982dcbc357e3f772f92821fda Mon Sep 17 00:00:00 2001 From: Gonzalo Pena-Castellanos Date: Tue, 17 Apr 2018 14:21:40 -0500 Subject: [PATCH 097/703] Update before installing apt packages --- circle.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/circle.yml b/circle.yml index 1889d8e9..9f07b65c 100644 --- a/circle.yml +++ b/circle.yml @@ -15,6 +15,7 @@ machine: dependencies: pre: # We need this for QtMultimedia in 5.8 + - sudo apt-get update -y; true - sudo apt-get install libpulse-dev # For PySide2 - sudo apt-get autoremove libqt5* From 13b46b80d748f2150a129755a297b2c5075d58ee Mon Sep 17 00:00:00 2001 From: Daniel Pizetta Date: Fri, 27 Apr 2018 10:36:18 -0300 Subject: [PATCH 098/703] Add a warning if API is changed automatically, closes #145 --- qtpy/__init__.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index b22366fc..11d3d0e4 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -64,6 +64,7 @@ import os import sys +import warnings # Version of QtPy from ._version import __version__ @@ -88,7 +89,9 @@ # Setting a default value for QT_API os.environ.setdefault(QT_API, 'pyqt5') + API = os.environ[QT_API].lower() +initial_api = API assert API in (PYQT5_API + PYQT4_API + PYSIDE_API + PYSIDE2_API) is_old_pyqt = is_pyqt46 = False @@ -168,6 +171,12 @@ class PythonQtWarning(Warning): except ImportError: raise PythonQtError('No Qt bindings could be found') +# If a correct API name is passed to QT_API and it could not be found, +# switches to another and informs through the warning +if API != initial_api: + warnings.warn('Selected binding "{}" could not be found, ' + 'using "{}"'.format(initial_api, API), RuntimeWarning) + API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4', 'pyside': 'PySide', 'pyside2':'PySide2'}[API] From 487e23b00632896bfce260fec1ed69ddaa05c356 Mon Sep 17 00:00:00 2001 From: stonebig Date: Sat, 28 Apr 2018 14:46:51 +0200 Subject: [PATCH 099/703] better compatibility with PySide2-5.11.0a1-5.11.0-cp36-cp36m-win_amd64.whl --- qtpy/QtCore.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index f605bec4..f8b2155c 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -24,6 +24,8 @@ del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR elif PYSIDE2: from PySide2.QtCore import * + from PySide2.QtGui import QStringListModel + elif PYQT4: from PyQt4.QtCore import * # Those are things we inherited from Spyder that fix crazy crashes under From 0cdde064d8572bf8b60e656df29d6d010038f308 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 12:06:58 -0500 Subject: [PATCH 100/703] Update Changelog --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 947cc298..b8715e0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # History of changes +## Version 1.4.1 (2018-04-28) + +### New features + +* Show a warning when QT_API is changed automatically by qtpy. + +### Issues Closed + +* [Issue 145](https://github.com/spyder-ide/qtpy/issues/145) - Raise a warning if QT_API value is changed automatically ([PR 146](https://github.com/spyder-ide/qtpy/pull/146)) +* [Issue 142](https://github.com/spyder-ide/qtpy/issues/142) - On OSX qtpy applications are forcing discrete graphics ([PR 143](https://github.com/spyder-ide/qtpy/pull/143)) + +In this release 2 issues were closed. + +### Pull Requests Merged + +* [PR 147](https://github.com/spyder-ide/qtpy/pull/147) - PR: Add better compatibility with PySide2 +* [PR 146](https://github.com/spyder-ide/qtpy/pull/146) - PR: Add a warning if API is changed automatically ([145](https://github.com/spyder-ide/qtpy/issues/145)) +* [PR 143](https://github.com/spyder-ide/qtpy/pull/143) - PR: Avoid using PyQt5.Qt, which imports unneeded stuff and forces discrete GPU on OSX ([142](https://github.com/spyder-ide/qtpy/issues/142)) + +In this release 3 pull requests were closed. + + +---- + + ## Version 1.4 (2018-03-11) ### New features From 37f3e381b5ae151261ac8b04445e662c90608896 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 12:07:44 -0500 Subject: [PATCH 101/703] Release 1.4.1 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 7907ee4e..b099c435 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 5, 0, 'dev0') +version_info = (1, 4, 1) __version__ = '.'.join(map(str, version_info)) From 193d697fd9feea4b936dbf8a2ce717f975538d34 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 12:10:33 -0500 Subject: [PATCH 102/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index b099c435..7907ee4e 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 4, 1) +version_info = (1, 5, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 1e1a258c4711b8800e3ee717f9139c64549a1ab6 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 12:42:24 -0500 Subject: [PATCH 103/703] Remove internal conda recipe --- conda.recipe/build.sh | 3 --- conda.recipe/meta.yaml | 28 ---------------------------- 2 files changed, 31 deletions(-) delete mode 100644 conda.recipe/build.sh delete mode 100644 conda.recipe/meta.yaml diff --git a/conda.recipe/build.sh b/conda.recipe/build.sh deleted file mode 100644 index 2981b577..00000000 --- a/conda.recipe/build.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -$PYTHON setup.py install --single-version-externally-managed --root=/ diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml deleted file mode 100644 index 0b25c5cd..00000000 --- a/conda.recipe/meta.yaml +++ /dev/null @@ -1,28 +0,0 @@ -package: - name: qtpy - version: {{ GIT_DESCRIBE_TAG|replace('v', '') }} - -build: - noarch_python: True - number: {{ GIT_DESCRIBE_NUMBER|int }} - -source: - git_url: ../ - -requirements: - build: - - python - - setuptools - run: - - python - -test: - imports: - - qtpy - requires: - - pyqt - -about: - home: https://github.com/spyder-ide/qtpy - license: MIT - summary: Provides an uniform layer to support PyQt4, PyQt5 and PySide with a single codebase From d71a9b9d7bbc8a2d103d4848e4fb82426d0d08f1 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 13:12:50 -0500 Subject: [PATCH 104/703] Use Qt official wheels to run tests for PySide2 --- ci/test-pyqt4.sh | 2 +- ci/test-pyside.sh | 2 +- ci/test-pyside2.sh | 21 +++++++-------------- circle.yml | 9 +-------- 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/ci/test-pyqt4.sh b/ci/test-pyqt4.sh index cc68554c..591fb402 100755 --- a/ci/test-pyqt4.sh +++ b/ci/test-pyqt4.sh @@ -3,7 +3,7 @@ export PATH="$HOME/miniconda/bin:$PATH" source activate test -# We use container 3 to test with pip and pyqt5 +# We use container 3 to test with pip if [ "$CIRCLE_NODE_INDEX" = "3" ]; then exit 0 else diff --git a/ci/test-pyside.sh b/ci/test-pyside.sh index 5aefe1bf..52327549 100755 --- a/ci/test-pyside.sh +++ b/ci/test-pyside.sh @@ -3,7 +3,7 @@ export PATH="$HOME/miniconda/bin:$PATH" source activate test -# We use container 3 to test with pip and pyqt5 +# We use container 3 to test with pip if [ "$CIRCLE_NODE_INDEX" = "3" ]; then exit 0 else diff --git a/ci/test-pyside2.sh b/ci/test-pyside2.sh index 82886aef..a5521e0e 100755 --- a/ci/test-pyside2.sh +++ b/ci/test-pyside2.sh @@ -4,27 +4,20 @@ export PATH="$HOME/miniconda/bin:$PATH" source activate test # Download PySide2 wheel -wget -q https://bintray.com/fredrikaverpil/pyside2-wheels/download_file?file_path=ubuntu14.04%2FPySide2-2.0.0.dev0-cp27-none-linux_x86_64.whl -O PySide2-2.0.0.dev0-cp27-none-linux_x86_64.whl +wget -q http://download.qt.io/snapshots/ci/pyside/5.11/latest/pyside2/PySide2-5.11.0a1-5.11.0-cp27-cp27mu-linux_x86_64.whl -O PySide2-5.11.0a1-5.11.0-cp27-cp27mu-linux_x86_64.whl +wget -q http://download.qt.io/snapshots/ci/pyside/5.11/latest/pyside2/PySide2-5.11.0a1-5.11.0-cp36-cp36m-linux_x86_64.whl -O PySide2-5.11.0a1-5.11.0-cp36-cp36m-linux_x86_64.whl -# We only use container 0 for PySide2 +# We use container 3 to test with pip if [ "$CIRCLE_NODE_INDEX" = "0" ]; then conda remove -q qt pyqt - pip install PySide2-2.0.0.dev0-cp27-none-linux_x86_64.whl + pip install PySide2-5.11.0a1-5.11.0-cp27-cp27mu-linux_x86_64.whl +elif [ "$CIRCLE_NODE_INDEX" = "3" ]; then + pip uninstall -q -y pyqt5 sip + pip install PySide2-5.11.0a1-5.11.0-cp36-cp36m-linux_x86_64.whl else exit 0 fi -# Make symlinks for Qt libraries (else imports fail) -pushd "$HOME/miniconda/envs/test/lib/python2.7/site-packages/PySide2/" - -for file in `ls Qt*x86_64-linux-gnu.so` -do - symlink=${file%.x86_64-linux-gnu.so}.so - ln -s $file $symlink -done - -popd - python qtpy/tests/runtests.py # Force quitting if exit status of runtests.py was not 0 diff --git a/circle.yml b/circle.yml index 9f07b65c..51d21c3e 100644 --- a/circle.yml +++ b/circle.yml @@ -4,7 +4,7 @@ machine: # Used by test scripts TEST_CI: "True" # Python versions to test (Maximum of 4 different versions for now) - PY_VERSIONS: "2.7 3.5 3.6 3.5" + PY_VERSIONS: "2.7 3.5 3.6 3.6" # For Coveralls COVERALLS_REPO_TOKEN: xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5 # Used by astropy-ci helpers @@ -17,13 +17,6 @@ dependencies: # We need this for QtMultimedia in 5.8 - sudo apt-get update -y; true - sudo apt-get install libpulse-dev - # For PySide2 - - sudo apt-get autoremove libqt5* - - sudo apt-get install apt -y - - sudo add-apt-repository -y ppa:beineri/opt-qt562-trusty - - sudo apt-get update -y; true - - sudo apt-get install -q qt56webengine - - sudo rsync -a /opt/qt56/ /usr/ override: # First convert PY_VERSIONS to an array and then select the Python version From 6998386b518df372d04934c89ed56b5fce7d6e10 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 13:37:04 -0500 Subject: [PATCH 105/703] Remove warnings for PySide2 in QtMultimedia and QtHelp --- qtpy/QtHelp.py | 5 +---- qtpy/QtMultimedia.py | 5 +---- qtpy/tests/test_qthelp.py | 16 ---------------- qtpy/tests/test_qtmultimedia.py | 16 ---------------- 4 files changed, 2 insertions(+), 40 deletions(-) diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index abe0aae9..ca9d93dd 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -13,14 +13,11 @@ from . import PYQT4 from . import PYSIDE from . import PYSIDE2 -from . import PythonQtWarning if PYQT5: from PyQt5.QtHelp import * elif PYSIDE2: - # Current wheels don't have this module - # from PySide2.QtHelp - warnings.warn("QtHelp binding is missing in PySide2", PythonQtWarning) + from PySide2.QtHelp import * elif PYQT4: from PyQt4.QtHelp import * elif PYSIDE: diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index 47dd090f..9015ece9 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -4,14 +4,11 @@ from . import PYQT4 from . import PYSIDE from . import PYSIDE2 -from . import PythonQtWarning if PYQT5: from PyQt5.QtMultimedia import * elif PYSIDE2: - # Current wheels don't have this module - # from PySide2.QtMultimedia import * - warnings.warn("QtMultimedia binding is missing in PySide2", PythonQtWarning) + from PySide2.QtMultimedia import * elif PYQT4: from PyQt4.QtMultimedia import * from PyQt4.QtGui import QSound diff --git a/qtpy/tests/test_qthelp.py b/qtpy/tests/test_qthelp.py index 07ee1e0f..2b70ca75 100644 --- a/qtpy/tests/test_qthelp.py +++ b/qtpy/tests/test_qthelp.py @@ -1,13 +1,10 @@ """Test for QtHelp namespace.""" from __future__ import absolute_import -import warnings import pytest -from qtpy import PYSIDE2, PythonQtWarning -@pytest.mark.skipif(PYSIDE2, reason="QtHelp binding is missing in PySide2") def test_qthelp(): """Test the qtpy.QtHelp namespace.""" from qtpy import QtHelp @@ -23,16 +20,3 @@ def test_qthelp(): assert QtHelp.QHelpSearchQuery is not None assert QtHelp.QHelpSearchQueryWidget is not None assert QtHelp.QHelpSearchResultWidget is not None - - -@pytest.mark.skipif(not PYSIDE2, reason="Only runs in not implemented bindings") -def test_qthelp_not_implemented(): - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - # Try to import QtHelp. - from qtpy import QtHelp - - assert len(w) == 1 - assert issubclass(w[-1].category, PythonQtWarning) - assert "missing" in str(w[-1].message) diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index fcebed9c..02b415ff 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -1,11 +1,8 @@ from __future__ import absolute_import import pytest -import warnings -from qtpy import PYSIDE2, PythonQtWarning -@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" from qtpy import QtMultimedia @@ -15,16 +12,3 @@ def test_qtmultimedia(): assert QtMultimedia.QAudioDeviceInfo is not None assert QtMultimedia.QAudioInput is not None assert QtMultimedia.QSound is not None - - -@pytest.mark.skipif(not PYSIDE2, reason="Only runs in not implemented bindings") -def test_qtmultimedia_not_implemented(): - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - # Try to import QtMultimedia. - from qtpy import QtMultimedia - - assert len(w) == 1 - assert issubclass(w[-1].category, PythonQtWarning) - assert "missing" in str(w[-1].message) From 8c68617744f3b430bbc364b9353e58033264abb0 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 13:42:35 -0500 Subject: [PATCH 106/703] Run tests for QtSvg and QtNetwork in PySide2 --- qtpy/tests/test_qtnetwork.py | 2 +- qtpy/tests/test_qtsvg.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index 8f4b71f4..1e1afa23 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -4,7 +4,7 @@ from qtpy import PYSIDE, PYSIDE2, QtNetwork -@pytest.mark.skipif(PYSIDE2 or PYSIDE, reason="It fails on PySide/PySide2") +@pytest.mark.skipif(PYSIDE, reason="It fails on PySide/PySide2") def test_qtnetwork(): """Test the qtpy.QtNetwork namespace""" assert QtNetwork.QAbstractNetworkCache is not None diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py index 51b743bb..74d8522e 100644 --- a/qtpy/tests/test_qtsvg.py +++ b/qtpy/tests/test_qtsvg.py @@ -1,9 +1,8 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2 -@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") + def test_qtsvg(): """Test the qtpy.QtSvg namespace""" from qtpy import QtSvg @@ -11,4 +10,4 @@ def test_qtsvg(): assert QtSvg.QGraphicsSvgItem is not None assert QtSvg.QSvgGenerator is not None assert QtSvg.QSvgRenderer is not None - assert QtSvg.QSvgWidget is not None \ No newline at end of file + assert QtSvg.QSvgWidget is not None From 2920ed2a4add47e61a77db8c084fe84bd3dfc36c Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 13:49:50 -0500 Subject: [PATCH 107/703] Revert change in tests for QtNetwork --- qtpy/tests/test_qtnetwork.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index 1e1afa23..8f4b71f4 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -4,7 +4,7 @@ from qtpy import PYSIDE, PYSIDE2, QtNetwork -@pytest.mark.skipif(PYSIDE, reason="It fails on PySide/PySide2") +@pytest.mark.skipif(PYSIDE2 or PYSIDE, reason="It fails on PySide/PySide2") def test_qtnetwork(): """Test the qtpy.QtNetwork namespace""" assert QtNetwork.QAbstractNetworkCache is not None From b98d0a8c29d95e8a2413be458b49d84589d0fc40 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 13:50:20 -0500 Subject: [PATCH 108/703] Run QDesktopServices tests in PySide2 --- qtpy/tests/test_qdesktopservice_split.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qtpy/tests/test_qdesktopservice_split.py b/qtpy/tests/test_qdesktopservice_split.py index 6883e5c8..472f2df1 100644 --- a/qtpy/tests/test_qdesktopservice_split.py +++ b/qtpy/tests/test_qdesktopservice_split.py @@ -1,12 +1,12 @@ +"""Test QDesktopServices split in Qt5.""" + from __future__ import absolute_import import pytest import warnings -from qtpy import PYQT4, PYSIDE, PYSIDE2 +from qtpy import PYQT4, PYSIDE -"""Test QDesktopServices split in Qt5.""" -@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_qstandarpath(): """Test the qtpy.QStandardPaths namespace""" from qtpy.QtCore import QStandardPaths @@ -17,7 +17,7 @@ def test_qstandarpath(): with pytest.raises(AttributeError) as excinfo: QStandardPaths.setUrlHandler -@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") + def test_qdesktopservice(): """Test the qtpy.QDesktopServices namespace""" from qtpy.QtGui import QDesktopServices From 2491bfa05751d5ac41dc199c48c17a65f9cb5165 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 28 Apr 2018 14:05:05 -0500 Subject: [PATCH 109/703] Test with PyQt5 wheels on Windows --- appveyor.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 7a878839..779ad0eb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,8 +11,6 @@ environment: PYTHON_ARCH: "64" # needs to be set for CMD_IN_ENV to succeed. If a mix # of 32 bit and 64 bit builds are needed, move this # to the matrix section. - # Used by atropy ci-helpers - CONDA_CHANNELS: "spyder-ide qttesting" matrix: # Qt4 @@ -20,8 +18,6 @@ environment: CONDA_DEPENDENCIES: "pytest pytest-cov qt=4.* pyside" - PYTHON_VERSION: "2.7" CONDA_DEPENDENCIES: "pytest pytest-cov qt=4.* pyqt=4.*" - - PYTHON_VERSION: "3.4" - CONDA_DEPENDENCIES: "pytest pytest-cov qt=4.* pyqt=4.*" - PYTHON_VERSION: "3.5" CONDA_DEPENDENCIES: "pytest pytest-cov qt=4.* pyqt=4.*" # Qt5 @@ -29,6 +25,10 @@ environment: CONDA_DEPENDENCIES: "pytest pytest-cov qt=5.* pyqt=5.*" - PYTHON_VERSION: "3.5" CONDA_DEPENDENCIES: "pytest pytest-cov qt=5.* pyqt=5.*" + - PYTHON_VERSION: "3.6" + CONDA_DEPENDENCIES: "pytest pytest-cov" + PIP_DEPENDENCIES: "pyqt5==5.9.2" + PIP_DEPENDENCIES_FLAGS: "-q" platform: -x64 From 3a755964072977917de6c0cf2542483a6bf62d68 Mon Sep 17 00:00:00 2001 From: stonebig Date: Sun, 29 Apr 2018 11:55:18 +0200 Subject: [PATCH 110/703] PySide-5.11a2 preventive change --- qtpy/QtCore.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index f8b2155c..289fcac2 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -24,8 +24,10 @@ del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR elif PYSIDE2: from PySide2.QtCore import * - from PySide2.QtGui import QStringListModel - + try: # may be limited to PySide-5.11a1 only + from PySide2.QtGui import QStringListModel + except: + pass elif PYQT4: from PyQt4.QtCore import * # Those are things we inherited from Spyder that fix crazy crashes under From f2d1668803452fb233664804ffeadaa165c75da0 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 6 May 2018 16:30:26 -0500 Subject: [PATCH 111/703] Catch ValueError when trying to set sip API This happens when PyQt4 was imported before qtpy --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 11d3d0e4..af91e6f1 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -147,7 +147,7 @@ class PythonQtWarning(Warning): sip.setapi('QTextStream', 2) sip.setapi('QTime', 2) sip.setapi('QUrl', 2) - except AttributeError: + except (AttributeError, ValueError): # PyQt < v4.6 pass from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore From 4844232f779ace28e5dd94ed0724b3b68f986151 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 6 May 2018 16:51:51 -0500 Subject: [PATCH 112/703] Some style fixes --- qtpy/__init__.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index af91e6f1..54ad5486 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -69,6 +69,17 @@ # Version of QtPy from ._version import __version__ + +class PythonQtError(Exception): + """Error raise if no bindings could be selected""" + pass + + +class PythonQtWarning(Warning): + """Warning if some features are not implemented in a binding.""" + pass + + # Qt API environment variable name QT_API = 'QT_API' @@ -99,14 +110,6 @@ PYQT4 = PYSIDE = PYSIDE2 = False -class PythonQtError(Exception): - """Error raise if no bindings could be selected""" - pass - -class PythonQtWarning(Warning): - """Warning if some features are not implemented in a binding.""" - pass - if 'PyQt5' in sys.modules: API = 'pyqt5' elif 'PySide2' in sys.modules: @@ -181,8 +184,8 @@ class PythonQtWarning(Warning): 'pyside': 'PySide', 'pyside2':'PySide2'}[API] if PYQT4: - import sip - try: - API_NAME += (" (API v{0})".format(sip.getapi('QString'))) - except AttributeError: - pass + import sip + try: + API_NAME += (" (API v{0})".format(sip.getapi('QString'))) + except AttributeError: + pass From 0189f6cebdfa0789e68bb7ee73b255b2274201e3 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 6 May 2018 16:55:42 -0500 Subject: [PATCH 113/703] Testing: Fix downloading PySide2 wheels --- ci/test-pyside2.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ci/test-pyside2.sh b/ci/test-pyside2.sh index a5521e0e..043f244c 100755 --- a/ci/test-pyside2.sh +++ b/ci/test-pyside2.sh @@ -3,17 +3,17 @@ export PATH="$HOME/miniconda/bin:$PATH" source activate test -# Download PySide2 wheel -wget -q http://download.qt.io/snapshots/ci/pyside/5.11/latest/pyside2/PySide2-5.11.0a1-5.11.0-cp27-cp27mu-linux_x86_64.whl -O PySide2-5.11.0a1-5.11.0-cp27-cp27mu-linux_x86_64.whl -wget -q http://download.qt.io/snapshots/ci/pyside/5.11/latest/pyside2/PySide2-5.11.0a1-5.11.0-cp36-cp36m-linux_x86_64.whl -O PySide2-5.11.0a1-5.11.0-cp36-cp36m-linux_x86_64.whl +# Download PySide2 wheels +export URL="download.qt.io/snapshots/ci/pyside/5.11/latest/pyside2/" +wget -q -r --no-parent -A 'PySide2-*-linux_x86_64.whl' http://${URL} # We use container 3 to test with pip if [ "$CIRCLE_NODE_INDEX" = "0" ]; then conda remove -q qt pyqt - pip install PySide2-5.11.0a1-5.11.0-cp27-cp27mu-linux_x86_64.whl + pip install ./${URL}/PySide2-*-cp27-cp27mu-linux_x86_64.whl elif [ "$CIRCLE_NODE_INDEX" = "3" ]; then pip uninstall -q -y pyqt5 sip - pip install PySide2-5.11.0a1-5.11.0-cp36-cp36m-linux_x86_64.whl + pip install ./${URL}/PySide2-*-cp36-cp36m-linux_x86_64.whl else exit 0 fi From c030e8806fb97a4018f61b4efa22cb8216e759c2 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 6 May 2018 17:26:31 -0500 Subject: [PATCH 114/703] Update Changelog --- CHANGELOG.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8715e0d..50756d9c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # History of changes +## Version 1.4.2 (2018-05-06) + +### Issues Closed + +* [Issue 150](https://github.com/spyder-ide/qtpy/issues/150) - PySide2-5.11 alpha2 compatibility ([PR 151](https://github.com/spyder-ide/qtpy/pull/151)) +* [Issue 144](https://github.com/spyder-ide/qtpy/issues/144) - ValueError: API 'QString' has already been set to version 1 at line 141 in __init__.py file. ([PR 152](https://github.com/spyder-ide/qtpy/pull/152)) + +In this release 2 issues were closed. + +### Pull Requests Merged + +* [PR 152](https://github.com/spyder-ide/qtpy/pull/152) - PR: Catch ValueError when trying to set sip API ([144](https://github.com/spyder-ide/qtpy/issues/144)) +* [PR 151](https://github.com/spyder-ide/qtpy/pull/151) - PR: Add a preventive change for PySide-5.11a2 ([150](https://github.com/spyder-ide/qtpy/issues/150)) +* [PR 149](https://github.com/spyder-ide/qtpy/pull/149) - PR: Use Qt official wheels to run tests for PySide2 +* [PR 148](https://github.com/spyder-ide/qtpy/pull/148) - PR: Remove internal conda recipe + +In this release 4 pull requests were closed. + + +---- + + ## Version 1.4.1 (2018-04-28) ### New features From d398fbdf99a035a4e5779626a3435c90e41eec46 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 6 May 2018 17:27:27 -0500 Subject: [PATCH 115/703] Release 1.4.2 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 7907ee4e..3e2a74ac 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 5, 0, 'dev0') +version_info = (1, 4, 2) __version__ = '.'.join(map(str, version_info)) From e79af98a46a2fa029a625a44ed71ba96953e0d27 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 6 May 2018 17:29:11 -0500 Subject: [PATCH 116/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 3e2a74ac..7907ee4e 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 4, 2) +version_info = (1, 5, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 874ccc50e7970f2cc10de5a9837e7d5a24bc5f47 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Mon, 11 Jun 2018 16:04:28 +0200 Subject: [PATCH 117/703] Fix pyside2 wheels install --- ci/test-pyside2.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ci/test-pyside2.sh b/ci/test-pyside2.sh index 043f244c..22fb4984 100755 --- a/ci/test-pyside2.sh +++ b/ci/test-pyside2.sh @@ -4,16 +4,15 @@ export PATH="$HOME/miniconda/bin:$PATH" source activate test # Download PySide2 wheels -export URL="download.qt.io/snapshots/ci/pyside/5.11/latest/pyside2/" -wget -q -r --no-parent -A 'PySide2-*-linux_x86_64.whl' http://${URL} +export URL="http://download.qt.io/snapshots/ci/pyside/5.11/latest/" # We use container 3 to test with pip if [ "$CIRCLE_NODE_INDEX" = "0" ]; then conda remove -q qt pyqt - pip install ./${URL}/PySide2-*-cp27-cp27mu-linux_x86_64.whl + pip install --index-url=${URL} pyside2 --trusted-host download.qt.io elif [ "$CIRCLE_NODE_INDEX" = "3" ]; then pip uninstall -q -y pyqt5 sip - pip install ./${URL}/PySide2-*-cp36-cp36m-linux_x86_64.whl + pip install --index-url=${URL} pyside2 --trusted-host download.qt.io else exit 0 fi From ec96e1307f6f66dd3ca2a01517bc31fa1838922d Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Sun, 3 Jun 2018 14:08:15 +0200 Subject: [PATCH 118/703] Add more modules --- qtpy/QtMultimediaWidgets.py | 18 +++++++++ qtpy/QtQml.py | 18 +++++++++ qtpy/QtQuick.py | 18 +++++++++ qtpy/QtWebChannel.py | 18 +++++++++ qtpy/QtWebSockets.py | 18 +++++++++ qtpy/QtXmlPatterns.py | 22 +++++++++++ qtpy/tests/test_qtmultimediawidgets.py | 14 +++++++ qtpy/tests/test_qtqml.py | 34 +++++++++++++++++ qtpy/tests/test_qtquick.py | 53 ++++++++++++++++++++++++++ qtpy/tests/test_qtwebchannel.py | 13 +++++++ qtpy/tests/test_qtwebsockets.py | 15 ++++++++ qtpy/tests/test_qtxmlpatterns.py | 25 ++++++++++++ 12 files changed, 266 insertions(+) create mode 100644 qtpy/QtMultimediaWidgets.py create mode 100644 qtpy/QtQml.py create mode 100644 qtpy/QtQuick.py create mode 100644 qtpy/QtWebChannel.py create mode 100644 qtpy/QtWebSockets.py create mode 100644 qtpy/QtXmlPatterns.py create mode 100644 qtpy/tests/test_qtmultimediawidgets.py create mode 100644 qtpy/tests/test_qtqml.py create mode 100644 qtpy/tests/test_qtquick.py create mode 100644 qtpy/tests/test_qtwebchannel.py create mode 100644 qtpy/tests/test_qtwebsockets.py create mode 100644 qtpy/tests/test_qtxmlpatterns.py diff --git a/qtpy/QtMultimediaWidgets.py b/qtpy/QtMultimediaWidgets.py new file mode 100644 index 00000000..697845d9 --- /dev/null +++ b/qtpy/QtMultimediaWidgets.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtMultimediaWidgets classes and functions.""" + +# Local imports +from . import PYSIDE2, PYQT5, PythonQtError + +if PYQT5: + from PyQt5.QtMultimediaWidgets import * +elif PYSIDE2: + from PySide2.QtMultimediaWidgets import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtQml.py b/qtpy/QtQml.py new file mode 100644 index 00000000..117f977f --- /dev/null +++ b/qtpy/QtQml.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtQml classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtQml import * +elif PYSIDE2: + from PySide2.QtQml import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtQuick.py b/qtpy/QtQuick.py new file mode 100644 index 00000000..82910667 --- /dev/null +++ b/qtpy/QtQuick.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtQuick classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtQuick import * +elif PYSIDE2: + from PySide2.QtQuick import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebChannel.py b/qtpy/QtWebChannel.py new file mode 100644 index 00000000..2862a056 --- /dev/null +++ b/qtpy/QtWebChannel.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtWebChannel classes and functions.""" + +# Local imports +from . import PYSIDE2, PYQT5, PythonQtError + +if PYQT5: + from PyQt5.QtWebChannel import * +elif PYSIDE2: + from PySide2.QtWebChannel import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebSockets.py b/qtpy/QtWebSockets.py new file mode 100644 index 00000000..4b6a8204 --- /dev/null +++ b/qtpy/QtWebSockets.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtWebSockets classes and functions.""" + +# Local imports +from . import PYSIDE2, PYQT5, PythonQtError + +if PYQT5: + from PyQt5.QtWebSockets import * +elif PYSIDE2: + from PySide2.QtWebSockets import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py new file mode 100644 index 00000000..b41e13df --- /dev/null +++ b/qtpy/QtXmlPatterns.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtXmlPatterns classes and functions.""" + +# Local imports +from . import PYQT4, PYSIDE2, PYQT5, PYSIDE, PythonQtError + +if PYQT5: + from PyQt5.QtXmlPatterns import * +elif PYSIDE2: + from PySide2.QtXmlPatterns import * +elif PYQT4: + from PyQt4.QtXmlPatterns import * +elif PYSIDE: + from PySide.QtXmlPatterns import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtmultimediawidgets.py b/qtpy/tests/test_qtmultimediawidgets.py new file mode 100644 index 00000000..c0389a88 --- /dev/null +++ b/qtpy/tests/test_qtmultimediawidgets.py @@ -0,0 +1,14 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtmultimediawidgets(): + """Test the qtpy.QtMultimediaWidgets namespace""" + from qtpy import QtMultimediaWidgets + + assert QtMultimediaWidgets.QCameraViewfinder is not None + assert QtMultimediaWidgets.QGraphicsVideoItem is not None + assert QtMultimediaWidgets.QVideoWidget is not None + #assert QtMultimediaWidgets.QVideoWidgetControl is not None diff --git a/qtpy/tests/test_qtqml.py b/qtpy/tests/test_qtqml.py new file mode 100644 index 00000000..a6d7ca95 --- /dev/null +++ b/qtpy/tests/test_qtqml.py @@ -0,0 +1,34 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtqml(): + """Test the qtpy.QtQml namespace""" + from qtpy import QtQml + assert QtQml.QJSEngine is not None + assert QtQml.QJSValue is not None + assert QtQml.QJSValueIterator is not None + assert QtQml.QQmlAbstractUrlInterceptor is not None + assert QtQml.QQmlApplicationEngine is not None + assert QtQml.QQmlComponent is not None + assert QtQml.QQmlContext is not None + assert QtQml.QQmlEngine is not None + assert QtQml.QQmlImageProviderBase is not None + assert QtQml.QQmlError is not None + assert QtQml.QQmlExpression is not None + assert QtQml.QQmlExtensionPlugin is not None + assert QtQml.QQmlFileSelector is not None + assert QtQml.QQmlIncubationController is not None + assert QtQml.QQmlIncubator is not None + if not PYSIDE2: + # https://wiki.qt.io/Qt_for_Python_Missing_Bindings#QtQml + assert QtQml.QQmlListProperty is not None + assert QtQml.QQmlListReference is not None + assert QtQml.QQmlNetworkAccessManagerFactory is not None + assert QtQml.QQmlParserStatus is not None + assert QtQml.QQmlProperty is not None + assert QtQml.QQmlPropertyValueSource is not None + assert QtQml.QQmlScriptString is not None + assert QtQml.QQmlPropertyMap is not None diff --git a/qtpy/tests/test_qtquick.py b/qtpy/tests/test_qtquick.py new file mode 100644 index 00000000..257fd740 --- /dev/null +++ b/qtpy/tests/test_qtquick.py @@ -0,0 +1,53 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtquick(): + """Test the qtpy.QtQuick namespace""" + from qtpy import QtQuick + assert QtQuick.QQuickAsyncImageProvider is not None + if not PYSIDE2: + assert QtQuick.QQuickCloseEvent is not None + assert QtQuick.QQuickFramebufferObject is not None + assert QtQuick.QQuickImageProvider is not None + assert QtQuick.QQuickImageResponse is not None + assert QtQuick.QQuickItem is not None + assert QtQuick.QQuickItemGrabResult is not None + assert QtQuick.QQuickPaintedItem is not None + assert QtQuick.QQuickRenderControl is not None + assert QtQuick.QQuickTextDocument is not None + assert QtQuick.QQuickTextureFactory is not None + assert QtQuick.QQuickView is not None + assert QtQuick.QQuickWindow is not None + assert QtQuick.QSGAbstractRenderer is not None + assert QtQuick.QSGBasicGeometryNode is not None + assert QtQuick.QSGClipNode is not None + assert QtQuick.QSGDynamicTexture is not None + assert QtQuick.QSGEngine is not None + if not PYSIDE2: + assert QtQuick.QSGFlatColorMaterial is not None + assert QtQuick.QSGGeometry is not None + assert QtQuick.QSGGeometryNode is not None + #assert QtQuick.QSGImageNode is not None + if not PYSIDE2: + assert QtQuick.QSGMaterial is not None + assert QtQuick.QSGMaterialShader is not None + assert QtQuick.QSGMaterialType is not None + assert QtQuick.QSGNode is not None + assert QtQuick.QSGOpacityNode is not None + if not PYSIDE2: + assert QtQuick.QSGOpaqueTextureMaterial is not None + #assert QtQuick.QSGRectangleNode is not None + #assert QtQuick.QSGRenderNode is not None + #assert QtQuick.QSGRendererInterface is not None + assert QtQuick.QSGSimpleRectNode is not None + assert QtQuick.QSGSimpleTextureNode is not None + assert QtQuick.QSGTexture is not None + if not PYSIDE2: + assert QtQuick.QSGTextureMaterial is not None + assert QtQuick.QSGTextureProvider is not None + assert QtQuick.QSGTransformNode is not None + if not PYSIDE2: + assert QtQuick.QSGVertexColorMaterial is not None diff --git a/qtpy/tests/test_qtwebchannel.py b/qtpy/tests/test_qtwebchannel.py new file mode 100644 index 00000000..2beb70c0 --- /dev/null +++ b/qtpy/tests/test_qtwebchannel.py @@ -0,0 +1,13 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtwebchannel(): + """Test the qtpy.QtWebChannel namespace""" + from qtpy import QtWebChannel + + assert QtWebChannel.QWebChannel is not None + assert QtWebChannel.QWebChannelAbstractTransport is not None + diff --git a/qtpy/tests/test_qtwebsockets.py b/qtpy/tests/test_qtwebsockets.py new file mode 100644 index 00000000..5bdcc325 --- /dev/null +++ b/qtpy/tests/test_qtwebsockets.py @@ -0,0 +1,15 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtwebsockets(): + """Test the qtpy.QtWebSockets namespace""" + from qtpy import QtWebSockets + + assert QtWebSockets.QMaskGenerator is not None + assert QtWebSockets.QWebSocket is not None + assert QtWebSockets.QWebSocketCorsAuthenticator is not None + assert QtWebSockets.QWebSocketProtocol is not None + assert QtWebSockets.QWebSocketServer is not None diff --git a/qtpy/tests/test_qtxmlpatterns.py b/qtpy/tests/test_qtxmlpatterns.py new file mode 100644 index 00000000..4c6d4cb9 --- /dev/null +++ b/qtpy/tests/test_qtxmlpatterns.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2, PYSIDE + +def test_qtxmlpatterns(): + """Test the qtpy.QtXmlPatterns namespace""" + from qtpy import QtXmlPatterns + assert QtXmlPatterns.QAbstractMessageHandler is not None + assert QtXmlPatterns.QAbstractUriResolver is not None + assert QtXmlPatterns.QAbstractXmlNodeModel is not None + assert QtXmlPatterns.QAbstractXmlReceiver is not None + if not PYSIDE2 and not PYSIDE: + assert QtXmlPatterns.QSimpleXmlNodeModel is not None + assert QtXmlPatterns.QSourceLocation is not None + assert QtXmlPatterns.QXmlFormatter is not None + assert QtXmlPatterns.QXmlItem is not None + assert QtXmlPatterns.QXmlName is not None + assert QtXmlPatterns.QXmlNamePool is not None + assert QtXmlPatterns.QXmlNodeModelIndex is not None + assert QtXmlPatterns.QXmlQuery is not None + assert QtXmlPatterns.QXmlResultItems is not None + assert QtXmlPatterns.QXmlSchema is not None + assert QtXmlPatterns.QXmlSchemaValidator is not None + assert QtXmlPatterns.QXmlSerializer is not None From a2097bf7c3103ec6e0482bb34f3b33c753ea7889 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Sun, 3 Jun 2018 18:07:02 +0200 Subject: [PATCH 119/703] Enable QtNetwork test for PySide --- qtpy/tests/test_qtnetwork.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index 8f4b71f4..7f645910 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -4,13 +4,13 @@ from qtpy import PYSIDE, PYSIDE2, QtNetwork -@pytest.mark.skipif(PYSIDE2 or PYSIDE, reason="It fails on PySide/PySide2") def test_qtnetwork(): """Test the qtpy.QtNetwork namespace""" assert QtNetwork.QAbstractNetworkCache is not None assert QtNetwork.QNetworkCacheMetaData is not None - assert QtNetwork.QHttpMultiPart is not None - assert QtNetwork.QHttpPart is not None + if not PYSIDE and not PYSIDE2: + assert QtNetwork.QHttpMultiPart is not None + assert QtNetwork.QHttpPart is not None assert QtNetwork.QNetworkAccessManager is not None assert QtNetwork.QNetworkCookie is not None assert QtNetwork.QNetworkCookieJar is not None @@ -34,9 +34,10 @@ def test_qtnetwork(): assert QtNetwork.QTcpServer is not None assert QtNetwork.QTcpSocket is not None assert QtNetwork.QUdpSocket is not None - assert QtNetwork.QSslCertificate is not None - assert QtNetwork.QSslCipher is not None - assert QtNetwork.QSslConfiguration is not None - assert QtNetwork.QSslError is not None - assert QtNetwork.QSslKey is not None - assert QtNetwork.QSslSocket is not None + if not PYSIDE: + assert QtNetwork.QSslCertificate is not None + assert QtNetwork.QSslCipher is not None + assert QtNetwork.QSslConfiguration is not None + assert QtNetwork.QSslError is not None + assert QtNetwork.QSslKey is not None + assert QtNetwork.QSslSocket is not None From ee4c6172e89bf2210e6f76b52c3f9843a39b8490 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 11 Jun 2018 14:57:52 -0500 Subject: [PATCH 120/703] Remove outdated funding appeal from readme --- README.md | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/README.md b/README.md index 4783d3d9..19d89ee4 100644 --- a/README.md +++ b/README.md @@ -15,26 +15,6 @@ Copyright © 2009- The Spyder Development Team. [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/spyder-ide/qtpy/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/spyder-ide/qtpy/?branch=master) ----- - -## Important Announcement: Spyder is unfunded! - -Since mid November/2017, [Anaconda, Inc](https://www.anaconda.com/) has -stopped funding Spyder development, after doing it for the past 18 -months. Because of that, development will focus from now on maintaining -Spyder 3 at a much slower pace than before. - -If you want to contribute to maintain Spyder, please consider donating at - -https://opencollective.com/spyder - -We appreciate all the help you can provide us and can't thank you enough for -supporting the work of Spyder devs and Spyder development. - -If you want to know more about this, please read this -[page](https://github.com/spyder-ide/spyder/wiki/Anaconda-stopped-funding-Spyder). - ----- ## Description From 98334af3c51f98d83eed79bfa3c5626cd5fb6333 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 11 Jun 2018 15:00:31 -0500 Subject: [PATCH 121/703] Minor textual tweaks and fixes --- README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 19d89ee4..9998267f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # QtPy: Abstraction layer for PyQt5/PyQt4/PySide2/PySide -Copyright © 2009- The Spyder Development Team. - ## Project details [![license](https://img.shields.io/pypi/l/qtpy.svg)](./LICENSE) [![pypi version](https://img.shields.io/pypi/v/qtpy.svg)](https://pypi.python.org/pypi/qtpy) @@ -15,6 +13,8 @@ Copyright © 2009- The Spyder Development Team. [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/spyder-ide/qtpy/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/spyder-ide/qtpy/?branch=master) +*Copyright © 2009–2018 The Spyder Development Team* + ## Description @@ -24,11 +24,11 @@ write applications using a single API call to either PyQt or PySide. It provides support for PyQt5, PyQt4, PySide2 and PySide using the Qt5 layout (where the QtGui module has been split into QtGui and QtWidgets). -Basically, you write your code as if you were using PySide2 but import Qt modules -from `qtpy` instead of `PySide2` (or `PyQt5`) +Basically, you can write your code as if you were using PySide2 +but import Qt modules from `qtpy` instead of `PySide2` (or `PyQt5`) -### Attribution and acknowledgements +### Attribution and acknowledgments This project is based on the [pyqode.qt](https://github.com/pyQode/pyqode.qt) project and the [spyderlib.qt](https://github.com/spyder-ide/spyder/tree/2.3/spyderlib/qt) @@ -43,7 +43,7 @@ to a particular project or namespace. ### License -This project is licensed under the MIT license. +This project is released under the MIT license. ### Requirements @@ -72,10 +72,12 @@ or conda install qtpy ``` + ## Contributing Everyone is welcome to contribute! + ## Backers Support us with a monthly donation and help us continue our activities. From e68cad1b2bc41ce2d0806127dcc65722e8ef4925 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 11 Jun 2018 15:15:56 -0500 Subject: [PATCH 122/703] Add, update and standardize order of readme badges --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 9998267f..ecda29af 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@ # QtPy: Abstraction layer for PyQt5/PyQt4/PySide2/PySide -## Project details [![license](https://img.shields.io/pypi/l/qtpy.svg)](./LICENSE) -[![pypi version](https://img.shields.io/pypi/v/qtpy.svg)](https://pypi.python.org/pypi/qtpy) -[![Join the chat at https://gitter.im/spyder-ide/public](https://badges.gitter.im/spyder-ide/spyder.svg)](https://gitter.im/spyder-ide/public) +[![pypi version](https://img.shields.io/pypi/v/qtpy.svg)](https://pypi.org/project/QtPy/) +[![conda version](https://img.shields.io/conda/vn/conda-forge/qtpy.svg)](https://www.anaconda.com/download/) +[![download count](https://img.shields.io/conda/dn/conda-forge/qtpy.svg)](https://www.anaconda.com/download/) [![OpenCollective Backers](https://opencollective.com/spyder/backers/badge.svg?color=blue)](#backers) -[![OpenCollective Sponsors](https://opencollective.com/spyder/sponsors/badge.svg?color=blue)](#sponsors) - -## Build status +[![Join the chat at https://gitter.im/spyder-ide/public](https://badges.gitter.im/spyder-ide/spyder.svg)](https://gitter.im/spyder-ide/public)
+[![PyPI status](https://img.shields.io/pypi/status/qtpy.svg)](https://github.com/spyder-ide/qtpy) [![Build status](https://ci.appveyor.com/api/projects/status/62y6i02vhn4hefg0/branch/master?svg=true)](https://ci.appveyor.com/project/spyder-ide/qtpy/branch/master) [![CircleCI](https://circleci.com/gh/spyder-ide/qtpy.svg?style=shield)](https://circleci.com/gh/spyder-ide/qtpy) [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) From 1a58c33d0026be699e78c00784347cfc0e44e170 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Tue, 12 Jun 2018 09:56:04 +0200 Subject: [PATCH 123/703] Add QtLocation --- qtpy/QtLocation.py | 18 +++++++++++++ qtpy/tests/test_qtlocation.py | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 qtpy/QtLocation.py create mode 100644 qtpy/tests/test_qtlocation.py diff --git a/qtpy/QtLocation.py b/qtpy/QtLocation.py new file mode 100644 index 00000000..9dfe874a --- /dev/null +++ b/qtpy/QtLocation.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtLocation classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtLocation import * +elif PYSIDE2: + from PySide2.QtLocation import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtlocation.py b/qtpy/tests/test_qtlocation.py new file mode 100644 index 00000000..78bf9337 --- /dev/null +++ b/qtpy/tests/test_qtlocation.py @@ -0,0 +1,48 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtlocation(): + """Test the qtpy.QtLocation namespace""" + from qtpy import QtLocation + assert QtLocation.QGeoCodeReply is not None + assert QtLocation.QGeoCodingManager is not None + assert QtLocation.QGeoCodingManagerEngine is not None + assert QtLocation.QGeoManeuver is not None + assert QtLocation.QGeoRoute is not None + assert QtLocation.QGeoRouteReply is not None + assert QtLocation.QGeoRouteRequest is not None + assert QtLocation.QGeoRouteSegment is not None + assert QtLocation.QGeoRoutingManager is not None + assert QtLocation.QGeoRoutingManagerEngine is not None + assert QtLocation.QGeoServiceProvider is not None + #assert QtLocation.QGeoServiceProviderFactory is not None + assert QtLocation.QPlace is not None + assert QtLocation.QPlaceAttribute is not None + assert QtLocation.QPlaceCategory is not None + assert QtLocation.QPlaceContactDetail is not None + assert QtLocation.QPlaceContent is not None + assert QtLocation.QPlaceContentReply is not None + assert QtLocation.QPlaceContentRequest is not None + assert QtLocation.QPlaceDetailsReply is not None + assert QtLocation.QPlaceEditorial is not None + assert QtLocation.QPlaceIcon is not None + assert QtLocation.QPlaceIdReply is not None + assert QtLocation.QPlaceImage is not None + assert QtLocation.QPlaceManager is not None + assert QtLocation.QPlaceManagerEngine is not None + assert QtLocation.QPlaceMatchReply is not None + assert QtLocation.QPlaceMatchRequest is not None + assert QtLocation.QPlaceProposedSearchResult is not None + assert QtLocation.QPlaceRatings is not None + assert QtLocation.QPlaceReply is not None + assert QtLocation.QPlaceResult is not None + assert QtLocation.QPlaceReview is not None + assert QtLocation.QPlaceSearchReply is not None + assert QtLocation.QPlaceSearchRequest is not None + assert QtLocation.QPlaceSearchResult is not None + assert QtLocation.QPlaceSearchSuggestionReply is not None + assert QtLocation.QPlaceSupplier is not None + assert QtLocation.QPlaceUser is not None From 9deac02373d469a2e9fad3347fee2efd3de3af9c Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 5 Aug 2018 12:36:25 -0500 Subject: [PATCH 124/703] Testing: Move test files to .circleci dir --- {ci => .circleci}/test-pyqt4.sh | 0 {ci => .circleci}/test-pyqt5.sh | 0 {ci => .circleci}/test-pyside.sh | 0 {ci => .circleci}/test-pyside2.sh | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename {ci => .circleci}/test-pyqt4.sh (100%) rename {ci => .circleci}/test-pyqt5.sh (100%) rename {ci => .circleci}/test-pyside.sh (100%) rename {ci => .circleci}/test-pyside2.sh (100%) diff --git a/ci/test-pyqt4.sh b/.circleci/test-pyqt4.sh similarity index 100% rename from ci/test-pyqt4.sh rename to .circleci/test-pyqt4.sh diff --git a/ci/test-pyqt5.sh b/.circleci/test-pyqt5.sh similarity index 100% rename from ci/test-pyqt5.sh rename to .circleci/test-pyqt5.sh diff --git a/ci/test-pyside.sh b/.circleci/test-pyside.sh similarity index 100% rename from ci/test-pyside.sh rename to .circleci/test-pyside.sh diff --git a/ci/test-pyside2.sh b/.circleci/test-pyside2.sh similarity index 100% rename from ci/test-pyside2.sh rename to .circleci/test-pyside2.sh From 89dc653328cd808b39b9bdd70fa19b155f450550 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 5 Aug 2018 12:50:09 -0500 Subject: [PATCH 125/703] Testing: Move to CircleCI 2.0 --- .circleci/config.yml | 60 +++++++++++++++++++++++++++++++++++++++ .circleci/coverage.sh | 7 +++++ .circleci/install.sh | 17 +++++++++++ .circleci/test-pyqt4.sh | 3 +- .circleci/test-pyqt5.sh | 3 +- .circleci/test-pyside.sh | 3 +- .circleci/test-pyside2.sh | 15 +++++----- circle.yml | 44 ---------------------------- 8 files changed, 95 insertions(+), 57 deletions(-) create mode 100644 .circleci/config.yml create mode 100755 .circleci/coverage.sh create mode 100755 .circleci/install.sh delete mode 100644 circle.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..3293bea0 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,60 @@ +version: 2 + +main: &main + machine: true + steps: + - checkout + - run: + command: docker pull dorowu/ubuntu-desktop-lxde-vnc:trusty + - run: + name: Install system packages + command: | + sudo apt-get update + sudo apt-get install libpulse-dev + sudo apt-get install libegl1-mesa + - run: + command: ./.circleci/install.sh + - run: + command: ./.circleci/test-pyqt5.sh + - run: + command: ./.circleci/test-pyside2.sh + - run: + command: ./.circleci/test-pyqt4.sh + - run: + command: ./.circleci/test-pyside.sh + - run: + command: ./.circleci/coverage.sh + +jobs: + python2.7: + <<: *main + environment: + - PYTHON_VERSION: "2.7" + - USE_CONDA: "Yes" + + python3.5: + <<: *main + environment: + - PYTHON_VERSION: "3.5" + - USE_CONDA: "Yes" + + python3.6: + <<: *main + environment: + - PYTHON_VERSION: "3.6" + - USE_CONDA: "Yes" + + python3.6-pip: + <<: *main + environment: + - PYTHON_VERSION: "3.6" + - USE_CONDA: "No" + +workflows: + version: 2 + build_and_test: + jobs: + - python2.7 + - python3.5 + - python3.6 + - python3.6-pip diff --git a/.circleci/coverage.sh b/.circleci/coverage.sh new file mode 100755 index 00000000..6e4cef2e --- /dev/null +++ b/.circleci/coverage.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +export COVERALLS_REPO_TOKEN="xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5" +export PATH="$HOME/miniconda/bin:$PATH" +source activate test + +coveralls diff --git a/.circleci/install.sh b/.circleci/install.sh new file mode 100755 index 00000000..53ca36e6 --- /dev/null +++ b/.circleci/install.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +export TRAVIS_OS_NAME="linux" +export CONDA_DEPENDENCIES_FLAGS="--quiet" +export CONDA_DEPENDENCIES="pytest pytest-cov" +export PIP_DEPENDENCIES="coveralls" + +# Download and install miniconda and conda/pip dependencies +# with astropy helpers +echo -e "PYTHON = $PYTHON_VERSION \n============" +git clone git://github.com/astropy/ci-helpers.git > /dev/null +source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh +export PATH="$HOME/miniconda/bin:$PATH" +source activate test + +# Install the package in develop mode +python setup.py develop diff --git a/.circleci/test-pyqt4.sh b/.circleci/test-pyqt4.sh index 591fb402..5b3dec74 100755 --- a/.circleci/test-pyqt4.sh +++ b/.circleci/test-pyqt4.sh @@ -3,8 +3,7 @@ export PATH="$HOME/miniconda/bin:$PATH" source activate test -# We use container 3 to test with pip -if [ "$CIRCLE_NODE_INDEX" = "3" ]; then +if [ "$USE_CONDA" = "No" ]; then exit 0 else conda remove -q qt pyqt diff --git a/.circleci/test-pyqt5.sh b/.circleci/test-pyqt5.sh index 5fb0bd84..d7b18dec 100755 --- a/.circleci/test-pyqt5.sh +++ b/.circleci/test-pyqt5.sh @@ -3,8 +3,7 @@ export PATH="$HOME/miniconda/bin:$PATH" source activate test -# We use container 3 to test with pip -if [ "$CIRCLE_NODE_INDEX" != "3" ]; then +if [ "$USE_CONDA" = "Yes" ]; then conda install -q qt=5.* pyqt=5.* else # We are getting segfaults in 5.10 diff --git a/.circleci/test-pyside.sh b/.circleci/test-pyside.sh index 52327549..f9fd41da 100755 --- a/.circleci/test-pyside.sh +++ b/.circleci/test-pyside.sh @@ -3,8 +3,7 @@ export PATH="$HOME/miniconda/bin:$PATH" source activate test -# We use container 3 to test with pip -if [ "$CIRCLE_NODE_INDEX" = "3" ]; then +if [ "$USE_CONDA" = "No" ]; then exit 0 else conda remove -q qt pyqt diff --git a/.circleci/test-pyside2.sh b/.circleci/test-pyside2.sh index 22fb4984..05218d72 100755 --- a/.circleci/test-pyside2.sh +++ b/.circleci/test-pyside2.sh @@ -6,15 +6,16 @@ source activate test # Download PySide2 wheels export URL="http://download.qt.io/snapshots/ci/pyside/5.11/latest/" -# We use container 3 to test with pip -if [ "$CIRCLE_NODE_INDEX" = "0" ]; then - conda remove -q qt pyqt - pip install --index-url=${URL} pyside2 --trusted-host download.qt.io -elif [ "$CIRCLE_NODE_INDEX" = "3" ]; then +if [ "$USE_CONDA" = "Yes"]; then + if [ "$PYTHON_VERSION" = "2.7"]; then + conda remove -q qt pyqt + pip install --index-url=${URL} pyside2 --trusted-host download.qt.io + else + exit 0 + fi +else pip uninstall -q -y pyqt5 sip pip install --index-url=${URL} pyside2 --trusted-host download.qt.io -else - exit 0 fi python qtpy/tests/runtests.py diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 51d21c3e..00000000 --- a/circle.yml +++ /dev/null @@ -1,44 +0,0 @@ -# https://circleci.com/gh/spyder-ide/qtpy/ -machine: - environment: - # Used by test scripts - TEST_CI: "True" - # Python versions to test (Maximum of 4 different versions for now) - PY_VERSIONS: "2.7 3.5 3.6 3.6" - # For Coveralls - COVERALLS_REPO_TOKEN: xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5 - # Used by astropy-ci helpers - TRAVIS_OS_NAME: "linux" - CONDA_DEPENDENCIES: "pytest pytest-cov" - PIP_DEPENDENCIES: "coveralls" - -dependencies: - pre: - # We need this for QtMultimedia in 5.8 - - sudo apt-get update -y; true - - sudo apt-get install libpulse-dev - - override: - # First convert PY_VERSIONS to an array and then select the Python version - # based on the CIRCLE_NODE_INDEX - - PY_VERSIONS=($PY_VERSIONS) && - TRAVIS_PYTHON_VERSION=${PY_VERSIONS[$CIRCLE_NODE_INDEX]} && - echo -e "PYTHON = $TRAVIS_PYTHON_VERSION \n============" && - git clone git://github.com/astropy/ci-helpers.git && - source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh && - export PATH="$HOME/miniconda/bin:$PATH" && - source activate test && - python setup.py develop; - -test: - override: - - ./ci/test-pyqt5.sh: # note the colon - parallel: true - - ./ci/test-pyside2.sh: # note the colon - parallel: true - - ./ci/test-pyqt4.sh: # note the colon - parallel: true - - ./ci/test-pyside.sh: # note the colon - parallel: true - - export PATH="$HOME/miniconda/bin:$PATH" && source activate test && coveralls: # note the colon - parallel: true From 4755dc83af256de1f6e6a16d8f308d4537d77376 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 5 Aug 2018 14:30:47 -0500 Subject: [PATCH 126/703] Testing: Remove ciocheck config file --- .ciocheck | 107 ------------------------------------------------------ 1 file changed, 107 deletions(-) delete mode 100644 .ciocheck diff --git a/.ciocheck b/.ciocheck deleted file mode 100644 index 6c06a602..00000000 --- a/.ciocheck +++ /dev/null @@ -1,107 +0,0 @@ -# ----------------------------------------------------------------------------- -# ciocheck -# https://github.com/ContinuumIO/ciocheck -# ----------------------------------------------------------------------------- -[ciocheck] -branch = origin/master -diff_mode = commited -file_mode = all -check = pep8,pydocstyle,flake8,pylint - -# Python (pyformat) -add_copyright = true -add_header = true -add_init = true - -# ----------------------------------------------------------------------------- -# pep8 -# http://pep8.readthedocs.io/en/latest/intro.html#configuration -# ----------------------------------------------------------------------------- -[pep8] -exclude = */tests/* -ignore = E126 -max-line-length = 79 - -# ----------------------------------------------------------------------------- -# pydocstyle -# http://www.pydocstyle.org/en/latest/usage.html#example -# ----------------------------------------------------------------------------- -[pydocstyle] -add-ignore = D203 -inherit = false - -# ----------------------------------------------------------------------------- -# Flake 8 -# http://flake8.readthedocs.io/en/latest/config.html -# ----------------------------------------------------------------------------- -[flake8] -exclude = */tests/* -ignore = E126,F401 -max-line-length = 79 -max-complexity = 64 - -# ----------------------------------------------------------------------------- -# pylint -# https://pylint.readthedocs.io/en/latest/ -# ----------------------------------------------------------------------------- -[pylint:messages] -disable=unused-import - -# ----------------------------------------------------------------------------- -# isort -# https://github.com/timothycrosley/isort/wiki/isort-Settings -# ----------------------------------------------------------------------------- -[isort] -from_first = true -import_heading_stdlib = Standard library imports -import_heading_firstparty = Local imports -import_heading_thirdparty = Third party imports -indent = ' ' -known_first_party = qtpy -known_third_party = PySide, PyQt4, PyQt5 -line_length = 79 -sections = FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER - -# ----------------------------------------------------------------------------- -# yapf -# https://github.com/google/yapf#formatting-style -# ----------------------------------------------------------------------------- -[yapf:style] -based_on_style = pep8 -column_limit = 79 -spaces_before_comment = 2 - -# ----------------------------------------------------------------------------- -# autopep8 -# http://pep8.readthedocs.io/en/latest/intro.html#configuration -# ----------------------------------------------------------------------------- -[autopep8] -exclude = */tests/* -ignore = E126, -max-line-length = 79 - -# ----------------------------------------------------------------------------- -# Coverage -# http://coverage.readthedocs.io/en/latest/config.html -# ----------------------------------------------------------------------------- -[coverage:run] -omit = - */tests/* - */build/* - -[coverage:report] -fail_under = 0 -show_missing = true -skip_covered = true -exclude_lines = - pragma: no cover - def test(): - if __name__ == .__main__.: - -# ----------------------------------------------------------------------------- -# pytest -# http://doc.pytest.org/en/latest/usage.html -# ----------------------------------------------------------------------------- -[pytest] -addopts = -rfew --durations=10 -python_functions = test_* \ No newline at end of file From 9226f287736054cc4842d7c6559d8ff32c15078f Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 5 Aug 2018 14:31:17 -0500 Subject: [PATCH 127/703] Testing: Install PySide2 wheels in quiet mode --- .circleci/test-pyside2.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/test-pyside2.sh b/.circleci/test-pyside2.sh index 05218d72..7eef830b 100755 --- a/.circleci/test-pyside2.sh +++ b/.circleci/test-pyside2.sh @@ -9,13 +9,13 @@ export URL="http://download.qt.io/snapshots/ci/pyside/5.11/latest/" if [ "$USE_CONDA" = "Yes"]; then if [ "$PYTHON_VERSION" = "2.7"]; then conda remove -q qt pyqt - pip install --index-url=${URL} pyside2 --trusted-host download.qt.io + pip install -q --index-url=${URL} pyside2 --trusted-host download.qt.io else exit 0 fi else pip uninstall -q -y pyqt5 sip - pip install --index-url=${URL} pyside2 --trusted-host download.qt.io + pip install -q --index-url=${URL} pyside2 --trusted-host download.qt.io fi python qtpy/tests/runtests.py From 5534404dda1760c8ca1ee9f78fc2e65083c23454 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 5 Aug 2018 14:45:23 -0500 Subject: [PATCH 128/703] Testing: Fix error in PySide2 script --- .circleci/test-pyside2.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/test-pyside2.sh b/.circleci/test-pyside2.sh index 7eef830b..6e2518ac 100755 --- a/.circleci/test-pyside2.sh +++ b/.circleci/test-pyside2.sh @@ -6,8 +6,8 @@ source activate test # Download PySide2 wheels export URL="http://download.qt.io/snapshots/ci/pyside/5.11/latest/" -if [ "$USE_CONDA" = "Yes"]; then - if [ "$PYTHON_VERSION" = "2.7"]; then +if [ "$USE_CONDA" = "Yes" ]; then + if [ "$PYTHON_VERSION" = "2.7" ]; then conda remove -q qt pyqt pip install -q --index-url=${URL} pyside2 --trusted-host download.qt.io else From 8846212469733805773631b67e4c01432c480ad2 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 25 Aug 2018 12:07:55 -0500 Subject: [PATCH 129/703] Add warnings when trying to run Qt 5 programs in the wrong macOS versions --- qtpy/__init__.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 54ad5486..71027440 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -62,7 +62,9 @@ """ +from distutils.version import LooseVersion import os +import platform import sys import warnings @@ -125,6 +127,23 @@ class PythonQtWarning(Warning): from PyQt5.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt5.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore PYSIDE_VERSION = None + + if sys.platform == 'darwin': + macos_version = LooseVersion(platform.mac_ver()[0]) + if macos_version < LooseVersion('10.10'): + if LooseVersion(QT_VERSION) >= '5.9': + raise PythonQtError("Qt 5.9 or higher only works in " + "macOS 10.10 or higher. Your " + "program will fail in this " + "system.") + elif macos_version < LooseVersion('10.11'): + if LooseVersion(QT_VERSION) >= '5.11': + raise PythonQtError("Qt 5.11 or higher only works in " + "macOS 10.11 or higher. Your " + "program will fail in this " + "system.") + + del macos_version except ImportError: API = os.environ['QT_API'] = 'pyside2' From 42c4798f513afce111c3dbb3a3150ab9d8818142 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 25 Aug 2018 12:09:09 -0500 Subject: [PATCH 130/703] Testing: Add tests for macOS warnings --- qtpy/tests/test_macos_checks.py | 102 ++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 qtpy/tests/test_macos_checks.py diff --git a/qtpy/tests/test_macos_checks.py b/qtpy/tests/test_macos_checks.py new file mode 100644 index 00000000..18683a80 --- /dev/null +++ b/qtpy/tests/test_macos_checks.py @@ -0,0 +1,102 @@ +from __future__ import absolute_import + +import mock +import platform +import sys + +import pytest +from qtpy import PYQT5, QT_VERSION + + +@pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") +@mock.patch.object(platform, 'mac_ver') +def test_qt59_exception(mac_ver, monkeypatch): + # Remove qtpy to reimport it again + try: + del sys.modules["qtpy"] + except KeyError: + pass + + # Patch stdlib to emulate a macOS system + monkeypatch.setattr("sys.platform", 'darwin') + mac_ver.return_value = ('10.9.2',) + + # Patch Qt version + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.9.1') + + # This should raise an Exception + with pytest.raises(Exception) as e: + import qtpy + + assert '10.10' in str(e.value) + assert '5.9' in str(e.value) + + +@pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") +@mock.patch.object(platform, 'mac_ver') +def test_qt59_no_exception(mac_ver, monkeypatch): + # Remove qtpy to reimport it again + try: + del sys.modules["qtpy"] + except KeyError: + pass + + # Patch stdlib to emulate a macOS system + monkeypatch.setattr("sys.platform", 'darwin') + mac_ver.return_value = ('10.10.1',) + + # Patch Qt version + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.9.5') + + # This should not raise an Exception + try: + import qtpy + except Exception: + pytest.fail("Error!") + + +@pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") +@mock.patch.object(platform, 'mac_ver') +def test_qt511_exception(mac_ver, monkeypatch): + # Remove qtpy to reimport it again + try: + del sys.modules["qtpy"] + except KeyError: + pass + + # Patch stdlib to emulate a macOS system + monkeypatch.setattr("sys.platform", 'darwin') + mac_ver.return_value = ('10.10.3',) + + # Patch Qt version + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + + # This should raise an Exception + with pytest.raises(Exception) as e: + import qtpy + + assert '10.11' in str(e.value) + assert '5.11' in str(e.value) + + +@pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") +@mock.patch.object(platform, 'mac_ver') +def test_qt511_no_exception(mac_ver, monkeypatch): + # Remove qtpy to reimport it again + try: + del sys.modules["qtpy"] + except KeyError: + pass + + # Patch stdlib to emulate a macOS system + monkeypatch.setattr("sys.platform", 'darwin') + mac_ver.return_value = ('10.13.2',) + + # Patch Qt version + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + + # This should not raise an Exception + try: + import qtpy + except Exception: + pytest.fail("Error!") From d213e34f2fd10e38ec6e81b3ddacbfdb0cb8006f Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 25 Aug 2018 12:15:31 -0500 Subject: [PATCH 131/703] Add macOS warning and tests for PySide2 --- qtpy/__init__.py | 11 +++++++++++ qtpy/tests/test_macos_checks.py | 18 +++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 71027440..8db8caff 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -155,6 +155,17 @@ class PythonQtWarning(Warning): PYQT_VERSION = None PYQT5 = False PYSIDE2 = True + + if sys.platform == 'darwin': + macos_version = LooseVersion(platform.mac_ver()[0]) + if macos_version < LooseVersion('10.11'): + if LooseVersion(QT_VERSION) >= '5.11': + raise PythonQtError("Qt 5.11 or higher only works in " + "macOS 10.11 or higher. Your " + "program will fail in this " + "system.") + + del macos_version except ImportError: API = os.environ['QT_API'] = 'pyqt' diff --git a/qtpy/tests/test_macos_checks.py b/qtpy/tests/test_macos_checks.py index 18683a80..01aa8091 100644 --- a/qtpy/tests/test_macos_checks.py +++ b/qtpy/tests/test_macos_checks.py @@ -5,7 +5,7 @@ import sys import pytest -from qtpy import PYQT5, QT_VERSION +from qtpy import PYQT5, PYSIDE2 @pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") @@ -55,7 +55,8 @@ def test_qt59_no_exception(mac_ver, monkeypatch): pytest.fail("Error!") -@pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), + reason="Targeted to PyQt5 or PySide2") @mock.patch.object(platform, 'mac_ver') def test_qt511_exception(mac_ver, monkeypatch): # Remove qtpy to reimport it again @@ -69,7 +70,10 @@ def test_qt511_exception(mac_ver, monkeypatch): mac_ver.return_value = ('10.10.3',) # Patch Qt version - monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + if PYQT5: + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + else: + monkeypatch.setattr("PySide2.QtCore.__version__", '5.11.1') # This should raise an Exception with pytest.raises(Exception) as e: @@ -79,7 +83,8 @@ def test_qt511_exception(mac_ver, monkeypatch): assert '5.11' in str(e.value) -@pytest.mark.skipif(not PYQT5, reason="Targeted to PyQt5") +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), + reason="Targeted to PyQt5 or PySide2") @mock.patch.object(platform, 'mac_ver') def test_qt511_no_exception(mac_ver, monkeypatch): # Remove qtpy to reimport it again @@ -93,7 +98,10 @@ def test_qt511_no_exception(mac_ver, monkeypatch): mac_ver.return_value = ('10.13.2',) # Patch Qt version - monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + if PYQT5: + monkeypatch.setattr("PyQt5.QtCore.QT_VERSION_STR", '5.11.1') + else: + monkeypatch.setattr("PySide2.QtCore.__version__", '5.11.1') # This should not raise an Exception try: From a3ceaada32cda7c468276094a6d8b98c27d01c09 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 25 Aug 2018 12:30:08 -0500 Subject: [PATCH 132/703] Testing: Add missing mock dependency --- .circleci/install.sh | 2 +- appveyor.yml | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.circleci/install.sh b/.circleci/install.sh index 53ca36e6..c5249985 100755 --- a/.circleci/install.sh +++ b/.circleci/install.sh @@ -2,7 +2,7 @@ export TRAVIS_OS_NAME="linux" export CONDA_DEPENDENCIES_FLAGS="--quiet" -export CONDA_DEPENDENCIES="pytest pytest-cov" +export CONDA_DEPENDENCIES="pytest pytest-cov mock" export PIP_DEPENDENCIES="coveralls" # Download and install miniconda and conda/pip dependencies diff --git a/appveyor.yml b/appveyor.yml index 779ad0eb..5869caa7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,18 +15,18 @@ environment: matrix: # Qt4 - PYTHON_VERSION: "2.7" - CONDA_DEPENDENCIES: "pytest pytest-cov qt=4.* pyside" + CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=4.* pyside" - PYTHON_VERSION: "2.7" - CONDA_DEPENDENCIES: "pytest pytest-cov qt=4.* pyqt=4.*" + CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=4.* pyqt=4.*" - PYTHON_VERSION: "3.5" - CONDA_DEPENDENCIES: "pytest pytest-cov qt=4.* pyqt=4.*" + CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=4.* pyqt=4.*" # Qt5 - PYTHON_VERSION: "2.7" - CONDA_DEPENDENCIES: "pytest pytest-cov qt=5.* pyqt=5.*" + CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=5.* pyqt=5.*" - PYTHON_VERSION: "3.5" - CONDA_DEPENDENCIES: "pytest pytest-cov qt=5.* pyqt=5.*" + CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=5.* pyqt=5.*" - PYTHON_VERSION: "3.6" - CONDA_DEPENDENCIES: "pytest pytest-cov" + CONDA_DEPENDENCIES: "pytest pytest-cov mock" PIP_DEPENDENCIES: "pyqt5==5.9.2" PIP_DEPENDENCIES_FLAGS: "-q" From 6b292d3ae0722e5c96b15e352b9f1dc747e0533a Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 25 Aug 2018 13:12:56 -0500 Subject: [PATCH 133/703] Shim PyQt5 QDateTime.toPyDateTime to QDateTime.toPython for compatibility with PySide2 --- qtpy/QtCore.py | 4 ++++ qtpy/tests/test_qtcore.py | 12 ++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 289fcac2..c5400efd 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -20,6 +20,10 @@ from PyQt5.QtCore import pyqtProperty as Property from PyQt5.QtCore import QT_VERSION_STR as __version__ + # For issue #153 + from PyQt5.QtCore import QDateTime + QDateTime.toPython = QDateTime.toPyDateTime + # Those are imported from `import *` del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR elif PYSIDE2: diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 8dc8f74a..7a337bfa 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,10 +1,18 @@ from __future__ import absolute_import import pytest -from qtpy import QtCore +from qtpy import PYQT5, PYSIDE2, QtCore """Test QtCore.""" + def test_qtmsghandler(): - """Test the qtpy.QtMsgHandler""" + """Test qtpy.QtMsgHandler""" assert QtCore.qInstallMessageHandler is not None + + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), + reason="Targeted to PyQt5 or PySide2") +def test_DateTime_toPython(): + """Test QDateTime.toPython""" + assert QtCore.QDateTime.toPython is not None From 5abb5aee1e9c1a9f48c6d1aa0237e3558e2a79c5 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 25 Aug 2018 14:09:48 -0500 Subject: [PATCH 134/703] Release: Add missing instruction --- RELEASE.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RELEASE.md b/RELEASE.md index 8dedf615..fff5cb02 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,5 +1,7 @@ To release a new version of qtpy on PyPI: +* Close Github milestone + * git fetch upstream && git merge upstream/master * git clean -xfdi From e85a104d3f89d44e098fc57d9ed270f0b2ee76d0 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 25 Aug 2018 14:17:30 -0500 Subject: [PATCH 135/703] Update Changelog --- CHANGELOG.md | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50756d9c..3b9ebb18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ # History of changes +## Version 1.5 (2018-08-25) + +### New features + +* Add support for QtLocation, QtMultimediaWidgets, QtQml, QtQuick, + QtWebChannel, QtWebSockets and QtXmlPatterns. +* Raise an error when trying to use the wrong combination of macOS + and Qt versions. + +### Issues Closed + +* [Issue 155](https://github.com/spyder-ide/qtpy/issues/155) - Add warnings for Qt 5.9 in macOS 10.9 and Qt 5.11 and macOS 10.11 ([PR 168](https://github.com/spyder-ide/qtpy/pull/168)) +* [Issue 153](https://github.com/spyder-ide/qtpy/issues/153) - Shim PyQt5 ToPyDateTime for compatibility with PySide2 ([PR 169](https://github.com/spyder-ide/qtpy/pull/169)) +* [Issue 123](https://github.com/spyder-ide/qtpy/issues/123) - Wrap QWebChannel module ([PR 157](https://github.com/spyder-ide/qtpy/pull/157)) + +In this release 3 issues were closed. + +### Pull Requests Merged + +* [PR 169](https://github.com/spyder-ide/qtpy/pull/169) - PR: Shim PyQt5 QDateTime.toPyDateTime to QDateTime.toPython for compatibility with PySide2 ([153](https://github.com/spyder-ide/qtpy/issues/153)) +* [PR 168](https://github.com/spyder-ide/qtpy/pull/168) - PR: Raise error when trying to use the wrong combination of macOS and Qt versions ([155](https://github.com/spyder-ide/qtpy/issues/155)) +* [PR 167](https://github.com/spyder-ide/qtpy/pull/167) - PR: Migrate to CircleCI 2.0 +* [PR 163](https://github.com/spyder-ide/qtpy/pull/163) - PR: Add QtLocation +* [PR 162](https://github.com/spyder-ide/qtpy/pull/162) - PR: Update readme to remove funding appeal, harmonize with other readmes and minor fixes +* [PR 161](https://github.com/spyder-ide/qtpy/pull/161) - PR: Fix pyside2 wheels install +* [PR 157](https://github.com/spyder-ide/qtpy/pull/157) - PR: Add more Qt modules ([123](https://github.com/spyder-ide/qtpy/issues/123)) + +In this release 7 pull requests were closed. + + +---- + + ## Version 1.4.2 (2018-05-06) ### Issues Closed @@ -51,8 +84,7 @@ In this release 3 pull requests were closed. ### New features -* Add support for QtHelp -* Add support for QtSql +* Add support for QtHelp and QtSql * Use already imported bindings ### Issues Closed @@ -105,9 +137,7 @@ In this release 3 pull requests were merged ### New features -* Add support for PySide2 -* Add support for QtMultimedia -* Add support for PyQt 4.6 +* Add support for PySide2 and PyQt 4.6 ### Bugs fixed From 507a68ee7df9af84c457a18c890e78d5b82e4332 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 25 Aug 2018 14:18:12 -0500 Subject: [PATCH 136/703] Release 1.5.0 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 7907ee4e..509e723e 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 5, 0, 'dev0') +version_info = (1, 5, 0) __version__ = '.'.join(map(str, version_info)) From f7a2a9091dab8a065a5356d5b2f5cf6e328b1088 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 25 Aug 2018 14:20:24 -0500 Subject: [PATCH 137/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 509e723e..8948c57e 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 5, 0) +version_info = (1, 6, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 459e66e3bfae603006ec8e2401fb73689aac0bbd Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 18 Sep 2018 08:58:28 -0500 Subject: [PATCH 138/703] Fix tests --- .circleci/install.sh | 8 +++++--- .circleci/test-pyqt4.sh | 4 ++-- .circleci/test-pyqt5.sh | 5 +++-- .circleci/test-pyside.sh | 4 ++-- .circleci/test-pyside2.sh | 4 ++-- qtpy/__init__.py | 8 ++++---- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/.circleci/install.sh b/.circleci/install.sh index c5249985..b941bb0d 100755 --- a/.circleci/install.sh +++ b/.circleci/install.sh @@ -10,8 +10,10 @@ export PIP_DEPENDENCIES="coveralls" echo -e "PYTHON = $PYTHON_VERSION \n============" git clone git://github.com/astropy/ci-helpers.git > /dev/null source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh -export PATH="$HOME/miniconda/bin:$PATH" -source activate test + +# Activate conda +source $HOME/miniconda/etc/profile.d/conda.sh +conda activate test # Install the package in develop mode -python setup.py develop +pip install -e . diff --git a/.circleci/test-pyqt4.sh b/.circleci/test-pyqt4.sh index 5b3dec74..60fa8cc1 100755 --- a/.circleci/test-pyqt4.sh +++ b/.circleci/test-pyqt4.sh @@ -1,7 +1,7 @@ #!/bin/bash -export PATH="$HOME/miniconda/bin:$PATH" -source activate test +source $HOME/miniconda/etc/profile.d/conda.sh +conda activate test if [ "$USE_CONDA" = "No" ]; then exit 0 diff --git a/.circleci/test-pyqt5.sh b/.circleci/test-pyqt5.sh index d7b18dec..3aef03ee 100755 --- a/.circleci/test-pyqt5.sh +++ b/.circleci/test-pyqt5.sh @@ -1,10 +1,11 @@ #!/bin/bash -export PATH="$HOME/miniconda/bin:$PATH" -source activate test +source $HOME/miniconda/etc/profile.d/conda.sh +conda activate test if [ "$USE_CONDA" = "Yes" ]; then conda install -q qt=5.* pyqt=5.* + conda install -q sip=4.19.8 else # We are getting segfaults in 5.10 pip install -q pyqt5==5.9.2 diff --git a/.circleci/test-pyside.sh b/.circleci/test-pyside.sh index f9fd41da..4ade0246 100755 --- a/.circleci/test-pyside.sh +++ b/.circleci/test-pyside.sh @@ -1,7 +1,7 @@ #!/bin/bash -export PATH="$HOME/miniconda/bin:$PATH" -source activate test +source $HOME/miniconda/etc/profile.d/conda.sh +conda activate test if [ "$USE_CONDA" = "No" ]; then exit 0 diff --git a/.circleci/test-pyside2.sh b/.circleci/test-pyside2.sh index 6e2518ac..d89f1f04 100755 --- a/.circleci/test-pyside2.sh +++ b/.circleci/test-pyside2.sh @@ -1,7 +1,7 @@ #!/bin/bash -export PATH="$HOME/miniconda/bin:$PATH" -source activate test +source $HOME/miniconda/etc/profile.d/conda.sh +conda activate test # Download PySide2 wheels export URL="http://download.qt.io/snapshots/ci/pyside/5.11/latest/" diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 8db8caff..79f5856b 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -73,7 +73,7 @@ class PythonQtError(Exception): - """Error raise if no bindings could be selected""" + """Error raise if no bindings could be selected.""" pass @@ -131,13 +131,13 @@ class PythonQtWarning(Warning): if sys.platform == 'darwin': macos_version = LooseVersion(platform.mac_ver()[0]) if macos_version < LooseVersion('10.10'): - if LooseVersion(QT_VERSION) >= '5.9': + if LooseVersion(QT_VERSION) >= LooseVersion('5.9'): raise PythonQtError("Qt 5.9 or higher only works in " "macOS 10.10 or higher. Your " "program will fail in this " "system.") elif macos_version < LooseVersion('10.11'): - if LooseVersion(QT_VERSION) >= '5.11': + if LooseVersion(QT_VERSION) >= LooseVersion('5.11'): raise PythonQtError("Qt 5.11 or higher only works in " "macOS 10.11 or higher. Your " "program will fail in this " @@ -159,7 +159,7 @@ class PythonQtWarning(Warning): if sys.platform == 'darwin': macos_version = LooseVersion(platform.mac_ver()[0]) if macos_version < LooseVersion('10.11'): - if LooseVersion(QT_VERSION) >= '5.11': + if LooseVersion(QT_VERSION) >= LooseVersion('5.11'): raise PythonQtError("Qt 5.11 or higher only works in " "macOS 10.11 or higher. Your " "program will fail in this " From 0c41817f42ebc9050fa69925e3aa4138b648a40c Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 18 Sep 2018 10:29:05 -0500 Subject: [PATCH 139/703] Testing: Select PyQt5 conda builds with QtMultimedia --- .circleci/test-pyqt5.sh | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.circleci/test-pyqt5.sh b/.circleci/test-pyqt5.sh index 3aef03ee..fa04af93 100755 --- a/.circleci/test-pyqt5.sh +++ b/.circleci/test-pyqt5.sh @@ -3,8 +3,17 @@ source $HOME/miniconda/etc/profile.d/conda.sh conda activate test +# Select build with QtMultimedia +if [ "$PYTHON_VERSION" = "2.7" ]; then + export BUILD=py27h22d08a2_0 +elif [ "$PYTHON_VERSION" = "3.5" ]; then + export BUILD=py35h751905a_0 +else + export BUILD=py36h751905a_0 +fi + if [ "$USE_CONDA" = "Yes" ]; then - conda install -q qt=5.* pyqt=5.* + conda install -q qt=5.* pyqt=5.9.2=$BUILD conda install -q sip=4.19.8 else # We are getting segfaults in 5.10 From 14a0e08ecfe7f5e78e4f6740b5564f6849241173 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 18 Sep 2018 10:43:04 -0500 Subject: [PATCH 140/703] Testing: Use default PySide2 wheels --- .circleci/config.yml | 7 +++++++ .circleci/test-pyqt5.sh | 9 +++++++-- .circleci/test-pyside2.sh | 13 +++---------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3293bea0..b0fa1525 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -44,6 +44,12 @@ jobs: - PYTHON_VERSION: "3.6" - USE_CONDA: "Yes" + python2.7-pip: + <<: *main + environment: + - PYTHON_VERSION: "2.7" + - USE_CONDA: "No" + python3.6-pip: <<: *main environment: @@ -57,4 +63,5 @@ workflows: - python2.7 - python3.5 - python3.6 + - python2.7-pip - python3.6-pip diff --git a/.circleci/test-pyqt5.sh b/.circleci/test-pyqt5.sh index fa04af93..7ed56713 100755 --- a/.circleci/test-pyqt5.sh +++ b/.circleci/test-pyqt5.sh @@ -16,8 +16,13 @@ if [ "$USE_CONDA" = "Yes" ]; then conda install -q qt=5.* pyqt=5.9.2=$BUILD conda install -q sip=4.19.8 else - # We are getting segfaults in 5.10 - pip install -q pyqt5==5.9.2 + if [ "$PYTHON_VERSION" = "2.7" ]; then + # There are no pyqt5 wheels for Python 2 + exit 0 + else + # We are getting segfaults in 5.10 + pip install -q pyqt5==5.9.2 + fi fi python qtpy/tests/runtests.py diff --git a/.circleci/test-pyside2.sh b/.circleci/test-pyside2.sh index d89f1f04..06f119cf 100755 --- a/.circleci/test-pyside2.sh +++ b/.circleci/test-pyside2.sh @@ -3,19 +3,12 @@ source $HOME/miniconda/etc/profile.d/conda.sh conda activate test -# Download PySide2 wheels -export URL="http://download.qt.io/snapshots/ci/pyside/5.11/latest/" - if [ "$USE_CONDA" = "Yes" ]; then - if [ "$PYTHON_VERSION" = "2.7" ]; then - conda remove -q qt pyqt - pip install -q --index-url=${URL} pyside2 --trusted-host download.qt.io - else - exit 0 - fi + # There are no conda packages for PySide2 + exit 0 else pip uninstall -q -y pyqt5 sip - pip install -q --index-url=${URL} pyside2 --trusted-host download.qt.io + pip install -q pyside2 fi python qtpy/tests/runtests.py From a3811c24d075079da593443b5f263e583ef675b7 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 18 Sep 2018 11:03:59 -0500 Subject: [PATCH 141/703] Make PythonQtError inherit from RuntimeError to be easily catchable --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 79f5856b..855f44ea 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -72,7 +72,7 @@ from ._version import __version__ -class PythonQtError(Exception): +class PythonQtError(RuntimeError): """Error raise if no bindings could be selected.""" pass From 9a183a05967a215e40d16561657fa183c7ee5042 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 18 Sep 2018 11:47:26 -0500 Subject: [PATCH 142/703] Update Changelog --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b9ebb18..7f4523d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # History of changes +## Version 1.5.1 (2018-09-18) + +### Issues Closed + +* [Issue 170](https://github.com/spyder-ide/qtpy/issues/170) - Can't catch PythonQtError ([PR 173](https://github.com/spyder-ide/qtpy/pull/173)) + +In this release 1 issue was closed. + +### Pull Requests Merged + +* [PR 173](https://github.com/spyder-ide/qtpy/pull/173) - PR: Make PythonQtError inherit from RuntimeError to be easily catchable ([170](https://github.com/spyder-ide/qtpy/issues/170)) + +In this release 1 pull request was closed. + +---- + + ## Version 1.5 (2018-08-25) ### New features From 3755a0507a74d0b8f6a79543f320fd648f4ffcab Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 18 Sep 2018 11:48:11 -0500 Subject: [PATCH 143/703] Release 1.5.1 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 8948c57e..e80d3122 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 6, 0, 'dev0') +version_info = (1, 5, 1) __version__ = '.'.join(map(str, version_info)) From 977d4b772defde88ae4bb3f26109faa8966e7646 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 18 Sep 2018 11:49:55 -0500 Subject: [PATCH 144/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index e80d3122..8948c57e 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 5, 1) +version_info = (1, 6, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From c627fe4ccb39917a206c8639267402244f35b12d Mon Sep 17 00:00:00 2001 From: Kurt Jacobson Date: Tue, 16 Oct 2018 15:07:48 -0400 Subject: [PATCH 145/703] Add support for PySide2.QtOpenGL --- qtpy/QtOpenGL.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index ef62171a..92600aed 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -8,10 +8,12 @@ """Provides QtOpenGL classes and functions.""" # Local imports -from . import PYQT4, PYQT5, PYSIDE, PythonQtError +from . import PYQT4, PYQT5, PYSIDE, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtOpenGL import * +if PYSIDE2: + from PySide2.QtOpenGL import * elif PYQT4: from PyQt4.QtOpenGL import * elif PYSIDE: @@ -19,4 +21,4 @@ else: raise PythonQtError('No Qt bindings could be found') -del PYQT4, PYQT5, PYSIDE +del PYQT4, PYQT5, PYSIDE, PYSIDE2 From 45b4a300b81d5d41bd9737301b969c05996a9a7a Mon Sep 17 00:00:00 2001 From: Kurt Jacobson Date: Tue, 16 Oct 2018 15:34:42 -0400 Subject: [PATCH 146/703] Fix typo in if statement --- qtpy/QtOpenGL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 92600aed..69ef8228 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -12,7 +12,7 @@ if PYQT5: from PyQt5.QtOpenGL import * -if PYSIDE2: +elif PYSIDE2: from PySide2.QtOpenGL import * elif PYQT4: from PyQt4.QtOpenGL import * From 45eb9bf8a0bc71826c5748c56b7e9de155a64b40 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 16 Oct 2018 16:10:04 -0500 Subject: [PATCH 147/703] Testing: Don't fail when running coverage --- .circleci/coverage.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/coverage.sh b/.circleci/coverage.sh index 6e4cef2e..f9c4f704 100755 --- a/.circleci/coverage.sh +++ b/.circleci/coverage.sh @@ -5,3 +5,6 @@ export PATH="$HOME/miniconda/bin:$PATH" source activate test coveralls + +# Don't fail at this step +exit 0 From 543e0f4b596cdfd1d6c0d3172ef5803ac975244a Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 16 Oct 2018 16:17:34 -0500 Subject: [PATCH 148/703] Skip QtMultimedia tests on Windows and Python 3.5 --- qtpy/tests/test_qtmultimedia.py | 4 ++++ qtpy/tests/test_qtmultimediawidgets.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 02b415ff..7fc5cf66 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -1,8 +1,12 @@ from __future__ import absolute_import +import os +import sys import pytest +@pytest.mark.skipif(os.name == 'nt' and sys.version_info[:2] == (3, 5), + reason="Conda packages don't seem to include QtMultimedia") def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" from qtpy import QtMultimedia diff --git a/qtpy/tests/test_qtmultimediawidgets.py b/qtpy/tests/test_qtmultimediawidgets.py index c0389a88..2bb52d51 100644 --- a/qtpy/tests/test_qtmultimediawidgets.py +++ b/qtpy/tests/test_qtmultimediawidgets.py @@ -1,9 +1,13 @@ from __future__ import absolute_import +import os +import sys import pytest from qtpy import PYQT5, PYSIDE2 @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +@pytest.mark.skipif(os.name == 'nt' and sys.version_info[:2] == (3, 5), + reason="Conda packages don't seem to include QtMultimedia") def test_qtmultimediawidgets(): """Test the qtpy.QtMultimediaWidgets namespace""" from qtpy import QtMultimediaWidgets From 7b84c4e9494654ebab2897a21bbbe67afeae9efb Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 20 Oct 2018 12:41:22 -0500 Subject: [PATCH 149/703] Update Changelog --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f4523d1..47d6d32d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # History of changes +## Version 1.5.2 (2018-10-20) + + +### Pull Requests Merged + +* [PR 175](https://github.com/spyder-ide/qtpy/pull/175) - PR: Fix tests +* [PR 174](https://github.com/spyder-ide/qtpy/pull/174) - PR: Add support for PySide2.QtOpenGL + +In this release 2 pull requests were closed. + + +---- + + ## Version 1.5.1 (2018-09-18) ### Issues Closed From 00911abd24e926a1299a46bd80293eae6f5123df Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 20 Oct 2018 12:41:53 -0500 Subject: [PATCH 150/703] Release 1.5.2 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 8948c57e..2e414caf 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 6, 0, 'dev0') +version_info = (1, 5, 2) __version__ = '.'.join(map(str, version_info)) From d6b2614abad46ce454b5690a13298116c5cc63b9 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 20 Oct 2018 12:43:25 -0500 Subject: [PATCH 151/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 2e414caf..8948c57e 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 5, 2) +version_info = (1, 6, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 2eb18c74a477285cc6bcd65895fa27f206b00238 Mon Sep 17 00:00:00 2001 From: Ignacio Martinez Date: Mon, 3 Dec 2018 11:14:53 -0300 Subject: [PATCH 152/703] Do not warn user if he did not specify a binding --- qtpy/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 855f44ea..30d761e1 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -101,7 +101,9 @@ class PythonQtWarning(Warning): PYSIDE2_API = ['pyside2'] # Setting a default value for QT_API -os.environ.setdefault(QT_API, 'pyqt5') +binding_specified = PYQT5_API[0] in os.environ[QT_API] +if not binding_specified: + os.environ[QT_API] = PYQT5_API[0] API = os.environ[QT_API].lower() initial_api = API @@ -206,7 +208,7 @@ class PythonQtWarning(Warning): # If a correct API name is passed to QT_API and it could not be found, # switches to another and informs through the warning -if API != initial_api: +if API != initial_api and binding_specified: warnings.warn('Selected binding "{}" could not be found, ' 'using "{}"'.format(initial_api, API), RuntimeWarning) From 1e34123550c4f38ba12f44e771f81780ebebbc2b Mon Sep 17 00:00:00 2001 From: Ignacio Martinez Date: Mon, 3 Dec 2018 13:14:04 -0300 Subject: [PATCH 153/703] Fix typo when detecting if binding was specified --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 30d761e1..b582f928 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -101,7 +101,7 @@ class PythonQtWarning(Warning): PYSIDE2_API = ['pyside2'] # Setting a default value for QT_API -binding_specified = PYQT5_API[0] in os.environ[QT_API] +binding_specified = PYQT5_API[0] in os.environ if not binding_specified: os.environ[QT_API] = PYQT5_API[0] From 5ef0edc1690c4222ee27a510947d99767f52be79 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 10 Jan 2019 16:29:07 -0500 Subject: [PATCH 154/703] QtCore: Add __version__ to PySide2 --- qtpy/QtCore.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index c5400efd..f583b05f 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -28,10 +28,14 @@ del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR elif PYSIDE2: from PySide2.QtCore import * + try: # may be limited to PySide-5.11a1 only from PySide2.QtGui import QStringListModel except: pass + + import PySide2.QtCore + __version__ = PySide2.QtCore.__version__ elif PYQT4: from PyQt4.QtCore import * # Those are things we inherited from Spyder that fix crazy crashes under From b5dbc34fc07cf1a5f594297051720cbaccbdcb0a Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 10 Jan 2019 16:30:53 -0500 Subject: [PATCH 155/703] Testing: Skip segfaulting tests in PySide2 --- qtpy/tests/test_patch_qcombobox.py | 6 +++++- qtpy/tests/test_uic.py | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_patch_qcombobox.py b/qtpy/tests/test_patch_qcombobox.py index 2e5e6fe3..fcafe56a 100644 --- a/qtpy/tests/test_patch_qcombobox.py +++ b/qtpy/tests/test_patch_qcombobox.py @@ -1,5 +1,6 @@ from __future__ import absolute_import +import os import sys import pytest @@ -25,7 +26,8 @@ def __getitem__(self, item): raise ValueError("Failing") -@pytest.mark.skipif(PY3 or PYSIDE2, reason="It segfaults in Python 3 and PYSIDE2") +@pytest.mark.skipif(PY3 or (PYSIDE2 and os.environ.get('CI', None) is not None), + reason="It segfaults in Python 3 and in our CIs with PySide2") def test_patched_qcombobox(): """ In PySide, using Python objects as userData in QComboBox causes @@ -84,6 +86,8 @@ def test_patched_qcombobox(): assert widget.itemText(6) == 'f' +@pytest.mark.skipif((PYSIDE2 and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2") def test_model_item(): """ This is a regression test for an issue that caused the call to item(0) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 1c50e9fe..9a0fd280 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -42,6 +42,8 @@ def get_qapp(icon_path=None): return qapp +@pytest.mark.skipif((PYSIDE2 and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2") def test_load_ui(): """ Make sure that the patched loadUi function behaves as expected with a @@ -53,6 +55,8 @@ def test_load_ui(): assert isinstance(ui.comboBox, QComboBox) +@pytest.mark.skipif((PYSIDE2 and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2") def test_load_ui_custom_auto(tmpdir): """ Test that we can load a .ui file with custom widgets without having to @@ -70,11 +74,10 @@ def test_load_ui_custom_auto(tmpdir): assert isinstance(ui.comboBox, _QComboBoxSubclass) -@pytest.mark.skipif(PYSIDE2, reason="It fails on PySide2") def test_load_full_uic(): """Test that we load the full uic objects for PyQt5 and PyQt4.""" QT_API = os.environ.get('QT_API', '').lower() - if QT_API == 'pyside': + if QT_API.startswith('pyside'): assert hasattr(uic, 'loadUi') assert not hasattr(uic, 'loadUiType') else: From 83c4dbbf1f02e2d791cd120752932b30ea9062ac Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 10 Jan 2019 17:40:12 -0500 Subject: [PATCH 156/703] QtWebEngineWidgets: Restore QWebEngineSettings for PySide2 --- qtpy/QtWebEngineWidgets.py | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index c5577a22..d1df5bfb 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -28,17 +28,9 @@ from PyQt5.QtWebKit import QWebSettings as QWebEngineSettings WEBENGINE = False elif PYSIDE2: - try: - from PySide2.QtWebEngineWidgets import QWebEnginePage - from PySide2.QtWebEngineWidgets import QWebEngineView - # Current PySide2 wheels seem to be missing this. - # from PySide2.QtWebEngineWidgets import QWebEngineSettings - except ImportError: - from PySide2.QtWebKitWidgets import QWebPage as QWebEnginePage - from PySide2.QtWebKitWidgets import QWebView as QWebEngineView - # Current PySide2 wheels seem to be missing this. - # from PySide2.QtWebKit import QWebSettings as QWebEngineSettings - WEBENGINE = False + from PySide2.QtWebEngineWidgets import QWebEnginePage + from PySide2.QtWebEngineWidgets import QWebEngineView + from PySide2.QtWebEngineWidgets import QWebEngineSettings elif PYQT4: from PyQt4.QtWebKit import QWebPage as QWebEnginePage from PyQt4.QtWebKit import QWebView as QWebEngineView From 8d1216cae58f179e903b1f4e7873ba8282ca77bd Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 10 Jan 2019 17:46:32 -0500 Subject: [PATCH 157/703] Testing: Add a test for QtWebEngineWidgets --- qtpy/tests/test_qtwebenginewidgets.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 qtpy/tests/test_qtwebenginewidgets.py diff --git a/qtpy/tests/test_qtwebenginewidgets.py b/qtpy/tests/test_qtwebenginewidgets.py new file mode 100644 index 00000000..3486905e --- /dev/null +++ b/qtpy/tests/test_qtwebenginewidgets.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +import pytest + + +def test_qtwebenginewidgets(): + """Test the qtpy.QtWebSockets namespace""" + from qtpy import QtWebEngineWidgets + + assert QtWebEngineWidgets.QWebEnginePage is not None + assert QtWebEngineWidgets.QWebEngineView is not None + assert QtWebEngineWidgets.QWebEngineSettings is not None From 1eeffc2c07c93fe7c5bfcb2de78bd2c4856f92f1 Mon Sep 17 00:00:00 2001 From: Kurt Jacobson Date: Sun, 16 Dec 2018 17:50:32 -0500 Subject: [PATCH 158/703] Add QtQuickWidgets --- qtpy/QtQuickWidgets.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 qtpy/QtQuickWidgets.py diff --git a/qtpy/QtQuickWidgets.py b/qtpy/QtQuickWidgets.py new file mode 100644 index 00000000..545d52b6 --- /dev/null +++ b/qtpy/QtQuickWidgets.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtQuickWidgets classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtQuickWidgets import * +elif PYSIDE2: + from PySide2.QtQuickWidgets import * +else: + raise PythonQtError('No Qt bindings could be found') From 21385b3646ea146c78410f4e3bc4df70a04cbca9 Mon Sep 17 00:00:00 2001 From: Kurt Jacobson Date: Thu, 10 Jan 2019 18:53:59 -0500 Subject: [PATCH 159/703] Add tests for QtQuickWidgets --- qtpy/tests/test_qtquickwidgets.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 qtpy/tests/test_qtquickwidgets.py diff --git a/qtpy/tests/test_qtquickwidgets.py b/qtpy/tests/test_qtquickwidgets.py new file mode 100644 index 00000000..0b41a8bd --- /dev/null +++ b/qtpy/tests/test_qtquickwidgets.py @@ -0,0 +1,10 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtquickwidgets(): + """Test the qtpy.QtQuickWidgets namespace""" + from qtpy import QtQuickWidgets + assert QtQuickWidgets.QQuickWidget is not None From dc09143973640b2873dae7434ce654535fbfdd8c Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Thu, 10 Jan 2019 20:28:59 -0500 Subject: [PATCH 160/703] Tesitng: Fix failing tests in Python 2 --- qtpy/tests/test_qtwebenginewidgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtwebenginewidgets.py b/qtpy/tests/test_qtwebenginewidgets.py index 3486905e..77c8e1f5 100644 --- a/qtpy/tests/test_qtwebenginewidgets.py +++ b/qtpy/tests/test_qtwebenginewidgets.py @@ -1,11 +1,11 @@ from __future__ import absolute_import import pytest +from qtpy import QtWebEngineWidgets def test_qtwebenginewidgets(): """Test the qtpy.QtWebSockets namespace""" - from qtpy import QtWebEngineWidgets assert QtWebEngineWidgets.QWebEnginePage is not None assert QtWebEngineWidgets.QWebEngineView is not None From d845f3d9b263d2518100eb0a3ba1c35444ae33c7 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 11 Jan 2019 19:35:45 -0500 Subject: [PATCH 161/703] Update release instructions to use twine --- RELEASE.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index fff5cb02..834408bf 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -12,9 +12,11 @@ To release a new version of qtpy on PyPI: * git add and git commit -* python setup.py sdist upload +* python setup.py sdist -* python setup.py bdist_wheel upload +* python setup.py bdist_wheel + +* twine upload dist/* * git tag -a vX.X.X -m 'comment' From 2e689e0de32e5136a08c2b572538a987a748adfc Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 11 Jan 2019 19:39:24 -0500 Subject: [PATCH 162/703] Update Changelog --- CHANGELOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47d6d32d..f090d798 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # History of changes +## Version 1.6 (2019-01-12) + +### New features + +* Add support for QtQuickWidgets. + +### Issues Closed + +* [Issue 178](https://github.com/spyder-ide/qtpy/issues/178) - Error when import QtCore.__version__ in PySide2 ([PR 180](https://github.com/spyder-ide/qtpy/pull/180)) + +In this release 1 issue was closed. + +### Pull Requests Merged + +* [PR 181](https://github.com/spyder-ide/qtpy/pull/181) - PR: Restore QWebEngineSettings for PySide2 +* [PR 180](https://github.com/spyder-ide/qtpy/pull/180) - PR: Add QtCore.__version__ for PySide2 ([178](https://github.com/spyder-ide/qtpy/issues/178)) +* [PR 179](https://github.com/spyder-ide/qtpy/pull/179) - PR: Add QtQuickWidgets + +In this release 3 pull requests were closed. + + +---- + + ## Version 1.5.2 (2018-10-20) From 4f1bffb9040f7e012251afbde0a523a2105d1ec0 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 11 Jan 2019 19:41:18 -0500 Subject: [PATCH 163/703] Release 1.6.0 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 8948c57e..ff98aa3b 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 6, 0, 'dev0') +version_info = (1, 6, 0) __version__ = '.'.join(map(str, version_info)) From 725c7f109ecdf9bb79d1c36ac44dc42562a6daf1 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 11 Jan 2019 19:43:32 -0500 Subject: [PATCH 164/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index ff98aa3b..0125ef77 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 6, 0) +version_info = (1, 7, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 739dfde3e1e4f2c5ca7ef533debcca5d42b0f422 Mon Sep 17 00:00:00 2001 From: Federico Miorelli Date: Fri, 25 Jan 2019 11:39:01 +0100 Subject: [PATCH 165/703] Prevent warnings for equivalent APIs Situation: user has already imported PyQt4 and QT_API is set to pyqt. qtpy will issue a spurious warning complaining that initial_api pyqt is different from selected one pyqt4, even if they are the same. This code will make sure that in case of pre-loaded QT modules, the API is reset to the same initial_api if compatible, otherwise to the default. --- qtpy/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 855f44ea..cebf0ced 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -113,13 +113,13 @@ class PythonQtWarning(Warning): if 'PyQt5' in sys.modules: - API = 'pyqt5' + API = initial_api if initial_api in PYQT5_API else 'pyqt5' elif 'PySide2' in sys.modules: - API = 'pyside2' + API = initial_api if initial_api in PYSIDE2_API else 'pyside2' elif 'PyQt4' in sys.modules: - API = 'pyqt4' + API = initial_api if initial_api in PYQT4_API else 'pyqt4' elif 'PySide' in sys.modules: - API = 'pyside' + API = initial_api if initial_api in PYSIDE_API else 'pyside' if API in PYQT5_API: From a228a667829be5822cf810fb06b53bf03a2b7b39 Mon Sep 17 00:00:00 2001 From: phil65 Date: Fri, 1 Feb 2019 22:38:57 +0100 Subject: [PATCH 166/703] add QtCharts module --- qtpy/QtCharts.py | 22 ++++++++++++++++++++++ qtpy/tests/test_qtcharts.py | 11 +++++++++++ 2 files changed, 33 insertions(+) create mode 100644 qtpy/QtCharts.py create mode 100644 qtpy/tests/test_qtcharts.py diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py new file mode 100644 index 00000000..74671230 --- /dev/null +++ b/qtpy/QtCharts.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2019- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtChart classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + try: + from PyQt5 import QtChart as QtCharts + except ImportError: + raise PythonQtError('The QtChart module was not found. ' + 'It needs to be installed separately for PyQt5.') +elif PYSIDE2: + from PySide2.QtCharts import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtcharts.py b/qtpy/tests/test_qtcharts.py new file mode 100644 index 00000000..4c72dbc3 --- /dev/null +++ b/qtpy/tests/test_qtcharts.py @@ -0,0 +1,11 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYSIDE2 + + +@pytest.mark.skipif(not PYSIDE2, reason="Only available by default in PySide2") +def test_qtcharts(): + """Test the qtpy.QtCharts namespace""" + from qtpy import QtCharts + assert QtCharts.QtCharts.QChart is not None From ce5264a3dad4a333db69a729e4f63fb60d24f2d4 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 5 Mar 2019 11:20:28 +0100 Subject: [PATCH 167/703] Improve logic for detecting a binding specified by the user --- qtpy/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index b582f928..0735a72e 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -100,10 +100,11 @@ class PythonQtWarning(Warning): # Names of the expected PySide2 api PYSIDE2_API = ['pyside2'] +# Detecting if a binding was specified by the user +binding_specified = QT_API in os.environ + # Setting a default value for QT_API -binding_specified = PYQT5_API[0] in os.environ -if not binding_specified: - os.environ[QT_API] = PYQT5_API[0] +os.environ.setdefault(QT_API, 'pyqt5') API = os.environ[QT_API].lower() initial_api = API From 45667c9c2e486c6284fd6caf82939f86b1db4ff0 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 5 Mar 2019 11:47:18 +0100 Subject: [PATCH 168/703] Minor improvements to Readme --- README.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/README.md b/README.md index ecda29af..24fd048c 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,8 @@ [![Build status](https://ci.appveyor.com/api/projects/status/62y6i02vhn4hefg0/branch/master?svg=true)](https://ci.appveyor.com/project/spyder-ide/qtpy/branch/master) [![CircleCI](https://circleci.com/gh/spyder-ide/qtpy.svg?style=shield)](https://circleci.com/gh/spyder-ide/qtpy) [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/spyder-ide/qtpy/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/spyder-ide/qtpy/?branch=master) -*Copyright © 2009–2018 The Spyder Development Team* +*Copyright © 2009–2019 The Spyder Development Team* ## Description @@ -77,13 +76,6 @@ conda install qtpy Everyone is welcome to contribute! -## Backers - -Support us with a monthly donation and help us continue our activities. - -[![Backers](https://opencollective.com/spyder/backers.svg)](https://opencollective.com/spyder#support) - - ## Sponsors Become a sponsor to get your logo on our README on Github. From 32142bd0e0e1a4e8a47e4b23085a525871f0ca02 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 5 Mar 2019 11:53:17 +0100 Subject: [PATCH 169/703] Generate PyPI long description from README.md --- setup.py | 49 +++++++------------------------------------------ 1 file changed, 7 insertions(+), 42 deletions(-) diff --git a/setup.py b/setup.py index da092eab..439525f9 100644 --- a/setup.py +++ b/setup.py @@ -6,54 +6,18 @@ """ import os +import io + from setuptools import setup, find_packages -here = os.path.abspath(os.path.dirname(__file__)) +HERE = os.path.abspath(os.path.dirname(__file__)) version_ns = {} -with open(os.path.join(here, 'qtpy', '_version.py')) as f: +with open(os.path.join(HERE, 'qtpy', '_version.py')) as f: exec(f.read(), {}, version_ns) -LONG_DESCRIPTION = """ -.. image:: https://img.shields.io/pypi/v/QtPy.svg - :target: https://pypi.python.org/pypi/QtPy/ - :alt: Latest PyPI version - -.. image:: https://img.shields.io/pypi/dm/QtPy.svg - :target: https://pypi.python.org/pypi/QtPy/ - :alt: Number of PyPI downloads - -QtPy: Abtraction layer for PyQt5/PyQt4/PySide -============================================= - -**QtPy** (pronounced *'cutie pie'*) is a small abstraction layer that lets you -write applications using a single api call to either PyQt or PySide. - -It provides support for PyQt5, PyQt4 and PySide using the PyQt5 layout (where -the QtGui module has been split into QtGui and QtWidgets). - -Basically, you write your code as if you were using PyQt5 but import qt from -``qtpy`` instead of ``PyQt5``. - -- `Issue tracker`_ -- `Changelog`_ - - -Attribution and acknowledgements --------------------------------- - -This project is based on the `pyqode.qt`_ project and the `spyderlib.qt`_ -module from the `spyder`_ project. - -Unlike **pyqode.qt** this is not a namespace package so it is not *tied* -to a particular project, or namespace. - -.. _spyder: https://github.com/spyder-ide/spyder -.. _spyderlib.qt: https://github.com/spyder-ide/spyder/tree/master/spyderlib/qt -.. _pyqode.qt: https://github.com/pyQode/pyqode.qt -.. _Changelog: https://github.com/spyder-ide/qtpy/blob/master/CHANGELOG.md -.. _Issue tracker: https://github.com/spyder-ide/qtpy/issues -""" +with io.open(os.path.join(HERE, 'README.md'), encoding='utf-8') as f: + LONG_DESCRIPTION = f.read() setup( name='QtPy', @@ -70,6 +34,7 @@ 'bindings (PyQt5, PyQt4 and PySide) and additional custom ' 'QWidgets.', long_description=LONG_DESCRIPTION, + long_description_content_type='text/markdown', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: X11 Applications :: Qt', From ff6f99c4c175b6892351040db2877ab6811d2d8a Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 16 Mar 2019 11:43:54 +0100 Subject: [PATCH 170/703] Update Changelog --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f090d798..a4c9e39f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # History of changes +## Version 1.7.0 (2019-03-16) + +### New features + +* Add support for QtCharts. + +### Pull Requests Merged + +* [PR 186](https://github.com/spyder-ide/qtpy/pull/186) - PR: Generate PyPI long description from README.md +* [PR 183](https://github.com/spyder-ide/qtpy/pull/183) - PR: Add QtCharts module support +* [PR 182](https://github.com/spyder-ide/qtpy/pull/182) - PR: Prevent warnings for equivalent APIs +* [PR 176](https://github.com/spyder-ide/qtpy/pull/176) - PR: Don't warn about bindings change if user did not specify a binding + +In this release 4 pull requests were closed. + + +---- + + ## Version 1.6 (2019-01-12) ### New features From a3014b5d0f5d910cae3b5b1daf0d41686ac800dc Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 16 Mar 2019 11:44:53 +0100 Subject: [PATCH 171/703] Release 1.7.0 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 0125ef77..79fe076d 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 7, 0, 'dev0') +version_info = (1, 7, 0) __version__ = '.'.join(map(str, version_info)) From f22ca102b934c4605c0eaeae947c2f2ec90de9a5 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 16 Mar 2019 11:47:51 +0100 Subject: [PATCH 172/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 79fe076d..304a2f40 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 7, 0) +version_info = (1, 8, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 5eacf3bd11b3c2fefdbab8f0e2a71040e2712d25 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 3 May 2019 11:55:10 +0200 Subject: [PATCH 173/703] Testing: Use "-ex" in bash scripts --- .circleci/install.sh | 2 +- .circleci/test-pyqt4.sh | 7 +------ .circleci/test-pyqt5.sh | 7 +------ .circleci/test-pyside.sh | 7 +------ .circleci/test-pyside2.sh | 7 +------ 5 files changed, 5 insertions(+), 25 deletions(-) diff --git a/.circleci/install.sh b/.circleci/install.sh index b941bb0d..c51c2df7 100755 --- a/.circleci/install.sh +++ b/.circleci/install.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -ex export TRAVIS_OS_NAME="linux" export CONDA_DEPENDENCIES_FLAGS="--quiet" diff --git a/.circleci/test-pyqt4.sh b/.circleci/test-pyqt4.sh index 60fa8cc1..c772c98a 100755 --- a/.circleci/test-pyqt4.sh +++ b/.circleci/test-pyqt4.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -ex source $HOME/miniconda/etc/profile.d/conda.sh conda activate test @@ -11,8 +11,3 @@ else fi python qtpy/tests/runtests.py - -# Force quitting if exit status of runtests.py was not 0 -if [ $? -ne 0 ]; then - exit 1 -fi diff --git a/.circleci/test-pyqt5.sh b/.circleci/test-pyqt5.sh index 7ed56713..d614ec71 100755 --- a/.circleci/test-pyqt5.sh +++ b/.circleci/test-pyqt5.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -ex source $HOME/miniconda/etc/profile.d/conda.sh conda activate test @@ -26,8 +26,3 @@ else fi python qtpy/tests/runtests.py - -# Force quitting if exit status of runtests.py was not 0 -if [ $? -ne 0 ]; then - exit 1 -fi diff --git a/.circleci/test-pyside.sh b/.circleci/test-pyside.sh index 4ade0246..33086f61 100755 --- a/.circleci/test-pyside.sh +++ b/.circleci/test-pyside.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -ex source $HOME/miniconda/etc/profile.d/conda.sh conda activate test @@ -11,8 +11,3 @@ else fi python qtpy/tests/runtests.py - -# Force quitting if exit status of runtests.py was not 0 -if [ $? -ne 0 ]; then - exit 1 -fi diff --git a/.circleci/test-pyside2.sh b/.circleci/test-pyside2.sh index 06f119cf..a7d08a4a 100755 --- a/.circleci/test-pyside2.sh +++ b/.circleci/test-pyside2.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/bash -ex source $HOME/miniconda/etc/profile.d/conda.sh conda activate test @@ -13,9 +13,4 @@ fi python qtpy/tests/runtests.py -# Force quitting if exit status of runtests.py was not 0 -if [ $? -ne 0 ]; then - exit 1 -fi - pip uninstall -y -q pyside2 From 75dba9be6d1d7e3541d91db2c470bb8ed1289cc2 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Fri, 3 May 2019 11:58:33 +0200 Subject: [PATCH 174/703] Testing: Skip testing PyQt4 and PySide in Python 3.5 --- .circleci/test-pyqt4.sh | 4 ++++ .circleci/test-pyside.sh | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.circleci/test-pyqt4.sh b/.circleci/test-pyqt4.sh index c772c98a..b38caf11 100755 --- a/.circleci/test-pyqt4.sh +++ b/.circleci/test-pyqt4.sh @@ -5,6 +5,10 @@ conda activate test if [ "$USE_CONDA" = "No" ]; then exit 0 +elif [ "$PYTHON_VERSION" = "3.5" ]; then + # conda-forge doesn't provide packages for + # Python 3.5 anymore. + exit 0 else conda remove -q qt pyqt conda install -q -c conda-forge qt=4.* pyqt=4.* diff --git a/.circleci/test-pyside.sh b/.circleci/test-pyside.sh index 33086f61..65ef35af 100755 --- a/.circleci/test-pyside.sh +++ b/.circleci/test-pyside.sh @@ -5,6 +5,10 @@ conda activate test if [ "$USE_CONDA" = "No" ]; then exit 0 +elif [ "$PYTHON_VERSION" = "3.5" ]; then + # conda-forge doesn't provide packages for + # Python 3.5 anymore. + exit 0 else conda remove -q qt pyqt conda install -q -c conda-forge qt=4.* pyside From 96a9b44bb049f23997235f2715994aaa4317ea3f Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Thu, 2 May 2019 23:29:25 -0700 Subject: [PATCH 175/703] py3compat: compare ints to determine PY2 and PY3 Signed-off-by: David Aguilar --- qtpy/py3compat.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/py3compat.py b/qtpy/py3compat.py index 4d393bd3..996dd7a3 100755 --- a/qtpy/py3compat.py +++ b/qtpy/py3compat.py @@ -21,8 +21,8 @@ import sys import os -PY2 = sys.version[0] == '2' -PY3 = sys.version[0] == '3' +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 # ============================================================================= From 059b26e14f7bd3d39766a0b5e97f337630b560c7 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Thu, 2 May 2019 23:22:55 -0700 Subject: [PATCH 176/703] py3compat: avoid deprecated "from collections import MutableMapping" MutableMapping migrated to `collections.abc` in Python3.3 and will be removed from `collections` in Python3.8. Use `collections.abc` in Python3.3+ to silence deprecation warnings seen on Python3.7. `qtpy` is now Python3.8-compatible. Signed-off-by: David Aguilar --- qtpy/py3compat.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qtpy/py3compat.py b/qtpy/py3compat.py index 996dd7a3..971073a5 100755 --- a/qtpy/py3compat.py +++ b/qtpy/py3compat.py @@ -23,6 +23,7 @@ PY2 = sys.version_info[0] == 2 PY3 = sys.version_info[0] == 3 +PY33 = PY3 and sys.version_info[1] >= 3 # ============================================================================= @@ -73,7 +74,10 @@ from sys import maxsize import io import pickle - from collections import MutableMapping + if PY33: + from collections.abc import MutableMapping + else: + from collections import MutableMapping import _thread import reprlib From 2e2de422b6d06189814a400cdff1d87274b531d6 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Thu, 2 May 2019 23:47:22 -0700 Subject: [PATCH 177/703] py3compat: use unix line endings Signed-off-by: David Aguilar --- qtpy/py3compat.py | 530 +++++++++++++++++++++++----------------------- 1 file changed, 265 insertions(+), 265 deletions(-) diff --git a/qtpy/py3compat.py b/qtpy/py3compat.py index 971073a5..582fd8ee 100755 --- a/qtpy/py3compat.py +++ b/qtpy/py3compat.py @@ -1,265 +1,265 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012-2013 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.py3compat -------------------- - -Transitional module providing compatibility functions intended to help -migrating from Python 2 to Python 3. - -This module should be fully compatible with: - * Python >=v2.6 - * Python 3 -""" - -from __future__ import print_function - -import sys -import os - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY33 = PY3 and sys.version_info[1] >= 3 - - -# ============================================================================= -# Data types -# ============================================================================= -if PY2: - # Python 2 - TEXT_TYPES = (str, unicode) - INT_TYPES = (int, long) -else: - # Python 3 - TEXT_TYPES = (str,) - INT_TYPES = (int,) -NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex]) - - -# ============================================================================= -# Renamed/Reorganized modules -# ============================================================================= -if PY2: - # Python 2 - import __builtin__ as builtins - import ConfigParser as configparser - try: - import _winreg as winreg - except ImportError: - pass - from sys import maxint as maxsize - try: - import CStringIO as io - except ImportError: - import StringIO as io - try: - import cPickle as pickle - except ImportError: - import pickle - from UserDict import DictMixin as MutableMapping - import thread as _thread - import repr as reprlib -else: - # Python 3 - import builtins - import configparser - try: - import winreg - except ImportError: - pass - from sys import maxsize - import io - import pickle - if PY33: - from collections.abc import MutableMapping - else: - from collections import MutableMapping - import _thread - import reprlib - - -# ============================================================================= -# Strings -# ============================================================================= -if PY2: - # Python 2 - import codecs - - def u(obj): - """Make unicode object""" - return codecs.unicode_escape_decode(obj)[0] -else: - # Python 3 - def u(obj): - """Return string as it is""" - return obj - - -def is_text_string(obj): - """Return True if `obj` is a text string, False if it is anything else, - like binary data (Python 3) or QString (Python 2, PyQt API #1)""" - if PY2: - # Python 2 - return isinstance(obj, basestring) - else: - # Python 3 - return isinstance(obj, str) - - -def is_binary_string(obj): - """Return True if `obj` is a binary string, False if it is anything else""" - if PY2: - # Python 2 - return isinstance(obj, str) - else: - # Python 3 - return isinstance(obj, bytes) - - -def is_string(obj): - """Return True if `obj` is a text or binary Python string object, - False if it is anything else, like a QString (Python 2, PyQt API #1)""" - return is_text_string(obj) or is_binary_string(obj) - - -def is_unicode(obj): - """Return True if `obj` is unicode""" - if PY2: - # Python 2 - return isinstance(obj, unicode) - else: - # Python 3 - return isinstance(obj, str) - - -def to_text_string(obj, encoding=None): - """Convert `obj` to (unicode) text string""" - if PY2: - # Python 2 - if encoding is None: - return unicode(obj) - else: - return unicode(obj, encoding) - else: - # Python 3 - if encoding is None: - return str(obj) - elif isinstance(obj, str): - # In case this function is not used properly, this could happen - return obj - else: - return str(obj, encoding) - - -def to_binary_string(obj, encoding=None): - """Convert `obj` to binary string (bytes in Python 3, str in Python 2)""" - if PY2: - # Python 2 - if encoding is None: - return str(obj) - else: - return obj.encode(encoding) - else: - # Python 3 - return bytes(obj, 'utf-8' if encoding is None else encoding) - - -# ============================================================================= -# Function attributes -# ============================================================================= -def get_func_code(func): - """Return function code object""" - if PY2: - # Python 2 - return func.func_code - else: - # Python 3 - return func.__code__ - - -def get_func_name(func): - """Return function name""" - if PY2: - # Python 2 - return func.func_name - else: - # Python 3 - return func.__name__ - - -def get_func_defaults(func): - """Return function default argument values""" - if PY2: - # Python 2 - return func.func_defaults - else: - # Python 3 - return func.__defaults__ - - -# ============================================================================= -# Special method attributes -# ============================================================================= -def get_meth_func(obj): - """Return method function object""" - if PY2: - # Python 2 - return obj.im_func - else: - # Python 3 - return obj.__func__ - - -def get_meth_class_inst(obj): - """Return method class instance""" - if PY2: - # Python 2 - return obj.im_self - else: - # Python 3 - return obj.__self__ - - -def get_meth_class(obj): - """Return method class""" - if PY2: - # Python 2 - return obj.im_class - else: - # Python 3 - return obj.__self__.__class__ - - -# ============================================================================= -# Misc. -# ============================================================================= -if PY2: - # Python 2 - input = raw_input - getcwd = os.getcwdu - cmp = cmp - import string - str_lower = string.lower - from itertools import izip_longest as zip_longest -else: - # Python 3 - input = input - getcwd = os.getcwd - - def cmp(a, b): - return (a > b) - (a < b) - str_lower = str.lower - from itertools import zip_longest - - -def qbytearray_to_str(qba): - """Convert QByteArray object to str in a way compatible with Python 2/3""" - return str(bytes(qba.toHex().data()).decode()) - - -if __name__ == '__main__': - pass +# -*- coding: utf-8 -*- +# +# Copyright © 2012-2013 Pierre Raybaut +# Licensed under the terms of the MIT License +# (see spyderlib/__init__.py for details) + +""" +spyderlib.py3compat +------------------- + +Transitional module providing compatibility functions intended to help +migrating from Python 2 to Python 3. + +This module should be fully compatible with: + * Python >=v2.6 + * Python 3 +""" + +from __future__ import print_function + +import sys +import os + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 +PY33 = PY3 and sys.version_info[1] >= 3 + + +# ============================================================================= +# Data types +# ============================================================================= +if PY2: + # Python 2 + TEXT_TYPES = (str, unicode) + INT_TYPES = (int, long) +else: + # Python 3 + TEXT_TYPES = (str,) + INT_TYPES = (int,) +NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex]) + + +# ============================================================================= +# Renamed/Reorganized modules +# ============================================================================= +if PY2: + # Python 2 + import __builtin__ as builtins + import ConfigParser as configparser + try: + import _winreg as winreg + except ImportError: + pass + from sys import maxint as maxsize + try: + import CStringIO as io + except ImportError: + import StringIO as io + try: + import cPickle as pickle + except ImportError: + import pickle + from UserDict import DictMixin as MutableMapping + import thread as _thread + import repr as reprlib +else: + # Python 3 + import builtins + import configparser + try: + import winreg + except ImportError: + pass + from sys import maxsize + import io + import pickle + if PY33: + from collections.abc import MutableMapping + else: + from collections import MutableMapping + import _thread + import reprlib + + +# ============================================================================= +# Strings +# ============================================================================= +if PY2: + # Python 2 + import codecs + + def u(obj): + """Make unicode object""" + return codecs.unicode_escape_decode(obj)[0] +else: + # Python 3 + def u(obj): + """Return string as it is""" + return obj + + +def is_text_string(obj): + """Return True if `obj` is a text string, False if it is anything else, + like binary data (Python 3) or QString (Python 2, PyQt API #1)""" + if PY2: + # Python 2 + return isinstance(obj, basestring) + else: + # Python 3 + return isinstance(obj, str) + + +def is_binary_string(obj): + """Return True if `obj` is a binary string, False if it is anything else""" + if PY2: + # Python 2 + return isinstance(obj, str) + else: + # Python 3 + return isinstance(obj, bytes) + + +def is_string(obj): + """Return True if `obj` is a text or binary Python string object, + False if it is anything else, like a QString (Python 2, PyQt API #1)""" + return is_text_string(obj) or is_binary_string(obj) + + +def is_unicode(obj): + """Return True if `obj` is unicode""" + if PY2: + # Python 2 + return isinstance(obj, unicode) + else: + # Python 3 + return isinstance(obj, str) + + +def to_text_string(obj, encoding=None): + """Convert `obj` to (unicode) text string""" + if PY2: + # Python 2 + if encoding is None: + return unicode(obj) + else: + return unicode(obj, encoding) + else: + # Python 3 + if encoding is None: + return str(obj) + elif isinstance(obj, str): + # In case this function is not used properly, this could happen + return obj + else: + return str(obj, encoding) + + +def to_binary_string(obj, encoding=None): + """Convert `obj` to binary string (bytes in Python 3, str in Python 2)""" + if PY2: + # Python 2 + if encoding is None: + return str(obj) + else: + return obj.encode(encoding) + else: + # Python 3 + return bytes(obj, 'utf-8' if encoding is None else encoding) + + +# ============================================================================= +# Function attributes +# ============================================================================= +def get_func_code(func): + """Return function code object""" + if PY2: + # Python 2 + return func.func_code + else: + # Python 3 + return func.__code__ + + +def get_func_name(func): + """Return function name""" + if PY2: + # Python 2 + return func.func_name + else: + # Python 3 + return func.__name__ + + +def get_func_defaults(func): + """Return function default argument values""" + if PY2: + # Python 2 + return func.func_defaults + else: + # Python 3 + return func.__defaults__ + + +# ============================================================================= +# Special method attributes +# ============================================================================= +def get_meth_func(obj): + """Return method function object""" + if PY2: + # Python 2 + return obj.im_func + else: + # Python 3 + return obj.__func__ + + +def get_meth_class_inst(obj): + """Return method class instance""" + if PY2: + # Python 2 + return obj.im_self + else: + # Python 3 + return obj.__self__ + + +def get_meth_class(obj): + """Return method class""" + if PY2: + # Python 2 + return obj.im_class + else: + # Python 3 + return obj.__self__.__class__ + + +# ============================================================================= +# Misc. +# ============================================================================= +if PY2: + # Python 2 + input = raw_input + getcwd = os.getcwdu + cmp = cmp + import string + str_lower = string.lower + from itertools import izip_longest as zip_longest +else: + # Python 3 + input = input + getcwd = os.getcwd + + def cmp(a, b): + return (a > b) - (a < b) + str_lower = str.lower + from itertools import zip_longest + + +def qbytearray_to_str(qba): + """Convert QByteArray object to str in a way compatible with Python 2/3""" + return str(bytes(qba.toHex().data()).decode()) + + +if __name__ == '__main__': + pass From 7fc4aadd003bdb36874c4184b61342c5f94142d7 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Thu, 2 May 2019 23:48:36 -0700 Subject: [PATCH 178/703] py3compat: typechange: remove executable bit Signed-off-by: David Aguilar --- qtpy/py3compat.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 qtpy/py3compat.py diff --git a/qtpy/py3compat.py b/qtpy/py3compat.py old mode 100755 new mode 100644 From b86103392e47bf78aca5574874c23f2148b43422 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Thu, 2 May 2019 23:49:30 -0700 Subject: [PATCH 179/703] py3compat: remove unused __main__ boilerplate py3compat does not need to be executable, and has no main(), so remove the __main__ boilerplate. Signed-off-by: David Aguilar --- qtpy/py3compat.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/qtpy/py3compat.py b/qtpy/py3compat.py index 582fd8ee..43550b64 100644 --- a/qtpy/py3compat.py +++ b/qtpy/py3compat.py @@ -259,7 +259,3 @@ def cmp(a, b): def qbytearray_to_str(qba): """Convert QByteArray object to str in a way compatible with Python 2/3""" return str(bytes(qba.toHex().data()).decode()) - - -if __name__ == '__main__': - pass From d2801493af304329e32ec1b8c13811cd8dda4ae3 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sat, 4 May 2019 14:31:30 -0700 Subject: [PATCH 180/703] compat: use unix line endings Signed-off-by: David Aguilar --- qtpy/compat.py | 392 ++++++++++++++++++++++++------------------------- 1 file changed, 196 insertions(+), 196 deletions(-) diff --git a/qtpy/compat.py b/qtpy/compat.py index e6bdd1b4..f5794548 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -1,196 +1,196 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2009- The Spyder Development Team -# Licensed under the terms of the MIT License - -""" -Compatibility functions -""" - -from __future__ import print_function -import sys -import collections - -from . import PYQT4 -from .QtWidgets import QFileDialog -from .py3compat import is_text_string, to_text_string, TEXT_TYPES - - -# ============================================================================= -# QVariant conversion utilities -# ============================================================================= -PYQT_API_1 = False -if PYQT4: - import sip - try: - PYQT_API_1 = sip.getapi('QVariant') == 1 # PyQt API #1 - except AttributeError: - # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" - # Calling QFileDialog static method - if sys.platform == "win32": - # On Windows platforms: redirect standard outputs - _temp1, _temp2 = sys.stdout, sys.stderr - sys.stdout, sys.stderr = None, None - try: - result = QFileDialog.getExistingDirectory(parent, caption, basedir, - options) - finally: - if sys.platform == "win32": - # On Windows platforms: restore standard outputs - sys.stdout, sys.stderr = _temp1, _temp2 - if not is_text_string(result): - # PyQt API #1 - result = to_text_string(result) - return result - - -def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', - filters='', selectedfilter='', options=None): - if options is None: - options = QFileDialog.Options(0) - try: - # PyQt =v4.6 - QString = None # analysis:ignore - tuple_returned = True - try: - # PyQt >=v4.6 - func = getattr(QFileDialog, attr+'AndFilter') - except AttributeError: - # PySide or PyQt =v4.6 - output, selectedfilter = result - else: - # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" - return _qfiledialog_wrapper('getOpenFileName', parent=parent, - caption=caption, basedir=basedir, - filters=filters, selectedfilter=selectedfilter, - options=options) - - -def getopenfilenames(parent=None, caption='', basedir='', filters='', - selectedfilter='', options=None): - """Wrapper around QtGui.QFileDialog.getOpenFileNames static method - Returns a tuple (filenames, selectedfilter) -- when dialog box is canceled, - returns a tuple (empty list, empty string) - Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" - return _qfiledialog_wrapper('getOpenFileNames', parent=parent, - caption=caption, basedir=basedir, - filters=filters, selectedfilter=selectedfilter, - options=options) - - -def getsavefilename(parent=None, caption='', basedir='', filters='', - selectedfilter='', options=None): - """Wrapper around QtGui.QFileDialog.getSaveFileName static method - Returns a tuple (filename, selectedfilter) -- when dialog box is canceled, - returns a tuple of empty strings - Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" - return _qfiledialog_wrapper('getSaveFileName', parent=parent, - caption=caption, basedir=basedir, - filters=filters, selectedfilter=selectedfilter, - options=options) +# -*- coding: utf-8 -*- +# +# Copyright © 2009- The Spyder Development Team +# Licensed under the terms of the MIT License + +""" +Compatibility functions +""" + +from __future__ import print_function +import sys +import collections + +from . import PYQT4 +from .QtWidgets import QFileDialog +from .py3compat import is_text_string, to_text_string, TEXT_TYPES + + +# ============================================================================= +# QVariant conversion utilities +# ============================================================================= +PYQT_API_1 = False +if PYQT4: + import sip + try: + PYQT_API_1 = sip.getapi('QVariant') == 1 # PyQt API #1 + except AttributeError: + # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" + # Calling QFileDialog static method + if sys.platform == "win32": + # On Windows platforms: redirect standard outputs + _temp1, _temp2 = sys.stdout, sys.stderr + sys.stdout, sys.stderr = None, None + try: + result = QFileDialog.getExistingDirectory(parent, caption, basedir, + options) + finally: + if sys.platform == "win32": + # On Windows platforms: restore standard outputs + sys.stdout, sys.stderr = _temp1, _temp2 + if not is_text_string(result): + # PyQt API #1 + result = to_text_string(result) + return result + + +def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', + filters='', selectedfilter='', options=None): + if options is None: + options = QFileDialog.Options(0) + try: + # PyQt =v4.6 + QString = None # analysis:ignore + tuple_returned = True + try: + # PyQt >=v4.6 + func = getattr(QFileDialog, attr+'AndFilter') + except AttributeError: + # PySide or PyQt =v4.6 + output, selectedfilter = result + else: + # PyQt =v4.4 (API #1 and #2) and PySide >=v1.0""" + return _qfiledialog_wrapper('getOpenFileName', parent=parent, + caption=caption, basedir=basedir, + filters=filters, selectedfilter=selectedfilter, + options=options) + + +def getopenfilenames(parent=None, caption='', basedir='', filters='', + selectedfilter='', options=None): + """Wrapper around QtGui.QFileDialog.getOpenFileNames static method + Returns a tuple (filenames, selectedfilter) -- when dialog box is canceled, + returns a tuple (empty list, empty string) + Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" + return _qfiledialog_wrapper('getOpenFileNames', parent=parent, + caption=caption, basedir=basedir, + filters=filters, selectedfilter=selectedfilter, + options=options) + + +def getsavefilename(parent=None, caption='', basedir='', filters='', + selectedfilter='', options=None): + """Wrapper around QtGui.QFileDialog.getSaveFileName static method + Returns a tuple (filename, selectedfilter) -- when dialog box is canceled, + returns a tuple of empty strings + Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" + return _qfiledialog_wrapper('getSaveFileName', parent=parent, + caption=caption, basedir=basedir, + filters=filters, selectedfilter=selectedfilter, + options=options) From 6364cca673d4a8b1914704228a32c8e824e9e12d Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 5 May 2019 09:52:45 +0200 Subject: [PATCH 181/703] Update Changelog --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4c9e39f..29c7e741 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # History of changes +## Version 1.7.1 (2019-05-05) + + +### Pull Requests Merged + +* [PR 189](https://github.com/spyder-ide/qtpy/pull/189) - PR: Skip testing PyQt4 and PySide in Python 3.5 +* [PR 188](https://github.com/spyder-ide/qtpy/pull/188) - PR: Trivial maintenance tweaks +* [PR 187](https://github.com/spyder-ide/qtpy/pull/187) - PR: Avoid deprecated "from collections import MutableMapping" + +In this release 3 pull requests were closed. + + +---- + + ## Version 1.7.0 (2019-03-16) ### New features From 05ab35d3e48d75a5c3120fcdf931b5737f37cd2a Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 5 May 2019 09:54:03 +0200 Subject: [PATCH 182/703] Release 1.7.1 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 304a2f40..6b9d5f12 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 8, 0, 'dev0') +version_info = (1, 7, 1) __version__ = '.'.join(map(str, version_info)) From 6e3f0c0a20ed044f2f65297b743e31fcf76b6302 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 5 May 2019 09:56:37 +0200 Subject: [PATCH 183/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 6b9d5f12..304a2f40 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 7, 1) +version_info = (1, 8, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 13202df54a8a527c5faea69516fc8fb99ba602b8 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Tue, 11 Jun 2019 20:05:45 +0200 Subject: [PATCH 184/703] Add Qt 3D bindings --- qtpy/Qt3DAnimation.py | 22 ++++++ qtpy/Qt3DCore.py | 22 ++++++ qtpy/Qt3DExtras.py | 22 ++++++ qtpy/Qt3DInput.py | 22 ++++++ qtpy/Qt3DLogic.py | 22 ++++++ qtpy/Qt3DRender.py | 22 ++++++ qtpy/tests/test_qt3danimation.py | 25 +++++++ qtpy/tests/test_qt3dcore.py | 44 ++++++++++++ qtpy/tests/test_qt3dextras.py | 47 ++++++++++++ qtpy/tests/test_qt3dinput.py | 33 +++++++++ qtpy/tests/test_qt3dlogic.py | 12 ++++ qtpy/tests/test_qt3drender.py | 119 +++++++++++++++++++++++++++++++ 12 files changed, 412 insertions(+) create mode 100644 qtpy/Qt3DAnimation.py create mode 100644 qtpy/Qt3DCore.py create mode 100644 qtpy/Qt3DExtras.py create mode 100644 qtpy/Qt3DInput.py create mode 100644 qtpy/Qt3DLogic.py create mode 100644 qtpy/Qt3DRender.py create mode 100644 qtpy/tests/test_qt3danimation.py create mode 100644 qtpy/tests/test_qt3dcore.py create mode 100644 qtpy/tests/test_qt3dextras.py create mode 100644 qtpy/tests/test_qt3dinput.py create mode 100644 qtpy/tests/test_qt3dlogic.py create mode 100644 qtpy/tests/test_qt3drender.py diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py new file mode 100644 index 00000000..ace6cf0f --- /dev/null +++ b/qtpy/Qt3DAnimation.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DAnimation classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.Qt3DAnimation import * +elif PYSIDE2: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DAnimation as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DAnimation): + globals()[__name[0]] = __name[1] +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py new file mode 100644 index 00000000..2ae2a9e2 --- /dev/null +++ b/qtpy/Qt3DCore.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DCore classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.Qt3DCore import * +elif PYSIDE2: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DCore as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DCore): + globals()[__name[0]] = __name[1] +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py new file mode 100644 index 00000000..5996b270 --- /dev/null +++ b/qtpy/Qt3DExtras.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DExtras classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.Qt3DExtras import * +elif PYSIDE2: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DExtras as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DExtras): + globals()[__name[0]] = __name[1] +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py new file mode 100644 index 00000000..072fc44a --- /dev/null +++ b/qtpy/Qt3DInput.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DInput classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.Qt3DInput import * +elif PYSIDE2: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DInput as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DInput): + globals()[__name[0]] = __name[1] +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py new file mode 100644 index 00000000..d85fdb42 --- /dev/null +++ b/qtpy/Qt3DLogic.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DLogic classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.Qt3DLogic import * +elif PYSIDE2: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DLogic as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DLogic): + globals()[__name[0]] = __name[1] +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py new file mode 100644 index 00000000..e42ee52c --- /dev/null +++ b/qtpy/Qt3DRender.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides Qt3DRender classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.Qt3DRender import * +elif PYSIDE2: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DRender as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DRender): + globals()[__name[0]] = __name[1] +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qt3danimation.py b/qtpy/tests/test_qt3danimation.py new file mode 100644 index 00000000..650be19e --- /dev/null +++ b/qtpy/tests/test_qt3danimation.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3danimation(): + """Test the qtpy.Qt3DAnimation namespace""" + Qt3DAnimation = pytest.importorskip("qtpy.Qt3DAnimation") + + assert Qt3DAnimation.QAnimationController is not None + assert Qt3DAnimation.QAdditiveClipBlend is not None + assert Qt3DAnimation.QAbstractClipBlendNode is not None + assert Qt3DAnimation.QAbstractAnimation is not None + assert Qt3DAnimation.QKeyframeAnimation is not None + assert Qt3DAnimation.QAbstractAnimationClip is not None + assert Qt3DAnimation.QAbstractClipAnimator is not None + assert Qt3DAnimation.QClipAnimator is not None + assert Qt3DAnimation.QAnimationGroup is not None + assert Qt3DAnimation.QLerpClipBlend is not None + assert Qt3DAnimation.QMorphingAnimation is not None + assert Qt3DAnimation.QAnimationAspect is not None + assert Qt3DAnimation.QVertexBlendAnimation is not None + assert Qt3DAnimation.QBlendedClipAnimator is not None + assert Qt3DAnimation.QMorphTarget is not None diff --git a/qtpy/tests/test_qt3dcore.py b/qtpy/tests/test_qt3dcore.py new file mode 100644 index 00000000..821fbd45 --- /dev/null +++ b/qtpy/tests/test_qt3dcore.py @@ -0,0 +1,44 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3dcore(): + """Test the qtpy.Qt3DCore namespace""" + Qt3DCore = pytest.importorskip("qtpy.Qt3DCore") + + assert Qt3DCore.QPropertyValueAddedChange is not None + assert Qt3DCore.QSkeletonLoader is not None + assert Qt3DCore.QPropertyNodeRemovedChange is not None + assert Qt3DCore.QPropertyUpdatedChange is not None + assert Qt3DCore.QAspectEngine is not None + assert Qt3DCore.QPropertyValueAddedChangeBase is not None + assert Qt3DCore.QStaticPropertyValueRemovedChangeBase is not None + assert Qt3DCore.QPropertyNodeAddedChange is not None + assert Qt3DCore.QDynamicPropertyUpdatedChange is not None + assert Qt3DCore.QStaticPropertyUpdatedChangeBase is not None + assert Qt3DCore.ChangeFlags is not None + assert Qt3DCore.QAbstractAspect is not None + assert Qt3DCore.QBackendNode is not None + assert Qt3DCore.QTransform is not None + assert Qt3DCore.QPropertyUpdatedChangeBase is not None + assert Qt3DCore.QNodeId is not None + assert Qt3DCore.QJoint is not None + assert Qt3DCore.QSceneChange is not None + assert Qt3DCore.QNodeIdTypePair is not None + assert Qt3DCore.QAbstractSkeleton is not None + assert Qt3DCore.QComponentRemovedChange is not None + assert Qt3DCore.QComponent is not None + assert Qt3DCore.QEntity is not None + assert Qt3DCore.QNodeCommand is not None + assert Qt3DCore.QNode is not None + assert Qt3DCore.QPropertyValueRemovedChange is not None + assert Qt3DCore.QPropertyValueRemovedChangeBase is not None + assert Qt3DCore.QComponentAddedChange is not None + assert Qt3DCore.QNodeCreatedChangeBase is not None + assert Qt3DCore.QNodeDestroyedChange is not None + assert Qt3DCore.QArmature is not None + assert Qt3DCore.QStaticPropertyValueAddedChangeBase is not None + assert Qt3DCore.ChangeFlag is not None + assert Qt3DCore.QSkeleton is not None diff --git a/qtpy/tests/test_qt3dextras.py b/qtpy/tests/test_qt3dextras.py new file mode 100644 index 00000000..f63c7d57 --- /dev/null +++ b/qtpy/tests/test_qt3dextras.py @@ -0,0 +1,47 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3dextras(): + """Test the qtpy.Qt3DExtras namespace""" + Qt3DExtras = pytest.importorskip("qtpy.Qt3DExtras") + + assert Qt3DExtras.QTextureMaterial is not None + assert Qt3DExtras.QPhongAlphaMaterial is not None + assert Qt3DExtras.QOrbitCameraController is not None + assert Qt3DExtras.QAbstractSpriteSheet is not None + assert Qt3DExtras.QNormalDiffuseMapMaterial is not None + assert Qt3DExtras.QDiffuseSpecularMaterial is not None + assert Qt3DExtras.QSphereGeometry is not None + assert Qt3DExtras.QCuboidGeometry is not None + assert Qt3DExtras.QForwardRenderer is not None + assert Qt3DExtras.QPhongMaterial is not None + assert Qt3DExtras.QSpriteGrid is not None + assert Qt3DExtras.QDiffuseMapMaterial is not None + assert Qt3DExtras.QConeGeometry is not None + assert Qt3DExtras.QSpriteSheetItem is not None + assert Qt3DExtras.QPlaneGeometry is not None + assert Qt3DExtras.QSphereMesh is not None + assert Qt3DExtras.QNormalDiffuseSpecularMapMaterial is not None + assert Qt3DExtras.QCuboidMesh is not None + assert Qt3DExtras.QGoochMaterial is not None + assert Qt3DExtras.QText2DEntity is not None + assert Qt3DExtras.QTorusMesh is not None + assert Qt3DExtras.Qt3DWindow is not None + assert Qt3DExtras.QPerVertexColorMaterial is not None + assert Qt3DExtras.QExtrudedTextGeometry is not None + assert Qt3DExtras.QSkyboxEntity is not None + assert Qt3DExtras.QAbstractCameraController is not None + assert Qt3DExtras.QExtrudedTextMesh is not None + assert Qt3DExtras.QCylinderGeometry is not None + assert Qt3DExtras.QTorusGeometry is not None + assert Qt3DExtras.QMorphPhongMaterial is not None + assert Qt3DExtras.QPlaneMesh is not None + assert Qt3DExtras.QDiffuseSpecularMapMaterial is not None + assert Qt3DExtras.QSpriteSheet is not None + assert Qt3DExtras.QConeMesh is not None + assert Qt3DExtras.QFirstPersonCameraController is not None + assert Qt3DExtras.QMetalRoughMaterial is not None + assert Qt3DExtras.QCylinderMesh is not None diff --git a/qtpy/tests/test_qt3dinput.py b/qtpy/tests/test_qt3dinput.py new file mode 100644 index 00000000..48d73d03 --- /dev/null +++ b/qtpy/tests/test_qt3dinput.py @@ -0,0 +1,33 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3dinput(): + """Test the qtpy.Qt3DInput namespace""" + Qt3DInput = pytest.importorskip("qtpy.Qt3DInput") + + assert Qt3DInput.QAxisAccumulator is not None + assert Qt3DInput.QInputSettings is not None + assert Qt3DInput.QAnalogAxisInput is not None + assert Qt3DInput.QAbstractAxisInput is not None + assert Qt3DInput.QMouseHandler is not None + assert Qt3DInput.QButtonAxisInput is not None + assert Qt3DInput.QInputSequence is not None + assert Qt3DInput.QWheelEvent is not None + assert Qt3DInput.QActionInput is not None + assert Qt3DInput.QKeyboardDevice is not None + assert Qt3DInput.QMouseDevice is not None + assert Qt3DInput.QAxis is not None + assert Qt3DInput.QInputChord is not None + assert Qt3DInput.QMouseEvent is not None + assert Qt3DInput.QKeyboardHandler is not None + assert Qt3DInput.QKeyEvent is not None + assert Qt3DInput.QAbstractActionInput is not None + assert Qt3DInput.QInputAspect is not None + assert Qt3DInput.QLogicalDevice is not None + assert Qt3DInput.QAction is not None + assert Qt3DInput.QAbstractPhysicalDevice is not None + assert Qt3DInput.QAxisSetting is not None + diff --git a/qtpy/tests/test_qt3dlogic.py b/qtpy/tests/test_qt3dlogic.py new file mode 100644 index 00000000..34f7de67 --- /dev/null +++ b/qtpy/tests/test_qt3dlogic.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3dlogic(): + """Test the qtpy.Qt3DLogic namespace""" + Qt3DLogic = pytest.importorskip("qtpy.Qt3DLogic") + + assert Qt3DLogic.QLogicAspect is not None + assert Qt3DLogic.QFrameAction is not None diff --git a/qtpy/tests/test_qt3drender.py b/qtpy/tests/test_qt3drender.py new file mode 100644 index 00000000..f4647682 --- /dev/null +++ b/qtpy/tests/test_qt3drender.py @@ -0,0 +1,119 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qt3drender(): + """Test the qtpy.Qt3DRender namespace""" + Qt3DRender = pytest.importorskip("qtpy.Qt3DRender") + + assert Qt3DRender.QPointSize is not None + assert Qt3DRender.QFrustumCulling is not None + assert Qt3DRender.QPickPointEvent is not None + assert Qt3DRender.QRenderPassFilter is not None + assert Qt3DRender.QMesh is not None + assert Qt3DRender.QRayCaster is not None + assert Qt3DRender.QStencilMask is not None + assert Qt3DRender.QPickLineEvent is not None + assert Qt3DRender.QPickTriangleEvent is not None + assert Qt3DRender.QRenderState is not None + assert Qt3DRender.QTextureWrapMode is not None + assert Qt3DRender.QRenderPass is not None + assert Qt3DRender.QGeometryRenderer is not None + assert Qt3DRender.QAttribute is not None + assert Qt3DRender.QStencilOperation is not None + assert Qt3DRender.QScissorTest is not None + assert Qt3DRender.QTextureCubeMapArray is not None + assert Qt3DRender.QRenderTarget is not None + assert Qt3DRender.QStencilTest is not None + assert Qt3DRender.QTextureData is not None + assert Qt3DRender.QBuffer is not None + assert Qt3DRender.QLineWidth is not None + assert Qt3DRender.QLayer is not None + assert Qt3DRender.QTextureRectangle is not None + assert Qt3DRender.QRenderTargetSelector is not None + assert Qt3DRender.QPickingSettings is not None + assert Qt3DRender.QCullFace is not None + assert Qt3DRender.QAbstractFunctor is not None + assert Qt3DRender.PropertyReaderInterface is not None + assert Qt3DRender.QMaterial is not None + assert Qt3DRender.QAlphaCoverage is not None + assert Qt3DRender.QClearBuffers is not None + assert Qt3DRender.QAlphaTest is not None + assert Qt3DRender.QStencilOperationArguments is not None + assert Qt3DRender.QTexture2DMultisample is not None + assert Qt3DRender.QLevelOfDetailSwitch is not None + assert Qt3DRender.QRenderStateSet is not None + assert Qt3DRender.QViewport is not None + assert Qt3DRender.QObjectPicker is not None + assert Qt3DRender.QPolygonOffset is not None + assert Qt3DRender.QRenderSettings is not None + assert Qt3DRender.QFrontFace is not None + assert Qt3DRender.QTexture3D is not None + assert Qt3DRender.QTextureBuffer is not None + assert Qt3DRender.QTechniqueFilter is not None + assert Qt3DRender.QLayerFilter is not None + assert Qt3DRender.QFilterKey is not None + assert Qt3DRender.QRenderSurfaceSelector is not None + assert Qt3DRender.QEnvironmentLight is not None + assert Qt3DRender.QMemoryBarrier is not None + assert Qt3DRender.QNoDepthMask is not None + assert Qt3DRender.QBlitFramebuffer is not None + assert Qt3DRender.QGraphicsApiFilter is not None + assert Qt3DRender.QAbstractTexture is not None + assert Qt3DRender.QRenderCaptureReply is not None + assert Qt3DRender.QAbstractLight is not None + assert Qt3DRender.QAbstractRayCaster is not None + assert Qt3DRender.QDirectionalLight is not None + assert Qt3DRender.QDispatchCompute is not None + assert Qt3DRender.QBufferDataGenerator is not None + assert Qt3DRender.QPointLight is not None + assert Qt3DRender.QStencilTestArguments is not None + assert Qt3DRender.QTexture1D is not None + assert Qt3DRender.QCameraSelector is not None + assert Qt3DRender.QProximityFilter is not None + assert Qt3DRender.QTexture1DArray is not None + assert Qt3DRender.QBlendEquation is not None + assert Qt3DRender.QTextureImageDataGenerator is not None + assert Qt3DRender.QSpotLight is not None + assert Qt3DRender.QEffect is not None + assert Qt3DRender.QSeamlessCubemap is not None + assert Qt3DRender.QTexture2DMultisampleArray is not None + assert Qt3DRender.QComputeCommand is not None + assert Qt3DRender.QFrameGraphNode is not None + assert Qt3DRender.QSortPolicy is not None + assert Qt3DRender.QTextureImageData is not None + assert Qt3DRender.QCamera is not None + assert Qt3DRender.QGeometry is not None + assert Qt3DRender.QScreenRayCaster is not None + assert Qt3DRender.QClipPlane is not None + assert Qt3DRender.QMultiSampleAntiAliasing is not None + assert Qt3DRender.QRayCasterHit is not None + assert Qt3DRender.QAbstractTextureImage is not None + assert Qt3DRender.QNoDraw is not None + assert Qt3DRender.QPickEvent is not None + assert Qt3DRender.QRenderCapture is not None + assert Qt3DRender.QDepthTest is not None + assert Qt3DRender.QParameter is not None + assert Qt3DRender.QLevelOfDetail is not None + assert Qt3DRender.QGeometryFactory is not None + assert Qt3DRender.QTexture2D is not None + assert Qt3DRender.QRenderAspect is not None + assert Qt3DRender.QPaintedTextureImage is not None + assert Qt3DRender.QDithering is not None + assert Qt3DRender.QTextureGenerator is not None + assert Qt3DRender.QBlendEquationArguments is not None + assert Qt3DRender.QLevelOfDetailBoundingSphere is not None + assert Qt3DRender.QColorMask is not None + assert Qt3DRender.QSceneLoader is not None + assert Qt3DRender.QTextureLoader is not None + assert Qt3DRender.QShaderProgram is not None + assert Qt3DRender.QTextureCubeMap is not None + assert Qt3DRender.QTexture2DArray is not None + assert Qt3DRender.QTextureImage is not None + assert Qt3DRender.QCameraLens is not None + assert Qt3DRender.QRenderTargetOutput is not None + assert Qt3DRender.QShaderProgramBuilder is not None + assert Qt3DRender.QTechnique is not None + assert Qt3DRender.QShaderData is not None From 1b0d880bc81ab9cd1bd0c10d98dea5b5129233e8 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Tue, 11 Jun 2019 20:36:19 +0200 Subject: [PATCH 185/703] Add QtDatavisualization --- qtpy/QtDatavisualization.py | 22 ++++++++++++ qtpy/tests/test_qtdatavisualization.py | 46 ++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 qtpy/QtDatavisualization.py create mode 100644 qtpy/tests/test_qtdatavisualization.py diff --git a/qtpy/QtDatavisualization.py b/qtpy/QtDatavisualization.py new file mode 100644 index 00000000..cfb2b3b6 --- /dev/null +++ b/qtpy/QtDatavisualization.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtDataVisualization classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtDataVisualization import * +elif PYSIDE2: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.QtDataVisualization as __temp + import inspect + for __name in inspect.getmembers(__temp.QtDataVisualization): + globals()[__name[0]] = __name[1] +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtdatavisualization.py b/qtpy/tests/test_qtdatavisualization.py new file mode 100644 index 00000000..32142d66 --- /dev/null +++ b/qtpy/tests/test_qtdatavisualization.py @@ -0,0 +1,46 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtdatavisualization(): + """Test the qtpy.QtDataVisualization namespace""" + QtDataVisualization = pytest.importorskip("qtpy.QtDataVisualization") + + assert QtDataVisualization.QScatter3DSeries is not None + assert QtDataVisualization.QSurfaceDataItem is not None + assert QtDataVisualization.QSurface3DSeries is not None + assert QtDataVisualization.QAbstract3DInputHandler is not None + assert QtDataVisualization.QHeightMapSurfaceDataProxy is not None + assert QtDataVisualization.QAbstractDataProxy is not None + assert QtDataVisualization.Q3DCamera is not None + assert QtDataVisualization.QAbstract3DGraph is not None + assert QtDataVisualization.QCustom3DVolume is not None + assert QtDataVisualization.Q3DInputHandler is not None + assert QtDataVisualization.QBarDataProxy is not None + assert QtDataVisualization.QSurfaceDataProxy is not None + assert QtDataVisualization.QScatterDataItem is not None + assert QtDataVisualization.Q3DLight is not None + assert QtDataVisualization.QScatterDataProxy is not None + assert QtDataVisualization.QValue3DAxis is not None + assert QtDataVisualization.Q3DBars is not None + assert QtDataVisualization.QBarDataItem is not None + assert QtDataVisualization.QItemModelBarDataProxy is not None + assert QtDataVisualization.Q3DTheme is not None + assert QtDataVisualization.QCustom3DItem is not None + assert QtDataVisualization.QItemModelScatterDataProxy is not None + assert QtDataVisualization.QValue3DAxisFormatter is not None + assert QtDataVisualization.QItemModelSurfaceDataProxy is not None + assert QtDataVisualization.Q3DScatter is not None + assert QtDataVisualization.QTouch3DInputHandler is not None + assert QtDataVisualization.QBar3DSeries is not None + assert QtDataVisualization.QAbstract3DAxis is not None + assert QtDataVisualization.Q3DScene is not None + assert QtDataVisualization.QCategory3DAxis is not None + assert QtDataVisualization.QAbstract3DSeries is not None + assert QtDataVisualization.Q3DObject is not None + assert QtDataVisualization.QCustom3DLabel is not None + assert QtDataVisualization.Q3DSurface is not None + assert QtDataVisualization.QLogValue3DAxisFormatter is not None + From 32e3da1b637a955c608f351034338db97dde0c5d Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 12 Jun 2019 13:18:08 +0200 Subject: [PATCH 186/703] Release 1.8.0 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 304a2f40..da07c0af 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 8, 0, 'dev0') +version_info = (1, 8, 0) __version__ = '.'.join(map(str, version_info)) From 3ef465d5c08b2039204dfef8ff54497b46f3d9e9 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 12 Jun 2019 13:20:08 +0200 Subject: [PATCH 187/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index da07c0af..ad55922f 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 8, 0) +version_info = (1, 9, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 8736a99eaa274673f1088972fd424da71d4b3188 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 12 Jun 2019 13:22:54 +0200 Subject: [PATCH 188/703] Update Changelog --- CHANGELOG.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29c7e741..9d203e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # History of changes +## Version 1.8.0 (2019-06-12) + +### Issues Closed + +* [Issue 172](https://github.com/spyder-ide/qtpy/issues/172) - Support for Qt3D ([PR 191](https://github.com/spyder-ide/qtpy/pull/191)) + +In this release 1 issue was closed. + +### Pull Requests Merged + +* [PR 191](https://github.com/spyder-ide/qtpy/pull/191) - PR: Add Qt 3D bindings ([172](https://github.com/spyder-ide/qtpy/issues/172)) + +In this release 1 pull request was closed. + + +---- + + ## Version 1.7.1 (2019-05-05) From af42d9336467361b6037cad6ccd09fe56bc9f32f Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 12 Jun 2019 13:26:33 +0200 Subject: [PATCH 189/703] Mention new features in 1.8.0 in Changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d203e7c..971032c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Version 1.8.0 (2019-06-12) +### New features + +* Add support for several Qt 3D modules. + ### Issues Closed * [Issue 172](https://github.com/spyder-ide/qtpy/issues/172) - Support for Qt3D ([PR 191](https://github.com/spyder-ide/qtpy/pull/191)) From 3a53740289d9c6445495784d734998d31fef3f8b Mon Sep 17 00:00:00 2001 From: Jason Gilholme Date: Sat, 6 Jul 2019 06:45:43 +1000 Subject: [PATCH 190/703] Add `FORCE_QT_API` environment variable to ignore any previously imported python bindings. --- qtpy/__init__.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 284bc1f6..b2872bb8 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -114,15 +114,17 @@ class PythonQtWarning(Warning): PYQT5 = True PYQT4 = PYSIDE = PYSIDE2 = False - -if 'PyQt5' in sys.modules: - API = initial_api if initial_api in PYQT5_API else 'pyqt5' -elif 'PySide2' in sys.modules: - API = initial_api if initial_api in PYSIDE2_API else 'pyside2' -elif 'PyQt4' in sys.modules: - API = initial_api if initial_api in PYQT4_API else 'pyqt4' -elif 'PySide' in sys.modules: - API = initial_api if initial_api in PYSIDE_API else 'pyside' +# When `FORCE_QT_API` is set to "1", we disregard +# any previously imported python bindings. +if os.environ.get('FORCE_QT_API') == '1': + if 'PyQt5' in sys.modules: + API = initial_api if initial_api in PYQT5_API else 'pyqt5' + elif 'PySide2' in sys.modules: + API = initial_api if initial_api in PYSIDE2_API else 'pyside2' + elif 'PyQt4' in sys.modules: + API = initial_api if initial_api in PYQT4_API else 'pyqt4' + elif 'PySide' in sys.modules: + API = initial_api if initial_api in PYSIDE_API else 'pyside' if API in PYQT5_API: From e5840f4beecc573649ee6c90340b9c6e4d977f9e Mon Sep 17 00:00:00 2001 From: Jason Gilholme Date: Mon, 8 Jul 2019 18:45:42 +1000 Subject: [PATCH 191/703] Remove value check for `FORCE_QT_API` environment variable. --- qtpy/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index b2872bb8..ddbe4529 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -114,9 +114,9 @@ class PythonQtWarning(Warning): PYQT5 = True PYQT4 = PYSIDE = PYSIDE2 = False -# When `FORCE_QT_API` is set to "1", we disregard +# When `FORCE_QT_API` is set, we disregard # any previously imported python bindings. -if os.environ.get('FORCE_QT_API') == '1': +if os.environ.get('FORCE_QT_API') is not None: if 'PyQt5' in sys.modules: API = initial_api if initial_api in PYQT5_API else 'pyqt5' elif 'PySide2' in sys.modules: From 9e86214a4c64564398f8b52326081932919a0c40 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 14 Jul 2019 11:47:59 +0200 Subject: [PATCH 192/703] Testing: Use PySide2 5.12.3 to avoid errors in the Qt3D modules in 5.12.3+ This is failing due to a bug in Shiboken --- .circleci/test-pyside2.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/test-pyside2.sh b/.circleci/test-pyside2.sh index a7d08a4a..5afb72f5 100755 --- a/.circleci/test-pyside2.sh +++ b/.circleci/test-pyside2.sh @@ -8,7 +8,9 @@ if [ "$USE_CONDA" = "Yes" ]; then exit 0 else pip uninstall -q -y pyqt5 sip - pip install -q pyside2 + # Simple solution to avoid failures with the + # Qt3D modules + pip install -q pyside2==5.12.3 fi python qtpy/tests/runtests.py From d4a7e3162419f8760c7167bc923245d961425165 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sun, 14 Jul 2019 12:14:04 +0200 Subject: [PATCH 193/703] Don't load Qt3D modules for buggy versions of PySide2 --- qtpy/Qt3DAnimation.py | 16 ++++++++++------ qtpy/Qt3DCore.py | 16 ++++++++++------ qtpy/Qt3DExtras.py | 16 ++++++++++------ qtpy/Qt3DInput.py | 16 ++++++++++------ qtpy/Qt3DLogic.py | 16 ++++++++++------ qtpy/Qt3DRender.py | 16 ++++++++++------ 6 files changed, 60 insertions(+), 36 deletions(-) diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index ace6cf0f..c6625b2d 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -8,15 +8,19 @@ """Provides Qt3DAnimation classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 if PYQT5: from PyQt5.Qt3DAnimation import * elif PYSIDE2: - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DAnimation as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DAnimation): - globals()[__name[0]] = __name[1] + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DAnimation as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DAnimation): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 2ae2a9e2..523e1ded 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -8,15 +8,19 @@ """Provides Qt3DCore classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 if PYQT5: from PyQt5.Qt3DCore import * elif PYSIDE2: - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DCore as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DCore): - globals()[__name[0]] = __name[1] + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DCore as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DCore): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 5996b270..4f3a9c13 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -8,15 +8,19 @@ """Provides Qt3DExtras classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 if PYQT5: from PyQt5.Qt3DExtras import * elif PYSIDE2: - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DExtras as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DExtras): - globals()[__name[0]] = __name[1] + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DExtras as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DExtras): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 072fc44a..87b9a96a 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -8,15 +8,19 @@ """Provides Qt3DInput classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 if PYQT5: from PyQt5.Qt3DInput import * elif PYSIDE2: - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DInput as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DInput): - globals()[__name[0]] = __name[1] + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DInput as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DInput): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index d85fdb42..d17f1367 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -8,15 +8,19 @@ """Provides Qt3DLogic classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 if PYQT5: from PyQt5.Qt3DLogic import * elif PYSIDE2: - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DLogic as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DLogic): - globals()[__name[0]] = __name[1] + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DLogic as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DLogic): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index e42ee52c..f30331ae 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -8,15 +8,19 @@ """Provides Qt3DRender classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +from .py3compat import PY2 if PYQT5: from PyQt5.Qt3DRender import * elif PYSIDE2: - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DRender as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DRender): - globals()[__name[0]] = __name[1] + if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DRender as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DRender): + globals()[__name[0]] = __name[1] + else: + raise PythonQtError('A bug in Shiboken prevents this') else: raise PythonQtError('No Qt bindings could be found') From 29aabf691484fedae780d2fe408331e2bd01d8e1 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 23 Jul 2019 12:17:52 +0200 Subject: [PATCH 194/703] Update Changelog --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 971032c9..253f739c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # History of changes +## Version 1.9.0 (2019-07-23) + +### New features + +* Add the FORCE_QT_API environment variable to keep using the Qt + bindings selected with the QT_API variable and avoid switching + to the currently imported bindings. This allows to have + applications that import PySide and PyQt bindings at the same + time (which is possible if both bindings are compiled for the + same Qt version). + +### Issues Closed + +* [Issue 195](https://github.com/spyder-ide/qtpy/issues/195) - Errors in the Qt3D modules with PySide2 5.12.4+ and Python 2 ([PR 196](https://github.com/spyder-ide/qtpy/pull/196)) +* [Issue 192](https://github.com/spyder-ide/qtpy/issues/192) - Binding Selection Logic ([PR 194](https://github.com/spyder-ide/qtpy/pull/194)) + +In this release 2 issues were closed. + +### Pull Requests Merged + +* [PR 196](https://github.com/spyder-ide/qtpy/pull/196) - PR: Don't load Qt3D modules for buggy versions of PySide2 ([195](https://github.com/spyder-ide/qtpy/issues/195)) +* [PR 194](https://github.com/spyder-ide/qtpy/pull/194) - PR: Add FORCE_QT_API environment variable ([192](https://github.com/spyder-ide/qtpy/issues/192)) + +In this release 2 pull requests were closed. + + +---- + + ## Version 1.8.0 (2019-06-12) ### New features From d0273b86738190ea9c3a4d2ccb176c1cc7698abd Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 23 Jul 2019 12:19:20 +0200 Subject: [PATCH 195/703] Release 1.9.0 --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index ad55922f..a8ef90de 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 9, 0, 'dev0') +version_info = (1, 9, 0) __version__ = '.'.join(map(str, version_info)) From 0aec37a98c740a026e3088aa06d7bb38b467ae8f Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Tue, 23 Jul 2019 12:23:09 +0200 Subject: [PATCH 196/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index a8ef90de..f5216d17 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 9, 0) +version_info = (1, 10, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 0fd0a37a524ff793c5b06385863b4516de65e38a Mon Sep 17 00:00:00 2001 From: Milan Matic Date: Fri, 20 Sep 2019 17:33:58 +0200 Subject: [PATCH 197/703] #198 adding support for PyQt4-sip==4.19.13 --- qtpy/QtGui.py | 5 ++++- qtpy/__init__.py | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 071be132..2d3ae9e4 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -33,7 +33,10 @@ qFuzzyCompare) except ImportError: pass - from PyQt4.Qt import QKeySequence, QTextCursor + try: + from PyQt4.Qt import QKeySequence, QTextCursor + except ImportError: + from PyQt4.QtGui import QKeySequence, QTextCursor from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, diff --git a/qtpy/__init__.py b/qtpy/__init__.py index ddbe4529..1e734229 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -188,8 +188,12 @@ class PythonQtWarning(Warning): except (AttributeError, ValueError): # PyQt < v4.6 pass - from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore - from PyQt4.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore + try: + from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore + from PyQt4.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore + except ImportError: + from PyQt4.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore + from PyQt4.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore PYSIDE_VERSION = None PYQT5 = False PYQT4 = True From a3af57e0dd6d2cfffd72c9c661ac89ab5ffedf9d Mon Sep 17 00:00:00 2001 From: Milan Matic Date: Fri, 20 Sep 2019 17:52:57 +0200 Subject: [PATCH 198/703] #198 Adding comments in code explaining why PR is needed --- qtpy/QtGui.py | 1 + qtpy/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 2d3ae9e4..5c0c58a8 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -36,6 +36,7 @@ try: from PyQt4.Qt import QKeySequence, QTextCursor except ImportError: + # In PyQt4-sip==4.19.13 QKeySequence and QTextCursor are in PyQt4.QtGui from PyQt4.QtGui import QKeySequence, QTextCursor from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 1e734229..9386b701 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -192,6 +192,7 @@ class PythonQtWarning(Warning): from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt4.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore except ImportError: + # In PyQt4-sip==4.19.13 PYQT_VERSION_STR and QT_VERSION_STR are in PyQt4.QtCore from PyQt4.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt4.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore PYSIDE_VERSION = None From 15ad0ac48747b27ff0ff620e6f010199b8473f3b Mon Sep 17 00:00:00 2001 From: Milan Matic Date: Wed, 25 Sep 2019 14:49:10 +0200 Subject: [PATCH 199/703] #198 Adapting comments in code explaining why PR is needed --- qtpy/QtGui.py | 2 +- qtpy/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 5c0c58a8..be8f5688 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -36,7 +36,7 @@ try: from PyQt4.Qt import QKeySequence, QTextCursor except ImportError: - # In PyQt4-sip==4.19.13 QKeySequence and QTextCursor are in PyQt4.QtGui + # In PyQt4-sip 4.19.13 QKeySequence and QTextCursor are in PyQt4.QtGui from PyQt4.QtGui import QKeySequence, QTextCursor from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, QCloseEvent, QColor, diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 9386b701..47e5bc28 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -192,7 +192,7 @@ class PythonQtWarning(Warning): from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt4.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore except ImportError: - # In PyQt4-sip==4.19.13 PYQT_VERSION_STR and QT_VERSION_STR are in PyQt4.QtCore + # In PyQt4-sip 4.19.13 PYQT_VERSION_STR and QT_VERSION_STR are in PyQt4.QtCore from PyQt4.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt4.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore PYSIDE_VERSION = None From 84f06584726074198687fdad8f09069e0e804cf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Pe=C3=B1a-Castellanos?= Date: Tue, 3 Dec 2019 21:04:26 -0500 Subject: [PATCH 200/703] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..f7beb14d --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +open_collective: spyder From 8aa9251dafd2c35ed0c0bc5c46eeb672b267fc13 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Sat, 7 Dec 2019 12:43:41 +0530 Subject: [PATCH 201/703] Import ABC from collections.abc for Python 3.9 compatibility. --- qtpy/compat.py | 5 ++--- qtpy/py3compat.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/qtpy/compat.py b/qtpy/compat.py index f5794548..949d8854 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -9,11 +9,10 @@ from __future__ import print_function import sys -import collections from . import PYQT4 from .QtWidgets import QFileDialog -from .py3compat import is_text_string, to_text_string, TEXT_TYPES +from .py3compat import Callable, is_text_string, to_text_string, TEXT_TYPES # ============================================================================= @@ -46,7 +45,7 @@ def from_qvariant(qobj=None, convfunc=None): to PyQt API #2 and Pyside (QVariant does not exist)""" if PYQT_API_1: # PyQt API #1 - assert isinstance(convfunc, collections.Callable) + assert isinstance(convfunc, Callable) if convfunc in TEXT_TYPES or convfunc is to_text_string: return convfunc(qobj.toString()) elif convfunc is bool: diff --git a/qtpy/py3compat.py b/qtpy/py3compat.py index 43550b64..cc4bdbf5 100644 --- a/qtpy/py3compat.py +++ b/qtpy/py3compat.py @@ -75,9 +75,9 @@ import io import pickle if PY33: - from collections.abc import MutableMapping + from collections.abc import Callable, MutableMapping else: - from collections import MutableMapping + from collections import Callable, MutableMapping import _thread import reprlib From 42c56084ad96dd226db0abbda37bd702d7a37d25 Mon Sep 17 00:00:00 2001 From: Stanowczo Date: Thu, 27 Feb 2020 10:16:06 +0100 Subject: [PATCH 202/703] Added support for QtSerialPort add-on --- qtpy/QtSerialPort.py | 17 +++++++++++++++++ qtpy/tests/test_qtserialport.py | 12 ++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 qtpy/QtSerialPort.py create mode 100644 qtpy/tests/test_qtserialport.py diff --git a/qtpy/QtSerialPort.py b/qtpy/QtSerialPort.py new file mode 100644 index 00000000..26fcae18 --- /dev/null +++ b/qtpy/QtSerialPort.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2020 Marcin Stano +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtSerialPort classes and functions.""" + +# Local imports +from . import PYQT5, PythonQtError + +if PYQT5: + from PyQt5.QtSerialPort import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtserialport.py b/qtpy/tests/test_qtserialport.py new file mode 100644 index 00000000..26daaf76 --- /dev/null +++ b/qtpy/tests/test_qtserialport.py @@ -0,0 +1,12 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5 + +@pytest.mark.skipif(not PYQT5, reason="Only available in Qt5 bindings, but still not in PySide2") +def test_qtserialport(): + """Test the qtpy.QtSerialPort namespace""" + from qtpy import QtSerialPort + + assert QtSerialPort.QSerialPort is not None + assert QtSerialPort.QSerialPortInfo is not None From b26d01e1ed82b3f54f0d0817aed97d619aff66f6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Tue, 10 Mar 2020 19:06:39 +0530 Subject: [PATCH 203/703] Use list instead of getchildren for Python 3 compatibility. --- qtpy/uic.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qtpy/uic.py b/qtpy/uic.py index 07d7a787..91a1c4ca 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -2,6 +2,7 @@ from . import PYSIDE, PYSIDE2, PYQT4, PYQT5 from .QtWidgets import QComboBox +from .pycompat import PY33 if PYQT5: @@ -180,8 +181,12 @@ def _get_custom_widgets(ui_file): return {} custom_widget_classes = {} + if PY33: + children = list(custom_widgets) + else: + children = custom_widgets.getchildren() - for custom_widget in custom_widgets.getchildren(): + for custom_widget in children: cw_class = custom_widget.find('class').text cw_header = custom_widget.find('header').text From 3676c08f8eca281eda73130cc0c32c437bfe16bb Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Thu, 18 Jun 2020 22:01:20 -0400 Subject: [PATCH 204/703] Handle QtCore.SignalInstance/pyqtBoundSignal --- qtpy/QtCore.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index f583b05f..a15ae3bf 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -16,6 +16,7 @@ if PYQT5: from PyQt5.QtCore import * from PyQt5.QtCore import pyqtSignal as Signal + from PyQt5.QtCore import pyqtBoundSignal as SignalInstance from PyQt5.QtCore import pyqtSlot as Slot from PyQt5.QtCore import pyqtProperty as Property from PyQt5.QtCore import QT_VERSION_STR as __version__ @@ -25,7 +26,7 @@ QDateTime.toPython = QDateTime.toPyDateTime # Those are imported from `import *` - del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR + del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR elif PYSIDE2: from PySide2.QtCore import * @@ -43,6 +44,7 @@ from PyQt4.QtCore import QCoreApplication from PyQt4.QtCore import Qt from PyQt4.QtCore import pyqtSignal as Signal + from PyQt4.Qtcore import pyqtBoundSignal as SignalInstance from PyQt4.QtCore import pyqtSlot as Slot from PyQt4.QtCore import pyqtProperty as Property from PyQt4.QtGui import (QItemSelection, QItemSelectionModel, @@ -73,7 +75,7 @@ class QStandardPaths(): writableLocation = _QDesktopServices.storageLocation # Those are imported from `import *` - del pyqtSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR, qInstallMsgHandler + del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR, qInstallMsgHandler elif PYSIDE: from PySide.QtCore import * from PySide.QtGui import (QItemSelection, QItemSelectionModel, From d1387e421619c713eb3a49e4d4057c3f7cb05f5f Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Thu, 18 Jun 2020 22:09:21 -0400 Subject: [PATCH 205/703] Slight typo fix --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 47e5bc28..31175bd8 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -8,7 +8,7 @@ """ **QtPy** is a shim over the various Python Qt bindings. It is used to write -Qt binding indenpendent libraries or applications. +Qt binding independent libraries or applications. If one of the APIs has already been imported, then it will be used. From e6c7a7c426672ddac9d33e9150913a52e37e838e Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Fri, 19 Jun 2020 16:47:39 -0400 Subject: [PATCH 206/703] add test for QtCore.SignalInstance --- qtpy/tests/test_qtcore.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 7a337bfa..c32bcafe 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -16,3 +16,12 @@ def test_qtmsghandler(): def test_DateTime_toPython(): """Test QDateTime.toPython""" assert QtCore.QDateTime.toPython is not None + + +def test_QtCore_SignalInstance(): + class ClassWithSignal(QtCore.QObject): + signal = QtCore.Signal() + + instance = ClassWithSignal() + + assert isinstance(instance.signal, QtCore.SignalInstance) From d2813779d8714936dc5450c37b228a2d1fcd94b3 Mon Sep 17 00:00:00 2001 From: phil65 Date: Mon, 13 Jul 2020 20:08:34 +0200 Subject: [PATCH 207/703] add QtWinExtras module --- qtpy/QtWinExtras.py | 16 ++++++++++++++++ qtpy/tests/test_qtwinextras.py | 27 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 qtpy/QtWinExtras.py create mode 100644 qtpy/tests/test_qtwinextras.py diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py new file mode 100644 index 00000000..c033ff98 --- /dev/null +++ b/qtpy/QtWinExtras.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +from . import PYQT5, PYSIDE2, PythonQtError + + +if PYQT5: + from PyQt5.QtWinExtras import * +elif PYSIDE2: + from PySide2.QtWinExtras import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py new file mode 100644 index 00000000..4e349a8f --- /dev/null +++ b/qtpy/tests/test_qtwinextras.py @@ -0,0 +1,27 @@ +from __future__ import absolute_import + +import sys + +import pytest +from qtpy import PYQT5, PYSIDE2 + + +@pytest.mark.skipif(sys.platform != "win32" or not (PYQT5 or PYSIDE2), + reason="Only available in Qt5 bindings and Windows platform") +def test_qtwinextras(): + """Test the qtpy.QtWinExtras namespace""" + from qtpy import QtWinExtras + assert QtWinExtras.QWinJumpList is not None + assert QtWinExtras.QWinJumpListCategory is not None + assert QtWinExtras.QWinJumpListItem is not None + assert QtWinExtras.QWinTaskbarButton is not None + assert QtWinExtras.QWinTaskbarProgress is not None + assert QtWinExtras.QWinThumbnailToolBar is not None + assert QtWinExtras.QWinThumbnailToolButton is not None + assert QtWinExtras.QtWin is not None + + if PYSIDE2: + assert QtWinExtras.QWinColorizationChangeEvent is not None + assert QtWinExtras.QWinCompositionChangeEvent is not None + assert QtWinExtras.QWinEvent is not None + From ca0d01938e163166e86bcf90110262c75b878053 Mon Sep 17 00:00:00 2001 From: Pierre Raybaut Date: Thu, 20 Aug 2020 08:18:17 +0200 Subject: [PATCH 208/703] Added support for QStyleOptionFrameV3 from PyQt4 In other words: - From PyQt5 : `from PyQt5.QtWidgets import QStyleOptionFrame` - From PyQt4: `from PyQt4.QtWidgets import QStyleOptionFrameV3` - From QtPy: `from qtpy.QtWidgets import QStyleOptionFrame` --- qtpy/QtWidgets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 739f9ce1..66ef3aba 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -26,6 +26,8 @@ from PyQt4.QtGui import * QStyleOptionViewItem = QStyleOptionViewItemV4 del QStyleOptionViewItemV4 + QStyleOptionFrame = QStyleOptionFrameV3 + del QStyleOptionFrameV3 # These objects belong to QtGui try: From 0350fc1cffef4a16c7c0b6608c7cc6cec8c0cb3d Mon Sep 17 00:00:00 2001 From: irrcombat Date: Thu, 19 Nov 2020 16:22:18 +0800 Subject: [PATCH 209/703] Update uic.py Support python 3.9 --- qtpy/uic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/uic.py b/qtpy/uic.py index 07d7a787..84525abd 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -181,7 +181,7 @@ def _get_custom_widgets(ui_file): custom_widget_classes = {} - for custom_widget in custom_widgets.getchildren(): + for custom_widget in list(custom_widgets): cw_class = custom_widget.find('class').text cw_header = custom_widget.find('header').text From a6dd21692444e32eb1ce7b363b039d19e05ad36d Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Thu, 14 Jan 2021 09:52:37 +0100 Subject: [PATCH 210/703] Rename QtDatavisualization.py to use uppercase v Both `PyQt5` and `PySide2` call the module QtDatavisualization (with an uppercase V) and the `tests/test_qtdatavisualization.py` file even does `pytest.importorskip("qtpy.QtDataVisualization")` which means the test is always skipped since the module didn't exist. Another solution would be to fix the test but for homogeneity with PyQt5 and PySide2 I think it's better to rename QtDatavisualization.py to QtDataVisualization.py . --- qtpy/{QtDatavisualization.py => QtDataVisualization.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename qtpy/{QtDatavisualization.py => QtDataVisualization.py} (100%) diff --git a/qtpy/QtDatavisualization.py b/qtpy/QtDataVisualization.py similarity index 100% rename from qtpy/QtDatavisualization.py rename to qtpy/QtDataVisualization.py From 1e5d4ca1b1c182ef261b0f6832e5be87d0d813de Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Thu, 21 Jan 2021 10:16:50 +0100 Subject: [PATCH 211/703] Add QtDatavisualization alias to QtDataVisualization There might be applications using the wrong name. This commit introduces an alias so they keep working as usual. --- qtpy/QtDatavisualization.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 qtpy/QtDatavisualization.py diff --git a/qtpy/QtDatavisualization.py b/qtpy/QtDatavisualization.py new file mode 100644 index 00000000..cd0cb4db --- /dev/null +++ b/qtpy/QtDatavisualization.py @@ -0,0 +1 @@ +from .QtDataVisualization import * From 3f676d9216dd989aab9def08dd70d26e28461f98 Mon Sep 17 00:00:00 2001 From: hiaselhans Date: Mon, 8 Feb 2021 12:47:30 +0100 Subject: [PATCH 212/703] fix imported modules logic if 'FORCE_QT_API' is empty --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 31175bd8..71ec168c 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -116,7 +116,7 @@ class PythonQtWarning(Warning): # When `FORCE_QT_API` is set, we disregard # any previously imported python bindings. -if os.environ.get('FORCE_QT_API') is not None: +if not os.environ.get('FORCE_QT_API'): if 'PyQt5' in sys.modules: API = initial_api if initial_api in PYQT5_API else 'pyqt5' elif 'PySide2' in sys.modules: From 4a27af38c11285dc837186d604034a1ef4fc7f0d Mon Sep 17 00:00:00 2001 From: goanpeca Date: Wed, 5 Feb 2020 18:29:53 -0500 Subject: [PATCH 213/703] Move ci to github actions --- .circleci/config.yml | 67 -------- .circleci/install.sh | 19 --- .circleci/test-pyqt4.sh | 17 -- .circleci/test-pyqt5.sh | 28 --- .circleci/test-pyside.sh | 17 -- .circleci/test-pyside2.sh | 18 -- .github/workflows/ci.yml | 171 +++++++++++++++++++ {.circleci => .github/workflows}/coverage.sh | 7 +- .github/workflows/test-pyqt4.sh | 22 +++ .github/workflows/test-pyqt5.sh | 61 +++++++ .github/workflows/test-pyside.sh | 26 +++ .github/workflows/test-pyside2.sh | 23 +++ .gitignore | 3 + README.md | 3 +- appveyor.yml | 56 ------ 15 files changed, 311 insertions(+), 227 deletions(-) delete mode 100644 .circleci/config.yml delete mode 100755 .circleci/install.sh delete mode 100755 .circleci/test-pyqt4.sh delete mode 100755 .circleci/test-pyqt5.sh delete mode 100755 .circleci/test-pyside.sh delete mode 100755 .circleci/test-pyside2.sh create mode 100644 .github/workflows/ci.yml rename {.circleci => .github/workflows}/coverage.sh (61%) create mode 100755 .github/workflows/test-pyqt4.sh create mode 100755 .github/workflows/test-pyqt5.sh create mode 100755 .github/workflows/test-pyside.sh create mode 100755 .github/workflows/test-pyside2.sh delete mode 100644 appveyor.yml diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index b0fa1525..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,67 +0,0 @@ -version: 2 - -main: &main - machine: true - steps: - - checkout - - run: - command: docker pull dorowu/ubuntu-desktop-lxde-vnc:trusty - - run: - name: Install system packages - command: | - sudo apt-get update - sudo apt-get install libpulse-dev - sudo apt-get install libegl1-mesa - - run: - command: ./.circleci/install.sh - - run: - command: ./.circleci/test-pyqt5.sh - - run: - command: ./.circleci/test-pyside2.sh - - run: - command: ./.circleci/test-pyqt4.sh - - run: - command: ./.circleci/test-pyside.sh - - run: - command: ./.circleci/coverage.sh - -jobs: - python2.7: - <<: *main - environment: - - PYTHON_VERSION: "2.7" - - USE_CONDA: "Yes" - - python3.5: - <<: *main - environment: - - PYTHON_VERSION: "3.5" - - USE_CONDA: "Yes" - - python3.6: - <<: *main - environment: - - PYTHON_VERSION: "3.6" - - USE_CONDA: "Yes" - - python2.7-pip: - <<: *main - environment: - - PYTHON_VERSION: "2.7" - - USE_CONDA: "No" - - python3.6-pip: - <<: *main - environment: - - PYTHON_VERSION: "3.6" - - USE_CONDA: "No" - -workflows: - version: 2 - build_and_test: - jobs: - - python2.7 - - python3.5 - - python3.6 - - python2.7-pip - - python3.6-pip diff --git a/.circleci/install.sh b/.circleci/install.sh deleted file mode 100755 index c51c2df7..00000000 --- a/.circleci/install.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -ex - -export TRAVIS_OS_NAME="linux" -export CONDA_DEPENDENCIES_FLAGS="--quiet" -export CONDA_DEPENDENCIES="pytest pytest-cov mock" -export PIP_DEPENDENCIES="coveralls" - -# Download and install miniconda and conda/pip dependencies -# with astropy helpers -echo -e "PYTHON = $PYTHON_VERSION \n============" -git clone git://github.com/astropy/ci-helpers.git > /dev/null -source ci-helpers/travis/setup_conda_$TRAVIS_OS_NAME.sh - -# Activate conda -source $HOME/miniconda/etc/profile.d/conda.sh -conda activate test - -# Install the package in develop mode -pip install -e . diff --git a/.circleci/test-pyqt4.sh b/.circleci/test-pyqt4.sh deleted file mode 100755 index b38caf11..00000000 --- a/.circleci/test-pyqt4.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -ex - -source $HOME/miniconda/etc/profile.d/conda.sh -conda activate test - -if [ "$USE_CONDA" = "No" ]; then - exit 0 -elif [ "$PYTHON_VERSION" = "3.5" ]; then - # conda-forge doesn't provide packages for - # Python 3.5 anymore. - exit 0 -else - conda remove -q qt pyqt - conda install -q -c conda-forge qt=4.* pyqt=4.* -fi - -python qtpy/tests/runtests.py diff --git a/.circleci/test-pyqt5.sh b/.circleci/test-pyqt5.sh deleted file mode 100755 index d614ec71..00000000 --- a/.circleci/test-pyqt5.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -ex - -source $HOME/miniconda/etc/profile.d/conda.sh -conda activate test - -# Select build with QtMultimedia -if [ "$PYTHON_VERSION" = "2.7" ]; then - export BUILD=py27h22d08a2_0 -elif [ "$PYTHON_VERSION" = "3.5" ]; then - export BUILD=py35h751905a_0 -else - export BUILD=py36h751905a_0 -fi - -if [ "$USE_CONDA" = "Yes" ]; then - conda install -q qt=5.* pyqt=5.9.2=$BUILD - conda install -q sip=4.19.8 -else - if [ "$PYTHON_VERSION" = "2.7" ]; then - # There are no pyqt5 wheels for Python 2 - exit 0 - else - # We are getting segfaults in 5.10 - pip install -q pyqt5==5.9.2 - fi -fi - -python qtpy/tests/runtests.py diff --git a/.circleci/test-pyside.sh b/.circleci/test-pyside.sh deleted file mode 100755 index 65ef35af..00000000 --- a/.circleci/test-pyside.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash -ex - -source $HOME/miniconda/etc/profile.d/conda.sh -conda activate test - -if [ "$USE_CONDA" = "No" ]; then - exit 0 -elif [ "$PYTHON_VERSION" = "3.5" ]; then - # conda-forge doesn't provide packages for - # Python 3.5 anymore. - exit 0 -else - conda remove -q qt pyqt - conda install -q -c conda-forge qt=4.* pyside -fi - -python qtpy/tests/runtests.py diff --git a/.circleci/test-pyside2.sh b/.circleci/test-pyside2.sh deleted file mode 100755 index 5afb72f5..00000000 --- a/.circleci/test-pyside2.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -ex - -source $HOME/miniconda/etc/profile.d/conda.sh -conda activate test - -if [ "$USE_CONDA" = "Yes" ]; then - # There are no conda packages for PySide2 - exit 0 -else - pip uninstall -q -y pyqt5 sip - # Simple solution to avoid failures with the - # Qt3D modules - pip install -q pyside2==5.12.3 -fi - -python qtpy/tests/runtests.py - -pip uninstall -y -q pyside2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..8b35efdb --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,171 @@ +name: Tests + +on: + # This avoids having duplicate builds for a pull request + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + smoke: + name: Linux smoke test Py${{ matrix.PYTHON_VERSION }} conda=yes + runs-on: ubuntu-latest + env: + CI: True + PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} + USE_CONDA: 'Yes' + strategy: + fail-fast: false + matrix: + PYTHON_VERSION: ['3.5'] + steps: + - name: Checkout branch + uses: actions/checkout@v1.2.0 + - name: Install System Packages + run: | + sudo apt-get update + sudo apt-get install libpulse-dev + sudo apt-get install libegl1-mesa + sudo apt-get install libqtwebkit4 + - name: Install Conda + uses: goanpeca/setup-miniconda@v1 + with: + activate-environment: '' + auto-update-conda: true + auto-activate-base: false + - name: Test PyQt5 + shell: bash -l {0} + run: | + eval "$(conda shell.bash hook)" + xvfb-run --auto-servernum ./.github/workflows/test-pyqt5.sh + - name: Test PySide2 + shell: bash -l {0} + run: xvfb-run --auto-servernum ./.github/workflows/test-pyside2.sh + - name: Test PyQt4 + shell: bash -l {0} + run: xvfb-run --auto-servernum ./.github/workflows/test-pyqt4.sh + - name: Test PySide + shell: bash -l {0} + run: xvfb-run --auto-servernum ./.github/workflows/test-pyside.sh + - name: Upload coverage + shell: bash -l {0} + run: ./.github/workflows/coverage.sh + + linux: + name: Linux Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} + needs: smoke + runs-on: ubuntu-latest + env: + CI: True + PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} + USE_CONDA: ${{ matrix.USE_CONDA }} + strategy: + fail-fast: false + matrix: + PYTHON_VERSION: ['2.7', '3.6'] + USE_CONDA: ['Yes', 'No'] + steps: + - name: Checkout branch + uses: actions/checkout@v1.2.0 + - name: Install System Packages + run: | + sudo apt-get update + sudo apt-get install libpulse-dev + sudo apt-get install libegl1-mesa + sudo apt-get install libqtwebkit4 + - name: Install Conda + uses: goanpeca/setup-miniconda@v1 + with: + activate-environment: '' + auto-update-conda: true + auto-activate-base: false + - name: Test PyQt5 + shell: bash -l {0} + run: | + eval "$(conda shell.bash hook)" + xvfb-run --auto-servernum ./.github/workflows/test-pyqt5.sh + - name: Test PySide2 + shell: bash -l {0} + run: xvfb-run --auto-servernum ./.github/workflows/test-pyside2.sh + - name: Test PyQt4 + shell: bash -l {0} + run: xvfb-run --auto-servernum ./.github/workflows/test-pyqt4.sh + - name: Test PySide + shell: bash -l {0} + run: xvfb-run --auto-servernum ./.github/workflows/test-pyside.sh + + windows: + name: Windows Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} + needs: smoke + runs-on: windows-latest + env: + CI: True + PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} + USE_CONDA: ${{ matrix.USE_CONDA }} + strategy: + fail-fast: false + matrix: + PYTHON_VERSION: ['2.7', '3.5', '3.6'] + USE_CONDA: ['Yes', 'No'] + steps: + - name: Checkout branch + uses: actions/checkout@v1.2.0 + - name: Install Conda + uses: goanpeca/setup-miniconda@v1 + with: + activate-environment: '' + auto-update-conda: true + auto-activate-base: true + - name: Test PyQt5 + shell: bash -l {0} + run: ./.github/workflows/test-pyqt5.sh + - name: Test PySide2 + shell: bash -l {0} + run: ./.github/workflows/test-pyqt5.sh + - name: Test PyQt4 + shell: bash -l {0} + run: ./.github/workflows/test-pyqt4.sh + - name: Test PySide + shell: bash -l {0} + run: ./.github/workflows/test-pyside.sh + + macos: + name: Mac Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} + needs: smoke + runs-on: macos-latest + env: + CI: True + PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} + USE_CONDA: ${{ matrix.USE_CONDA }} + strategy: + fail-fast: false + matrix: + PYTHON_VERSION: ['2.7', '3.5', '3.6'] + USE_CONDA: ['Yes', 'No'] + exclude: + - PYTHON_VERSION: '2.7' + USE_CONDA: 'Yes' + steps: + - name: Checkout branch + uses: actions/checkout@v1.2.0 + - name: Install Conda + uses: goanpeca/setup-miniconda@v1 + with: + activate-environment: '' + auto-update-conda: true + auto-activate-base: false + - name: Test PyQt5 + shell: bash -l {0} + run: ./.github/workflows/test-pyqt5.sh + - name: Test PySide2 + shell: bash -l {0} + run: ./.github/workflows/test-pyside2.sh + - name: Test PyQt4 + shell: bash -l {0} + run: ./.github/workflows/test-pyqt4.sh + - name: Test PySide + shell: bash -l {0} + run: ./.github/workflows/test-pyside.sh diff --git a/.circleci/coverage.sh b/.github/workflows/coverage.sh similarity index 61% rename from .circleci/coverage.sh rename to .github/workflows/coverage.sh index f9c4f704..f68ecb3e 100755 --- a/.circleci/coverage.sh +++ b/.github/workflows/coverage.sh @@ -1,9 +1,10 @@ #!/bin/bash -export COVERALLS_REPO_TOKEN="xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5" -export PATH="$HOME/miniconda/bin:$PATH" -source activate test +eval "$(conda shell.bash hook)" +conda deactivate +conda activate test-pyqt5 +export COVERALLS_REPO_TOKEN="xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5" coveralls # Don't fail at this step diff --git a/.github/workflows/test-pyqt4.sh b/.github/workflows/test-pyqt4.sh new file mode 100755 index 00000000..79e5ba78 --- /dev/null +++ b/.github/workflows/test-pyqt4.sh @@ -0,0 +1,22 @@ +#!/bin/bash -ex + +eval "$(conda shell.bash hook)" + +# Create conda environment for this test +conda create -n test-pyqt4 +conda activate test-pyqt4 + +if [ "$USE_CONDA" = "No" ]; then + exit 0 +elif [ "$PYTHON_VERSION" = "3.6" ]; then + exit 0 +else + conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q + conda install qt=4.* pyqt=4.* -c anaconda -q +fi + +# Install package +python setup.py develop + +# Run tests +python qtpy/tests/runtests.py diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh new file mode 100755 index 00000000..66b43de8 --- /dev/null +++ b/.github/workflows/test-pyqt5.sh @@ -0,0 +1,61 @@ +#!/bin/bash -ex + +eval "$(conda shell.bash hook)" + +# Create conda environment for this test +conda create -n test-pyqt5 +conda activate test-pyqt5 + +# Select build with QtMultimedia +if [ "$(uname)" == "Darwin" ]; then + + if [ "$PYTHON_VERSION" = "2.7" ]; then + export PYQT_VER=5.9.2=py27h655552a_0 + elif [ "$PYTHON_VERSION" = "3.5" ]; then + export PYQT_VER=5.9.2=py35h11d3b92_0 + else + export PYQT_VER=5.9.2=py36h11d3b92_0 + fi + +elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then + + if [ "$PYTHON_VERSION" = "2.7" ]; then + export PYQT_VER=5.9.2=py27h22d08a2_0 + elif [ "$PYTHON_VERSION" = "3.5" ]; then + export PYQT_VER=5.9.2=py35h751905a_0 + else + export PYQT_VER=5.9.2=py36h751905a_0 + fi + +else + + if [ "$PYTHON_VERSION" = "2.7" ]; then + exit 0 + elif [ "$PYTHON_VERSION" = "3.5" ]; then + export PYQT_VER=5.9.2=py35h1aa27d4_0 + else + export PYQT_VER=5.9.2=py36h1aa27d4_0 + fi + +fi + +if [ "$USE_CONDA" = "Yes" ]; then + conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q + conda install -q qt=5.* pyqt=$PYQT_VER -c anaconda -q + conda install -q sip=4.19.8 -c anaconda -q +else + if [ "$PYTHON_VERSION" = "2.7" ]; then + # There are no pyqt5 wheels for Python 2 + exit 0 + else + # We are getting segfaults in 5.10 + conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q + pip install -q pyqt5==5.9.2 + fi +fi + +# Install package +python setup.py develop + +# Run tests +python qtpy/tests/runtests.py diff --git a/.github/workflows/test-pyside.sh b/.github/workflows/test-pyside.sh new file mode 100755 index 00000000..4dbc41bb --- /dev/null +++ b/.github/workflows/test-pyside.sh @@ -0,0 +1,26 @@ +#!/bin/bash -ex + +eval "$(conda shell.bash hook)" + +# Create conda environment for this test +conda create -n test-pyside +conda activate test-pyside + +if [ "$USE_CONDA" = "No" ]; then + exit 0 +else + if [ "$PYTHON_VERSION" = "3.6" ]; then + exit 0 + elif [ "$PYTHON_VERSION" = "3.5" ]; then + exit 0 + else + conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q + conda install qt=4.* pyside -c anaconda -q + fi +fi + +# Install package +python setup.py develop + +# Run tests +python qtpy/tests/runtests.py diff --git a/.github/workflows/test-pyside2.sh b/.github/workflows/test-pyside2.sh new file mode 100755 index 00000000..cf7bc0ec --- /dev/null +++ b/.github/workflows/test-pyside2.sh @@ -0,0 +1,23 @@ +#!/bin/bash -ex + +eval "$(conda shell.bash hook)" + +# Create conda environment for this test +conda create -n test-pyside2 +conda activate test-pyside2 + +if [ "$USE_CONDA" = "Yes" ]; then + # There are no conda packages for PySide2 + exit 0 +else + # Simple solution to avoid failures with the + # Qt3D modules + conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge -q + pip install -q pyside2==5.12.3 +fi + +# Install package +python setup.py develop + +# Run tests +python qtpy/tests/runtests.py diff --git a/.gitignore b/.gitignore index 9f30b4e8..2aa984af 100644 --- a/.gitignore +++ b/.gitignore @@ -36,4 +36,7 @@ toread.md .chache .idea/ +# Macos +*.DS_Store + # End of File diff --git a/README.md b/README.md index 24fd048c..d6734d35 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,7 @@ [![OpenCollective Backers](https://opencollective.com/spyder/backers/badge.svg?color=blue)](#backers) [![Join the chat at https://gitter.im/spyder-ide/public](https://badges.gitter.im/spyder-ide/spyder.svg)](https://gitter.im/spyder-ide/public)
[![PyPI status](https://img.shields.io/pypi/status/qtpy.svg)](https://github.com/spyder-ide/qtpy) -[![Build status](https://ci.appveyor.com/api/projects/status/62y6i02vhn4hefg0/branch/master?svg=true)](https://ci.appveyor.com/project/spyder-ide/qtpy/branch/master) -[![CircleCI](https://circleci.com/gh/spyder-ide/qtpy.svg?style=shield)](https://circleci.com/gh/spyder-ide/qtpy) +[![Github build status](https://github.com/spyder-ide/qtpy/workflows/Tests/badge.svg)](https://github.com/spyder-ide/qtpy/actions) [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) *Copyright © 2009–2019 The Spyder Development Team* diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 5869caa7..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,56 +0,0 @@ -# https://ci.appveyor.com/project/goanpeca/qtpy - -branches: - only: - - master - -environment: - global: - PYTHON: "C:\\conda" - CMD_IN_ENV: "cmd /E:ON /V:ON /C .\\ci-helpers\\appveyor\\windows_sdk.cmd" - PYTHON_ARCH: "64" # needs to be set for CMD_IN_ENV to succeed. If a mix - # of 32 bit and 64 bit builds are needed, move this - # to the matrix section. - - matrix: - # Qt4 - - PYTHON_VERSION: "2.7" - CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=4.* pyside" - - PYTHON_VERSION: "2.7" - CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=4.* pyqt=4.*" - - PYTHON_VERSION: "3.5" - CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=4.* pyqt=4.*" - # Qt5 - - PYTHON_VERSION: "2.7" - CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=5.* pyqt=5.*" - - PYTHON_VERSION: "3.5" - CONDA_DEPENDENCIES: "pytest pytest-cov mock qt=5.* pyqt=5.*" - - PYTHON_VERSION: "3.6" - CONDA_DEPENDENCIES: "pytest pytest-cov mock" - PIP_DEPENDENCIES: "pyqt5==5.9.2" - PIP_DEPENDENCIES_FLAGS: "-q" - -platform: - -x64 - -install: - # If there is a newer build queued for the same PR, cancel this one. - # The AppVeyor 'rollout builds' option is supposed to serve the same - # purpose but it is problematic because it tends to cancel builds pushed - # directly to master instead of just PR builds (or the converse). - # credits: JuliaLang developers. - - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod ` - https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | ` - Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { ` - throw "There are newer queued builds for this pull request, failing early." } - - "git clone git://github.com/astropy/ci-helpers.git" - - "powershell ci-helpers/appveyor/install-miniconda.ps1" - - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%" - - "activate test" - - "python setup.py develop" - -# Not a .NET project, we build in the install step instead -build: false - -test_script: - - "%CMD_IN_ENV% python qtpy/tests/runtests.py" From 89343b473fc936e88963a191f8b7a7debcd9e66b Mon Sep 17 00:00:00 2001 From: goanpeca Date: Thu, 5 Mar 2020 18:39:41 -0500 Subject: [PATCH 214/703] Test on modern Python versions --- .github/workflows/ci.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b35efdb..2ec84755 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - PYTHON_VERSION: ['3.5'] + PYTHON_VERSION: ['2.7', '3.6'] steps: - name: Checkout branch uses: actions/checkout@v1.2.0 @@ -50,9 +50,6 @@ jobs: - name: Test PySide shell: bash -l {0} run: xvfb-run --auto-servernum ./.github/workflows/test-pyside.sh - - name: Upload coverage - shell: bash -l {0} - run: ./.github/workflows/coverage.sh linux: name: Linux Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} @@ -65,7 +62,7 @@ jobs: strategy: fail-fast: false matrix: - PYTHON_VERSION: ['2.7', '3.6'] + PYTHON_VERSION: ['3.8'] USE_CONDA: ['Yes', 'No'] steps: - name: Checkout branch @@ -96,6 +93,10 @@ jobs: - name: Test PySide shell: bash -l {0} run: xvfb-run --auto-servernum ./.github/workflows/test-pyside.sh + - name: Upload coverage + if: matrix.PYTHON_VERSION == '3.8' + shell: bash -l {0} + run: ./.github/workflows/coverage.sh windows: name: Windows Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} @@ -108,7 +109,7 @@ jobs: strategy: fail-fast: false matrix: - PYTHON_VERSION: ['2.7', '3.5', '3.6'] + PYTHON_VERSION: ['2.7', '3.6', '3.8'] USE_CONDA: ['Yes', 'No'] steps: - name: Checkout branch @@ -143,7 +144,7 @@ jobs: strategy: fail-fast: false matrix: - PYTHON_VERSION: ['2.7', '3.5', '3.6'] + PYTHON_VERSION: ['2.7', '3.6', '3.8'] USE_CONDA: ['Yes', 'No'] exclude: - PYTHON_VERSION: '2.7' From 82c11715f28795a7c559d971c67a5ce22fdd1785 Mon Sep 17 00:00:00 2001 From: goanpeca Date: Thu, 5 Mar 2020 18:50:02 -0500 Subject: [PATCH 215/703] Further update tests and classifiers to use modern Python versions --- .github/workflows/ci.yml | 26 +++++++++++++------------- .github/workflows/test-pyqt5.sh | 18 +++++++++--------- setup.py | 6 +++--- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ec84755..95735c57 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,10 +98,10 @@ jobs: shell: bash -l {0} run: ./.github/workflows/coverage.sh - windows: - name: Windows Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} + macos: + name: Mac Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} needs: smoke - runs-on: windows-latest + runs-on: macos-latest env: CI: True PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} @@ -111,6 +111,9 @@ jobs: matrix: PYTHON_VERSION: ['2.7', '3.6', '3.8'] USE_CONDA: ['Yes', 'No'] + exclude: + - PYTHON_VERSION: '2.7' + USE_CONDA: 'Yes' steps: - name: Checkout branch uses: actions/checkout@v1.2.0 @@ -119,13 +122,13 @@ jobs: with: activate-environment: '' auto-update-conda: true - auto-activate-base: true + auto-activate-base: false - name: Test PyQt5 shell: bash -l {0} run: ./.github/workflows/test-pyqt5.sh - name: Test PySide2 shell: bash -l {0} - run: ./.github/workflows/test-pyqt5.sh + run: ./.github/workflows/test-pyside2.sh - name: Test PyQt4 shell: bash -l {0} run: ./.github/workflows/test-pyqt4.sh @@ -133,10 +136,10 @@ jobs: shell: bash -l {0} run: ./.github/workflows/test-pyside.sh - macos: - name: Mac Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} + windows: + name: Windows Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} needs: smoke - runs-on: macos-latest + runs-on: windows-latest env: CI: True PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} @@ -146,9 +149,6 @@ jobs: matrix: PYTHON_VERSION: ['2.7', '3.6', '3.8'] USE_CONDA: ['Yes', 'No'] - exclude: - - PYTHON_VERSION: '2.7' - USE_CONDA: 'Yes' steps: - name: Checkout branch uses: actions/checkout@v1.2.0 @@ -157,13 +157,13 @@ jobs: with: activate-environment: '' auto-update-conda: true - auto-activate-base: false + auto-activate-base: true - name: Test PyQt5 shell: bash -l {0} run: ./.github/workflows/test-pyqt5.sh - name: Test PySide2 shell: bash -l {0} - run: ./.github/workflows/test-pyside2.sh + run: ./.github/workflows/test-pyqt5.sh - name: Test PyQt4 shell: bash -l {0} run: ./.github/workflows/test-pyqt4.sh diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh index 66b43de8..719663a4 100755 --- a/.github/workflows/test-pyqt5.sh +++ b/.github/workflows/test-pyqt5.sh @@ -11,30 +11,30 @@ if [ "$(uname)" == "Darwin" ]; then if [ "$PYTHON_VERSION" = "2.7" ]; then export PYQT_VER=5.9.2=py27h655552a_0 - elif [ "$PYTHON_VERSION" = "3.5" ]; then - export PYQT_VER=5.9.2=py35h11d3b92_0 - else + elif [ "$PYTHON_VERSION" = "3.6" ]; then export PYQT_VER=5.9.2=py36h11d3b92_0 + else + export PYQT_VER=5.* fi elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then if [ "$PYTHON_VERSION" = "2.7" ]; then export PYQT_VER=5.9.2=py27h22d08a2_0 - elif [ "$PYTHON_VERSION" = "3.5" ]; then - export PYQT_VER=5.9.2=py35h751905a_0 - else + elif [ "$PYTHON_VERSION" = "3.6" ]; then export PYQT_VER=5.9.2=py36h751905a_0 + else + export PYQT_VER=5.* fi else if [ "$PYTHON_VERSION" = "2.7" ]; then exit 0 - elif [ "$PYTHON_VERSION" = "3.5" ]; then - export PYQT_VER=5.9.2=py35h1aa27d4_0 - else + elif [ "$PYTHON_VERSION" = "3.6" ]; then export PYQT_VER=5.9.2=py36h1aa27d4_0 + else + export PYQT_VER=5.* fi fi diff --git a/setup.py b/setup.py index 439525f9..4be9a05a 100644 --- a/setup.py +++ b/setup.py @@ -45,7 +45,7 @@ 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', - 'Programming Language :: Python :: 3.5'] + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8'] ) From b4962c48ea396219903a67847c182f371626c0a3 Mon Sep 17 00:00:00 2001 From: goanpeca Date: Thu, 5 Mar 2020 18:58:45 -0500 Subject: [PATCH 216/703] Update xvfb bash invocation in tests --- .github/workflows/ci.yml | 34 +++++++++++++++---------------- .github/workflows/test-pyqt4.sh | 2 -- .github/workflows/test-pyqt5.sh | 4 +--- .github/workflows/test-pyside.sh | 2 -- .github/workflows/test-pyside2.sh | 2 -- 5 files changed, 18 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 95735c57..ee0c3704 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,16 +40,16 @@ jobs: shell: bash -l {0} run: | eval "$(conda shell.bash hook)" - xvfb-run --auto-servernum ./.github/workflows/test-pyqt5.sh + xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyqt5.sh - name: Test PySide2 shell: bash -l {0} - run: xvfb-run --auto-servernum ./.github/workflows/test-pyside2.sh + run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside2.sh - name: Test PyQt4 shell: bash -l {0} - run: xvfb-run --auto-servernum ./.github/workflows/test-pyqt4.sh + run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyqt4.sh - name: Test PySide shell: bash -l {0} - run: xvfb-run --auto-servernum ./.github/workflows/test-pyside.sh + run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside.sh linux: name: Linux Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} @@ -83,20 +83,20 @@ jobs: shell: bash -l {0} run: | eval "$(conda shell.bash hook)" - xvfb-run --auto-servernum ./.github/workflows/test-pyqt5.sh + xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyqt5.sh - name: Test PySide2 shell: bash -l {0} - run: xvfb-run --auto-servernum ./.github/workflows/test-pyside2.sh + run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside2.sh - name: Test PyQt4 shell: bash -l {0} - run: xvfb-run --auto-servernum ./.github/workflows/test-pyqt4.sh + run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyqt4.sh - name: Test PySide shell: bash -l {0} - run: xvfb-run --auto-servernum ./.github/workflows/test-pyside.sh + run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside.sh - name: Upload coverage if: matrix.PYTHON_VERSION == '3.8' shell: bash -l {0} - run: ./.github/workflows/coverage.sh + run: bash -l ./.github/workflows/coverage.sh macos: name: Mac Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} @@ -125,16 +125,16 @@ jobs: auto-activate-base: false - name: Test PyQt5 shell: bash -l {0} - run: ./.github/workflows/test-pyqt5.sh + run: bash -l ./.github/workflows/test-pyqt5.sh - name: Test PySide2 shell: bash -l {0} - run: ./.github/workflows/test-pyside2.sh + run: bash -l ./.github/workflows/test-pyside2.sh - name: Test PyQt4 shell: bash -l {0} - run: ./.github/workflows/test-pyqt4.sh + run: bash -l ./.github/workflows/test-pyqt4.sh - name: Test PySide shell: bash -l {0} - run: ./.github/workflows/test-pyside.sh + run: bash -l ./.github/workflows/test-pyside.sh windows: name: Windows Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} @@ -160,13 +160,13 @@ jobs: auto-activate-base: true - name: Test PyQt5 shell: bash -l {0} - run: ./.github/workflows/test-pyqt5.sh + run: bash -l ./.github/workflows/test-pyqt5.sh - name: Test PySide2 shell: bash -l {0} - run: ./.github/workflows/test-pyqt5.sh + run: bash -l ./.github/workflows/test-pyqt5.sh - name: Test PyQt4 shell: bash -l {0} - run: ./.github/workflows/test-pyqt4.sh + run: bash -l ./.github/workflows/test-pyqt4.sh - name: Test PySide shell: bash -l {0} - run: ./.github/workflows/test-pyside.sh + run: bash -l ./.github/workflows/test-pyside.sh diff --git a/.github/workflows/test-pyqt4.sh b/.github/workflows/test-pyqt4.sh index 79e5ba78..cb5357ef 100755 --- a/.github/workflows/test-pyqt4.sh +++ b/.github/workflows/test-pyqt4.sh @@ -1,7 +1,5 @@ #!/bin/bash -ex -eval "$(conda shell.bash hook)" - # Create conda environment for this test conda create -n test-pyqt4 conda activate test-pyqt4 diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh index 719663a4..e891fe0b 100755 --- a/.github/workflows/test-pyqt5.sh +++ b/.github/workflows/test-pyqt5.sh @@ -1,7 +1,5 @@ #!/bin/bash -ex -eval "$(conda shell.bash hook)" - # Create conda environment for this test conda create -n test-pyqt5 conda activate test-pyqt5 @@ -50,7 +48,7 @@ else else # We are getting segfaults in 5.10 conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q - pip install -q pyqt5==5.9.2 + pip install -q pyqt5 fi fi diff --git a/.github/workflows/test-pyside.sh b/.github/workflows/test-pyside.sh index 4dbc41bb..738f9140 100755 --- a/.github/workflows/test-pyside.sh +++ b/.github/workflows/test-pyside.sh @@ -1,7 +1,5 @@ #!/bin/bash -ex -eval "$(conda shell.bash hook)" - # Create conda environment for this test conda create -n test-pyside conda activate test-pyside diff --git a/.github/workflows/test-pyside2.sh b/.github/workflows/test-pyside2.sh index cf7bc0ec..e7344db7 100755 --- a/.github/workflows/test-pyside2.sh +++ b/.github/workflows/test-pyside2.sh @@ -1,7 +1,5 @@ #!/bin/bash -ex -eval "$(conda shell.bash hook)" - # Create conda environment for this test conda create -n test-pyside2 conda activate test-pyside2 From b3fecf87c2d8ce27ec745ac1255e2a3ad80ba206 Mon Sep 17 00:00:00 2001 From: goanpeca Date: Thu, 5 Mar 2020 19:00:16 -0500 Subject: [PATCH 217/703] Comment out hardcoded installation of SIP verison in CIs --- .github/workflows/test-pyqt5.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh index e891fe0b..fd2d0d5a 100755 --- a/.github/workflows/test-pyqt5.sh +++ b/.github/workflows/test-pyqt5.sh @@ -40,7 +40,7 @@ fi if [ "$USE_CONDA" = "Yes" ]; then conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q conda install -q qt=5.* pyqt=$PYQT_VER -c anaconda -q - conda install -q sip=4.19.8 -c anaconda -q + # conda install -q sip=4.19.8 -c anaconda -q else if [ "$PYTHON_VERSION" = "2.7" ]; then # There are no pyqt5 wheels for Python 2 From 133c99ca5af95fe47b9f3d0b04de281a67ed6a30 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 19 Mar 2021 03:04:11 -0500 Subject: [PATCH 218/703] Disable CI tests on Qt4 since the packages are no longer availible --- .github/workflows/ci.yml | 28 +--------------------------- .github/workflows/test-pyqt4.sh | 20 -------------------- .github/workflows/test-pyside.sh | 24 ------------------------ 3 files changed, 1 insertion(+), 71 deletions(-) delete mode 100755 .github/workflows/test-pyqt4.sh delete mode 100755 .github/workflows/test-pyside.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee0c3704..f1104200 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,6 @@ jobs: sudo apt-get update sudo apt-get install libpulse-dev sudo apt-get install libegl1-mesa - sudo apt-get install libqtwebkit4 - name: Install Conda uses: goanpeca/setup-miniconda@v1 with: @@ -44,12 +43,6 @@ jobs: - name: Test PySide2 shell: bash -l {0} run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside2.sh - - name: Test PyQt4 - shell: bash -l {0} - run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyqt4.sh - - name: Test PySide - shell: bash -l {0} - run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside.sh linux: name: Linux Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} @@ -72,7 +65,6 @@ jobs: sudo apt-get update sudo apt-get install libpulse-dev sudo apt-get install libegl1-mesa - sudo apt-get install libqtwebkit4 - name: Install Conda uses: goanpeca/setup-miniconda@v1 with: @@ -87,12 +79,6 @@ jobs: - name: Test PySide2 shell: bash -l {0} run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside2.sh - - name: Test PyQt4 - shell: bash -l {0} - run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyqt4.sh - - name: Test PySide - shell: bash -l {0} - run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside.sh - name: Upload coverage if: matrix.PYTHON_VERSION == '3.8' shell: bash -l {0} @@ -129,12 +115,6 @@ jobs: - name: Test PySide2 shell: bash -l {0} run: bash -l ./.github/workflows/test-pyside2.sh - - name: Test PyQt4 - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyqt4.sh - - name: Test PySide - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyside.sh windows: name: Windows Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} @@ -163,10 +143,4 @@ jobs: run: bash -l ./.github/workflows/test-pyqt5.sh - name: Test PySide2 shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyqt5.sh - - name: Test PyQt4 - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyqt4.sh - - name: Test PySide - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyside.sh + run: bash -l ./.github/workflows/test-pyside2.sh diff --git a/.github/workflows/test-pyqt4.sh b/.github/workflows/test-pyqt4.sh deleted file mode 100755 index cb5357ef..00000000 --- a/.github/workflows/test-pyqt4.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -ex - -# Create conda environment for this test -conda create -n test-pyqt4 -conda activate test-pyqt4 - -if [ "$USE_CONDA" = "No" ]; then - exit 0 -elif [ "$PYTHON_VERSION" = "3.6" ]; then - exit 0 -else - conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q - conda install qt=4.* pyqt=4.* -c anaconda -q -fi - -# Install package -python setup.py develop - -# Run tests -python qtpy/tests/runtests.py diff --git a/.github/workflows/test-pyside.sh b/.github/workflows/test-pyside.sh deleted file mode 100755 index 738f9140..00000000 --- a/.github/workflows/test-pyside.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash -ex - -# Create conda environment for this test -conda create -n test-pyside -conda activate test-pyside - -if [ "$USE_CONDA" = "No" ]; then - exit 0 -else - if [ "$PYTHON_VERSION" = "3.6" ]; then - exit 0 - elif [ "$PYTHON_VERSION" = "3.5" ]; then - exit 0 - else - conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q - conda install qt=4.* pyside -c anaconda -q - fi -fi - -# Install package -python setup.py develop - -# Run tests -python qtpy/tests/runtests.py From f2b841c7d7dfa842108543a339d7490dd4296cd5 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 19 Mar 2021 03:06:00 -0500 Subject: [PATCH 219/703] Fix Github Actions CI issues on latest versions --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/test-pyqt5.sh | 2 +- .github/workflows/test-pyside2.sh | 3 +++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f1104200..ea09c9e9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,7 +30,7 @@ jobs: sudo apt-get install libpulse-dev sudo apt-get install libegl1-mesa - name: Install Conda - uses: goanpeca/setup-miniconda@v1 + uses: conda-incubator/setup-miniconda@v2 with: activate-environment: '' auto-update-conda: true @@ -66,7 +66,7 @@ jobs: sudo apt-get install libpulse-dev sudo apt-get install libegl1-mesa - name: Install Conda - uses: goanpeca/setup-miniconda@v1 + uses: conda-incubator/setup-miniconda@v2 with: activate-environment: '' auto-update-conda: true @@ -104,7 +104,7 @@ jobs: - name: Checkout branch uses: actions/checkout@v1.2.0 - name: Install Conda - uses: goanpeca/setup-miniconda@v1 + uses: conda-incubator/setup-miniconda@v2 with: activate-environment: '' auto-update-conda: true @@ -133,7 +133,7 @@ jobs: - name: Checkout branch uses: actions/checkout@v1.2.0 - name: Install Conda - uses: goanpeca/setup-miniconda@v1 + uses: conda-incubator/setup-miniconda@v2 with: activate-environment: '' auto-update-conda: true diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh index fd2d0d5a..168b93ec 100755 --- a/.github/workflows/test-pyqt5.sh +++ b/.github/workflows/test-pyqt5.sh @@ -48,7 +48,7 @@ else else # We are getting segfaults in 5.10 conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q - pip install -q pyqt5 + pip install -q pyqt5 PyQtWebEngine fi fi diff --git a/.github/workflows/test-pyside2.sh b/.github/workflows/test-pyside2.sh index e7344db7..22d3ebed 100755 --- a/.github/workflows/test-pyside2.sh +++ b/.github/workflows/test-pyside2.sh @@ -7,6 +7,9 @@ conda activate test-pyside2 if [ "$USE_CONDA" = "Yes" ]; then # There are no conda packages for PySide2 exit 0 +elif [ "$PYTHON_VERSION" != "3.6" ] && [ "$RUNNER_OS" = "Windows" ]; then + # There is no wheel for PySide 5.12.3 on Windows and Python 2.7 or 3.8 + exit 0 else # Simple solution to avoid failures with the # Qt3D modules From 4c3ca1147494fe778bf2927285192f9c948fb792 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 19 Mar 2021 03:06:27 -0500 Subject: [PATCH 220/703] Skip failing tests due to segfaults/deps on specific Qt builds --- qtpy/QtCore.py | 2 +- qtpy/tests/test_patch_qcombobox.py | 7 ++++--- qtpy/tests/test_qtcore.py | 2 ++ qtpy/tests/test_qtmultimedia.py | 2 +- qtpy/tests/test_qtmultimediawidgets.py | 2 +- qtpy/tests/test_uic.py | 12 +++++++----- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index a15ae3bf..d4bbd0d3 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -44,7 +44,7 @@ from PyQt4.QtCore import QCoreApplication from PyQt4.QtCore import Qt from PyQt4.QtCore import pyqtSignal as Signal - from PyQt4.Qtcore import pyqtBoundSignal as SignalInstance + from PyQt4.QtCore import pyqtBoundSignal as SignalInstance from PyQt4.QtCore import pyqtSlot as Slot from PyQt4.QtCore import pyqtProperty as Property from PyQt4.QtGui import (QItemSelection, QItemSelectionModel, diff --git a/qtpy/tests/test_patch_qcombobox.py b/qtpy/tests/test_patch_qcombobox.py index fcafe56a..1e1f04a3 100644 --- a/qtpy/tests/test_patch_qcombobox.py +++ b/qtpy/tests/test_patch_qcombobox.py @@ -4,7 +4,7 @@ import sys import pytest -from qtpy import PYSIDE2, QtGui, QtWidgets +from qtpy import PYQT5, PYSIDE2, QtGui, QtWidgets PY3 = sys.version[0] == "3" @@ -86,8 +86,9 @@ def test_patched_qcombobox(): assert widget.itemText(6) == 'f' -@pytest.mark.skipif((PYSIDE2 and os.environ.get('CI', None) is not None), - reason="It segfaults in our CIs with PYSIDE2") +@pytest.mark.skipif(((PYSIDE2 or PYQT5) + and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2 or PYQT5") def test_model_item(): """ This is a regression test for an issue that caused the call to item(0) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index c32bcafe..81c1e6c4 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -18,6 +18,8 @@ def test_DateTime_toPython(): assert QtCore.QDateTime.toPython is not None +@pytest.mark.skipif(PYSIDE2, + reason="Doesn't seem to be present on PySide2") def test_QtCore_SignalInstance(): class ClassWithSignal(QtCore.QObject): signal = QtCore.Signal() diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 7fc5cf66..1fc7ec97 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -5,7 +5,7 @@ import pytest -@pytest.mark.skipif(os.name == 'nt' and sys.version_info[:2] == (3, 5), +@pytest.mark.skipif(sys.version_info[0] == 3, reason="Conda packages don't seem to include QtMultimedia") def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" diff --git a/qtpy/tests/test_qtmultimediawidgets.py b/qtpy/tests/test_qtmultimediawidgets.py index 2bb52d51..bd659e51 100644 --- a/qtpy/tests/test_qtmultimediawidgets.py +++ b/qtpy/tests/test_qtmultimediawidgets.py @@ -6,7 +6,7 @@ from qtpy import PYQT5, PYSIDE2 @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") -@pytest.mark.skipif(os.name == 'nt' and sys.version_info[:2] == (3, 5), +@pytest.mark.skipif(sys.version_info[0] == 3, reason="Conda packages don't seem to include QtMultimedia") def test_qtmultimediawidgets(): """Test the qtpy.QtMultimediaWidgets namespace""" diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 9a0fd280..9c885f7f 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -3,7 +3,7 @@ import contextlib import pytest -from qtpy import PYSIDE2, QtWidgets +from qtpy import PYQT5, PYSIDE2, QtWidgets from qtpy.QtWidgets import QComboBox from qtpy import uic from qtpy.uic import loadUi @@ -42,8 +42,9 @@ def get_qapp(icon_path=None): return qapp -@pytest.mark.skipif((PYSIDE2 and os.environ.get('CI', None) is not None), - reason="It segfaults in our CIs with PYSIDE2") +@pytest.mark.skipif(((PYSIDE2 or PYQT5) + and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2 or PYQT5") def test_load_ui(): """ Make sure that the patched loadUi function behaves as expected with a @@ -55,8 +56,9 @@ def test_load_ui(): assert isinstance(ui.comboBox, QComboBox) -@pytest.mark.skipif((PYSIDE2 and os.environ.get('CI', None) is not None), - reason="It segfaults in our CIs with PYSIDE2") +@pytest.mark.skipif(((PYSIDE2 or PYQT5) + and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2 or PYQT5") def test_load_ui_custom_auto(tmpdir): """ Test that we can load a .ui file with custom widgets without having to From e320d7ab0087c1f2ebcbfc4b05be4176f9e258f3 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 19 Mar 2021 03:08:15 -0500 Subject: [PATCH 221/703] Upgrade CI commands and infra and eliminate smoke tests --- .github/workflows/ci.yml | 59 ++++++------------------------- .github/workflows/test-pyqt5.sh | 2 +- .github/workflows/test-pyside2.sh | 2 +- 3 files changed, 13 insertions(+), 50 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea09c9e9..6b17fee8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,61 +10,26 @@ on: - master jobs: - smoke: - name: Linux smoke test Py${{ matrix.PYTHON_VERSION }} conda=yes - runs-on: ubuntu-latest - env: - CI: True - PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} - USE_CONDA: 'Yes' - strategy: - fail-fast: false - matrix: - PYTHON_VERSION: ['2.7', '3.6'] - steps: - - name: Checkout branch - uses: actions/checkout@v1.2.0 - - name: Install System Packages - run: | - sudo apt-get update - sudo apt-get install libpulse-dev - sudo apt-get install libegl1-mesa - - name: Install Conda - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: '' - auto-update-conda: true - auto-activate-base: false - - name: Test PyQt5 - shell: bash -l {0} - run: | - eval "$(conda shell.bash hook)" - xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyqt5.sh - - name: Test PySide2 - shell: bash -l {0} - run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside2.sh - linux: name: Linux Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} - needs: smoke runs-on: ubuntu-latest env: CI: True PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} USE_CONDA: ${{ matrix.USE_CONDA }} strategy: - fail-fast: false + fail-fast: false matrix: - PYTHON_VERSION: ['3.8'] + PYTHON_VERSION: ['2.7', '3.6', '3.8'] USE_CONDA: ['Yes', 'No'] steps: - name: Checkout branch - uses: actions/checkout@v1.2.0 + uses: actions/checkout@v2 - name: Install System Packages - run: | - sudo apt-get update - sudo apt-get install libpulse-dev - sudo apt-get install libegl1-mesa + run: | + sudo apt update + sudo apt install libpulse-dev + sudo apt install libegl1-mesa - name: Install Conda uses: conda-incubator/setup-miniconda@v2 with: @@ -86,14 +51,13 @@ jobs: macos: name: Mac Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} - needs: smoke runs-on: macos-latest env: CI: True PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} USE_CONDA: ${{ matrix.USE_CONDA }} strategy: - fail-fast: false + fail-fast: false matrix: PYTHON_VERSION: ['2.7', '3.6', '3.8'] USE_CONDA: ['Yes', 'No'] @@ -102,7 +66,7 @@ jobs: USE_CONDA: 'Yes' steps: - name: Checkout branch - uses: actions/checkout@v1.2.0 + uses: actions/checkout@v2 - name: Install Conda uses: conda-incubator/setup-miniconda@v2 with: @@ -118,20 +82,19 @@ jobs: windows: name: Windows Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} - needs: smoke runs-on: windows-latest env: CI: True PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} USE_CONDA: ${{ matrix.USE_CONDA }} strategy: - fail-fast: false + fail-fast: false matrix: PYTHON_VERSION: ['2.7', '3.6', '3.8'] USE_CONDA: ['Yes', 'No'] steps: - name: Checkout branch - uses: actions/checkout@v1.2.0 + uses: actions/checkout@v2 - name: Install Conda uses: conda-incubator/setup-miniconda@v2 with: diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh index 168b93ec..79982996 100755 --- a/.github/workflows/test-pyqt5.sh +++ b/.github/workflows/test-pyqt5.sh @@ -53,7 +53,7 @@ else fi # Install package -python setup.py develop +python -m pip install -e . # Run tests python qtpy/tests/runtests.py diff --git a/.github/workflows/test-pyside2.sh b/.github/workflows/test-pyside2.sh index 22d3ebed..5ca9e56b 100755 --- a/.github/workflows/test-pyside2.sh +++ b/.github/workflows/test-pyside2.sh @@ -18,7 +18,7 @@ else fi # Install package -python setup.py develop +python -m pip install -e . # Run tests python qtpy/tests/runtests.py From 551ea0968ae6c595e686034ab5e46bc765e050e0 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 19 Mar 2021 03:17:38 -0500 Subject: [PATCH 222/703] Upgrade Qt versions being tested on CIs --- .github/workflows/test-pyqt5.sh | 21 ++++++++++----------- .github/workflows/test-pyside2.sh | 7 +++---- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh index 79982996..678a3ec7 100755 --- a/.github/workflows/test-pyqt5.sh +++ b/.github/workflows/test-pyqt5.sh @@ -8,21 +8,21 @@ conda activate test-pyqt5 if [ "$(uname)" == "Darwin" ]; then if [ "$PYTHON_VERSION" = "2.7" ]; then - export PYQT_VER=5.9.2=py27h655552a_0 + export QT_VER=5.9 elif [ "$PYTHON_VERSION" = "3.6" ]; then - export PYQT_VER=5.9.2=py36h11d3b92_0 + export QT_VER=5.12 else - export PYQT_VER=5.* + export QT_VER=5.* fi elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then if [ "$PYTHON_VERSION" = "2.7" ]; then - export PYQT_VER=5.9.2=py27h22d08a2_0 + export QT_VER=5.9 elif [ "$PYTHON_VERSION" = "3.6" ]; then - export PYQT_VER=5.9.2=py36h751905a_0 + export QT_VER=5.12 else - export PYQT_VER=5.* + export QT_VER=5.* fi else @@ -30,17 +30,16 @@ else if [ "$PYTHON_VERSION" = "2.7" ]; then exit 0 elif [ "$PYTHON_VERSION" = "3.6" ]; then - export PYQT_VER=5.9.2=py36h1aa27d4_0 + export QT_VER=5.9 else - export PYQT_VER=5.* + export QT_VER=5.* fi fi if [ "$USE_CONDA" = "Yes" ]; then - conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q - conda install -q qt=5.* pyqt=$PYQT_VER -c anaconda -q - # conda install -q sip=4.19.8 -c anaconda -q + conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge -q + conda install -q qt=$QT_VER pyqt=$QT_VER -c conda-forge -q else if [ "$PYTHON_VERSION" = "2.7" ]; then # There are no pyqt5 wheels for Python 2 diff --git a/.github/workflows/test-pyside2.sh b/.github/workflows/test-pyside2.sh index 5ca9e56b..f5e1c271 100755 --- a/.github/workflows/test-pyside2.sh +++ b/.github/workflows/test-pyside2.sh @@ -8,13 +8,12 @@ if [ "$USE_CONDA" = "Yes" ]; then # There are no conda packages for PySide2 exit 0 elif [ "$PYTHON_VERSION" != "3.6" ] && [ "$RUNNER_OS" = "Windows" ]; then - # There is no wheel for PySide 5.12.3 on Windows and Python 2.7 or 3.8 + # There is no wheel for PySide 5.12 on Windows and Python 2.7 or 3.8 exit 0 else - # Simple solution to avoid failures with the - # Qt3D modules + # Simple solution to avoid failures with the Qt3D modules conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge -q - pip install -q pyside2==5.12.3 + pip install -q pyside2==5.12 fi # Install package From d8f2ada3d226fcf66e69bba61578197673d73d39 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 19 Mar 2021 03:29:58 -0500 Subject: [PATCH 223/703] Update metadata, supported version and maintainer info --- AUTHORS.md | 19 ++++++++++--------- README.md | 2 +- setup.py | 12 +++++++----- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index a6b4d15e..cb19d7b5 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,16 +1,17 @@ -Maintainer -========== +# Authors -Gonzalo Peña-Castellanos ([@goanpeca](http://github.com/goanpeca)) +## Maintainer -Main Authors -============ +Spyder Development Team ([Spyder-IDE](http://github.com/spyder-ide)) + + +## Main Authors * Colin Duquesnoy ([@ColinDuquesnoy](http://github.com/ColinDuquesnoy)) -* [The Spyder Development Team](https://github.com/spyder-ide/spyder/graphs/contributors) +* [The QtPy Contributors](https://github.com/spyder-ide/qtpy/graphs/contributors) + -Contributors -============ +## Contributors -* Thomas Robitaille ([@astrofrog](http://www.github.com/astrofrog)) \ No newline at end of file +* Thomas Robitaille ([@astrofrog](http://www.github.com/astrofrog)) diff --git a/README.md b/README.md index d6734d35..22f3d008 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![Github build status](https://github.com/spyder-ide/qtpy/workflows/Tests/badge.svg)](https://github.com/spyder-ide/qtpy/actions) [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) -*Copyright © 2009–2019 The Spyder Development Team* +*Copyright © 2009–2021 The Spyder Development Team* ## Description diff --git a/setup.py b/setup.py index 4be9a05a..3b582d49 100644 --- a/setup.py +++ b/setup.py @@ -23,13 +23,14 @@ name='QtPy', version=version_ns['__version__'], packages=find_packages(exclude=['contrib', 'docs', 'tests*']), + python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*', keywords=["qt PyQt4 PyQt5 PySide"], url='https://github.com/spyder-ide/qtpy', license='MIT', - author='Colin Duquesnoy, The Spyder Development Team', - author_email='goanpeca@gmail.com', - maintainer='Gonzalo Peña-Castellanos', - maintainer_email='goanpeca@gmail.com', + author='Colin Duquesnoy and the Spyder Development Team', + author_email='spyder.python@gmail.com', + maintainer='Spyder Development Team and QtPy Contributors', + maintainer_email='spyder.python@gmail.com', description='Provides an abstraction layer on top of the various Qt ' 'bindings (PyQt5, PyQt4 and PySide) and additional custom ' 'QWidgets.', @@ -47,5 +48,6 @@ 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8'] + 'Programming Language :: Python :: 3.8', + ] ) From 27a1cb32092dc15bdb0e1db994c6b4f82c1d9cbd Mon Sep 17 00:00:00 2001 From: Antonio Valentino Date: Thu, 13 Feb 2020 16:56:53 +0100 Subject: [PATCH 224/703] Add QPositioning module and tests --- qtpy/QtPositioning.py | 18 ++++++++++++++++++ qtpy/tests/test_qtpositioning.py | 25 +++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 qtpy/QtPositioning.py create mode 100644 qtpy/tests/test_qtpositioning.py diff --git a/qtpy/QtPositioning.py b/qtpy/QtPositioning.py new file mode 100644 index 00000000..2b46d356 --- /dev/null +++ b/qtpy/QtPositioning.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright 2020 Antonio Valentino +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtPositioning classes and functions.""" + +# Local imports +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtPositioning import * +elif PYSIDE2: + from PySide2.QtPositioning import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py new file mode 100644 index 00000000..16073e96 --- /dev/null +++ b/qtpy/tests/test_qtpositioning.py @@ -0,0 +1,25 @@ +from __future__ import absolute_import + +import pytest +from qtpy import PYQT5, PYSIDE2 + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +def test_qtpositioning(): + """Test the qtpy.QtPositioning namespace""" + from qtpy import QtPositioning + assert QtPositioning.QGeoAddress is not None + assert QtPositioning.QGeoAreaMonitorInfo is not None + assert QtPositioning.QGeoAreaMonitorSource is not None + assert QtPositioning.QGeoCircle is not None + assert QtPositioning.QGeoCoordinate is not None + assert QtPositioning.QGeoLocation is not None + assert QtPositioning.QGeoPath is not None + # assert QtPositioning.QGeoPolygon is not None # New in Qt 5.10 + assert QtPositioning.QGeoPositionInfo is not None + assert QtPositioning.QGeoPositionInfoSource is not None + # assert QtPositioning.QGeoPositionInfoSourceFactory is not None + assert QtPositioning.QGeoRectangle is not None + assert QtPositioning.QGeoSatelliteInfo is not None + assert QtPositioning.QGeoSatelliteInfoSource is not None + assert QtPositioning.QGeoShape is not None + assert QtPositioning.QNmeaPositionInfoSource is not None From 8213202a1de4ab803aa8bfa6936e370c51489e0a Mon Sep 17 00:00:00 2001 From: Antonio Valentino Date: Sun, 27 Oct 2019 20:30:35 +0100 Subject: [PATCH 225/703] loadUiType implementation --- qtpy/tests/test_uic.py | 28 +++++++++++++++++++++++-- qtpy/uic.py | 47 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 9c885f7f..696c9e53 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -6,7 +6,7 @@ from qtpy import PYQT5, PYSIDE2, QtWidgets from qtpy.QtWidgets import QComboBox from qtpy import uic -from qtpy.uic import loadUi +from qtpy.uic import loadUi, loadUiType QCOMBOBOX_SUBCLASS = """ @@ -56,6 +56,30 @@ def test_load_ui(): assert isinstance(ui.comboBox, QComboBox) +@pytest.mark.skipif(((PYSIDE2 or PYQT5) + and os.environ.get('CI', None) is not None), + reason="It segfaults in our CIs with PYSIDE2 or PYQT5") +def test_load_ui_type(): + """ + Make sure that the patched loadUiType function behaves as expected with a + simple .ui file. + """ + app = get_qapp() + ui_type, ui_base_type = loadUiType( + os.path.join(os.path.dirname(__file__), 'test.ui')) + assert ui_type.__name__ == 'Ui_Form' + + class Widget(ui_base_type, ui_type): + def __init__(self): + super(Widget, self).__init__() + self.setupUi(self) + + ui = Widget() + assert isinstance(ui, QtWidgets.QWidget) + assert isinstance(ui.pushButton, QtWidgets.QPushButton) + assert isinstance(ui.comboBox, QComboBox) + + @pytest.mark.skipif(((PYSIDE2 or PYQT5) and os.environ.get('CI', None) is not None), reason="It segfaults in our CIs with PYSIDE2 or PYQT5") @@ -81,7 +105,7 @@ def test_load_full_uic(): QT_API = os.environ.get('QT_API', '').lower() if QT_API.startswith('pyside'): assert hasattr(uic, 'loadUi') - assert not hasattr(uic, 'loadUiType') + assert hasattr(uic, 'loadUiType') else: objects = ['compileUi', 'compileUiDir', 'loadUi', 'loadUiType', 'widgetPluginPath'] diff --git a/qtpy/uic.py b/qtpy/uic.py index 07d7a787..f1e3175c 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -14,7 +14,7 @@ else: - __all__ = ['loadUi'] + __all__ = ['loadUi', 'loadUiType'] # In PySide, loadUi does not exist, so we define it using QUiLoader, and # then make sure we expose that function. This is adapted from qt-helpers @@ -81,9 +81,11 @@ if PYSIDE: from PySide.QtCore import QMetaObject from PySide.QtUiTools import QUiLoader + from pysideuic import compileUi elif PYSIDE2: from PySide2.QtCore import QMetaObject from PySide2.QtUiTools import QUiLoader + from pyside2uic import compileUi class UiLoader(QUiLoader): """ @@ -226,3 +228,46 @@ def loadUi(uifile, baseinstance=None, workingDirectory=None): widget = loader.load(uifile) QMetaObject.connectSlotsByName(widget) return widget + + + # Credit: + # http://stackoverflow.com/questions/4442286/python-code-genration-with-pyside-uic/14195313#14195313 + def loadUiType(uifile, from_imports=False): + """Load a .ui file and return the generated form class and + the Qt base class. + + The "loadUiType" command convert the ui file to py code + in-memory first and then execute it in a special frame to + retrieve the form_class. + + """ + + import sys + if sys.version_info >= (3, 0): + from io import StringIO + else: + from io import BytesIO as StringIO + from xml.etree.ElementTree import ElementTree + from . import QtWidgets + + # Parse the UI file + etree = ElementTree() + ui = etree.parse(uifile) + + widget_class = ui.find('widget').get('class') + form_class = ui.find('class').text + + with open(uifile, 'r') as fd: + code_stream = StringIO() + frame = {} + + compileUi(fd, code_stream, indent=0, from_imports=from_imports) + pyc = compile(code_stream.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 = getattr(QtWidgets, widget_class) + + return form_class, base_class From 5ea2b94cce94494cdaf03c929071db46ddf036c7 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Sat, 7 Aug 2021 18:29:38 +0530 Subject: [PATCH 226/703] Update qtpy/uic.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Althviz Moré --- qtpy/uic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/uic.py b/qtpy/uic.py index 91a1c4ca..a7c1fd31 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -2,7 +2,7 @@ from . import PYSIDE, PYSIDE2, PYQT4, PYQT5 from .QtWidgets import QComboBox -from .pycompat import PY33 +from .py3compat import PY33 if PYQT5: From 1d7f805a194530a6aa1a7e016b996f3cae92e73f Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 10 Aug 2021 10:12:49 +0200 Subject: [PATCH 227/703] Undo changes for custom_widgets iteration --- qtpy/uic.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/qtpy/uic.py b/qtpy/uic.py index a7c1fd31..07d7a787 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -2,7 +2,6 @@ from . import PYSIDE, PYSIDE2, PYQT4, PYQT5 from .QtWidgets import QComboBox -from .py3compat import PY33 if PYQT5: @@ -181,12 +180,8 @@ def _get_custom_widgets(ui_file): return {} custom_widget_classes = {} - if PY33: - children = list(custom_widgets) - else: - children = custom_widgets.getchildren() - for custom_widget in children: + for custom_widget in custom_widgets.getchildren(): cw_class = custom_widget.find('class').text cw_header = custom_widget.find('header').text From 4bdbce97c31408bc46c03cc51729b56ec17fc94e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Tue, 10 Aug 2021 23:13:36 +0200 Subject: [PATCH 228/703] Update setup.py classifiers --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 3b582d49..696eb21e 100644 --- a/setup.py +++ b/setup.py @@ -49,5 +49,6 @@ 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', ] ) From 3331c6ab36333318f1903ee63f7793764337aa63 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 17 Aug 2021 09:49:38 +0200 Subject: [PATCH 229/703] Release 1.10.0 --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ qtpy/_version.py | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 253f739c..56e90421 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # History of changes +## Version 1.10.0 (2021-08-17) + +### Issues Closed + +* [Issue 238](https://github.com/spyder-ide/qtpy/issues/238) - PySide2 and Python3.9: xml.etree.ElementTree.Element' object has no attribute 'getchildren +* [Issue 222](https://github.com/spyder-ide/qtpy/issues/222) - Imported modules are not respected +* [Issue 220](https://github.com/spyder-ide/qtpy/issues/220) - MNT: Stop using ci-helpers in appveyor.yml +* [Issue 206](https://github.com/spyder-ide/qtpy/issues/206) - DeprecationWarning for getchildren ([PR 224](https://github.com/spyder-ide/qtpy/pull/224) by [@irrcombat](https://github.com/irrcombat)) +* [Issue 198](https://github.com/spyder-ide/qtpy/issues/198) - PyQt4-sip==4.19.13 not supported + +In this release 5 issues were closed. + +### Pull Requests Merged + +* [PR 241](https://github.com/spyder-ide/qtpy/pull/241) - PR: Update setup.py classifiers, by [@dalthviz](https://github.com/dalthviz) +* [PR 230](https://github.com/spyder-ide/qtpy/pull/230) - PR: Fix imported modules logic if 'FORCE_QT_API' is empty, by [@hiaselhans](https://github.com/hiaselhans) +* [PR 224](https://github.com/spyder-ide/qtpy/pull/224) - PR: Support python 3.9 `custom_widgets` iteration, by [@irrcombat](https://github.com/irrcombat) ([206](https://github.com/spyder-ide/qtpy/issues/206)) +* [PR 215](https://github.com/spyder-ide/qtpy/pull/215) - PR: Slight typo fix, by [@altendky](https://github.com/altendky) +* [PR 214](https://github.com/spyder-ide/qtpy/pull/214) - PR: Handle QtCore.SignalInstance/pyqtBoundSignal, by [@altendky](https://github.com/altendky) +* [PR 208](https://github.com/spyder-ide/qtpy/pull/208) - PR: Move CI to Github Actions, by [@goanpeca](https://github.com/goanpeca) +* [PR 204](https://github.com/spyder-ide/qtpy/pull/204) - PR: Add Python 3.9 compatibility for `collections.abc` module, by [@tirkarthi](https://github.com/tirkarthi) +* [PR 199](https://github.com/spyder-ide/qtpy/pull/199) - PR: Add support to PyQt4-sip 4.19.13, by [@milanmatic](https://github.com/milanmatic) + +In this release 8 pull requests were closed. + + +---- + + ## Version 1.9.0 (2019-07-23) ### New features diff --git a/qtpy/_version.py b/qtpy/_version.py index f5216d17..d7bb0d61 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 10, 0, 'dev0') +version_info = (1, 10, 0) __version__ = '.'.join(map(str, version_info)) From c2be6eec2fee5c5114e42ed052d74804945140ab Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 17 Aug 2021 09:55:28 +0200 Subject: [PATCH 230/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index d7bb0d61..cb9f17f0 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 10, 0) +version_info = (2, 0, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From a7972f29ec953bae81c8fb8e7d04f153c2c6c291 Mon Sep 17 00:00:00 2001 From: Antonio Valentino Date: Tue, 24 Aug 2021 19:16:51 +0200 Subject: [PATCH 231/703] Add credits in the loadUiType docstring --- qtpy/uic.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qtpy/uic.py b/qtpy/uic.py index f1e3175c..a7d71c22 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -229,9 +229,6 @@ def loadUi(uifile, baseinstance=None, workingDirectory=None): QMetaObject.connectSlotsByName(widget) return widget - - # Credit: - # http://stackoverflow.com/questions/4442286/python-code-genration-with-pyside-uic/14195313#14195313 def loadUiType(uifile, from_imports=False): """Load a .ui file and return the generated form class and the Qt base class. @@ -240,6 +237,7 @@ def loadUiType(uifile, from_imports=False): in-memory first and then execute it in a special frame to retrieve the form_class. + Credit: https://stackoverflow.com/a/14195313/15954282 """ import sys From 86b13645660b1d32c24df55b54c9f0c3f8395cda Mon Sep 17 00:00:00 2001 From: Antonio Valentino Date: Wed, 25 Aug 2021 18:06:00 +0200 Subject: [PATCH 232/703] Test QtPositioning.QGeoPolygon --- qtpy/tests/test_qtpositioning.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py index 16073e96..68f4c12e 100644 --- a/qtpy/tests/test_qtpositioning.py +++ b/qtpy/tests/test_qtpositioning.py @@ -14,10 +14,12 @@ def test_qtpositioning(): assert QtPositioning.QGeoCoordinate is not None assert QtPositioning.QGeoLocation is not None assert QtPositioning.QGeoPath is not None - # assert QtPositioning.QGeoPolygon is not None # New in Qt 5.10 + assert QtPositioning.QGeoPolygon is not None assert QtPositioning.QGeoPositionInfo is not None assert QtPositioning.QGeoPositionInfoSource is not None - # assert QtPositioning.QGeoPositionInfoSourceFactory is not None + # QGeoPositionInfoSourceFactory is not available in PyQt + # assert QtPositioning.QGeoPositionInfoSourceFactory is not None # New in Qt 5.2 + # assert QtPositioning.QGeoPositionInfoSourceFactoryV2 is not None # New in Qt 5.14 assert QtPositioning.QGeoRectangle is not None assert QtPositioning.QGeoSatelliteInfo is not None assert QtPositioning.QGeoSatelliteInfoSource is not None From 34f5100656ae15f30371b4cbbfb82e2b7b24b731 Mon Sep 17 00:00:00 2001 From: Antonio Valentino Date: Wed, 25 Aug 2021 21:23:15 +0200 Subject: [PATCH 233/703] Commend out assertion on QtPositioning.QGeoPolygon (not availabel in Qt 5.9) --- qtpy/tests/test_qtpositioning.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py index 68f4c12e..f6b5bffa 100644 --- a/qtpy/tests/test_qtpositioning.py +++ b/qtpy/tests/test_qtpositioning.py @@ -14,7 +14,8 @@ def test_qtpositioning(): assert QtPositioning.QGeoCoordinate is not None assert QtPositioning.QGeoLocation is not None assert QtPositioning.QGeoPath is not None - assert QtPositioning.QGeoPolygon is not None + # CI for Python 2.7 and 3.6 uses Qt 5.9 + # assert QtPositioning.QGeoPolygon is not None # New in Qt 5.10 assert QtPositioning.QGeoPositionInfo is not None assert QtPositioning.QGeoPositionInfoSource is not None # QGeoPositionInfoSourceFactory is not available in PyQt From 1615b260a0e8596405d0b0b5d2bce99ed46fbd49 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 1 Sep 2021 20:08:53 +0200 Subject: [PATCH 234/703] QtWinExtras: QtWin is unavailable in PySide2 --- qtpy/tests/test_qtwinextras.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index 4e349a8f..77e5c282 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -18,7 +18,8 @@ def test_qtwinextras(): assert QtWinExtras.QWinTaskbarProgress is not None assert QtWinExtras.QWinThumbnailToolBar is not None assert QtWinExtras.QWinThumbnailToolButton is not None - assert QtWinExtras.QtWin is not None + if not PYSIDE2: # See https://bugreports.qt.io/browse/PYSIDE-1047 + assert QtWinExtras.QtWin is not None if PYSIDE2: assert QtWinExtras.QWinColorizationChangeEvent is not None From add793c923688a35c13981f5ff817fac9dfa880a Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 1 Sep 2021 20:39:40 +0200 Subject: [PATCH 235/703] QtWinExtras: Skip test with conda, Python 3 and PyQt5 --- qtpy/tests/test_qtwinextras.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index 77e5c282..3f00bb75 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -1,13 +1,16 @@ from __future__ import absolute_import +import os import sys import pytest from qtpy import PYQT5, PYSIDE2 +from qtpy.py3compat import PY3 - -@pytest.mark.skipif(sys.platform != "win32" or not (PYQT5 or PYSIDE2), - reason="Only available in Qt5 bindings and Windows platform") +@pytest.mark.skipif( + sys.platform != "win32" or not (PYQT5 or PYSIDE2) or + os.environ['USE_CONDA'] == 'Yes' and PY3 and PYQT5, + reason="Only available in Qt5 bindings and Windows platform") def test_qtwinextras(): """Test the qtpy.QtWinExtras namespace""" from qtpy import QtWinExtras From 1a71a35648a4464bdd73d1f37032ac5a261d8fd5 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 2 Sep 2021 18:59:44 +0200 Subject: [PATCH 236/703] QtWebEngineWidgets: Add QWebEngineProfile for PyQt5 and PySide2 --- qtpy/QtWebEngineWidgets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index d1df5bfb..33a66575 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -22,6 +22,8 @@ from PyQt5.QtWebEngineWidgets import QWebEnginePage from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtWebEngineWidgets import QWebEngineSettings + # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 + from PyQt5.QtWebEngineWidgets import QWebEngineProfile except ImportError: from PyQt5.QtWebKitWidgets import QWebPage as QWebEnginePage from PyQt5.QtWebKitWidgets import QWebView as QWebEngineView @@ -31,6 +33,8 @@ from PySide2.QtWebEngineWidgets import QWebEnginePage from PySide2.QtWebEngineWidgets import QWebEngineView from PySide2.QtWebEngineWidgets import QWebEngineSettings + # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 + from PySide2.QtWebEngineWidgets import QWebEngineProfile elif PYQT4: from PyQt4.QtWebKit import QWebPage as QWebEnginePage from PyQt4.QtWebKit import QWebView as QWebEngineView From 3230678446c23c8dc8255ea0d6bf8c42be6cfdfe Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 2 Sep 2021 13:56:48 -0500 Subject: [PATCH 237/703] QtDataVisualization: Support backward compatibility also on Windows Needed since Windows doesn't do upper/lowercase differentiation for file names --- qtpy/QtDatavisualization.py | 1 - qtpy/__init__.py | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) delete mode 100644 qtpy/QtDatavisualization.py diff --git a/qtpy/QtDatavisualization.py b/qtpy/QtDatavisualization.py deleted file mode 100644 index cd0cb4db..00000000 --- a/qtpy/QtDatavisualization.py +++ /dev/null @@ -1 +0,0 @@ -from .QtDataVisualization import * diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 71ec168c..bd9b7d82 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -229,3 +229,6 @@ class PythonQtWarning(Warning): API_NAME += (" (API v{0})".format(sip.getapi('QString'))) except AttributeError: pass + +# QtDataVisualization backward compatibility (valid also on Windows) +from . import QtDataVisualization as QtDatavisualization From dd496fb4f04575b7936e032968a73a0f6e5df14c Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 3 Sep 2021 11:58:43 -0500 Subject: [PATCH 238/703] QtDataVisualization: Update test and add validations for alias import --- qtpy/__init__.py | 7 +- qtpy/tests/test_qtdatavisualization.py | 114 ++++++++++++++++--------- 2 files changed, 81 insertions(+), 40 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index bd9b7d82..836509bd 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -70,6 +70,7 @@ # Version of QtPy from ._version import __version__ +from .py3compat import PY2 class PythonQtError(RuntimeError): @@ -230,5 +231,7 @@ class PythonQtWarning(Warning): except AttributeError: pass -# QtDataVisualization backward compatibility (valid also on Windows) -from . import QtDataVisualization as QtDatavisualization +if (PYQT5 or PYSIDE2) and PY2: + # QtDataVisualization backward compatibility (valid also on Windows) + # Only available for Python 2 and Qt5 bindings + from . import QtDataVisualization as QtDatavisualization diff --git a/qtpy/tests/test_qtdatavisualization.py b/qtpy/tests/test_qtdatavisualization.py index 32142d66..8ae7bd3c 100644 --- a/qtpy/tests/test_qtdatavisualization.py +++ b/qtpy/tests/test_qtdatavisualization.py @@ -2,45 +2,83 @@ import pytest from qtpy import PYQT5, PYSIDE2 +from qtpy.py3compat import PY3 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +@pytest.mark.skipif( + not (PYQT5 or PYSIDE2) or PY3, + reason="Only available in Qt5 bindings and Python 2") def test_qtdatavisualization(): """Test the qtpy.QtDataVisualization namespace""" - QtDataVisualization = pytest.importorskip("qtpy.QtDataVisualization") + # QtDataVisualization + assert qtpy.QtDataVisualization.QScatter3DSeries is not None + assert qtpy.QtDataVisualization.QSurfaceDataItem is not None + assert qtpy.QtDataVisualization.QSurface3DSeries is not None + assert qtpy.QtDataVisualization.QAbstract3DInputHandler is not None + assert qtpy.QtDataVisualization.QHeightMapSurfaceDataProxy is not None + assert qtpy.QtDataVisualization.QAbstractDataProxy is not None + assert qtpy.QtDataVisualization.Q3DCamera is not None + assert qtpy.QtDataVisualization.QAbstract3DGraph is not None + assert qtpy.QtDataVisualization.QCustom3DVolume is not None + assert qtpy.QtDataVisualization.Q3DInputHandler is not None + assert qtpy.QtDataVisualization.QBarDataProxy is not None + assert qtpy.QtDataVisualization.QSurfaceDataProxy is not None + assert qtpy.QtDataVisualization.QScatterDataItem is not None + assert qtpy.QtDataVisualization.Q3DLight is not None + assert qtpy.QtDataVisualization.QScatterDataProxy is not None + assert qtpy.QtDataVisualization.QValue3DAxis is not None + assert qtpy.QtDataVisualization.Q3DBars is not None + assert qtpy.QtDataVisualization.QBarDataItem is not None + assert qtpy.QtDataVisualization.QItemModelBarDataProxy is not None + assert qtpy.QtDataVisualization.Q3DTheme is not None + assert qtpy.QtDataVisualization.QCustom3DItem is not None + assert qtpy.QtDataVisualization.QItemModelScatterDataProxy is not None + assert qtpy.QtDataVisualization.QValue3DAxisFormatter is not None + assert qtpy.QtDataVisualization.QItemModelSurfaceDataProxy is not None + assert qtpy.QtDataVisualization.Q3DScatter is not None + assert qtpy.QtDataVisualization.QTouch3DInputHandler is not None + assert qtpy.QtDataVisualization.QBar3DSeries is not None + assert qtpy.QtDataVisualization.QAbstract3DAxis is not None + assert qtpy.QtDataVisualization.Q3DScene is not None + assert qtpy.QtDataVisualization.QCategory3DAxis is not None + assert qtpy.QtDataVisualization.QAbstract3DSeries is not None + assert qtpy.QtDataVisualization.Q3DObject is not None + assert qtpy.QtDataVisualization.QCustom3DLabel is not None + assert qtpy.QtDataVisualization.Q3DSurface is not None + assert qtpy.QtDataVisualization.QLogValue3DAxisFormatter is not None - assert QtDataVisualization.QScatter3DSeries is not None - assert QtDataVisualization.QSurfaceDataItem is not None - assert QtDataVisualization.QSurface3DSeries is not None - assert QtDataVisualization.QAbstract3DInputHandler is not None - assert QtDataVisualization.QHeightMapSurfaceDataProxy is not None - assert QtDataVisualization.QAbstractDataProxy is not None - assert QtDataVisualization.Q3DCamera is not None - assert QtDataVisualization.QAbstract3DGraph is not None - assert QtDataVisualization.QCustom3DVolume is not None - assert QtDataVisualization.Q3DInputHandler is not None - assert QtDataVisualization.QBarDataProxy is not None - assert QtDataVisualization.QSurfaceDataProxy is not None - assert QtDataVisualization.QScatterDataItem is not None - assert QtDataVisualization.Q3DLight is not None - assert QtDataVisualization.QScatterDataProxy is not None - assert QtDataVisualization.QValue3DAxis is not None - assert QtDataVisualization.Q3DBars is not None - assert QtDataVisualization.QBarDataItem is not None - assert QtDataVisualization.QItemModelBarDataProxy is not None - assert QtDataVisualization.Q3DTheme is not None - assert QtDataVisualization.QCustom3DItem is not None - assert QtDataVisualization.QItemModelScatterDataProxy is not None - assert QtDataVisualization.QValue3DAxisFormatter is not None - assert QtDataVisualization.QItemModelSurfaceDataProxy is not None - assert QtDataVisualization.Q3DScatter is not None - assert QtDataVisualization.QTouch3DInputHandler is not None - assert QtDataVisualization.QBar3DSeries is not None - assert QtDataVisualization.QAbstract3DAxis is not None - assert QtDataVisualization.Q3DScene is not None - assert QtDataVisualization.QCategory3DAxis is not None - assert QtDataVisualization.QAbstract3DSeries is not None - assert QtDataVisualization.Q3DObject is not None - assert QtDataVisualization.QCustom3DLabel is not None - assert QtDataVisualization.Q3DSurface is not None - assert QtDataVisualization.QLogValue3DAxisFormatter is not None - + # QtDatavisualization + assert qtpy.QtDatavisualization.QScatter3DSeries is not None + assert qtpy.QtDatavisualization.QSurfaceDataItem is not None + assert qtpy.QtDatavisualization.QSurface3DSeries is not None + assert qtpy.QtDatavisualization.QAbstract3DInputHandler is not None + assert qtpy.QtDatavisualization.QHeightMapSurfaceDataProxy is not None + assert qtpy.QtDatavisualization.QAbstractDataProxy is not None + assert qtpy.QtDatavisualization.Q3DCamera is not None + assert qtpy.QtDatavisualization.QAbstract3DGraph is not None + assert qtpy.QtDatavisualization.QCustom3DVolume is not None + assert qtpy.QtDatavisualization.Q3DInputHandler is not None + assert qtpy.QtDatavisualization.QBarDataProxy is not None + assert qtpy.QtDatavisualization.QSurfaceDataProxy is not None + assert qtpy.QtDatavisualization.QScatterDataItem is not None + assert qtpy.QtDatavisualization.Q3DLight is not None + assert qtpy.QtDatavisualization.QScatterDataProxy is not None + assert qtpy.QtDatavisualization.QValue3DAxis is not None + assert qtpy.QtDatavisualization.Q3DBars is not None + assert qtpy.QtDatavisualization.QBarDataItem is not None + assert qtpy.QtDatavisualization.QItemModelBarDataProxy is not None + assert qtpy.QtDatavisualization.Q3DTheme is not None + assert qtpy.QtDatavisualization.QCustom3DItem is not None + assert qtpy.QtDatavisualization.QItemModelScatterDataProxy is not None + assert qtpy.QtDatavisualization.QValue3DAxisFormatter is not None + assert qtpy.QtDatavisualization.QItemModelSurfaceDataProxy is not None + assert qtpy.QtDatavisualization.Q3DScatter is not None + assert qtpy.QtDatavisualization.QTouch3DInputHandler is not None + assert qtpy.QtDatavisualization.QBar3DSeries is not None + assert qtpy.QtDatavisualization.QAbstract3DAxis is not None + assert qtpy.QtDatavisualization.Q3DScene is not None + assert qtpy.QtDatavisualization.QCategory3DAxis is not None + assert qtpy.QtDatavisualization.QAbstract3DSeries is not None + assert qtpy.QtDatavisualization.Q3DObject is not None + assert qtpy.QtDatavisualization.QCustom3DLabel is not None + assert qtpy.QtDatavisualization.Q3DSurface is not None + assert qtpy.QtDatavisualization.QLogValue3DAxisFormatter is not None From 9ef4c803cf38f594c80044ca7e8f46f3a30452b6 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 3 Sep 2021 12:12:20 -0500 Subject: [PATCH 239/703] QtDataVisualization: Only available on Windows --- qtpy/__init__.py | 4 ++-- qtpy/tests/test_qtdatavisualization.py | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 836509bd..a19a0039 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -231,7 +231,7 @@ class PythonQtWarning(Warning): except AttributeError: pass -if (PYQT5 or PYSIDE2) and PY2: +if (PYQT5 or PYSIDE2) and PY2 and sys.platform == "win32": # QtDataVisualization backward compatibility (valid also on Windows) - # Only available for Python 2 and Qt5 bindings + # Only available for Python 2 on Windows and Qt5 bindings from . import QtDataVisualization as QtDatavisualization diff --git a/qtpy/tests/test_qtdatavisualization.py b/qtpy/tests/test_qtdatavisualization.py index 8ae7bd3c..8e287da6 100644 --- a/qtpy/tests/test_qtdatavisualization.py +++ b/qtpy/tests/test_qtdatavisualization.py @@ -1,12 +1,14 @@ from __future__ import absolute_import +import sys + import pytest from qtpy import PYQT5, PYSIDE2 from qtpy.py3compat import PY3 @pytest.mark.skipif( - not (PYQT5 or PYSIDE2) or PY3, - reason="Only available in Qt5 bindings and Python 2") + sys.platform != "win32" or not (PYQT5 or PYSIDE2) or PY3, + reason="Only available in Qt5 bindings and Python 2 on Windows") def test_qtdatavisualization(): """Test the qtpy.QtDataVisualization namespace""" # QtDataVisualization From 6b12fba39e7cef2f6b189c5eb6814e508efce323 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 3 Sep 2021 19:47:36 +0200 Subject: [PATCH 240/703] Release 1.11.0 --- CHANGELOG.md | 25 +++++++++++++++++++++++++ qtpy/_version.py | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 56e90421..f9113f87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,30 @@ # History of changes +## Version 1.11.0 (2021-09-03) + +### Issues Closed + +* [Issue 201](https://github.com/spyder-ide/qtpy/issues/201) - Missing QWebEngineProfile from QtWebEngineWidgets ([PR 242](https://github.com/spyder-ide/qtpy/pull/242) by [@dalthviz](https://github.com/dalthviz)) + +In this release 1 issue was closed. + +### Pull Requests Merged + +* [PR 243](https://github.com/spyder-ide/qtpy/pull/243) - PR: `QtDataVisualization` backward compatibility handling on Windows, by [@dalthviz](https://github.com/dalthviz) +* [PR 242](https://github.com/spyder-ide/qtpy/pull/242) - PR: Add `QtWebEngineWidgets.QWebEngineProfile` for PyQt5 and PySide2, by [@dalthviz](https://github.com/dalthviz) ([201](https://github.com/spyder-ide/qtpy/issues/201)) +* [PR 228](https://github.com/spyder-ide/qtpy/pull/228) - PR: Rename QtDatavisualization to use uppercase v, by [@antlarr](https://github.com/antlarr) +* [PR 219](https://github.com/spyder-ide/qtpy/pull/219) - PR: Add support for QStyleOptionFrameV3 from PyQt4, by [@PierreRaybaut](https://github.com/PierreRaybaut) +* [PR 218](https://github.com/spyder-ide/qtpy/pull/218) - PR: Add QtWinExtras module, by [@phil65](https://github.com/phil65) +* [PR 209](https://github.com/spyder-ide/qtpy/pull/209) - PR: Add support for QtSerialPort add-on, by [@Stanowczo](https://github.com/Stanowczo) +* [PR 205](https://github.com/spyder-ide/qtpy/pull/205) - PR: Add support for the QtPositioning module, by [@avalentino](https://github.com/avalentino) +* [PR 202](https://github.com/spyder-ide/qtpy/pull/202) - PR: Add loadUiType implementation for PySide2, by [@avalentino](https://github.com/avalentino) + +In this release 8 pull requests were closed. + + +---- + + ## Version 1.10.0 (2021-08-17) ### Issues Closed diff --git a/qtpy/_version.py b/qtpy/_version.py index cb9f17f0..d0c22d81 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 0, 0, 'dev0') +version_info = (1, 11, 0) __version__ = '.'.join(map(str, version_info)) From a955c0afaf5259991f3419750fc7d64c7ded4e4e Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 3 Sep 2021 19:52:52 +0200 Subject: [PATCH 241/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index d0c22d81..cb9f17f0 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 11, 0) +version_info = (2, 0, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From faf91d3bed8a895be822c97b467e71d6ad70e961 Mon Sep 17 00:00:00 2001 From: Andrew Ammerlaan Date: Mon, 6 Sep 2021 20:05:07 +0200 Subject: [PATCH 242/703] qtpy/tests/test_uic.py: skip if pyside2uic not installed Signed-off-by: Andrew Ammerlaan --- qtpy/tests/test_uic.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 696c9e53..d7d3b599 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -3,8 +3,12 @@ import contextlib import pytest -from qtpy import PYQT5, PYSIDE2, QtWidgets +from qtpy import PYQT5, PYSIDE2, PYSIDE, QtWidgets from qtpy.QtWidgets import QComboBox + +if PYSIDE2 or PYSIDE: + pytest.importorskip("pyside2uic", reason="pyside2uic not installed") + from qtpy import uic from qtpy.uic import loadUi, loadUiType From 1e3c3ca00c7f9c761077840d4cf65754ec552155 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 13 Sep 2021 18:31:55 +0200 Subject: [PATCH 243/703] uic: wrapp pysideuic and pyside2uic imports with try since could be unavailable --- qtpy/uic.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/qtpy/uic.py b/qtpy/uic.py index c428f2b9..d26a25a1 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -81,11 +81,17 @@ if PYSIDE: from PySide.QtCore import QMetaObject from PySide.QtUiTools import QUiLoader - from pysideuic import compileUi + try: + from pysideuic import compileUi + except ImportError: + pass elif PYSIDE2: from PySide2.QtCore import QMetaObject from PySide2.QtUiTools import QUiLoader - from pyside2uic import compileUi + try: + from pyside2uic import compileUi + except ImportError: + pass class UiLoader(QUiLoader): """ From e855c3b169a126c851c8eabb23ecaf7ceeaae643 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 13 Sep 2021 19:26:45 +0200 Subject: [PATCH 244/703] Release v1.11.1 --- CHANGELOG.md | 19 +++++++++++++++++++ qtpy/_version.py | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9113f87..8000abac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # History of changes +## Version 1.11.1 (2021-09-13) + +### Issues Closed + +* [Issue 245](https://github.com/spyder-ide/qtpy/issues/245) - Importing `qtpy.uic` raises an exception ([PR 246](https://github.com/spyder-ide/qtpy/pull/246) by [@dalthviz](https://github.com/dalthviz)) + +In this release 1 issue was closed. + +### Pull Requests Merged + +* [PR 246](https://github.com/spyder-ide/qtpy/pull/246) - PR: Wrap `pysideuic` and `pyside2uic` imports since they could be unavailable, by [@dalthviz](https://github.com/dalthviz) ([245](https://github.com/spyder-ide/qtpy/issues/245)) +* [PR 244](https://github.com/spyder-ide/qtpy/pull/244) - qtpy/tests/test_uic.py: skip if pyside2uic not installed, by [@AndrewAmmerlaan](https://github.com/AndrewAmmerlaan) + +In this release 2 pull requests were closed. + + +---- + + ## Version 1.11.0 (2021-09-03) ### Issues Closed diff --git a/qtpy/_version.py b/qtpy/_version.py index cb9f17f0..214e9f0c 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 0, 0, 'dev0') +version_info = (1, 11, 1) __version__ = '.'.join(map(str, version_info)) From 704826d3955b490684f2bb774f343f7585ab55d5 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 14 Sep 2021 09:30:17 +0200 Subject: [PATCH 245/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 214e9f0c..cb9f17f0 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 11, 1) +version_info = (2, 0, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 35007f35d1cc966a9baa3e3cc410130a4b1c286b Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 21 Sep 2021 15:30:21 +0200 Subject: [PATCH 246/703] QtDataVisualization: Fix importing with alias when the module doesn't exists --- qtpy/__init__.py | 8 +++++--- qtpy/tests/test_qtwinextras.py | 8 +++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index a19a0039..ccb7d417 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -231,7 +231,9 @@ class PythonQtWarning(Warning): except AttributeError: pass -if (PYQT5 or PYSIDE2) and PY2 and sys.platform == "win32": - # QtDataVisualization backward compatibility (valid also on Windows) - # Only available for Python 2 on Windows and Qt5 bindings +try: + # QtDataVisualization backward compatibility (QtDataVisualization vs. QtDatavisualization) + # Only available for Qt5 bindings > 5.9 on Windows from . import QtDataVisualization as QtDatavisualization +except ImportError: + pass \ No newline at end of file diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index 3f00bb75..f41f9ff6 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -4,13 +4,11 @@ import sys import pytest -from qtpy import PYQT5, PYSIDE2 -from qtpy.py3compat import PY3 +from qtpy import PYSIDE2 @pytest.mark.skipif( - sys.platform != "win32" or not (PYQT5 or PYSIDE2) or - os.environ['USE_CONDA'] == 'Yes' and PY3 and PYQT5, - reason="Only available in Qt5 bindings and Windows platform") + sys.platform != "win32" or os.environ['USE_CONDA'] == 'Yes', + reason="Only available in Qt5 bindings > 5.9 (only available with pip in the current CI setup) and Windows platform") def test_qtwinextras(): """Test the qtpy.QtWinExtras namespace""" from qtpy import QtWinExtras From 7cc341dcab7029361a1b4ffeba82f068864e1252 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 23 Sep 2021 18:18:00 +0200 Subject: [PATCH 247/703] Release 1.11.2 --- CHANGELOG.md | 18 ++++++++++++++++++ qtpy/_version.py | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8000abac..4141eb32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # History of changes +## Version 1.11.2 (2021-09-23) + +### Issues Closed + +* [Issue 248](https://github.com/spyder-ide/qtpy/issues/248) - Missing QtDataVisualization ([PR 249](https://github.com/spyder-ide/qtpy/pull/249) by [@dalthviz](https://github.com/dalthviz)) + +In this release 1 issue was closed. + +### Pull Requests Merged + +* [PR 249](https://github.com/spyder-ide/qtpy/pull/249) - PR: Add handling for QtDataVisualization when missing, by [@dalthviz](https://github.com/dalthviz) ([248](https://github.com/spyder-ide/qtpy/issues/248)) + +In this release 1 pull request was closed. + + +---- + + ## Version 1.11.1 (2021-09-13) ### Issues Closed diff --git a/qtpy/_version.py b/qtpy/_version.py index cb9f17f0..3745f022 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 0, 0, 'dev0') +version_info = (1, 11, 2) __version__ = '.'.join(map(str, version_info)) From da3bd7640fa652ebc8e4bfdba3d4f6ea641b541d Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 23 Sep 2021 18:23:25 +0200 Subject: [PATCH 248/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 3745f022..cb9f17f0 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 11, 2) +version_info = (2, 0, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From a84bf8262f6fa772632daec94ef47e3d94b1de80 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Mon, 14 Dec 2020 17:32:09 +0100 Subject: [PATCH 249/703] Add support for PySide6 Several modules are not available yet and will be re-added later in qt 6.x series: https://doc-snapshots.qt.io/qt6-dev/whatsnew60.html#removed-modules-in-qt-6-0 --- README.md | 3 +- qtpy/QtCore.py | 21 ++++++++- qtpy/QtDataVisualization.py | 8 +++- qtpy/QtGui.py | 10 +++- qtpy/QtHelp.py | 7 ++- qtpy/QtMultimedia.py | 3 ++ qtpy/QtNetwork.py | 4 +- qtpy/QtOpenGL.py | 2 + qtpy/QtPrintSupport.py | 4 +- qtpy/QtQml.py | 4 +- qtpy/QtQuick.py | 4 +- qtpy/QtQuickWidgets.py | 4 +- qtpy/QtSql.py | 4 +- qtpy/QtSvg.py | 8 +++- qtpy/QtTest.py | 9 ++-- qtpy/QtWebChannel.py | 4 +- qtpy/QtWebEngine.py | 20 ++++++++ qtpy/QtWebEngineWidgets.py | 7 ++- qtpy/QtWidgets.py | 16 +++++-- qtpy/__init__.py | 66 +++++++++++++++++++++++---- qtpy/tests/conftest.py | 12 +++++ qtpy/tests/test_main.py | 31 ++++++++++++- qtpy/tests/test_patch_qcombobox.py | 10 ++-- qtpy/tests/test_qtdesigner.py | 6 +-- qtpy/tests/test_qtmultimedia.py | 1 + qtpy/tests/test_qtnetwork.py | 9 ++-- qtpy/tests/test_qtsvg.py | 7 +-- qtpy/tests/test_qtwebenginewidgets.py | 6 ++- qtpy/tests/test_qtwinextras.py | 4 +- qtpy/tests/test_qtxmlpatterns.py | 3 +- qtpy/tests/test_uic.py | 14 +++--- qtpy/uic.py | 11 ++++- 32 files changed, 256 insertions(+), 66 deletions(-) create mode 100644 qtpy/QtWebEngine.py diff --git a/README.md b/README.md index 22f3d008..3f6e46b7 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ **QtPy** is a small abstraction layer that lets you write applications using a single API call to either PyQt or PySide. -It provides support for PyQt5, PyQt4, PySide2 and PySide using the Qt5 layout +It provides support for PyQt5, PyQt4, PySide6, PySide2 and PySide using the Qt5 layout (where the QtGui module has been split into QtGui and QtWidgets). Basically, you can write your code as if you were using PySide2 @@ -53,6 +53,7 @@ default unless you set the `QT_API` environment variable. * `pyqt5` (to use PyQt5). * `pyqt` or `pyqt4` (to use PyQt4). +* `pyside6` (to use PySide6) * `pyside2` (to use PySide2) * `pyside` (to use PySide). diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index d4bbd0d3..b353a0dc 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -10,10 +10,14 @@ Provides QtCore classes and functions. """ -from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError +from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PYQT4, PYSIDE, PythonQtError +if PYQT6: + from PyQt6.QtCore import * + from PyQt6.QtCore import pyqtSignal as Signal + from PyQt6.QtCore import QT_VERSION_STR as __version__ -if PYQT5: +elif PYQT5: from PyQt5.QtCore import * from PyQt5.QtCore import pyqtSignal as Signal from PyQt5.QtCore import pyqtBoundSignal as SignalInstance @@ -27,6 +31,19 @@ # Those are imported from `import *` del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR + +elif PYSIDE6: + from PySide6.QtCore import * + import PySide6.QtCore + __version__ = PySide6.QtCore.__version__ + + # obsolete in qt6 + Qt.BackgroundColorRole = Qt.BackgroundRole + Qt.TextColorRole = Qt.ForegroundRole + Qt.BackgroundColorRole = Qt.BackgroundRole + Qt.TextColorRole = Qt.ForegroundRole + Qt.MidButton = Qt.MiddleButton + elif PYSIDE2: from PySide2.QtCore import * diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index cfb2b3b6..a50fe233 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -8,10 +8,14 @@ """Provides QtDataVisualization classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if PYQT5: +if PYQT6: + from PyQt6.QtDataVisualization import * +elif PYQT5: from PyQt5.QtDataVisualization import * +elif PYSIDE6: + from PySide6.QtDataVisualization import * elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.QtDataVisualization as __temp diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index be8f5688..90c074a4 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -14,13 +14,19 @@ """ import warnings -from . import PYQT5, PYQT4, PYSIDE, PYSIDE2, PythonQtError +from . import PYQT6, PYQT5, PYQT4, PYSIDE, PYSIDE2, PYSIDE6, PythonQtError -if PYQT5: +if PYQT6: + from PyQt6.QtGui import * +elif PYQT5: from PyQt5.QtGui import * elif PYSIDE2: from PySide2.QtGui import * +elif PYSIDE6: + from PySide6.QtGui import * + QFontMetrics.width = QFontMetrics.horizontalAdvance + elif PYQT4: try: # Older versions of PyQt4 do not provide these diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index ca9d93dd..bbd44036 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -9,13 +9,12 @@ import warnings -from . import PYQT5 -from . import PYQT4 -from . import PYSIDE -from . import PYSIDE2 +from . import PYQT5, PYQT4, PYSIDE6, PYSIDE2, PYSIDE if PYQT5: from PyQt5.QtHelp import * +elif PYSIDE6: + from PySide6.QtHelp import * elif PYSIDE2: from PySide2.QtHelp import * elif PYQT4: diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index 9015ece9..96d7e906 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -4,9 +4,12 @@ from . import PYQT4 from . import PYSIDE from . import PYSIDE2 +from . import PYSIDE6 if PYQT5: from PyQt5.QtMultimedia import * +elif PYSIDE6: + from PySide6.QtMultimedia import * elif PYSIDE2: from PySide2.QtMultimedia import * elif PYQT4: diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index 49faded7..d8b6aa87 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -10,11 +10,13 @@ Provides QtNetwork classes and functions. """ -from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYSIDE2, PYSIDE6, PYQT4, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtNetwork import * +elif PYSIDE6: + from PySide6.QtNetwork import * elif PYSIDE2: from PySide2.QtNetwork import * elif PYQT4: diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 69ef8228..5b3a80d7 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -12,6 +12,8 @@ if PYQT5: from PyQt5.QtOpenGL import * +elif PYSIDE6: + from PySide6.QtOpenGL import * elif PYSIDE2: from PySide2.QtOpenGL import * elif PYQT4: diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index b821d411..d7f4ff10 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -9,11 +9,13 @@ Provides QtPrintSupport classes and functions. """ -from . import PYQT5, PYQT4,PYSIDE2, PYSIDE, PythonQtError +from . import PYQT5, PYQT4, PYSIDE6, PYSIDE2, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtPrintSupport import * +elif PYSIDE6: + from PySide6.QtPrintSupport import * elif PYSIDE2: from PySide2.QtPrintSupport import * elif PYQT4: diff --git a/qtpy/QtQml.py b/qtpy/QtQml.py index 117f977f..762ac8ae 100644 --- a/qtpy/QtQml.py +++ b/qtpy/QtQml.py @@ -8,10 +8,12 @@ """Provides QtQml classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtQml import * +elif PYSIDE6: + from PySide6.QtQml import * elif PYSIDE2: from PySide2.QtQml import * else: diff --git a/qtpy/QtQuick.py b/qtpy/QtQuick.py index 82910667..0a28eaa0 100644 --- a/qtpy/QtQuick.py +++ b/qtpy/QtQuick.py @@ -8,10 +8,12 @@ """Provides QtQuick classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtQuick import * +elif PYSIDE6: + from PySide6.QtQuick import * elif PYSIDE2: from PySide2.QtQuick import * else: diff --git a/qtpy/QtQuickWidgets.py b/qtpy/QtQuickWidgets.py index 545d52b6..eb7884fe 100644 --- a/qtpy/QtQuickWidgets.py +++ b/qtpy/QtQuickWidgets.py @@ -8,10 +8,12 @@ """Provides QtQuickWidgets classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtQuickWidgets import * +elif PYSIDE6: + from PySide6.QtQuickWidgets import * elif PYSIDE2: from PySide2.QtQuickWidgets import * else: diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 98520bef..583e5cb0 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -8,10 +8,12 @@ """Provides QtSql classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYSIDE6, PYSIDE2, PYQT4, PYSIDE, PythonQtError if PYQT5: from PyQt5.QtSql import * +elif PYSIDE6: + from PySide6.QtSql import * elif PYSIDE2: from PySide2.QtSql import * elif PYQT4: diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index edc075ea..ccc3fbd3 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -8,10 +8,14 @@ """Provides QtSvg classes and functions.""" # Local imports -from . import PYQT4, PYSIDE2, PYQT5, PYSIDE, PythonQtError +from . import PYQT4, PYSIDE6, PYSIDE2, PYQT5, PYQT6, PYSIDE, PythonQtError -if PYQT5: +if PYQT6: + from PyQt6.QtSvg import * +elif PYQT5: from PyQt5.QtSvg import * +elif PYSIDE6: + from PySide6.QtSvg import * elif PYSIDE2: from PySide2.QtSvg import * elif PYQT4: diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index cca5e192..b86c174e 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -10,11 +10,14 @@ Provides QtTest and functions """ -from . import PYQT5,PYSIDE2, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PYQT4, PYSIDE, PythonQtError - -if PYQT5: +if PYQT6: + from PyQt6.QtTest import QTest +elif PYQT5: from PyQt5.QtTest import QTest +elif PYSIDE6: + from PySide6.QtTest import QTest elif PYSIDE2: from PySide2.QtTest import QTest elif PYQT4: diff --git a/qtpy/QtWebChannel.py b/qtpy/QtWebChannel.py index 2862a056..789bfe80 100644 --- a/qtpy/QtWebChannel.py +++ b/qtpy/QtWebChannel.py @@ -8,10 +8,12 @@ """Provides QtWebChannel classes and functions.""" # Local imports -from . import PYSIDE2, PYQT5, PythonQtError +from . import PYSIDE2, PYSIDE6, PYQT5, PythonQtError if PYQT5: from PyQt5.QtWebChannel import * +elif PYSIDE6: + from PySide6.QtWebChannel import * elif PYSIDE2: from PySide2.QtWebChannel import * else: diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py new file mode 100644 index 00000000..69f0a03c --- /dev/null +++ b/qtpy/QtWebEngine.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# +# Copyright © 2014-2015 Colin Duquesnoy +# Copyright © 2009- The Spyder development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +""" +Provides QtWebEngine classes and functions. +""" + +from . import PYQT5, PYSIDE6 + +if PYQT5: + from PyQt5.QtWebEngine import * +elif PYSIDE6: + from PySide6.QtWebEngine import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 33a66575..20de297b 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -10,7 +10,7 @@ Provides QtWebEngineWidgets classes and functions. """ -from . import PYQT5,PYSIDE2, PYQT4, PYSIDE, PythonQtError +from . import PYQT5,PYSIDE2, PYSIDE6, PYQT4, PYSIDE, PythonQtError # To test if we are using WebEngine or WebKit @@ -29,6 +29,11 @@ from PyQt5.QtWebKitWidgets import QWebView as QWebEngineView from PyQt5.QtWebKit import QWebSettings as QWebEngineSettings WEBENGINE = False +elif PYSIDE6: + from PySide6.QtWebEngineWidgets import * + from PySide6.QtWebEngineCore import QWebEnginePage + from PySide6.QtWebEngineCore import QWebEngineSettings + from PySide6.QtWebEngineCore import QWebEngineProfile elif PYSIDE2: from PySide2.QtWebEngineWidgets import QWebEnginePage from PySide2.QtWebEngineWidgets import QWebEngineView diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 66ef3aba..0dbb4a76 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -13,13 +13,23 @@ were the ``PyQt5.QtWidgets`` module. """ -from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYQT4, PYSIDE, PYSIDE6, PythonQtError from ._patch.qcombobox import patch_qcombobox from ._patch.qheaderview import introduce_renamed_methods_qheaderview - -if PYQT5: +if PYQT6: + from PyQt6.QtWidgets import * + from PyQt6.QtGui import QAction, QActionGroup +elif PYQT5: from PyQt5.QtWidgets import * +elif PYSIDE6: + from PySide6.QtWidgets import * + from PySide6.QtGui import QAction, QActionGroup + from PySide6.QtOpenGLWidgets import QOpenGLWidget + QTextEdit.setTabStopWidth = QTextEdit.setTabStopDistance + QTextEdit.tabStopWidth = QTextEdit.tabStopDistance + QPlainTextEdit.setTabStopWidth = QPlainTextEdit.setTabStopDistance + QPlainTextEdit.tabStopWidth = QPlainTextEdit.tabStopDistance elif PYSIDE2: from PySide2.QtWidgets import * elif PYQT4: diff --git a/qtpy/__init__.py b/qtpy/__init__.py index ccb7d417..ab0e9e51 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -12,8 +12,8 @@ If one of the APIs has already been imported, then it will be used. -Otherwise, the shim will automatically select the first available API (PyQt5, -PySide2, PyQt4 and finally PySide); in that case, you can force the use of one +Otherwise, the shim will automatically select the first available API (PyQt5, PyQt6, +PySide2, PySide6, PyQt4 and finally PySide); in that case, you can force the use of one specific bindings (e.g. if your application is using one specific bindings and you need to use library that use QtPy) by setting up the ``QT_API`` environment variable. @@ -26,6 +26,13 @@ >>> from qtpy import QtGui, QtWidgets, QtCore >>> print(QtWidgets.QWidget) +PyQt6 +===== + + >>> import os + >>> os.environ['QT_API'] = 'pyqt6' + >>> from qtpy import QtGui, QtWidgets, QtCore + >>> print(QtWidgets.QWidget) PySide2 ====== @@ -38,6 +45,14 @@ >>> from qtpy import QtGui, QtWidgets, QtCore >>> print(QtWidgets.QWidget) +PySide6 +======= + + >>> import os + >>> os.environ['QT_API'] = 'pyside6' + >>> from qtpy import QtGui, QtWidgets, QtCore + >>> print(QtWidgets.QWidget) + PyQt4 ===== @@ -89,6 +104,8 @@ class PythonQtWarning(Warning): # Names of the expected PyQt5 api PYQT5_API = ['pyqt5'] +PYQT6_API = ['pyqt6'] + # Names of the expected PyQt4 api PYQT4_API = [ 'pyqt', # name used in IPython.qt @@ -101,6 +118,9 @@ class PythonQtWarning(Warning): # Names of the expected PySide2 api PYSIDE2_API = ['pyside2'] +# Names of the expected PySide6 api +PYSIDE6_API = ['pyside6'] + # Detecting if a binding was specified by the user binding_specified = QT_API in os.environ @@ -109,17 +129,21 @@ class PythonQtWarning(Warning): API = os.environ[QT_API].lower() initial_api = API -assert API in (PYQT5_API + PYQT4_API + PYSIDE_API + PYSIDE2_API) +assert API in (PYQT5_API + PYQT6_API + PYQT4_API + PYSIDE_API + PYSIDE2_API + PYSIDE6_API) is_old_pyqt = is_pyqt46 = False PYQT5 = True -PYQT4 = PYSIDE = PYSIDE2 = False +PYQT4 = PYQT6 = PYSIDE = PYSIDE2 = PYSIDE6 = False # When `FORCE_QT_API` is set, we disregard # any previously imported python bindings. -if not os.environ.get('FORCE_QT_API'): - if 'PyQt5' in sys.modules: +if 'FORCE_QT_API' in os.environ: + if 'PyQt6' in sys.modules: + API = initial_api if initial_api in PYQT6_API else 'pyqt6' + elif 'PyQt5' in sys.modules: API = initial_api if initial_api in PYQT5_API else 'pyqt5' + elif 'PySide6' in sys.modules: + API = initial_api if initial_api in PYSIDE6_API else 'pyside6' elif 'PySide2' in sys.modules: API = initial_api if initial_api in PYSIDE2_API else 'pyside2' elif 'PyQt4' in sys.modules: @@ -150,9 +174,20 @@ class PythonQtWarning(Warning): "system.") del macos_version + except ImportError: + API = os.environ['QT_API'] = 'pyqt6' + +if API in PYQT6_API: + try: + from PyQt6.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore + from PyQt6.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore + PYSIDE_VERSION = None + PYQT5 = False + PYQT6 = True except ImportError: API = os.environ['QT_API'] = 'pyside2' + if API in PYSIDE2_API: try: from PySide2 import __version__ as PYSIDE_VERSION # analysis:ignore @@ -172,9 +207,22 @@ class PythonQtWarning(Warning): "system.") del macos_version + except ImportError: + API = os.environ['QT_API'] = 'pyside6' + +if API in PYSIDE6_API: + try: + from PySide6 import __version__ as PYSIDE_VERSION # analysis:ignore + from PySide6.QtCore import __version__ as QT_VERSION # analysis:ignore + + PYQT_VERSION = None + PYQT5 = False + PYSIDE6 = True + except ImportError: API = os.environ['QT_API'] = 'pyqt' + if API in PYQT4_API: try: import sip @@ -210,7 +258,7 @@ class PythonQtWarning(Warning): from PySide import __version__ as PYSIDE_VERSION # analysis:ignore from PySide.QtCore import __version__ as QT_VERSION # analysis:ignore PYQT_VERSION = None - PYQT5 = PYSIDE2 = False + PYQT5 = PYSIDE2 = PYSIDE6 = False PYSIDE = True except ImportError: raise PythonQtError('No Qt bindings could be found') @@ -221,8 +269,8 @@ class PythonQtWarning(Warning): warnings.warn('Selected binding "{}" could not be found, ' 'using "{}"'.format(initial_api, API), RuntimeWarning) -API_NAME = {'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4', - 'pyside': 'PySide', 'pyside2':'PySide2'}[API] +API_NAME = {'pyqt6': 'PyQt6', 'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4', + 'pyside': 'PySide', 'pyside2':'PySide2', 'pyside6': 'PySide6'}[API] if PYQT4: import sip diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index c631886f..134d6d1a 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -66,6 +66,18 @@ def pytest_report_header(config): except AttributeError: versions += 'unknown version' + versions += os.linesep + versions += 'PySide6: ' + + try: + import PySide6 + from PySide6 import QtCore + versions += "PySide: {0} - Qt: {1}".format(PySide6.__version__, QtCore.__version__) + except ImportError: + versions += 'not installed' + except AttributeError: + versions += 'unknown version' + versions += os.linesep return versions diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index 2449249c..16e334e8 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -1,6 +1,11 @@ import os -from qtpy import QtCore, QtGui, QtWidgets, QtWebEngineWidgets +from qtpy import QtCore, QtGui, QtWidgets +try: + # removed in qt 6.0 + from qtpy import QtWebEngineWidgets +except Exception: + pass def assert_pyside(): @@ -23,6 +28,17 @@ def assert_pyside2(): assert QtWidgets.QWidget is PySide2.QtWidgets.QWidget assert QtWebEngineWidgets.QWebEnginePage is PySide2.QtWebEngineWidgets.QWebEnginePage +def assert_pyside6(): + """ + Make sure that we are using PySide + """ + import PySide6 + assert QtCore.QEvent is PySide6.QtCore.QEvent + assert QtGui.QPainter is PySide6.QtGui.QPainter + assert QtWidgets.QWidget is PySide6.QtWidgets.QWidget + # Only valid for qt>=6.2 + # assert QtWebEngineWidgets.QWebEnginePage is PySide6.QtWebEngineCore.QWebEnginePage + def assert_pyqt4(): """ Make sure that we are using PyQt4 @@ -47,6 +63,15 @@ def assert_pyqt5(): else: assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebKitWidgets.QWebPage +def assert_pyqt6(): + """ + Make sure that we are using PyQt6 + """ + import PyQt6 + assert QtCore.QEvent is PyQt6.QtCore.QEvent + assert QtGui.QPainter is PyQt6.QtGui.QPainter + assert QtWidgets.QWidget is PyQt6.QtWidgets.QWidget + def test_qt_api(): """ @@ -61,8 +86,12 @@ def test_qt_api(): assert_pyqt4() elif QT_API == 'pyqt5': assert_pyqt5() + elif QT_API == 'pyqt6': + assert_pyqt6() elif QT_API == 'pyside2': assert_pyside2() + elif QT_API == 'pyside6': + assert_pyside6() else: # If the tests are run locally, USE_QT_API and QT_API may not be # defined, but we still want to make sure qtpy is behaving sensibly. diff --git a/qtpy/tests/test_patch_qcombobox.py b/qtpy/tests/test_patch_qcombobox.py index 1e1f04a3..12536fac 100644 --- a/qtpy/tests/test_patch_qcombobox.py +++ b/qtpy/tests/test_patch_qcombobox.py @@ -4,8 +4,8 @@ import sys import pytest -from qtpy import PYQT5, PYSIDE2, QtGui, QtWidgets +from qtpy import PYQT5, PYSIDE2, PYSIDE6, QtGui, QtWidgets PY3 = sys.version[0] == "3" @@ -26,8 +26,8 @@ def __getitem__(self, item): raise ValueError("Failing") -@pytest.mark.skipif(PY3 or (PYSIDE2 and os.environ.get('CI', None) is not None), - reason="It segfaults in Python 3 and in our CIs with PySide2") +@pytest.mark.skipif(PY3 or PYSIDE2 or PYSIDE6, + reason="It segfaults in Python 3 and with PySide2/6") def test_patched_qcombobox(): """ In PySide, using Python objects as userData in QComboBox causes @@ -86,9 +86,9 @@ def test_patched_qcombobox(): assert widget.itemText(6) == 'f' -@pytest.mark.skipif(((PYSIDE2 or PYQT5) +@pytest.mark.skipif(((PYSIDE2 or PYSIDE6 or PYQT5) and os.environ.get('CI', None) is not None), - reason="It segfaults in our CIs with PYSIDE2 or PYQT5") + reason="It segfaults in our CIs with PYSIDE2/PYSIDE6 or PYQT5") def test_model_item(): """ This is a regression test for an issue that caused the call to item(0) diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index 0327c6f7..aa4bebe2 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -1,9 +1,9 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2, PYSIDE +from qtpy import PYSIDE6, PYSIDE2, PYSIDE -@pytest.mark.skipif(PYSIDE2 or PYSIDE, reason="QtDesigner is not avalaible in PySide/PySide2") +@pytest.mark.skipif((PYSIDE6 or PYSIDE2 or PYSIDE), reason="QtDesigner is not avalaible in PySide/PySide2/PySide6") def test_qtdesigner(): from qtpy import QtDesigner """Test the qtpy.QtDesigner namespace""" @@ -25,4 +25,4 @@ def test_qtdesigner(): assert QtDesigner.QDesignerWidgetBoxInterface is not None assert QtDesigner.QExtensionFactory is not None assert QtDesigner.QExtensionManager is not None - assert QtDesigner.QFormBuilder is not None \ No newline at end of file + assert QtDesigner.QFormBuilder is not None diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 1fc7ec97..1d6b11e2 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -3,6 +3,7 @@ import sys import pytest +from qtpy import PYSIDE6 @pytest.mark.skipif(sys.version_info[0] == 3, diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index 7f645910..8093fe29 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -1,7 +1,7 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE, PYSIDE2, QtNetwork +from qtpy import PYSIDE, PYSIDE2, PYSIDE6, QtNetwork def test_qtnetwork(): @@ -17,9 +17,10 @@ def test_qtnetwork(): assert QtNetwork.QNetworkDiskCache is not None assert QtNetwork.QNetworkReply is not None assert QtNetwork.QNetworkRequest is not None - assert QtNetwork.QNetworkConfigurationManager is not None - assert QtNetwork.QNetworkConfiguration is not None - assert QtNetwork.QNetworkSession is not None + if not PYSIDE6: + assert QtNetwork.QNetworkConfigurationManager is not None + assert QtNetwork.QNetworkConfiguration is not None + assert QtNetwork.QNetworkSession is not None assert QtNetwork.QAuthenticator is not None assert QtNetwork.QHostAddress is not None assert QtNetwork.QHostInfo is not None diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py index 74d8522e..34f57a1e 100644 --- a/qtpy/tests/test_qtsvg.py +++ b/qtpy/tests/test_qtsvg.py @@ -1,13 +1,14 @@ from __future__ import absolute_import import pytest - +from qtpy import PYSIDE6 def test_qtsvg(): """Test the qtpy.QtSvg namespace""" from qtpy import QtSvg - assert QtSvg.QGraphicsSvgItem is not None + if not PYSIDE6: + assert QtSvg.QGraphicsSvgItem is not None + assert QtSvg.QSvgWidget is not None assert QtSvg.QSvgGenerator is not None assert QtSvg.QSvgRenderer is not None - assert QtSvg.QSvgWidget is not None diff --git a/qtpy/tests/test_qtwebenginewidgets.py b/qtpy/tests/test_qtwebenginewidgets.py index 77c8e1f5..7d60048c 100644 --- a/qtpy/tests/test_qtwebenginewidgets.py +++ b/qtpy/tests/test_qtwebenginewidgets.py @@ -1,12 +1,14 @@ from __future__ import absolute_import import pytest -from qtpy import QtWebEngineWidgets - +from qtpy import PYSIDE6 +@pytest.mark.skipif(PYSIDE6, reason="Only available in Qt<6 bindings") def test_qtwebenginewidgets(): """Test the qtpy.QtWebSockets namespace""" + QtWebEngineWidgets = pytest.importorskip("qtpy.QtWebEngineWidgets") + assert QtWebEngineWidgets.QWebEnginePage is not None assert QtWebEngineWidgets.QWebEngineView is not None assert QtWebEngineWidgets.QWebEngineSettings is not None diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index f41f9ff6..546bf298 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -4,10 +4,10 @@ import sys import pytest -from qtpy import PYSIDE2 +from qtpy import PYSIDE2, PYSIDE6 @pytest.mark.skipif( - sys.platform != "win32" or os.environ['USE_CONDA'] == 'Yes', + sys.platform != "win32" or os.environ['USE_CONDA'] == 'Yes' or PYSIDE6, reason="Only available in Qt5 bindings > 5.9 (only available with pip in the current CI setup) and Windows platform") def test_qtwinextras(): """Test the qtpy.QtWinExtras namespace""" diff --git a/qtpy/tests/test_qtxmlpatterns.py b/qtpy/tests/test_qtxmlpatterns.py index 4c6d4cb9..11ff2902 100644 --- a/qtpy/tests/test_qtxmlpatterns.py +++ b/qtpy/tests/test_qtxmlpatterns.py @@ -1,8 +1,9 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2, PYSIDE +from qtpy import PYSIDE2, PYSIDE6, PYSIDE +@pytest.mark.skipif(PYSIDE6, reason="not available with qt 6.0") def test_qtxmlpatterns(): """Test the qtpy.QtXmlPatterns namespace""" from qtpy import QtXmlPatterns diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index d7d3b599..5e0a32de 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -3,7 +3,7 @@ import contextlib import pytest -from qtpy import PYQT5, PYSIDE2, PYSIDE, QtWidgets +from qtpy import PYQT5, PYSIDE6, PYSIDE2, PYSIDE, QtWidgets from qtpy.QtWidgets import QComboBox if PYSIDE2 or PYSIDE: @@ -46,9 +46,9 @@ def get_qapp(icon_path=None): return qapp -@pytest.mark.skipif(((PYSIDE2 or PYQT5) +@pytest.mark.skipif(((PYSIDE2 or PYSIDE6 or PYQT5) and os.environ.get('CI', None) is not None), - reason="It segfaults in our CIs with PYSIDE2 or PYQT5") + reason="It segfaults in our CIs with PYSIDE2/PYSIDE6 or PYQT5") def test_load_ui(): """ Make sure that the patched loadUi function behaves as expected with a @@ -61,7 +61,7 @@ def test_load_ui(): @pytest.mark.skipif(((PYSIDE2 or PYQT5) - and os.environ.get('CI', None) is not None), + and os.environ.get('CI', None) is not None) or PYSIDE6, reason="It segfaults in our CIs with PYSIDE2 or PYQT5") def test_load_ui_type(): """ @@ -84,9 +84,9 @@ def __init__(self): assert isinstance(ui.comboBox, QComboBox) -@pytest.mark.skipif(((PYSIDE2 or PYQT5) +@pytest.mark.skipif(((PYSIDE2 or PYSIDE6 or PYQT5) and os.environ.get('CI', None) is not None), - reason="It segfaults in our CIs with PYSIDE2 or PYQT5") + reason="It segfaults in our CIs with PYSIDE2/PYSIDE6 or PYQT5") def test_load_ui_custom_auto(tmpdir): """ Test that we can load a .ui file with custom widgets without having to @@ -103,7 +103,7 @@ def test_load_ui_custom_auto(tmpdir): assert isinstance(ui.pushButton, QtWidgets.QPushButton) assert isinstance(ui.comboBox, _QComboBoxSubclass) - +@pytest.mark.skipif(PYSIDE6, reason="unavailable") def test_load_full_uic(): """Test that we load the full uic objects for PyQt5 and PyQt4.""" QT_API = os.environ.get('QT_API', '').lower() diff --git a/qtpy/uic.py b/qtpy/uic.py index d26a25a1..992a231b 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -1,10 +1,14 @@ import os -from . import PYSIDE, PYSIDE2, PYQT4, PYQT5 +from . import PYSIDE, PYSIDE6, PYSIDE2, PYQT4, PYQT5, PYQT6 from .QtWidgets import QComboBox -if PYQT5: +if PYQT6: + + from PyQt6.uic import * + +elif PYQT5: from PyQt5.uic import * @@ -85,6 +89,9 @@ from pysideuic import compileUi except ImportError: pass + elif PYSIDE6: + from PySide6.QtCore import QMetaObject + from PySide6.QtUiTools import QUiLoader elif PYSIDE2: from PySide2.QtCore import QMetaObject from PySide2.QtUiTools import QUiLoader From 3175a4ad59e8895636cb522b0b6fd14a5a5ae2ff Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Tue, 12 Oct 2021 08:56:36 +0200 Subject: [PATCH 250/703] CI: Add Pyside6 --- .github/workflows/ci.yml | 12 ++++++++++-- .github/workflows/test-pyside6.sh | 23 +++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100755 .github/workflows/test-pyside6.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b17fee8..3ea1a6ed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,8 +28,7 @@ jobs: - name: Install System Packages run: | sudo apt update - sudo apt install libpulse-dev - sudo apt install libegl1-mesa + sudo apt install libpulse-dev libegl1-mesa libopengl0 - name: Install Conda uses: conda-incubator/setup-miniconda@v2 with: @@ -44,6 +43,9 @@ jobs: - name: Test PySide2 shell: bash -l {0} run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside2.sh + - name: Test PySide6 + shell: bash -l {0} + run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside6.sh - name: Upload coverage if: matrix.PYTHON_VERSION == '3.8' shell: bash -l {0} @@ -79,6 +81,9 @@ jobs: - name: Test PySide2 shell: bash -l {0} run: bash -l ./.github/workflows/test-pyside2.sh + - name: Test PySide6 + shell: bash -l {0} + run: bash -l ./.github/workflows/test-pyside6.sh windows: name: Windows Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} @@ -107,3 +112,6 @@ jobs: - name: Test PySide2 shell: bash -l {0} run: bash -l ./.github/workflows/test-pyside2.sh + - name: Test PySide6 + shell: bash -l {0} + run: bash -l ./.github/workflows/test-pyside6.sh diff --git a/.github/workflows/test-pyside6.sh b/.github/workflows/test-pyside6.sh new file mode 100755 index 00000000..42b3ddcb --- /dev/null +++ b/.github/workflows/test-pyside6.sh @@ -0,0 +1,23 @@ +#!/bin/bash -ex + +# Create conda environment for this test +conda create -n test-pyside6 +conda activate test-pyside6 + +if [ "$USE_CONDA" = "Yes" ]; then + # There are no conda packages for PySide6 + exit 0 +elif [ "$PYTHON_VERSION" == "2.7" ]; then + # There is no wheel for PySide6 on Python 2.7 + exit 0 +else + # Simple solution to avoid failures with the Qt3D modules + conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge -q + pip install -q pyside6==6.2.0 +fi + +# Install package +python -m pip install -e . + +# Run tests +python qtpy/tests/runtests.py From 659f554ea315c89023154a4158d3ac3cc23c6415 Mon Sep 17 00:00:00 2001 From: kumattau Date: Wed, 13 Oct 2021 10:11:51 +0900 Subject: [PATCH 251/703] Add QShortcut class to QtWidgets module --- qtpy/QtWidgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 0dbb4a76..d1448985 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -24,7 +24,7 @@ from PyQt5.QtWidgets import * elif PYSIDE6: from PySide6.QtWidgets import * - from PySide6.QtGui import QAction, QActionGroup + from PySide6.QtGui import QAction, QActionGroup, QShortcut from PySide6.QtOpenGLWidgets import QOpenGLWidget QTextEdit.setTabStopWidth = QTextEdit.setTabStopDistance QTextEdit.tabStopWidth = QTextEdit.tabStopDistance From 65d7c07c5103ecf060156fa0edd88c2a063b5d6e Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Tue, 12 Oct 2021 17:19:28 +0200 Subject: [PATCH 252/703] Nuke Qt4 support Closes #70 --- README.md | 9 +- qtpy/QtCore.py | 72 +----------- qtpy/QtDesigner.py | 4 +- qtpy/QtGui.py | 142 +---------------------- qtpy/QtHelp.py | 6 +- qtpy/QtMultimedia.py | 8 -- qtpy/QtNetwork.py | 6 +- qtpy/QtOpenGL.py | 7 +- qtpy/QtPrintSupport.py | 10 +- qtpy/QtSql.py | 7 +- qtpy/QtSvg.py | 7 +- qtpy/QtTest.py | 11 +- qtpy/QtWebEngineWidgets.py | 12 +- qtpy/QtWidgets.py | 115 +----------------- qtpy/QtXmlPatterns.py | 6 +- qtpy/__init__.py | 95 +-------------- qtpy/_patch/qcombobox.py | 101 ---------------- qtpy/compat.py | 99 +++------------- qtpy/tests/conftest.py | 16 +-- qtpy/tests/runtests.py | 2 +- qtpy/tests/test_main.py | 38 +----- qtpy/tests/test_patch_qcombobox.py | 107 ----------------- qtpy/tests/test_patch_qheaderview.py | 23 +--- qtpy/tests/test_qdesktopservice_split.py | 15 --- qtpy/tests/test_qtdesigner.py | 4 +- qtpy/tests/test_qtnetwork.py | 17 ++- qtpy/tests/test_qtxmlpatterns.py | 4 +- qtpy/tests/test_uic.py | 6 +- qtpy/uic.py | 15 +-- setup.py | 4 +- 30 files changed, 67 insertions(+), 901 deletions(-) delete mode 100644 qtpy/_patch/qcombobox.py delete mode 100644 qtpy/tests/test_patch_qcombobox.py diff --git a/README.md b/README.md index 3f6e46b7..59691109 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# QtPy: Abstraction layer for PyQt5/PyQt4/PySide2/PySide +# QtPy: Abstraction layer for PyQt5/PyQt6/PySide2/PySide6 [![license](https://img.shields.io/pypi/l/qtpy.svg)](./LICENSE) [![pypi version](https://img.shields.io/pypi/v/qtpy.svg)](https://pypi.org/project/QtPy/) @@ -18,7 +18,7 @@ **QtPy** is a small abstraction layer that lets you write applications using a single API call to either PyQt or PySide. -It provides support for PyQt5, PyQt4, PySide6, PySide2 and PySide using the Qt5 layout +It provides support for PyQt5, PyQt6, PySide6, PySide2 using the Qt5 layout (where the QtGui module has been split into QtGui and QtWidgets). Basically, you can write your code as if you were using PySide2 @@ -45,17 +45,16 @@ This project is released under the MIT license. ### Requirements -You need PyQt5, PyQt4, PySide2 or PySide installed in your system to make use +You need PyQt5, PyQt6, PySide2 or PySide6 installed in your system to make use of QtPy. If several of these packages are found, PyQt5 is used by default unless you set the `QT_API` environment variable. `QT_API` can take the following values: * `pyqt5` (to use PyQt5). -* `pyqt` or `pyqt4` (to use PyQt4). +* `pyqt6` (to use PyQt6). * `pyside6` (to use PySide6) * `pyside2` (to use PySide2) -* `pyside` (to use PySide). ### Installation diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index b353a0dc..a6142bf9 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -10,7 +10,7 @@ Provides QtCore classes and functions. """ -from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PYQT4, PYSIDE, PythonQtError +from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError if PYQT6: from PyQt6.QtCore import * @@ -54,75 +54,5 @@ import PySide2.QtCore __version__ = PySide2.QtCore.__version__ -elif PYQT4: - from PyQt4.QtCore import * - # Those are things we inherited from Spyder that fix crazy crashes under - # some specific situations. (See #34) - from PyQt4.QtCore import QCoreApplication - from PyQt4.QtCore import Qt - from PyQt4.QtCore import pyqtSignal as Signal - from PyQt4.QtCore import pyqtBoundSignal as SignalInstance - from PyQt4.QtCore import pyqtSlot as Slot - from PyQt4.QtCore import pyqtProperty as Property - from PyQt4.QtGui import (QItemSelection, QItemSelectionModel, - QItemSelectionRange, QSortFilterProxyModel, - QStringListModel) - from PyQt4.QtCore import QT_VERSION_STR as __version__ - from PyQt4.QtCore import qInstallMsgHandler as qInstallMessageHandler - - # QDesktopServices has has been split into (QDesktopServices and - # QStandardPaths) in Qt5 - # This creates a dummy class that emulates QStandardPaths - from PyQt4.QtGui import QDesktopServices as _QDesktopServices - - class QStandardPaths(): - StandardLocation = _QDesktopServices.StandardLocation - displayName = _QDesktopServices.displayName - DesktopLocation = _QDesktopServices.DesktopLocation - DocumentsLocation = _QDesktopServices.DocumentsLocation - FontsLocation = _QDesktopServices.FontsLocation - ApplicationsLocation = _QDesktopServices.ApplicationsLocation - MusicLocation = _QDesktopServices.MusicLocation - MoviesLocation = _QDesktopServices.MoviesLocation - PicturesLocation = _QDesktopServices.PicturesLocation - TempLocation = _QDesktopServices.TempLocation - HomeLocation = _QDesktopServices.HomeLocation - DataLocation = _QDesktopServices.DataLocation - CacheLocation = _QDesktopServices.CacheLocation - writableLocation = _QDesktopServices.storageLocation - - # Those are imported from `import *` - del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR, qInstallMsgHandler -elif PYSIDE: - from PySide.QtCore import * - from PySide.QtGui import (QItemSelection, QItemSelectionModel, - QItemSelectionRange, QSortFilterProxyModel, - QStringListModel) - from PySide.QtCore import qInstallMsgHandler as qInstallMessageHandler - del qInstallMsgHandler - - # QDesktopServices has has been split into (QDesktopServices and - # QStandardPaths) in Qt5 - # This creates a dummy class that emulates QStandardPaths - from PySide.QtGui import QDesktopServices as _QDesktopServices - - class QStandardPaths(): - StandardLocation = _QDesktopServices.StandardLocation - displayName = _QDesktopServices.displayName - DesktopLocation = _QDesktopServices.DesktopLocation - DocumentsLocation = _QDesktopServices.DocumentsLocation - FontsLocation = _QDesktopServices.FontsLocation - ApplicationsLocation = _QDesktopServices.ApplicationsLocation - MusicLocation = _QDesktopServices.MusicLocation - MoviesLocation = _QDesktopServices.MoviesLocation - PicturesLocation = _QDesktopServices.PicturesLocation - TempLocation = _QDesktopServices.TempLocation - HomeLocation = _QDesktopServices.HomeLocation - DataLocation = _QDesktopServices.DataLocation - CacheLocation = _QDesktopServices.CacheLocation - writableLocation = _QDesktopServices.storageLocation - - import PySide.QtCore - __version__ = PySide.QtCore.__version__ else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index 4aaafc81..04576785 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -9,12 +9,10 @@ Provides QtDesigner classes and functions. """ -from . import PYQT5, PYQT4, PythonQtError +from . import PYQT5, PythonQtError if PYQT5: from PyQt5.QtDesigner import * -elif PYQT4: - from PyQt4.QtDesigner import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 90c074a4..62c0ee7c 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -8,13 +8,10 @@ """ Provides QtGui classes and functions. -.. warning:: Only PyQt4/PySide QtGui classes compatible with PyQt5.QtGui are - exposed here. Therefore, you need to treat/use this package as if it were - the ``PyQt5.QtGui`` module. """ import warnings -from . import PYQT6, PYQT5, PYQT4, PYSIDE, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError if PYQT6: @@ -26,142 +23,5 @@ elif PYSIDE6: from PySide6.QtGui import * QFontMetrics.width = QFontMetrics.horizontalAdvance - -elif PYQT4: - try: - # Older versions of PyQt4 do not provide these - from PyQt4.QtGui import (QGlyphRun, QMatrix2x2, QMatrix2x3, - QMatrix2x4, QMatrix3x2, QMatrix3x3, - QMatrix3x4, QMatrix4x2, QMatrix4x3, - QMatrix4x4, QTouchEvent, QQuaternion, - QRadialGradient, QRawFont, QStaticText, - QVector2D, QVector3D, QVector4D, - qFuzzyCompare) - except ImportError: - pass - try: - from PyQt4.Qt import QKeySequence, QTextCursor - except ImportError: - # In PyQt4-sip 4.19.13 QKeySequence and QTextCursor are in PyQt4.QtGui - from PyQt4.QtGui import QKeySequence, QTextCursor - from PyQt4.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, - QBrush, QClipboard, QCloseEvent, QColor, - QConicalGradient, QContextMenuEvent, QCursor, - QDoubleValidator, QDrag, - QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, - QDropEvent, QFileOpenEvent, QFocusEvent, QFont, - QFontDatabase, QFontInfo, QFontMetrics, - QFontMetricsF, QGradient, QHelpEvent, - QHideEvent, QHoverEvent, QIcon, QIconDragEvent, - QIconEngine, QImage, QImageIOHandler, QImageReader, - QImageWriter, QInputEvent, QInputMethodEvent, - QKeyEvent, QLinearGradient, - QMouseEvent, QMoveEvent, QMovie, - QPaintDevice, QPaintEngine, QPaintEngineState, - QPaintEvent, QPainter, QPainterPath, - QPainterPathStroker, QPalette, QPen, QPicture, - QPictureIO, QPixmap, QPixmapCache, QPolygon, - QPolygonF, QRegExpValidator, QRegion, QResizeEvent, - QSessionManager, QShortcutEvent, QShowEvent, - QStandardItem, QStandardItemModel, - QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, - QTextBlock, QTextBlockFormat, QTextBlockGroup, - QTextBlockUserData, QTextCharFormat, - QTextDocument, QTextDocumentFragment, - QTextDocumentWriter, QTextFormat, QTextFragment, - QTextFrame, QTextFrameFormat, QTextImageFormat, - QTextInlineObject, QTextItem, QTextLayout, - QTextLength, QTextLine, QTextList, QTextListFormat, - QTextObject, QTextObjectInterface, QTextOption, - QTextTable, QTextTableCell, QTextTableCellFormat, - QTextTableFormat, QTransform, - QValidator, QWhatsThisClickedEvent, QWheelEvent, - QWindowStateChangeEvent, qAlpha, qBlue, - qGray, qGreen, qIsGray, qRed, qRgb, - qRgba, QIntValidator) - - # QDesktopServices has has been split into (QDesktopServices and - # QStandardPaths) in Qt5 - # It only exposes QDesktopServices that are still in pyqt5 - from PyQt4.QtGui import QDesktopServices as _QDesktopServices - - class QDesktopServices(): - openUrl = _QDesktopServices.openUrl - setUrlHandler = _QDesktopServices.setUrlHandler - unsetUrlHandler = _QDesktopServices.unsetUrlHandler - - def __getattr__(self, name): - attr = getattr(_QDesktopServices, name) - - new_name = name - if name == 'storageLocation': - new_name = 'writableLocation' - warnings.warn(("Warning QDesktopServices.{} is deprecated in Qt5" - "we recommend you use QDesktopServices.{} instead").format(name, new_name), - DeprecationWarning) - return attr - QDesktopServices = QDesktopServices() - -elif PYSIDE: - from PySide.QtGui import (QAbstractTextDocumentLayout, QActionEvent, QBitmap, - QBrush, QClipboard, QCloseEvent, QColor, - QConicalGradient, QContextMenuEvent, QCursor, - QDoubleValidator, QDrag, - QDragEnterEvent, QDragLeaveEvent, QDragMoveEvent, - QDropEvent, QFileOpenEvent, QFocusEvent, QFont, - QFontDatabase, QFontInfo, QFontMetrics, - QFontMetricsF, QGradient, QHelpEvent, - QHideEvent, QHoverEvent, QIcon, QIconDragEvent, - QIconEngine, QImage, QImageIOHandler, QImageReader, - QImageWriter, QInputEvent, QInputMethodEvent, - QKeyEvent, QKeySequence, QLinearGradient, - QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, - QMatrix3x3, QMatrix3x4, QMatrix4x2, QMatrix4x3, - QMatrix4x4, QMouseEvent, QMoveEvent, QMovie, - QPaintDevice, QPaintEngine, QPaintEngineState, - QPaintEvent, QPainter, QPainterPath, - QPainterPathStroker, QPalette, QPen, QPicture, - QPictureIO, QPixmap, QPixmapCache, QPolygon, - QPolygonF, QQuaternion, QRadialGradient, - QRegExpValidator, QRegion, QResizeEvent, - QSessionManager, QShortcutEvent, QShowEvent, - QStandardItem, QStandardItemModel, - QStatusTipEvent, QSyntaxHighlighter, QTabletEvent, - QTextBlock, QTextBlockFormat, QTextBlockGroup, - QTextBlockUserData, QTextCharFormat, QTextCursor, - QTextDocument, QTextDocumentFragment, - QTextFormat, QTextFragment, - QTextFrame, QTextFrameFormat, QTextImageFormat, - QTextInlineObject, QTextItem, QTextLayout, - QTextLength, QTextLine, QTextList, QTextListFormat, - QTextObject, QTextObjectInterface, QTextOption, - QTextTable, QTextTableCell, QTextTableCellFormat, - QTextTableFormat, QTouchEvent, QTransform, - QValidator, QVector2D, QVector3D, QVector4D, - QWhatsThisClickedEvent, QWheelEvent, - QWindowStateChangeEvent, qAlpha, qBlue, - qGray, qGreen, qIsGray, qRed, qRgb, qRgba, - QIntValidator) - # QDesktopServices has has been split into (QDesktopServices and - # QStandardPaths) in Qt5 - # It only exposes QDesktopServices that are still in pyqt5 - from PySide.QtGui import QDesktopServices as _QDesktopServices - - class QDesktopServices(): - openUrl = _QDesktopServices.openUrl - setUrlHandler = _QDesktopServices.setUrlHandler - unsetUrlHandler = _QDesktopServices.unsetUrlHandler - - def __getattr__(self, name): - attr = getattr(_QDesktopServices, name) - - new_name = name - if name == 'storageLocation': - new_name = 'writableLocation' - warnings.warn(("Warning QDesktopServices.{} is deprecated in Qt5" - "we recommend you use QDesktopServices.{} instead").format(name, new_name), - DeprecationWarning) - return attr - QDesktopServices = QDesktopServices() else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index bbd44036..de06baf7 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -9,7 +9,7 @@ import warnings -from . import PYQT5, PYQT4, PYSIDE6, PYSIDE2, PYSIDE +from . import PYQT5, PYSIDE6, PYSIDE2 if PYQT5: from PyQt5.QtHelp import * @@ -17,7 +17,3 @@ from PySide6.QtHelp import * elif PYSIDE2: from PySide2.QtHelp import * -elif PYQT4: - from PyQt4.QtHelp import * -elif PYSIDE: - from PySide.QtHelp import * diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index 96d7e906..88cbdf10 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -1,8 +1,6 @@ import warnings from . import PYQT5 -from . import PYQT4 -from . import PYSIDE from . import PYSIDE2 from . import PYSIDE6 @@ -12,9 +10,3 @@ from PySide6.QtMultimedia import * elif PYSIDE2: from PySide2.QtMultimedia import * -elif PYQT4: - from PyQt4.QtMultimedia import * - from PyQt4.QtGui import QSound -elif PYSIDE: - from PySide.QtMultimedia import * - from PySide.QtGui import QSound diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index d8b6aa87..c1fcd588 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -10,7 +10,7 @@ Provides QtNetwork classes and functions. """ -from . import PYQT5, PYSIDE2, PYSIDE6, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: @@ -19,9 +19,5 @@ from PySide6.QtNetwork import * elif PYSIDE2: from PySide2.QtNetwork import * -elif PYQT4: - from PyQt4.QtNetwork import * -elif PYSIDE: - from PySide.QtNetwork import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 5b3a80d7..aaa8cd2b 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -8,7 +8,7 @@ """Provides QtOpenGL classes and functions.""" # Local imports -from . import PYQT4, PYQT5, PYSIDE, PYSIDE2, PythonQtError +from . import PYQT5, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtOpenGL import * @@ -16,11 +16,6 @@ from PySide6.QtOpenGL import * elif PYSIDE2: from PySide2.QtOpenGL import * -elif PYQT4: - from PyQt4.QtOpenGL import * -elif PYSIDE: - from PySide.QtOpenGL import * else: raise PythonQtError('No Qt bindings could be found') -del PYQT4, PYQT5, PYSIDE, PYSIDE2 diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index d7f4ff10..872d8179 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -9,7 +9,7 @@ Provides QtPrintSupport classes and functions. """ -from . import PYQT5, PYQT4, PYSIDE6, PYSIDE2, PYSIDE, PythonQtError +from . import PYQT5, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: @@ -18,13 +18,5 @@ from PySide6.QtPrintSupport import * elif PYSIDE2: from PySide2.QtPrintSupport import * -elif PYQT4: - from PyQt4.QtGui import (QAbstractPrintDialog, QPageSetupDialog, - QPrintDialog, QPrintEngine, QPrintPreviewDialog, - QPrintPreviewWidget, QPrinter, QPrinterInfo) -elif PYSIDE: - from PySide.QtGui import (QAbstractPrintDialog, QPageSetupDialog, - QPrintDialog, QPrintEngine, QPrintPreviewDialog, - QPrintPreviewWidget, QPrinter, QPrinterInfo) else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 583e5cb0..0c8dcb3b 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -8,7 +8,7 @@ """Provides QtSql classes and functions.""" # Local imports -from . import PYQT5, PYSIDE6, PYSIDE2, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtSql import * @@ -16,11 +16,6 @@ from PySide6.QtSql import * elif PYSIDE2: from PySide2.QtSql import * -elif PYQT4: - from PyQt4.QtSql import * -elif PYSIDE: - from PySide.QtSql import * else: raise PythonQtError('No Qt bindings could be found') -del PYQT4, PYQT5, PYSIDE, PYSIDE2 diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index ccc3fbd3..cb99e12c 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -8,7 +8,7 @@ """Provides QtSvg classes and functions.""" # Local imports -from . import PYQT4, PYSIDE6, PYSIDE2, PYQT5, PYQT6, PYSIDE, PythonQtError +from . import PYSIDE6, PYSIDE2, PYQT5, PYQT6, PythonQtError if PYQT6: from PyQt6.QtSvg import * @@ -18,11 +18,6 @@ from PySide6.QtSvg import * elif PYSIDE2: from PySide2.QtSvg import * -elif PYQT4: - from PyQt4.QtSvg import * -elif PYSIDE: - from PySide.QtSvg import * else: raise PythonQtError('No Qt bindings could be found') -del PYQT4, PYQT5, PYSIDE, PYSIDE2 diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index b86c174e..83ecb694 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -10,7 +10,7 @@ Provides QtTest and functions """ -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT6: from PyQt6.QtTest import QTest @@ -20,14 +20,5 @@ from PySide6.QtTest import QTest elif PYSIDE2: from PySide2.QtTest import QTest -elif PYQT4: - from PyQt4.QtTest import QTest as OldQTest - - class QTest(OldQTest): - @staticmethod - def qWaitForWindowActive(QWidget): - OldQTest.qWaitForWindowShown(QWidget) -elif PYSIDE: - from PySide.QtTest import QTest else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 20de297b..3b41b09a 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -10,7 +10,7 @@ Provides QtWebEngineWidgets classes and functions. """ -from . import PYQT5,PYSIDE2, PYSIDE6, PYQT4, PYSIDE, PythonQtError +from . import PYQT5, PYSIDE2, PYSIDE6, PythonQtError # To test if we are using WebEngine or WebKit @@ -40,15 +40,5 @@ from PySide2.QtWebEngineWidgets import QWebEngineSettings # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 from PySide2.QtWebEngineWidgets import QWebEngineProfile -elif PYQT4: - from PyQt4.QtWebKit import QWebPage as QWebEnginePage - from PyQt4.QtWebKit import QWebView as QWebEngineView - from PyQt4.QtWebKit import QWebSettings as QWebEngineSettings - WEBENGINE = False -elif PYSIDE: - from PySide.QtWebKit import QWebPage as QWebEnginePage - from PySide.QtWebKit import QWebView as QWebEngineView - from PySide.QtWebKit import QWebSettings as QWebEngineSettings - WEBENGINE = False else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 0dbb4a76..cd13d18e 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -8,13 +8,9 @@ """ Provides widget classes and functions. -.. warning:: Only PyQt4/PySide QtGui classes compatible with PyQt5.QtWidgets - are exposed here. Therefore, you need to treat/use this package as if it - were the ``PyQt5.QtWidgets`` module. """ -from . import PYQT5, PYQT6, PYSIDE2, PYQT4, PYSIDE, PYSIDE6, PythonQtError -from ._patch.qcombobox import patch_qcombobox +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError from ._patch.qheaderview import introduce_renamed_methods_qheaderview if PYQT6: @@ -32,114 +28,5 @@ QPlainTextEdit.tabStopWidth = QPlainTextEdit.tabStopDistance elif PYSIDE2: from PySide2.QtWidgets import * -elif PYQT4: - from PyQt4.QtGui import * - QStyleOptionViewItem = QStyleOptionViewItemV4 - del QStyleOptionViewItemV4 - QStyleOptionFrame = QStyleOptionFrameV3 - del QStyleOptionFrameV3 - - # These objects belong to QtGui - try: - # Older versions of PyQt4 do not provide these - del (QGlyphRun, - QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, - QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, - QQuaternion, QRadialGradient, QRawFont, QRegExpValidator, - QStaticText, QTouchEvent, QVector2D, QVector3D, QVector4D, - qFuzzyCompare) - except NameError: - pass - del (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, - QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, - QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent, - QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, - QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, - QFontMetricsF, QGradient, QHelpEvent, QHideEvent, - QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, - QImageIOHandler, QImageReader, QImageWriter, QInputEvent, - QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient, - QMouseEvent, QMoveEvent, QMovie, QPaintDevice, QPaintEngine, - QPaintEngineState, QPaintEvent, QPainter, QPainterPath, - QPainterPathStroker, QPalette, QPen, QPicture, QPictureIO, QPixmap, - QPixmapCache, QPolygon, QPolygonF, - QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, - QStandardItem, QStandardItemModel, QStatusTipEvent, - QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, - QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor, - QTextDocument, QTextDocumentFragment, QTextDocumentWriter, - QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat, - QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout, - QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, - QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, - QTextTableCellFormat, QTextTableFormat, QTransform, - QValidator, QWhatsThisClickedEvent, - QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, - qGray, qGreen, qIsGray, qRed, qRgb, qRgba, QIntValidator, - QStringListModel) - - # These objects belong to QtPrintSupport - del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, - QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo) - - # These objects belong to QtCore - del (QItemSelection, QItemSelectionModel, QItemSelectionRange, - QSortFilterProxyModel) - - # Patch QComboBox to allow Python objects to be passed to userData - patch_qcombobox(QComboBox) - - # QHeaderView: renamed methods - introduce_renamed_methods_qheaderview(QHeaderView) - -elif PYSIDE: - from PySide.QtGui import * - QStyleOptionViewItem = QStyleOptionViewItemV4 - del QStyleOptionViewItemV4 - - # These objects belong to QtGui - del (QAbstractTextDocumentLayout, QActionEvent, QBitmap, QBrush, QClipboard, - QCloseEvent, QColor, QConicalGradient, QContextMenuEvent, QCursor, - QDesktopServices, QDoubleValidator, QDrag, QDragEnterEvent, - QDragLeaveEvent, QDragMoveEvent, QDropEvent, QFileOpenEvent, - QFocusEvent, QFont, QFontDatabase, QFontInfo, QFontMetrics, - QFontMetricsF, QGradient, QHelpEvent, QHideEvent, - QHoverEvent, QIcon, QIconDragEvent, QIconEngine, QImage, - QImageIOHandler, QImageReader, QImageWriter, QInputEvent, - QInputMethodEvent, QKeyEvent, QKeySequence, QLinearGradient, - QMatrix2x2, QMatrix2x3, QMatrix2x4, QMatrix3x2, QMatrix3x3, - QMatrix3x4, QMatrix4x2, QMatrix4x3, QMatrix4x4, QMouseEvent, - QMoveEvent, QMovie, QPaintDevice, QPaintEngine, QPaintEngineState, - QPaintEvent, QPainter, QPainterPath, QPainterPathStroker, QPalette, - QPen, QPicture, QPictureIO, QPixmap, QPixmapCache, QPolygon, - QPolygonF, QQuaternion, QRadialGradient, QRegExpValidator, - QRegion, QResizeEvent, QSessionManager, QShortcutEvent, QShowEvent, - QStandardItem, QStandardItemModel, QStatusTipEvent, - QSyntaxHighlighter, QTabletEvent, QTextBlock, QTextBlockFormat, - QTextBlockGroup, QTextBlockUserData, QTextCharFormat, QTextCursor, - QTextDocument, QTextDocumentFragment, - QTextFormat, QTextFragment, QTextFrame, QTextFrameFormat, - QTextImageFormat, QTextInlineObject, QTextItem, QTextLayout, - QTextLength, QTextLine, QTextList, QTextListFormat, QTextObject, - QTextObjectInterface, QTextOption, QTextTable, QTextTableCell, - QTextTableCellFormat, QTextTableFormat, QTouchEvent, QTransform, - QValidator, QVector2D, QVector3D, QVector4D, QWhatsThisClickedEvent, - QWheelEvent, QWindowStateChangeEvent, qAlpha, qBlue, qGray, qGreen, - qIsGray, qRed, qRgb, qRgba, QIntValidator, QStringListModel) - - # These objects belong to QtPrintSupport - del (QAbstractPrintDialog, QPageSetupDialog, QPrintDialog, QPrintEngine, - QPrintPreviewDialog, QPrintPreviewWidget, QPrinter, QPrinterInfo) - - # These objects belong to QtCore - del (QItemSelection, QItemSelectionModel, QItemSelectionRange, - QSortFilterProxyModel) - - # Patch QComboBox to allow Python objects to be passed to userData - patch_qcombobox(QComboBox) - - # QHeaderView: renamed methods - introduce_renamed_methods_qheaderview(QHeaderView) - else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py index b41e13df..3ab2661e 100644 --- a/qtpy/QtXmlPatterns.py +++ b/qtpy/QtXmlPatterns.py @@ -8,15 +8,11 @@ """Provides QtXmlPatterns classes and functions.""" # Local imports -from . import PYQT4, PYSIDE2, PYQT5, PYSIDE, PythonQtError +from . import PYSIDE2, PYQT5, PythonQtError if PYQT5: from PyQt5.QtXmlPatterns import * elif PYSIDE2: from PySide2.QtXmlPatterns import * -elif PYQT4: - from PyQt4.QtXmlPatterns import * -elif PYSIDE: - from PySide.QtXmlPatterns import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/__init__.py b/qtpy/__init__.py index ab0e9e51..fa62f8b2 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -13,7 +13,7 @@ If one of the APIs has already been imported, then it will be used. Otherwise, the shim will automatically select the first available API (PyQt5, PyQt6, -PySide2, PySide6, PyQt4 and finally PySide); in that case, you can force the use of one +PySide2 and PySide6); in that case, you can force the use of one specific bindings (e.g. if your application is using one specific bindings and you need to use library that use QtPy) by setting up the ``QT_API`` environment variable. @@ -53,28 +53,6 @@ >>> from qtpy import QtGui, QtWidgets, QtCore >>> print(QtWidgets.QWidget) -PyQt4 -===== - -Set the ``QT_API`` environment variable to 'pyqt' before importing any python -package:: - - >>> import os - >>> os.environ['QT_API'] = 'pyqt' - >>> from qtpy import QtGui, QtWidgets, QtCore - >>> print(QtWidgets.QWidget) - -PySide -====== - -Set the QT_API environment variable to 'pyside' before importing other -packages:: - - >>> import os - >>> os.environ['QT_API'] = 'pyside' - >>> from qtpy import QtGui, QtWidgets, QtCore - >>> print(QtWidgets.QWidget) - """ from distutils.version import LooseVersion @@ -106,15 +84,6 @@ class PythonQtWarning(Warning): PYQT6_API = ['pyqt6'] -# Names of the expected PyQt4 api -PYQT4_API = [ - 'pyqt', # name used in IPython.qt - 'pyqt4' # pyqode.qt original name -] - -# Names of the expected PySide api -PYSIDE_API = ['pyside'] - # Names of the expected PySide2 api PYSIDE2_API = ['pyside2'] @@ -129,11 +98,11 @@ class PythonQtWarning(Warning): API = os.environ[QT_API].lower() initial_api = API -assert API in (PYQT5_API + PYQT6_API + PYQT4_API + PYSIDE_API + PYSIDE2_API + PYSIDE6_API) +assert API in (PYQT5_API + PYQT6_API + PYSIDE2_API + PYSIDE6_API) is_old_pyqt = is_pyqt46 = False PYQT5 = True -PYQT4 = PYQT6 = PYSIDE = PYSIDE2 = PYSIDE6 = False +PYQT6 = PYSIDE2 = PYSIDE6 = False # When `FORCE_QT_API` is set, we disregard # any previously imported python bindings. @@ -146,11 +115,6 @@ class PythonQtWarning(Warning): API = initial_api if initial_api in PYSIDE6_API else 'pyside6' elif 'PySide2' in sys.modules: API = initial_api if initial_api in PYSIDE2_API else 'pyside2' - elif 'PyQt4' in sys.modules: - API = initial_api if initial_api in PYQT4_API else 'pyqt4' - elif 'PySide' in sys.modules: - API = initial_api if initial_api in PYSIDE_API else 'pyside' - if API in PYQT5_API: try: @@ -223,65 +187,18 @@ class PythonQtWarning(Warning): API = os.environ['QT_API'] = 'pyqt' -if API in PYQT4_API: - try: - import sip - try: - sip.setapi('QString', 2) - sip.setapi('QVariant', 2) - sip.setapi('QDate', 2) - sip.setapi('QDateTime', 2) - sip.setapi('QTextStream', 2) - sip.setapi('QTime', 2) - sip.setapi('QUrl', 2) - except (AttributeError, ValueError): - # PyQt < v4.6 - pass - try: - from PyQt4.Qt import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore - from PyQt4.Qt import QT_VERSION_STR as QT_VERSION # analysis:ignore - except ImportError: - # In PyQt4-sip 4.19.13 PYQT_VERSION_STR and QT_VERSION_STR are in PyQt4.QtCore - from PyQt4.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore - from PyQt4.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore - PYSIDE_VERSION = None - PYQT5 = False - PYQT4 = True - except ImportError: - API = os.environ['QT_API'] = 'pyside' - else: - is_old_pyqt = PYQT_VERSION.startswith(('4.4', '4.5', '4.6', '4.7')) - is_pyqt46 = PYQT_VERSION.startswith('4.6') - -if API in PYSIDE_API: - try: - from PySide import __version__ as PYSIDE_VERSION # analysis:ignore - from PySide.QtCore import __version__ as QT_VERSION # analysis:ignore - PYQT_VERSION = None - PYQT5 = PYSIDE2 = PYSIDE6 = False - PYSIDE = True - except ImportError: - raise PythonQtError('No Qt bindings could be found') - # If a correct API name is passed to QT_API and it could not be found, # switches to another and informs through the warning if API != initial_api and binding_specified: warnings.warn('Selected binding "{}" could not be found, ' 'using "{}"'.format(initial_api, API), RuntimeWarning) -API_NAME = {'pyqt6': 'PyQt6', 'pyqt5': 'PyQt5', 'pyqt': 'PyQt4', 'pyqt4': 'PyQt4', - 'pyside': 'PySide', 'pyside2':'PySide2', 'pyside6': 'PySide6'}[API] - -if PYQT4: - import sip - try: - API_NAME += (" (API v{0})".format(sip.getapi('QString'))) - except AttributeError: - pass +API_NAME = {'pyqt6': 'PyQt6', 'pyqt5': 'PyQt5', + 'pyside2':'PySide2', 'pyside6': 'PySide6'}[API] try: # QtDataVisualization backward compatibility (QtDataVisualization vs. QtDatavisualization) # Only available for Qt5 bindings > 5.9 on Windows from . import QtDataVisualization as QtDatavisualization except ImportError: - pass \ No newline at end of file + pass diff --git a/qtpy/_patch/qcombobox.py b/qtpy/_patch/qcombobox.py deleted file mode 100644 index d3e98bed..00000000 --- a/qtpy/_patch/qcombobox.py +++ /dev/null @@ -1,101 +0,0 @@ -# The code below, as well as the associated test were adapted from -# qt-helpers, which was released under a 3-Clause BSD license: -# -# Copyright (c) 2015, Chris Beaumont and Thomas Robitaille -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are -# met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the -# distribution. -# * Neither the name of the Glue project nor the names of its -# contributors may be used to endorse or promote products derived -# from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -def patch_qcombobox(QComboBox): - """ - In PySide, using Python objects as userData in QComboBox causes - Segmentation faults under certain conditions. Even in cases where it - doesn't, findData does not work correctly. Likewise, findData also does not - work correctly with Python objects when using PyQt4. On the other hand, - PyQt5 deals with this case correctly. We therefore patch QComboBox when - using PyQt4 and PySide to avoid issues. - """ - - from ..QtGui import QIcon - from ..QtCore import Qt, QObject - - class userDataWrapper(): - """ - This class is used to wrap any userData object. If we don't do this, - then certain types of objects can cause segmentation faults or issues - depending on whether/how __getitem__ is defined. - """ - def __init__(self, data): - self.data = data - - _addItem = QComboBox.addItem - - def addItem(self, *args, **kwargs): - if len(args) == 3 or (not isinstance(args[0], QIcon) - and len(args) == 2): - args, kwargs['userData'] = args[:-1], args[-1] - if 'userData' in kwargs: - kwargs['userData'] = userDataWrapper(kwargs['userData']) - _addItem(self, *args, **kwargs) - - _insertItem = QComboBox.insertItem - - def insertItem(self, *args, **kwargs): - if len(args) == 4 or (not isinstance(args[1], QIcon) - and len(args) == 3): - args, kwargs['userData'] = args[:-1], args[-1] - if 'userData' in kwargs: - kwargs['userData'] = userDataWrapper(kwargs['userData']) - _insertItem(self, *args, **kwargs) - - _setItemData = QComboBox.setItemData - - def setItemData(self, index, value, role=Qt.UserRole): - value = userDataWrapper(value) - _setItemData(self, index, value, role=role) - - _itemData = QComboBox.itemData - - def itemData(self, index, role=Qt.UserRole): - userData = _itemData(self, index, role=role) - if isinstance(userData, userDataWrapper): - userData = userData.data - return userData - - def findData(self, value): - for i in range(self.count()): - if self.itemData(i) == value: - return i - return -1 - - QComboBox.addItem = addItem - QComboBox.insertItem = insertItem - QComboBox.setItemData = setItemData - QComboBox.itemData = itemData - QComboBox.findData = findData \ No newline at end of file diff --git a/qtpy/compat.py b/qtpy/compat.py index 949d8854..4e34ecb4 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -10,7 +10,6 @@ from __future__ import print_function import sys -from . import PYQT4 from .QtWidgets import QFileDialog from .py3compat import Callable, is_text_string, to_text_string, TEXT_TYPES @@ -19,58 +18,17 @@ # QVariant conversion utilities # ============================================================================= PYQT_API_1 = False -if PYQT4: - import sip - try: - PYQT_API_1 = sip.getapi('QVariant') == 1 # PyQt API #1 - except AttributeError: - # PyQt =v4.6 - QString = None # analysis:ignore - tuple_returned = True - try: - # PyQt >=v4.6 - func = getattr(QFileDialog, attr+'AndFilter') - except AttributeError: - # PySide or PyQt =v4.6 - output, selectedfilter = result - else: - # PyQt =6.2 # assert QtWebEngineWidgets.QWebEnginePage is PySide6.QtWebEngineCore.QWebEnginePage -def assert_pyqt4(): - """ - Make sure that we are using PyQt4 - """ - import PyQt4 - assert QtCore.QEvent is PyQt4.QtCore.QEvent - assert QtGui.QPainter is PyQt4.QtGui.QPainter - assert QtWidgets.QWidget is PyQt4.QtGui.QWidget - assert QtWebEngineWidgets.QWebEnginePage is PyQt4.QtWebKit.QWebPage - - def assert_pyqt5(): """ Make sure that we are using PyQt5 @@ -80,11 +58,7 @@ def test_qt_api(): QT_API = os.environ.get('QT_API', '').lower() - if QT_API == 'pyside': - assert_pyside() - elif QT_API in ('pyqt', 'pyqt4'): - assert_pyqt4() - elif QT_API == 'pyqt5': + if QT_API == 'pyqt5': assert_pyqt5() elif QT_API == 'pyqt6': assert_pyqt6() @@ -96,16 +70,16 @@ def test_qt_api(): # If the tests are run locally, USE_QT_API and QT_API may not be # defined, but we still want to make sure qtpy is behaving sensibly. # We should then be loading, in order of decreasing preference, PyQt5, - # PyQt4, and PySide. + # PyQt6, and PySide2. try: import PyQt5 except ImportError: try: - import PyQt4 + import PyQt6 except ImportError: - import PySide - assert_pyside() + import PySide2 + assert_pyside2() else: - assert_pyqt4() + assert_pyqt6() else: assert_pyqt5() diff --git a/qtpy/tests/test_patch_qcombobox.py b/qtpy/tests/test_patch_qcombobox.py deleted file mode 100644 index 12536fac..00000000 --- a/qtpy/tests/test_patch_qcombobox.py +++ /dev/null @@ -1,107 +0,0 @@ -from __future__ import absolute_import - -import os -import sys - -import pytest - -from qtpy import PYQT5, PYSIDE2, PYSIDE6, QtGui, QtWidgets - -PY3 = sys.version[0] == "3" - - -def get_qapp(icon_path=None): - qapp = QtWidgets.QApplication.instance() - if qapp is None: - qapp = QtWidgets.QApplication(['']) - return qapp - - -class Data(object): - """ - Test class to store in userData. The __getitem__ is needed in order to - reproduce the segmentation fault. - """ - def __getitem__(self, item): - raise ValueError("Failing") - - -@pytest.mark.skipif(PY3 or PYSIDE2 or PYSIDE6, - reason="It segfaults in Python 3 and with PySide2/6") -def test_patched_qcombobox(): - """ - In PySide, using Python objects as userData in QComboBox causes - Segmentation faults under certain conditions. Even in cases where it - doesn't, findData does not work correctly. Likewise, findData also - does not work correctly with Python objects when using PyQt4. On the - other hand, PyQt5 deals with this case correctly. We therefore patch - QComboBox when using PyQt4 and PySide to avoid issues. - """ - - app = get_qapp() - - data1 = Data() - data2 = Data() - data3 = Data() - data4 = Data() - data5 = Data() - data6 = Data() - - icon1 = QtGui.QIcon() - icon2 = QtGui.QIcon() - - widget = QtWidgets.QComboBox() - widget.addItem('a', data1) - widget.insertItem(0, 'b', data2) - widget.addItem('c', data1) - widget.setItemData(2, data3) - widget.addItem(icon1, 'd', data4) - widget.insertItem(3, icon2, 'e', data5) - widget.addItem(icon1, 'f') - widget.insertItem(5, icon2, 'g') - - widget.show() - - assert widget.findData(data1) == 1 - assert widget.findData(data2) == 0 - assert widget.findData(data3) == 2 - assert widget.findData(data4) == 4 - assert widget.findData(data5) == 3 - assert widget.findData(data6) == -1 - - assert widget.itemData(0) == data2 - assert widget.itemData(1) == data1 - assert widget.itemData(2) == data3 - assert widget.itemData(3) == data5 - assert widget.itemData(4) == data4 - assert widget.itemData(5) is None - assert widget.itemData(6) is None - - assert widget.itemText(0) == 'b' - assert widget.itemText(1) == 'a' - assert widget.itemText(2) == 'c' - assert widget.itemText(3) == 'e' - assert widget.itemText(4) == 'd' - assert widget.itemText(5) == 'g' - assert widget.itemText(6) == 'f' - - -@pytest.mark.skipif(((PYSIDE2 or PYSIDE6 or PYQT5) - and os.environ.get('CI', None) is not None), - reason="It segfaults in our CIs with PYSIDE2/PYSIDE6 or PYQT5") -def test_model_item(): - """ - This is a regression test for an issue that caused the call to item(0) - below to trigger segmentation faults in PySide. The issue is - non-deterministic when running the call once, so we include a loop to make - sure that we trigger the fault. - """ - app = get_qapp() - combo = QtWidgets.QComboBox() - label_data = [('a', None)] - for iter in range(10000): - combo.clear() - for i, (label, data) in enumerate(label_data): - combo.addItem(label, userData=data) - model = combo.model() - model.item(0) diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index 17037f34..74df889d 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -3,7 +3,7 @@ import sys import pytest -from qtpy import PYSIDE, PYSIDE2, PYQT4 +from qtpy import PYSIDE2 from qtpy.QtWidgets import QApplication from qtpy.QtWidgets import QHeaderView from qtpy.QtCore import Qt @@ -46,11 +46,7 @@ class Model(QAbstractListModel): # test it assert isinstance(headerview.sectionsClickable(), bool) assert isinstance(headerview.sectionsMovable(), bool) - if PYSIDE: - assert isinstance(headerview.sectionResizeMode(0), - QHeaderView.ResizeMode) - else: - assert isinstance(headerview.sectionResizeMode(0), int) + assert isinstance(headerview.sectionResizeMode(0), int) headerview.setSectionsClickable(True) assert headerview.sectionsClickable() == True @@ -80,19 +76,4 @@ class Model(QAbstractListModel): headerview.setSectionResizeMode(0, QHeaderView.ResizeToContents) assert headerview.sectionResizeMode(0) == QHeaderView.ResizeToContents - # test that the old methods in Qt4 raise exceptions - if PYQT4 or PYSIDE: - with pytest.warns(UserWarning): - headerview.isClickable() - with pytest.warns(UserWarning): - headerview.isMovable() - with pytest.warns(UserWarning): - headerview.resizeMode(0) - with pytest.warns(UserWarning): - headerview.setClickable(True) - with pytest.warns(UserWarning): - headerview.setMovable(True) - with pytest.warns(UserWarning): - headerview.setResizeMode(0, QHeaderView.Interactive) - diff --git a/qtpy/tests/test_qdesktopservice_split.py b/qtpy/tests/test_qdesktopservice_split.py index 472f2df1..59e516aa 100644 --- a/qtpy/tests/test_qdesktopservice_split.py +++ b/qtpy/tests/test_qdesktopservice_split.py @@ -4,7 +4,6 @@ import pytest import warnings -from qtpy import PYQT4, PYSIDE def test_qstandarpath(): @@ -25,17 +24,3 @@ def test_qdesktopservice(): assert QDesktopServices.setUrlHandler is not None -@pytest.mark.skipif(not (PYQT4 or PYSIDE), reason="Warning is only raised in old bindings") -def test_qdesktopservice_qt4_pyside(): - from qtpy.QtGui import QDesktopServices - # Attributes from QStandardPaths should raise a warning when imported - # from QDesktopServices - with warnings.catch_warnings(record=True) as w: - # Cause all warnings to always be triggered. - warnings.simplefilter("always") - # Try to import QtHelp. - QDesktopServices.StandardLocation - - assert len(w) == 1 - assert issubclass(w[-1].category, DeprecationWarning) - assert "deprecated" in str(w[-1].message) diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index aa4bebe2..3497f733 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -1,9 +1,9 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE6, PYSIDE2, PYSIDE +from qtpy import PYSIDE6, PYSIDE2 -@pytest.mark.skipif((PYSIDE6 or PYSIDE2 or PYSIDE), reason="QtDesigner is not avalaible in PySide/PySide2/PySide6") +@pytest.mark.skipif((PYSIDE6 or PYSIDE2), reason="QtDesigner is not avalaible in PySide2/PySide6") def test_qtdesigner(): from qtpy import QtDesigner """Test the qtpy.QtDesigner namespace""" diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index 8093fe29..d8079bdf 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -1,14 +1,14 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE, PYSIDE2, PYSIDE6, QtNetwork +from qtpy import PYSIDE2, PYSIDE6, QtNetwork def test_qtnetwork(): """Test the qtpy.QtNetwork namespace""" assert QtNetwork.QAbstractNetworkCache is not None assert QtNetwork.QNetworkCacheMetaData is not None - if not PYSIDE and not PYSIDE2: + if not PYSIDE2: assert QtNetwork.QHttpMultiPart is not None assert QtNetwork.QHttpPart is not None assert QtNetwork.QNetworkAccessManager is not None @@ -35,10 +35,9 @@ def test_qtnetwork(): assert QtNetwork.QTcpServer is not None assert QtNetwork.QTcpSocket is not None assert QtNetwork.QUdpSocket is not None - if not PYSIDE: - assert QtNetwork.QSslCertificate is not None - assert QtNetwork.QSslCipher is not None - assert QtNetwork.QSslConfiguration is not None - assert QtNetwork.QSslError is not None - assert QtNetwork.QSslKey is not None - assert QtNetwork.QSslSocket is not None + assert QtNetwork.QSslCertificate is not None + assert QtNetwork.QSslCipher is not None + assert QtNetwork.QSslConfiguration is not None + assert QtNetwork.QSslError is not None + assert QtNetwork.QSslKey is not None + assert QtNetwork.QSslSocket is not None diff --git a/qtpy/tests/test_qtxmlpatterns.py b/qtpy/tests/test_qtxmlpatterns.py index 11ff2902..5b22d2de 100644 --- a/qtpy/tests/test_qtxmlpatterns.py +++ b/qtpy/tests/test_qtxmlpatterns.py @@ -1,7 +1,7 @@ from __future__ import absolute_import import pytest -from qtpy import PYSIDE2, PYSIDE6, PYSIDE +from qtpy import PYSIDE2, PYSIDE6 @pytest.mark.skipif(PYSIDE6, reason="not available with qt 6.0") def test_qtxmlpatterns(): @@ -11,7 +11,7 @@ def test_qtxmlpatterns(): assert QtXmlPatterns.QAbstractUriResolver is not None assert QtXmlPatterns.QAbstractXmlNodeModel is not None assert QtXmlPatterns.QAbstractXmlReceiver is not None - if not PYSIDE2 and not PYSIDE: + if not PYSIDE2: assert QtXmlPatterns.QSimpleXmlNodeModel is not None assert QtXmlPatterns.QSourceLocation is not None assert QtXmlPatterns.QXmlFormatter is not None diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 5e0a32de..516d2a0e 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -3,10 +3,10 @@ import contextlib import pytest -from qtpy import PYQT5, PYSIDE6, PYSIDE2, PYSIDE, QtWidgets +from qtpy import PYQT5, PYSIDE6, PYSIDE2, QtWidgets from qtpy.QtWidgets import QComboBox -if PYSIDE2 or PYSIDE: +if PYSIDE2: pytest.importorskip("pyside2uic", reason="pyside2uic not installed") from qtpy import uic @@ -105,7 +105,7 @@ def test_load_ui_custom_auto(tmpdir): @pytest.mark.skipif(PYSIDE6, reason="unavailable") def test_load_full_uic(): - """Test that we load the full uic objects for PyQt5 and PyQt4.""" + """Test that we load the full uic objects for PyQt5.""" QT_API = os.environ.get('QT_API', '').lower() if QT_API.startswith('pyside'): assert hasattr(uic, 'loadUi') diff --git a/qtpy/uic.py b/qtpy/uic.py index 992a231b..4e2a15d2 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -1,6 +1,6 @@ import os -from . import PYSIDE, PYSIDE6, PYSIDE2, PYQT4, PYQT5, PYQT6 +from . import PYSIDE6, PYSIDE2, PYQT5, PYQT6 from .QtWidgets import QComboBox @@ -12,10 +12,6 @@ from PyQt5.uic import * -elif PYQT4: - - from PyQt4.uic import * - else: __all__ = ['loadUi', 'loadUiType'] @@ -82,14 +78,7 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. - if PYSIDE: - from PySide.QtCore import QMetaObject - from PySide.QtUiTools import QUiLoader - try: - from pysideuic import compileUi - except ImportError: - pass - elif PYSIDE6: + if PYSIDE6: from PySide6.QtCore import QMetaObject from PySide6.QtUiTools import QUiLoader elif PYSIDE2: diff --git a/setup.py b/setup.py index 696eb21e..3ae3e673 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ version=version_ns['__version__'], packages=find_packages(exclude=['contrib', 'docs', 'tests*']), python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*', - keywords=["qt PyQt4 PyQt5 PySide"], + keywords=["qt PyQt5 PyQt6 PySide2 PySide6"], url='https://github.com/spyder-ide/qtpy', license='MIT', author='Colin Duquesnoy and the Spyder Development Team', @@ -32,7 +32,7 @@ maintainer='Spyder Development Team and QtPy Contributors', maintainer_email='spyder.python@gmail.com', description='Provides an abstraction layer on top of the various Qt ' - 'bindings (PyQt5, PyQt4 and PySide) and additional custom ' + 'bindings (PyQt5/6 and PySide2/6) and additional custom ' 'QWidgets.', long_description=LONG_DESCRIPTION, long_description_content_type='text/markdown', From 17da075699b6ea9a78c8f34443cc2c25712c2fea Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 29 Sep 2021 18:06:08 +0200 Subject: [PATCH 253/703] Drop Python 2: Initial Python 2 related code removal Update RELEASE instructions too Remove py3compat module Remove sys.version based validations --- .coveragerc | 1 - .github/workflows/ci.yml | 9 +- .github/workflows/test-pyqt5.sh | 23 +-- .github/workflows/test-pyside2.sh | 2 +- RELEASE.md | 12 +- qtpy/Qt3DAnimation.py | 14 +- qtpy/Qt3DCore.py | 16 +- qtpy/Qt3DExtras.py | 16 +- qtpy/Qt3DInput.py | 16 +- qtpy/Qt3DLogic.py | 16 +- qtpy/Qt3DRender.py | 16 +- qtpy/__init__.py | 1 - qtpy/compat.py | 23 ++- qtpy/py3compat.py | 261 ------------------------- qtpy/tests/test_patch_qheaderview.py | 9 +- qtpy/tests/test_qtdatavisualization.py | 8 +- qtpy/tests/test_qtmultimedia.py | 10 - qtpy/tests/test_qtwinextras.py | 2 +- qtpy/uic.py | 5 +- setup.cfg | 2 - setup.py | 4 +- 21 files changed, 80 insertions(+), 386 deletions(-) delete mode 100644 qtpy/py3compat.py delete mode 100644 setup.cfg diff --git a/.coveragerc b/.coveragerc index 105878e9..fcc86494 100644 --- a/.coveragerc +++ b/.coveragerc @@ -4,6 +4,5 @@ omit = */tests/* # Omit other files */__init__.py - */py3compat.py */compat.py */_version.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ea1a6ed..0c763d7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: strategy: fail-fast: false matrix: - PYTHON_VERSION: ['2.7', '3.6', '3.8'] + PYTHON_VERSION: ['3.6', '3.8'] USE_CONDA: ['Yes', 'No'] steps: - name: Checkout branch @@ -61,11 +61,10 @@ jobs: strategy: fail-fast: false matrix: - PYTHON_VERSION: ['2.7', '3.6', '3.8'] + PYTHON_VERSION: ['3.6', '3.8'] USE_CONDA: ['Yes', 'No'] exclude: - - PYTHON_VERSION: '2.7' - USE_CONDA: 'Yes' + - USE_CONDA: 'Yes' steps: - name: Checkout branch uses: actions/checkout@v2 @@ -95,7 +94,7 @@ jobs: strategy: fail-fast: false matrix: - PYTHON_VERSION: ['2.7', '3.6', '3.8'] + PYTHON_VERSION: ['3.6', '3.8'] USE_CONDA: ['Yes', 'No'] steps: - name: Checkout branch diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh index 678a3ec7..f1be5dd4 100755 --- a/.github/workflows/test-pyqt5.sh +++ b/.github/workflows/test-pyqt5.sh @@ -7,9 +7,7 @@ conda activate test-pyqt5 # Select build with QtMultimedia if [ "$(uname)" == "Darwin" ]; then - if [ "$PYTHON_VERSION" = "2.7" ]; then - export QT_VER=5.9 - elif [ "$PYTHON_VERSION" = "3.6" ]; then + if [ "$PYTHON_VERSION" = "3.6" ]; then export QT_VER=5.12 else export QT_VER=5.* @@ -17,9 +15,7 @@ if [ "$(uname)" == "Darwin" ]; then elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then - if [ "$PYTHON_VERSION" = "2.7" ]; then - export QT_VER=5.9 - elif [ "$PYTHON_VERSION" = "3.6" ]; then + if [ "$PYTHON_VERSION" = "3.6" ]; then export QT_VER=5.12 else export QT_VER=5.* @@ -27,9 +23,7 @@ elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then else - if [ "$PYTHON_VERSION" = "2.7" ]; then - exit 0 - elif [ "$PYTHON_VERSION" = "3.6" ]; then + if [ "$PYTHON_VERSION" = "3.6" ]; then export QT_VER=5.9 else export QT_VER=5.* @@ -41,14 +35,9 @@ if [ "$USE_CONDA" = "Yes" ]; then conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge -q conda install -q qt=$QT_VER pyqt=$QT_VER -c conda-forge -q else - if [ "$PYTHON_VERSION" = "2.7" ]; then - # There are no pyqt5 wheels for Python 2 - exit 0 - else - # We are getting segfaults in 5.10 - conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q - pip install -q pyqt5 PyQtWebEngine - fi + # We are getting segfaults in 5.10 + conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q + pip install -q pyqt5 PyQtWebEngine fi # Install package diff --git a/.github/workflows/test-pyside2.sh b/.github/workflows/test-pyside2.sh index f5e1c271..04b3fc58 100755 --- a/.github/workflows/test-pyside2.sh +++ b/.github/workflows/test-pyside2.sh @@ -8,7 +8,7 @@ if [ "$USE_CONDA" = "Yes" ]; then # There are no conda packages for PySide2 exit 0 elif [ "$PYTHON_VERSION" != "3.6" ] && [ "$RUNNER_OS" = "Windows" ]; then - # There is no wheel for PySide 5.12 on Windows and Python 2.7 or 3.8 + # There is no wheel for PySide 5.12 on Windows and Python 3.8 exit 0 else # Simple solution to avoid failures with the Qt3D modules diff --git a/RELEASE.md b/RELEASE.md index 834408bf..c9ca3799 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -6,23 +6,27 @@ To release a new version of qtpy on PyPI: * git clean -xfdi -* Update CHANGELOG.md +* Update CHANGELOG.md using `loghub` to generate the list of issues and PRs merged to add at the top of the file + + loghub -m vX.X.X spyder-ide/qtpy * Update `_version.py` (set release version, remove 'dev0') -* git add and git commit +* git add . && git commit -m 'Release X.X.X' * python setup.py sdist * python setup.py bdist_wheel +* twine check dist/* + * twine upload dist/* -* git tag -a vX.X.X -m 'comment' +* git tag -a vX.X.X -m 'Release X.X.X' * Update `_version.py` (add 'dev0' and increment minor) -* git add and git commit +* git add . && git commit -m 'Back to work' * git push diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index c6625b2d..020d9ae3 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -9,18 +9,14 @@ # Local imports from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION -from .py3compat import PY2 if PYQT5: from PyQt5.Qt3DAnimation import * elif PYSIDE2: - if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DAnimation as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DAnimation): - globals()[__name[0]] = __name[1] - else: - raise PythonQtError('A bug in Shiboken prevents this') + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DAnimation as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DAnimation): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 523e1ded..2ae2a9e2 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -8,19 +8,15 @@ """Provides Qt3DCore classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION -from .py3compat import PY2 +from . import PYQT5, PYSIDE2, PythonQtError if PYQT5: from PyQt5.Qt3DCore import * elif PYSIDE2: - if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DCore as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DCore): - globals()[__name[0]] = __name[1] - else: - raise PythonQtError('A bug in Shiboken prevents this') + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DCore as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DCore): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 4f3a9c13..5996b270 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -8,19 +8,15 @@ """Provides Qt3DExtras classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION -from .py3compat import PY2 +from . import PYQT5, PYSIDE2, PythonQtError if PYQT5: from PyQt5.Qt3DExtras import * elif PYSIDE2: - if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DExtras as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DExtras): - globals()[__name[0]] = __name[1] - else: - raise PythonQtError('A bug in Shiboken prevents this') + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DExtras as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DExtras): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 87b9a96a..072fc44a 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -8,19 +8,15 @@ """Provides Qt3DInput classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION -from .py3compat import PY2 +from . import PYQT5, PYSIDE2, PythonQtError if PYQT5: from PyQt5.Qt3DInput import * elif PYSIDE2: - if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DInput as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DInput): - globals()[__name[0]] = __name[1] - else: - raise PythonQtError('A bug in Shiboken prevents this') + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DInput as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DInput): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index d17f1367..d85fdb42 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -8,19 +8,15 @@ """Provides Qt3DLogic classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION -from .py3compat import PY2 +from . import PYQT5, PYSIDE2, PythonQtError if PYQT5: from PyQt5.Qt3DLogic import * elif PYSIDE2: - if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DLogic as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DLogic): - globals()[__name[0]] = __name[1] - else: - raise PythonQtError('A bug in Shiboken prevents this') + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DLogic as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DLogic): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index f30331ae..e42ee52c 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -8,19 +8,15 @@ """Provides Qt3DRender classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION -from .py3compat import PY2 +from . import PYQT5, PYSIDE2, PythonQtError if PYQT5: from PyQt5.Qt3DRender import * elif PYSIDE2: - if not PY2 or (PY2 and PYSIDE_VERSION < '5.12.4'): - # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 - import PySide2.Qt3DRender as __temp - import inspect - for __name in inspect.getmembers(__temp.Qt3DRender): - globals()[__name[0]] = __name[1] - else: - raise PythonQtError('A bug in Shiboken prevents this') + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.Qt3DRender as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DRender): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/__init__.py b/qtpy/__init__.py index fa62f8b2..7cd4b272 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -63,7 +63,6 @@ # Version of QtPy from ._version import __version__ -from .py3compat import PY2 class PythonQtError(RuntimeError): diff --git a/qtpy/compat.py b/qtpy/compat.py index 4e34ecb4..5526ea06 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -7,11 +7,30 @@ Compatibility functions """ -from __future__ import print_function +from collections.abc import Callable import sys from .QtWidgets import QFileDialog -from .py3compat import Callable, is_text_string, to_text_string, TEXT_TYPES + + +TEXT_TYPES = (str,) + + +def is_text_string(obj): + """Return True if `obj` is a text string, False if it is anything else, + like binary data.""" + return isinstance(obj, str) + + +def to_text_string(obj, encoding=None): + """Convert `obj` to (unicode) text string""" + if encoding is None: + return str(obj) + elif isinstance(obj, str): + # In case this function is not used properly, this could happen + return obj + else: + return str(obj, encoding) # ============================================================================= diff --git a/qtpy/py3compat.py b/qtpy/py3compat.py deleted file mode 100644 index cc4bdbf5..00000000 --- a/qtpy/py3compat.py +++ /dev/null @@ -1,261 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright © 2012-2013 Pierre Raybaut -# Licensed under the terms of the MIT License -# (see spyderlib/__init__.py for details) - -""" -spyderlib.py3compat -------------------- - -Transitional module providing compatibility functions intended to help -migrating from Python 2 to Python 3. - -This module should be fully compatible with: - * Python >=v2.6 - * Python 3 -""" - -from __future__ import print_function - -import sys -import os - -PY2 = sys.version_info[0] == 2 -PY3 = sys.version_info[0] == 3 -PY33 = PY3 and sys.version_info[1] >= 3 - - -# ============================================================================= -# Data types -# ============================================================================= -if PY2: - # Python 2 - TEXT_TYPES = (str, unicode) - INT_TYPES = (int, long) -else: - # Python 3 - TEXT_TYPES = (str,) - INT_TYPES = (int,) -NUMERIC_TYPES = tuple(list(INT_TYPES) + [float, complex]) - - -# ============================================================================= -# Renamed/Reorganized modules -# ============================================================================= -if PY2: - # Python 2 - import __builtin__ as builtins - import ConfigParser as configparser - try: - import _winreg as winreg - except ImportError: - pass - from sys import maxint as maxsize - try: - import CStringIO as io - except ImportError: - import StringIO as io - try: - import cPickle as pickle - except ImportError: - import pickle - from UserDict import DictMixin as MutableMapping - import thread as _thread - import repr as reprlib -else: - # Python 3 - import builtins - import configparser - try: - import winreg - except ImportError: - pass - from sys import maxsize - import io - import pickle - if PY33: - from collections.abc import Callable, MutableMapping - else: - from collections import Callable, MutableMapping - import _thread - import reprlib - - -# ============================================================================= -# Strings -# ============================================================================= -if PY2: - # Python 2 - import codecs - - def u(obj): - """Make unicode object""" - return codecs.unicode_escape_decode(obj)[0] -else: - # Python 3 - def u(obj): - """Return string as it is""" - return obj - - -def is_text_string(obj): - """Return True if `obj` is a text string, False if it is anything else, - like binary data (Python 3) or QString (Python 2, PyQt API #1)""" - if PY2: - # Python 2 - return isinstance(obj, basestring) - else: - # Python 3 - return isinstance(obj, str) - - -def is_binary_string(obj): - """Return True if `obj` is a binary string, False if it is anything else""" - if PY2: - # Python 2 - return isinstance(obj, str) - else: - # Python 3 - return isinstance(obj, bytes) - - -def is_string(obj): - """Return True if `obj` is a text or binary Python string object, - False if it is anything else, like a QString (Python 2, PyQt API #1)""" - return is_text_string(obj) or is_binary_string(obj) - - -def is_unicode(obj): - """Return True if `obj` is unicode""" - if PY2: - # Python 2 - return isinstance(obj, unicode) - else: - # Python 3 - return isinstance(obj, str) - - -def to_text_string(obj, encoding=None): - """Convert `obj` to (unicode) text string""" - if PY2: - # Python 2 - if encoding is None: - return unicode(obj) - else: - return unicode(obj, encoding) - else: - # Python 3 - if encoding is None: - return str(obj) - elif isinstance(obj, str): - # In case this function is not used properly, this could happen - return obj - else: - return str(obj, encoding) - - -def to_binary_string(obj, encoding=None): - """Convert `obj` to binary string (bytes in Python 3, str in Python 2)""" - if PY2: - # Python 2 - if encoding is None: - return str(obj) - else: - return obj.encode(encoding) - else: - # Python 3 - return bytes(obj, 'utf-8' if encoding is None else encoding) - - -# ============================================================================= -# Function attributes -# ============================================================================= -def get_func_code(func): - """Return function code object""" - if PY2: - # Python 2 - return func.func_code - else: - # Python 3 - return func.__code__ - - -def get_func_name(func): - """Return function name""" - if PY2: - # Python 2 - return func.func_name - else: - # Python 3 - return func.__name__ - - -def get_func_defaults(func): - """Return function default argument values""" - if PY2: - # Python 2 - return func.func_defaults - else: - # Python 3 - return func.__defaults__ - - -# ============================================================================= -# Special method attributes -# ============================================================================= -def get_meth_func(obj): - """Return method function object""" - if PY2: - # Python 2 - return obj.im_func - else: - # Python 3 - return obj.__func__ - - -def get_meth_class_inst(obj): - """Return method class instance""" - if PY2: - # Python 2 - return obj.im_self - else: - # Python 3 - return obj.__self__ - - -def get_meth_class(obj): - """Return method class""" - if PY2: - # Python 2 - return obj.im_class - else: - # Python 3 - return obj.__self__.__class__ - - -# ============================================================================= -# Misc. -# ============================================================================= -if PY2: - # Python 2 - input = raw_input - getcwd = os.getcwdu - cmp = cmp - import string - str_lower = string.lower - from itertools import izip_longest as zip_longest -else: - # Python 3 - input = input - getcwd = os.getcwd - - def cmp(a, b): - return (a > b) - (a < b) - str_lower = str.lower - from itertools import zip_longest - - -def qbytearray_to_str(qba): - """Convert QByteArray object to str in a way compatible with Python 2/3""" - return str(bytes(qba.toHex().data()).decode()) diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index 74df889d..ffea5528 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -1,7 +1,3 @@ -from __future__ import absolute_import - -import sys - import pytest from qtpy import PYSIDE2 from qtpy.QtWidgets import QApplication @@ -10,9 +6,6 @@ from qtpy.QtCore import QAbstractListModel -PY3 = sys.version[0] == "3" - - def get_qapp(icon_path=None): qapp = QApplication.instance() if qapp is None: @@ -20,7 +13,7 @@ def get_qapp(icon_path=None): return qapp -@pytest.mark.skipif(PY3 or PYSIDE2, reason="It fails on Python 3 and PySide2") +@pytest.mark.skipif(True, reason="It fails on Python 3 and PySide2") # TODO: Check patched code to remove or see if fixable for Python 3 def test_patched_qheaderview(): """ This will test whether QHeaderView has the new methods introduced in Qt5. diff --git a/qtpy/tests/test_qtdatavisualization.py b/qtpy/tests/test_qtdatavisualization.py index 8e287da6..54c7ed8f 100644 --- a/qtpy/tests/test_qtdatavisualization.py +++ b/qtpy/tests/test_qtdatavisualization.py @@ -1,13 +1,7 @@ -from __future__ import absolute_import - -import sys - import pytest -from qtpy import PYQT5, PYSIDE2 -from qtpy.py3compat import PY3 @pytest.mark.skipif( - sys.platform != "win32" or not (PYQT5 or PYSIDE2) or PY3, + True, # TODO: Check to see future usage reason="Only available in Qt5 bindings and Python 2 on Windows") def test_qtdatavisualization(): """Test the qtpy.QtDataVisualization namespace""" diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 1d6b11e2..53e8dbc9 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -1,13 +1,3 @@ -from __future__ import absolute_import -import os -import sys - -import pytest -from qtpy import PYSIDE6 - - -@pytest.mark.skipif(sys.version_info[0] == 3, - reason="Conda packages don't seem to include QtMultimedia") def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" from qtpy import QtMultimedia diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index 546bf298..df02d31e 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -7,7 +7,7 @@ from qtpy import PYSIDE2, PYSIDE6 @pytest.mark.skipif( - sys.platform != "win32" or os.environ['USE_CONDA'] == 'Yes' or PYSIDE6, + sys.platform != "win32" or os.environ.get('USE_CONDA', 'Yes') == 'Yes' or PYSIDE6, reason="Only available in Qt5 bindings > 5.9 (only available with pip in the current CI setup) and Windows platform") def test_qtwinextras(): """Test the qtpy.QtWinExtras namespace""" diff --git a/qtpy/uic.py b/qtpy/uic.py index 4e2a15d2..a67be387 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -243,10 +243,7 @@ def loadUiType(uifile, from_imports=False): """ import sys - if sys.version_info >= (3, 0): - from io import StringIO - else: - from io import BytesIO as StringIO + from io import StringIO from xml.etree.ElementTree import ElementTree from . import QtWidgets diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index 3c6e79cf..00000000 --- a/setup.cfg +++ /dev/null @@ -1,2 +0,0 @@ -[bdist_wheel] -universal=1 diff --git a/setup.py b/setup.py index 3ae3e673..7223988c 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ name='QtPy', version=version_ns['__version__'], packages=find_packages(exclude=['contrib', 'docs', 'tests*']), - python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*', + python_requires='>2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*', keywords=["qt PyQt5 PyQt6 PySide2 PySide6"], url='https://github.com/spyder-ide/qtpy', license='MIT', @@ -43,8 +43,6 @@ 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', From 79fd9867411d1bb8b932a9bd15354825fa269007 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 4 Oct 2021 10:08:18 -0500 Subject: [PATCH 254/703] Drop Python 2: Remove Python 2 mention in test comment. Simplify python_requires --- qtpy/tests/test_qtpositioning.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py index f6b5bffa..0cf95e86 100644 --- a/qtpy/tests/test_qtpositioning.py +++ b/qtpy/tests/test_qtpositioning.py @@ -14,7 +14,7 @@ def test_qtpositioning(): assert QtPositioning.QGeoCoordinate is not None assert QtPositioning.QGeoLocation is not None assert QtPositioning.QGeoPath is not None - # CI for Python 2.7 and 3.6 uses Qt 5.9 + # CI for 3.6 uses Qt 5.9 # assert QtPositioning.QGeoPolygon is not None # New in Qt 5.10 assert QtPositioning.QGeoPositionInfo is not None assert QtPositioning.QGeoPositionInfoSource is not None diff --git a/setup.py b/setup.py index 7223988c..fcc2b606 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ name='QtPy', version=version_ns['__version__'], packages=find_packages(exclude=['contrib', 'docs', 'tests*']), - python_requires='>2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*', + python_requires='>=3.6', keywords=["qt PyQt5 PyQt6 PySide2 PySide6"], url='https://github.com/spyder-ide/qtpy', license='MIT', From 1013eb102ef52df9c526bef8901d0664be1f3f22 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 4 Oct 2021 10:33:03 -0500 Subject: [PATCH 255/703] Drop Python 2: Remove exclude entry on MacOs workflow definition --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0c763d7a..358ab04a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,8 +63,6 @@ jobs: matrix: PYTHON_VERSION: ['3.6', '3.8'] USE_CONDA: ['Yes', 'No'] - exclude: - - USE_CONDA: 'Yes' steps: - name: Checkout branch uses: actions/checkout@v2 From 8cc229b538f1d85da3e329af695c08f14297bd71 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 5 Oct 2021 11:41:52 -0500 Subject: [PATCH 256/703] Drop Python 2: Update tests that where skipped in Python 3 --- qtpy/tests/test_patch_qheaderview.py | 19 ++- qtpy/tests/test_qtdatavisualization.py | 154 +++++++++++++------------ 2 files changed, 97 insertions(+), 76 deletions(-) diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index ffea5528..1eec41dc 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -1,5 +1,8 @@ +import sys + import pytest -from qtpy import PYSIDE2 + +from qtpy import PYSIDE2, QT_VERSION from qtpy.QtWidgets import QApplication from qtpy.QtWidgets import QHeaderView from qtpy.QtCore import Qt @@ -13,7 +16,12 @@ def get_qapp(icon_path=None): return qapp -@pytest.mark.skipif(True, reason="It fails on Python 3 and PySide2") # TODO: Check patched code to remove or see if fixable for Python 3 +@pytest.mark.skipif( + QT_VERSION.startswith('5.15') or + (PYSIDE2 and sys.version_info.major == 3 and sys.version_info.minor == 8 and + (sys.platform == 'darwin' or sys.platform.startswith('linux')) + ), + reason="It segfaults with Qt 5.15 and fails with PySide2, Python 3.8, on MacOS and Linux") def test_patched_qheaderview(): """ This will test whether QHeaderView has the new methods introduced in Qt5. @@ -30,6 +38,7 @@ def test_patched_qheaderview(): # setup a model and add it to a headerview qapp = get_qapp() headerview = QHeaderView(Qt.Horizontal) + # Fails here on PySide 2 and Python 3.8 due a bug: https://bugreports.qt.io/browse/PYSIDE-1140 class Model(QAbstractListModel): pass model = Model() @@ -39,7 +48,11 @@ class Model(QAbstractListModel): # test it assert isinstance(headerview.sectionsClickable(), bool) assert isinstance(headerview.sectionsMovable(), bool) - assert isinstance(headerview.sectionResizeMode(0), int) + if PYSIDE2: + assert isinstance(headerview.sectionResizeMode(0), + QHeaderView.ResizeMode) + else: + assert isinstance(headerview.sectionResizeMode(0), int) headerview.setSectionsClickable(True) assert headerview.sectionsClickable() == True diff --git a/qtpy/tests/test_qtdatavisualization.py b/qtpy/tests/test_qtdatavisualization.py index 54c7ed8f..4c9968fa 100644 --- a/qtpy/tests/test_qtdatavisualization.py +++ b/qtpy/tests/test_qtdatavisualization.py @@ -1,80 +1,88 @@ import pytest -@pytest.mark.skipif( - True, # TODO: Check to see future usage - reason="Only available in Qt5 bindings and Python 2 on Windows") +from qtpy import PYQT5, PYSIDE2 + + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtdatavisualization(): """Test the qtpy.QtDataVisualization namespace""" + # Using import skip here since with Python 3 you need to install another package + # besides the base `PyQt5` or `PySide2`. + # For example in the case of `PyQt5` you need `PyQtDataVisualization` + # QtDataVisualization - assert qtpy.QtDataVisualization.QScatter3DSeries is not None - assert qtpy.QtDataVisualization.QSurfaceDataItem is not None - assert qtpy.QtDataVisualization.QSurface3DSeries is not None - assert qtpy.QtDataVisualization.QAbstract3DInputHandler is not None - assert qtpy.QtDataVisualization.QHeightMapSurfaceDataProxy is not None - assert qtpy.QtDataVisualization.QAbstractDataProxy is not None - assert qtpy.QtDataVisualization.Q3DCamera is not None - assert qtpy.QtDataVisualization.QAbstract3DGraph is not None - assert qtpy.QtDataVisualization.QCustom3DVolume is not None - assert qtpy.QtDataVisualization.Q3DInputHandler is not None - assert qtpy.QtDataVisualization.QBarDataProxy is not None - assert qtpy.QtDataVisualization.QSurfaceDataProxy is not None - assert qtpy.QtDataVisualization.QScatterDataItem is not None - assert qtpy.QtDataVisualization.Q3DLight is not None - assert qtpy.QtDataVisualization.QScatterDataProxy is not None - assert qtpy.QtDataVisualization.QValue3DAxis is not None - assert qtpy.QtDataVisualization.Q3DBars is not None - assert qtpy.QtDataVisualization.QBarDataItem is not None - assert qtpy.QtDataVisualization.QItemModelBarDataProxy is not None - assert qtpy.QtDataVisualization.Q3DTheme is not None - assert qtpy.QtDataVisualization.QCustom3DItem is not None - assert qtpy.QtDataVisualization.QItemModelScatterDataProxy is not None - assert qtpy.QtDataVisualization.QValue3DAxisFormatter is not None - assert qtpy.QtDataVisualization.QItemModelSurfaceDataProxy is not None - assert qtpy.QtDataVisualization.Q3DScatter is not None - assert qtpy.QtDataVisualization.QTouch3DInputHandler is not None - assert qtpy.QtDataVisualization.QBar3DSeries is not None - assert qtpy.QtDataVisualization.QAbstract3DAxis is not None - assert qtpy.QtDataVisualization.Q3DScene is not None - assert qtpy.QtDataVisualization.QCategory3DAxis is not None - assert qtpy.QtDataVisualization.QAbstract3DSeries is not None - assert qtpy.QtDataVisualization.Q3DObject is not None - assert qtpy.QtDataVisualization.QCustom3DLabel is not None - assert qtpy.QtDataVisualization.Q3DSurface is not None - assert qtpy.QtDataVisualization.QLogValue3DAxisFormatter is not None + QtDataVisualization = pytest.importorskip("qtpy.QtDataVisualization") + assert QtDataVisualization.QScatter3DSeries is not None + assert QtDataVisualization.QSurfaceDataItem is not None + assert QtDataVisualization.QSurface3DSeries is not None + assert QtDataVisualization.QAbstract3DInputHandler is not None + assert QtDataVisualization.QHeightMapSurfaceDataProxy is not None + assert QtDataVisualization.QAbstractDataProxy is not None + assert QtDataVisualization.Q3DCamera is not None + assert QtDataVisualization.QAbstract3DGraph is not None + assert QtDataVisualization.QCustom3DVolume is not None + assert QtDataVisualization.Q3DInputHandler is not None + assert QtDataVisualization.QBarDataProxy is not None + assert QtDataVisualization.QSurfaceDataProxy is not None + assert QtDataVisualization.QScatterDataItem is not None + assert QtDataVisualization.Q3DLight is not None + assert QtDataVisualization.QScatterDataProxy is not None + assert QtDataVisualization.QValue3DAxis is not None + assert QtDataVisualization.Q3DBars is not None + assert QtDataVisualization.QBarDataItem is not None + assert QtDataVisualization.QItemModelBarDataProxy is not None + assert QtDataVisualization.Q3DTheme is not None + assert QtDataVisualization.QCustom3DItem is not None + assert QtDataVisualization.QItemModelScatterDataProxy is not None + assert QtDataVisualization.QValue3DAxisFormatter is not None + assert QtDataVisualization.QItemModelSurfaceDataProxy is not None + assert QtDataVisualization.Q3DScatter is not None + assert QtDataVisualization.QTouch3DInputHandler is not None + assert QtDataVisualization.QBar3DSeries is not None + assert QtDataVisualization.QAbstract3DAxis is not None + assert QtDataVisualization.Q3DScene is not None + assert QtDataVisualization.QCategory3DAxis is not None + assert QtDataVisualization.QAbstract3DSeries is not None + assert QtDataVisualization.Q3DObject is not None + assert QtDataVisualization.QCustom3DLabel is not None + assert QtDataVisualization.Q3DSurface is not None + assert QtDataVisualization.QLogValue3DAxisFormatter is not None # QtDatavisualization - assert qtpy.QtDatavisualization.QScatter3DSeries is not None - assert qtpy.QtDatavisualization.QSurfaceDataItem is not None - assert qtpy.QtDatavisualization.QSurface3DSeries is not None - assert qtpy.QtDatavisualization.QAbstract3DInputHandler is not None - assert qtpy.QtDatavisualization.QHeightMapSurfaceDataProxy is not None - assert qtpy.QtDatavisualization.QAbstractDataProxy is not None - assert qtpy.QtDatavisualization.Q3DCamera is not None - assert qtpy.QtDatavisualization.QAbstract3DGraph is not None - assert qtpy.QtDatavisualization.QCustom3DVolume is not None - assert qtpy.QtDatavisualization.Q3DInputHandler is not None - assert qtpy.QtDatavisualization.QBarDataProxy is not None - assert qtpy.QtDatavisualization.QSurfaceDataProxy is not None - assert qtpy.QtDatavisualization.QScatterDataItem is not None - assert qtpy.QtDatavisualization.Q3DLight is not None - assert qtpy.QtDatavisualization.QScatterDataProxy is not None - assert qtpy.QtDatavisualization.QValue3DAxis is not None - assert qtpy.QtDatavisualization.Q3DBars is not None - assert qtpy.QtDatavisualization.QBarDataItem is not None - assert qtpy.QtDatavisualization.QItemModelBarDataProxy is not None - assert qtpy.QtDatavisualization.Q3DTheme is not None - assert qtpy.QtDatavisualization.QCustom3DItem is not None - assert qtpy.QtDatavisualization.QItemModelScatterDataProxy is not None - assert qtpy.QtDatavisualization.QValue3DAxisFormatter is not None - assert qtpy.QtDatavisualization.QItemModelSurfaceDataProxy is not None - assert qtpy.QtDatavisualization.Q3DScatter is not None - assert qtpy.QtDatavisualization.QTouch3DInputHandler is not None - assert qtpy.QtDatavisualization.QBar3DSeries is not None - assert qtpy.QtDatavisualization.QAbstract3DAxis is not None - assert qtpy.QtDatavisualization.Q3DScene is not None - assert qtpy.QtDatavisualization.QCategory3DAxis is not None - assert qtpy.QtDatavisualization.QAbstract3DSeries is not None - assert qtpy.QtDatavisualization.Q3DObject is not None - assert qtpy.QtDatavisualization.QCustom3DLabel is not None - assert qtpy.QtDatavisualization.Q3DSurface is not None - assert qtpy.QtDatavisualization.QLogValue3DAxisFormatter is not None + QtDatavisualization = pytest.importorskip("qtpy.QtDatavisualization") + + assert QtDatavisualization.QScatter3DSeries is not None + assert QtDatavisualization.QSurfaceDataItem is not None + assert QtDatavisualization.QSurface3DSeries is not None + assert QtDatavisualization.QAbstract3DInputHandler is not None + assert QtDatavisualization.QHeightMapSurfaceDataProxy is not None + assert QtDatavisualization.QAbstractDataProxy is not None + assert QtDatavisualization.Q3DCamera is not None + assert QtDatavisualization.QAbstract3DGraph is not None + assert QtDatavisualization.QCustom3DVolume is not None + assert QtDatavisualization.Q3DInputHandler is not None + assert QtDatavisualization.QBarDataProxy is not None + assert QtDatavisualization.QSurfaceDataProxy is not None + assert QtDatavisualization.QScatterDataItem is not None + assert QtDatavisualization.Q3DLight is not None + assert QtDatavisualization.QScatterDataProxy is not None + assert QtDatavisualization.QValue3DAxis is not None + assert QtDatavisualization.Q3DBars is not None + assert QtDatavisualization.QBarDataItem is not None + assert QtDatavisualization.QItemModelBarDataProxy is not None + assert QtDatavisualization.Q3DTheme is not None + assert QtDatavisualization.QCustom3DItem is not None + assert QtDatavisualization.QItemModelScatterDataProxy is not None + assert QtDatavisualization.QValue3DAxisFormatter is not None + assert QtDatavisualization.QItemModelSurfaceDataProxy is not None + assert QtDatavisualization.Q3DScatter is not None + assert QtDatavisualization.QTouch3DInputHandler is not None + assert QtDatavisualization.QBar3DSeries is not None + assert QtDatavisualization.QAbstract3DAxis is not None + assert QtDatavisualization.Q3DScene is not None + assert QtDatavisualization.QCategory3DAxis is not None + assert QtDatavisualization.QAbstract3DSeries is not None + assert QtDatavisualization.Q3DObject is not None + assert QtDatavisualization.QCustom3DLabel is not None + assert QtDatavisualization.Q3DSurface is not None + assert QtDatavisualization.QLogValue3DAxisFormatter is not None From bc8f851e723c08a1ffe0978f9f9bd522ebfa1eec Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 11 Oct 2021 17:03:16 -0500 Subject: [PATCH 257/703] Run Pyupgrade to further clean up legacy Python <3.6 syntax --- .github/workflows/test-pyqt5.sh | 6 +++--- .github/workflows/test-pyside2.sh | 2 +- qtpy/Qt3DAnimation.py | 1 - qtpy/Qt3DCore.py | 1 - qtpy/Qt3DExtras.py | 1 - qtpy/Qt3DInput.py | 1 - qtpy/Qt3DLogic.py | 1 - qtpy/Qt3DRender.py | 1 - qtpy/QtCharts.py | 1 - qtpy/QtCore.py | 1 - qtpy/QtDataVisualization.py | 1 - qtpy/QtDesigner.py | 1 - qtpy/QtGui.py | 1 - qtpy/QtHelp.py | 1 - qtpy/QtLocation.py | 1 - qtpy/QtMultimediaWidgets.py | 1 - qtpy/QtNetwork.py | 1 - qtpy/QtOpenGL.py | 1 - qtpy/QtPositioning.py | 1 - qtpy/QtPrintSupport.py | 1 - qtpy/QtQml.py | 1 - qtpy/QtQuick.py | 1 - qtpy/QtQuickWidgets.py | 1 - qtpy/QtSerialPort.py | 1 - qtpy/QtSql.py | 1 - qtpy/QtSvg.py | 1 - qtpy/QtTest.py | 1 - qtpy/QtWebChannel.py | 1 - qtpy/QtWebEngineWidgets.py | 1 - qtpy/QtWebSockets.py | 1 - qtpy/QtWidgets.py | 1 - qtpy/QtWinExtras.py | 1 - qtpy/QtXmlPatterns.py | 1 - qtpy/__init__.py | 1 - qtpy/_patch/qheaderview.py | 1 - qtpy/compat.py | 1 - qtpy/tests/conftest.py | 6 +++--- qtpy/tests/runtests.py | 1 - qtpy/tests/test_macos_checks.py | 4 +--- qtpy/tests/test_qdesktopservice_split.py | 1 - qtpy/tests/test_qt3danimation.py | 2 -- qtpy/tests/test_qt3dcore.py | 2 -- qtpy/tests/test_qt3dextras.py | 2 -- qtpy/tests/test_qt3dinput.py | 2 -- qtpy/tests/test_qt3dlogic.py | 2 -- qtpy/tests/test_qt3drender.py | 2 -- qtpy/tests/test_qtcharts.py | 2 -- qtpy/tests/test_qtcore.py | 2 -- qtpy/tests/test_qtdesigner.py | 2 -- qtpy/tests/test_qthelp.py | 1 - qtpy/tests/test_qtlocation.py | 2 -- qtpy/tests/test_qtmultimediawidgets.py | 1 - qtpy/tests/test_qtnetwork.py | 2 -- qtpy/tests/test_qtpositioning.py | 2 -- qtpy/tests/test_qtprintsupport.py | 2 -- qtpy/tests/test_qtqml.py | 2 -- qtpy/tests/test_qtquick.py | 2 -- qtpy/tests/test_qtquickwidgets.py | 2 -- qtpy/tests/test_qtserialport.py | 2 -- qtpy/tests/test_qtsql.py | 2 -- qtpy/tests/test_qtsvg.py | 2 -- qtpy/tests/test_qttest.py | 2 -- qtpy/tests/test_qtwebchannel.py | 2 -- qtpy/tests/test_qtwebenginewidgets.py | 2 -- qtpy/tests/test_qtwebsockets.py | 2 -- qtpy/tests/test_qtwinextras.py | 2 -- qtpy/tests/test_qtxmlpatterns.py | 2 -- qtpy/tests/test_uic.py | 2 +- qtpy/uic.py | 2 +- setup.py | 4 +--- 70 files changed, 11 insertions(+), 103 deletions(-) diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh index f1be5dd4..cdd68b26 100755 --- a/.github/workflows/test-pyqt5.sh +++ b/.github/workflows/test-pyqt5.sh @@ -32,11 +32,11 @@ else fi if [ "$USE_CONDA" = "Yes" ]; then - conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge -q - conda install -q qt=$QT_VER pyqt=$QT_VER -c conda-forge -q + conda install -q coveralls pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge + conda install -q qt=$QT_VER pyqt=$QT_VER -c conda-forge else # We are getting segfaults in 5.10 - conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c anaconda -q + conda install -q coveralls pytest pytest-cov python="$PYTHON_VERSION" -c anaconda pip install -q pyqt5 PyQtWebEngine fi diff --git a/.github/workflows/test-pyside2.sh b/.github/workflows/test-pyside2.sh index 04b3fc58..46198351 100755 --- a/.github/workflows/test-pyside2.sh +++ b/.github/workflows/test-pyside2.sh @@ -12,7 +12,7 @@ elif [ "$PYTHON_VERSION" != "3.6" ] && [ "$RUNNER_OS" = "Windows" ]; then exit 0 else # Simple solution to avoid failures with the Qt3D modules - conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge -q + conda install -q coveralls pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge pip install -q pyside2==5.12 fi diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index 020d9ae3..9923d2eb 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 2ae2a9e2..cb9cc85c 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 5996b270..86b2635a 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 072fc44a..27a4ff77 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index d85fdb42..e1187da3 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index e42ee52c..27610e84 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 74671230..af309b13 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2019- The Spyder Development Team # diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index a6142bf9..6f75e397 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Development Team diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index a50fe233..a0e1dfc2 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index 04576785..a906f5ab 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 62c0ee7c..c909d1a3 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Development Team diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index de06baf7..b53a40e6 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtLocation.py b/qtpy/QtLocation.py index 9dfe874a..7241158d 100644 --- a/qtpy/QtLocation.py +++ b/qtpy/QtLocation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtMultimediaWidgets.py b/qtpy/QtMultimediaWidgets.py index 697845d9..328e166f 100644 --- a/qtpy/QtMultimediaWidgets.py +++ b/qtpy/QtMultimediaWidgets.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index c1fcd588..e2507d64 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Development Team diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index aaa8cd2b..fdcb5c71 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtPositioning.py b/qtpy/QtPositioning.py index 2b46d356..5535ef3f 100644 --- a/qtpy/QtPositioning.py +++ b/qtpy/QtPositioning.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright 2020 Antonio Valentino # diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 872d8179..4e512e71 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtQml.py b/qtpy/QtQml.py index 762ac8ae..213706e9 100644 --- a/qtpy/QtQml.py +++ b/qtpy/QtQml.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtQuick.py b/qtpy/QtQuick.py index 0a28eaa0..b0697da1 100644 --- a/qtpy/QtQuick.py +++ b/qtpy/QtQuick.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtQuickWidgets.py b/qtpy/QtQuickWidgets.py index eb7884fe..fb534cff 100644 --- a/qtpy/QtQuickWidgets.py +++ b/qtpy/QtQuickWidgets.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtSerialPort.py b/qtpy/QtSerialPort.py index 26fcae18..774005c7 100644 --- a/qtpy/QtSerialPort.py +++ b/qtpy/QtSerialPort.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2020 Marcin Stano # Copyright © 2009- The Spyder Development Team diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 0c8dcb3b..44f02884 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index cb99e12c..4e7733b9 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 83ecb694..7db1e14c 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Developmet Team diff --git a/qtpy/QtWebChannel.py b/qtpy/QtWebChannel.py index 789bfe80..ad4fbea3 100644 --- a/qtpy/QtWebChannel.py +++ b/qtpy/QtWebChannel.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 3b41b09a..e246ab51 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder development Team diff --git a/qtpy/QtWebSockets.py b/qtpy/QtWebSockets.py index 4b6a8204..9f5b3731 100644 --- a/qtpy/QtWebSockets.py +++ b/qtpy/QtWebSockets.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index d7da8ef3..912d6b7b 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Developmet Team diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index c033ff98..8b870bdf 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py index 3ab2661e..f6b22a32 100644 --- a/qtpy/QtXmlPatterns.py +++ b/qtpy/QtXmlPatterns.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 7cd4b272..514d23bc 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2009- The Spyder Development Team # Copyright © 2014-2015 Colin Duquesnoy diff --git a/qtpy/_patch/qheaderview.py b/qtpy/_patch/qheaderview.py index b6baddbb..9dee5712 100644 --- a/qtpy/_patch/qheaderview.py +++ b/qtpy/_patch/qheaderview.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © The Spyder Development Team # diff --git a/qtpy/compat.py b/qtpy/compat.py index 5526ea06..f559971b 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2009- The Spyder Development Team # Licensed under the terms of the MIT License diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index 740cb4d2..d89776c5 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -25,7 +25,7 @@ def pytest_report_header(config): try: from PyQt6 import Qt - versions += "PyQt: {0} - Qt: {1}".format(Qt.PYQT_VERSION_STR, Qt.QT_VERSION_STR) + versions += f"PyQt: {Qt.PYQT_VERSION_STR} - Qt: {Qt.QT_VERSION_STR}" except ImportError: versions += 'not installed' except AttributeError: @@ -36,7 +36,7 @@ def pytest_report_header(config): try: from PyQt5 import Qt - versions += "PyQt: {0} - Qt: {1}".format(Qt.PYQT_VERSION_STR, Qt.QT_VERSION_STR) + versions += f"PyQt: {Qt.PYQT_VERSION_STR} - Qt: {Qt.QT_VERSION_STR}" except ImportError: versions += 'not installed' except AttributeError: @@ -48,7 +48,7 @@ def pytest_report_header(config): try: import PySide2 from PySide2 import QtCore - versions += "PySide: {0} - Qt: {1}".format(PySide2.__version__, QtCore.__version__) + versions += f"PySide: {PySide2.__version__} - Qt: {QtCore.__version__}" except ImportError: versions += 'not installed' except AttributeError: diff --git a/qtpy/tests/runtests.py b/qtpy/tests/runtests.py index bbac70da..ba43b9e5 100755 --- a/qtpy/tests/runtests.py +++ b/qtpy/tests/runtests.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- # ---------------------------------------------------------------------------- # Copyright © 2015- The Spyder Development Team # diff --git a/qtpy/tests/test_macos_checks.py b/qtpy/tests/test_macos_checks.py index 01aa8091..6993b315 100644 --- a/qtpy/tests/test_macos_checks.py +++ b/qtpy/tests/test_macos_checks.py @@ -1,6 +1,4 @@ -from __future__ import absolute_import - -import mock +from unittest import mock import platform import sys diff --git a/qtpy/tests/test_qdesktopservice_split.py b/qtpy/tests/test_qdesktopservice_split.py index 59e516aa..d412e9c8 100644 --- a/qtpy/tests/test_qdesktopservice_split.py +++ b/qtpy/tests/test_qdesktopservice_split.py @@ -1,6 +1,5 @@ """Test QDesktopServices split in Qt5.""" -from __future__ import absolute_import import pytest import warnings diff --git a/qtpy/tests/test_qt3danimation.py b/qtpy/tests/test_qt3danimation.py index 650be19e..3e355cb8 100644 --- a/qtpy/tests/test_qt3danimation.py +++ b/qtpy/tests/test_qt3danimation.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qt3dcore.py b/qtpy/tests/test_qt3dcore.py index 821fbd45..e3ccac8d 100644 --- a/qtpy/tests/test_qt3dcore.py +++ b/qtpy/tests/test_qt3dcore.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qt3dextras.py b/qtpy/tests/test_qt3dextras.py index f63c7d57..4a30fcdc 100644 --- a/qtpy/tests/test_qt3dextras.py +++ b/qtpy/tests/test_qt3dextras.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qt3dinput.py b/qtpy/tests/test_qt3dinput.py index 48d73d03..3f6118b0 100644 --- a/qtpy/tests/test_qt3dinput.py +++ b/qtpy/tests/test_qt3dinput.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qt3dlogic.py b/qtpy/tests/test_qt3dlogic.py index 34f7de67..8a01c33f 100644 --- a/qtpy/tests/test_qt3dlogic.py +++ b/qtpy/tests/test_qt3dlogic.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qt3drender.py b/qtpy/tests/test_qt3drender.py index f4647682..8f8813aa 100644 --- a/qtpy/tests/test_qt3drender.py +++ b/qtpy/tests/test_qt3drender.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qtcharts.py b/qtpy/tests/test_qtcharts.py index 4c72dbc3..3b3f8a14 100644 --- a/qtpy/tests/test_qtcharts.py +++ b/qtpy/tests/test_qtcharts.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYSIDE2 diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 81c1e6c4..196f3de2 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2, QtCore diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index 3497f733..4e96b563 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYSIDE6, PYSIDE2 diff --git a/qtpy/tests/test_qthelp.py b/qtpy/tests/test_qthelp.py index 2b70ca75..9b78912d 100644 --- a/qtpy/tests/test_qthelp.py +++ b/qtpy/tests/test_qthelp.py @@ -1,6 +1,5 @@ """Test for QtHelp namespace.""" -from __future__ import absolute_import import pytest diff --git a/qtpy/tests/test_qtlocation.py b/qtpy/tests/test_qtlocation.py index 78bf9337..012db7bc 100644 --- a/qtpy/tests/test_qtlocation.py +++ b/qtpy/tests/test_qtlocation.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qtmultimediawidgets.py b/qtpy/tests/test_qtmultimediawidgets.py index bd659e51..dee6f16d 100644 --- a/qtpy/tests/test_qtmultimediawidgets.py +++ b/qtpy/tests/test_qtmultimediawidgets.py @@ -1,4 +1,3 @@ -from __future__ import absolute_import import os import sys diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index d8079bdf..54b3b17a 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYSIDE2, PYSIDE6, QtNetwork diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py index 0cf95e86..080ef4c5 100644 --- a/qtpy/tests/test_qtpositioning.py +++ b/qtpy/tests/test_qtpositioning.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py index 2e8f7861..cf3b1052 100644 --- a/qtpy/tests/test_qtprintsupport.py +++ b/qtpy/tests/test_qtprintsupport.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import QtPrintSupport diff --git a/qtpy/tests/test_qtqml.py b/qtpy/tests/test_qtqml.py index a6d7ca95..f2a229b9 100644 --- a/qtpy/tests/test_qtqml.py +++ b/qtpy/tests/test_qtqml.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qtquick.py b/qtpy/tests/test_qtquick.py index 257fd740..d21e2af8 100644 --- a/qtpy/tests/test_qtquick.py +++ b/qtpy/tests/test_qtquick.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qtquickwidgets.py b/qtpy/tests/test_qtquickwidgets.py index 0b41a8bd..f055a308 100644 --- a/qtpy/tests/test_qtquickwidgets.py +++ b/qtpy/tests/test_qtquickwidgets.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qtserialport.py b/qtpy/tests/test_qtserialport.py index 26daaf76..0d740b2e 100644 --- a/qtpy/tests/test_qtserialport.py +++ b/qtpy/tests/test_qtserialport.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5 diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index 1e7404ff..12e8d920 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import QtSql diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py index 34f57a1e..56d962b1 100644 --- a/qtpy/tests/test_qtsvg.py +++ b/qtpy/tests/test_qtsvg.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYSIDE6 diff --git a/qtpy/tests/test_qttest.py b/qtpy/tests/test_qttest.py index 5d2ab9e1..64fa0d4d 100644 --- a/qtpy/tests/test_qttest.py +++ b/qtpy/tests/test_qttest.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import QtTest diff --git a/qtpy/tests/test_qtwebchannel.py b/qtpy/tests/test_qtwebchannel.py index 2beb70c0..4f33a03b 100644 --- a/qtpy/tests/test_qtwebchannel.py +++ b/qtpy/tests/test_qtwebchannel.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qtwebenginewidgets.py b/qtpy/tests/test_qtwebenginewidgets.py index 7d60048c..276f5ff0 100644 --- a/qtpy/tests/test_qtwebenginewidgets.py +++ b/qtpy/tests/test_qtwebenginewidgets.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYSIDE6 diff --git a/qtpy/tests/test_qtwebsockets.py b/qtpy/tests/test_qtwebsockets.py index 5bdcc325..420aa866 100644 --- a/qtpy/tests/test_qtwebsockets.py +++ b/qtpy/tests/test_qtwebsockets.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYQT5, PYSIDE2 diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index df02d31e..261a4875 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import os import sys diff --git a/qtpy/tests/test_qtxmlpatterns.py b/qtpy/tests/test_qtxmlpatterns.py index 5b22d2de..4f8da789 100644 --- a/qtpy/tests/test_qtxmlpatterns.py +++ b/qtpy/tests/test_qtxmlpatterns.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from qtpy import PYSIDE2, PYSIDE6 diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 516d2a0e..8479de39 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -75,7 +75,7 @@ def test_load_ui_type(): class Widget(ui_base_type, ui_type): def __init__(self): - super(Widget, self).__init__() + super().__init__() self.setupUi(self) ui = Widget() diff --git a/qtpy/uic.py b/qtpy/uic.py index a67be387..ba0d2416 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -254,7 +254,7 @@ def loadUiType(uifile, from_imports=False): widget_class = ui.find('widget').get('class') form_class = ui.find('class').text - with open(uifile, 'r') as fd: + with open(uifile) as fd: code_stream = StringIO() frame = {} diff --git a/setup.py b/setup.py index fcc2b606..0631220f 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,10 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- """ Setup script for qtpy """ import os -import io from setuptools import setup, find_packages @@ -16,7 +14,7 @@ with open(os.path.join(HERE, 'qtpy', '_version.py')) as f: exec(f.read(), {}, version_ns) -with io.open(os.path.join(HERE, 'README.md'), encoding='utf-8') as f: +with open(os.path.join(HERE, 'README.md'), encoding='utf-8') as f: LONG_DESCRIPTION = f.read() setup( From 390092829b324727e6690c73d075c092f9e895bc Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 13 Oct 2021 11:55:50 -0500 Subject: [PATCH 258/703] Drop Python 2: Update test files after PySide6 addition --- .github/workflows/test-pyside6.sh | 3 --- qtpy/QtWebEngine.py | 1 - qtpy/tests/conftest.py | 2 +- qtpy/tests/test_patch_qheaderview.py | 6 +++--- qtpy/tests/test_qtmultimedia.py | 17 ++++++++++++++--- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test-pyside6.sh b/.github/workflows/test-pyside6.sh index 42b3ddcb..b1251ed2 100755 --- a/.github/workflows/test-pyside6.sh +++ b/.github/workflows/test-pyside6.sh @@ -7,9 +7,6 @@ conda activate test-pyside6 if [ "$USE_CONDA" = "Yes" ]; then # There are no conda packages for PySide6 exit 0 -elif [ "$PYTHON_VERSION" == "2.7" ]; then - # There is no wheel for PySide6 on Python 2.7 - exit 0 else # Simple solution to avoid failures with the Qt3D modules conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge -q diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index 69f0a03c..1b913a1a 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder development Team diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index d89776c5..4cbeb8dd 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -60,7 +60,7 @@ def pytest_report_header(config): try: import PySide6 from PySide6 import QtCore - versions += "PySide: {0} - Qt: {1}".format(PySide6.__version__, QtCore.__version__) + versions += f"PySide: {PySide6.__version__} - Qt: {QtCore.__version__}" except ImportError: versions += 'not installed' except AttributeError: diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index 1eec41dc..37fae6ed 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -2,7 +2,7 @@ import pytest -from qtpy import PYSIDE2, QT_VERSION +from qtpy import PYSIDE2, PYSIDE6, QT_VERSION from qtpy.QtWidgets import QApplication from qtpy.QtWidgets import QHeaderView from qtpy.QtCore import Qt @@ -17,8 +17,8 @@ def get_qapp(icon_path=None): @pytest.mark.skipif( - QT_VERSION.startswith('5.15') or - (PYSIDE2 and sys.version_info.major == 3 and sys.version_info.minor == 8 and + QT_VERSION.startswith('5.15') or PYSIDE6 or + ((PYSIDE2) and sys.version_info.major == 3 and sys.version_info.minor == 8 and (sys.platform == 'darwin' or sys.platform.startswith('linux')) ), reason="It segfaults with Qt 5.15 and fails with PySide2, Python 3.8, on MacOS and Linux") diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 53e8dbc9..369ca19e 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -1,9 +1,20 @@ +import sys + +import pytest + +from qtpy import PYSIDE6 + +@pytest.mark.skipif( + sys.platform.startswith('linux') and PYSIDE6, + reason="Needs to setup GStreamer on Linux") def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" from qtpy import QtMultimedia - assert QtMultimedia.QAbstractVideoBuffer is not None assert QtMultimedia.QAudio is not None - assert QtMultimedia.QAudioDeviceInfo is not None assert QtMultimedia.QAudioInput is not None - assert QtMultimedia.QSound is not None + + if not PYSIDE6: + assert QtMultimedia.QAbstractVideoBuffer is not None + assert QtMultimedia.QAudioDeviceInfo is not None + assert QtMultimedia.QSound is not None From 954a727bd17bfde01eca9b753134a6213b9f8342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Fri, 15 Oct 2021 12:18:48 -0500 Subject: [PATCH 259/703] Drop Python 2: Remove mock usage from test-pyside6 Co-authored-by: CAM Gerlach --- .github/workflows/test-pyside6.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test-pyside6.sh b/.github/workflows/test-pyside6.sh index b1251ed2..eb927888 100755 --- a/.github/workflows/test-pyside6.sh +++ b/.github/workflows/test-pyside6.sh @@ -9,7 +9,7 @@ if [ "$USE_CONDA" = "Yes" ]; then exit 0 else # Simple solution to avoid failures with the Qt3D modules - conda install coveralls mock pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge -q + conda install -q coveralls pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge pip install -q pyside6==6.2.0 fi From 9648f8a183ebbb5ebef5e7a8b367b8141dfbb9c2 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 15 Oct 2021 12:10:24 -0500 Subject: [PATCH 260/703] Drop Python 2: Remove dead Qt4 code from qfiledialog wrapper compat fn --- qtpy/compat.py | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/qtpy/compat.py b/qtpy/compat.py index f559971b..3a51c921 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -78,7 +78,7 @@ def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', filters='', selectedfilter='', options=None): if options is None: options = QFileDialog.Options(0) - + func = getattr(QFileDialog, attr) # Calling QFileDialog static method @@ -86,19 +86,10 @@ def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', # On Windows platforms: redirect standard outputs _temp1, _temp2 = sys.stdout, sys.stderr sys.stdout, sys.stderr = None, None - try: - result = func(parent, caption, basedir, - filters, selectedfilter, options) - except TypeError: - # The selectedfilter option (`initialFilter` in Qt) has only been - # introduced in Jan. 2010 for PyQt v4.7, that's why we handle here - # the TypeError exception which will be raised with PyQt v4.6 - # (see Issue 960 for more details) - result = func(parent, caption, basedir, filters, options) - finally: - if sys.platform == "win32": - # On Windows platforms: restore standard outputs - sys.stdout, sys.stderr = _temp1, _temp2 + result = func(parent, caption, basedir, filters, selectedfilter, options) + if sys.platform == "win32": + # On Windows platforms: restore standard outputs + sys.stdout, sys.stderr = _temp1, _temp2 output, selectedfilter = result From 9e95762e2a34e7e86d6b3413dba64b87dad7b3f5 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Sun, 17 Oct 2021 21:04:39 +0200 Subject: [PATCH 261/703] fix fallback API --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 514d23bc..772fe3d1 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -182,7 +182,7 @@ class PythonQtWarning(Warning): PYSIDE6 = True except ImportError: - API = os.environ['QT_API'] = 'pyqt' + API = os.environ['QT_API'] = 'pyqt5' # If a correct API name is passed to QT_API and it could not be found, From 6ac47205730ca8d536dd39d06bbdec1bc6fa2d89 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Sat, 16 Oct 2021 00:21:12 +0200 Subject: [PATCH 262/703] More modules --- qtpy/Qt3DCore.py | 6 +++++- qtpy/Qt3DExtras.py | 6 +++++- qtpy/Qt3DInput.py | 6 +++++- qtpy/Qt3DLogic.py | 6 +++++- qtpy/Qt3DRender.py | 6 +++++- qtpy/QtCharts.py | 6 +++++- qtpy/QtDBus.py | 19 +++++++++++++++++++ qtpy/QtDesigner.py | 6 +++++- qtpy/QtHelp.py | 6 +++++- qtpy/QtMultimedia.py | 6 +++++- qtpy/QtMultimediaWidgets.py | 6 +++++- qtpy/QtNetwork.py | 4 +++- qtpy/QtNetworkAuth.py | 19 +++++++++++++++++++ qtpy/QtOpenGL.py | 2 ++ qtpy/QtOpenGLWidgets.py | 17 +++++++++++++++++ qtpy/QtPrintSupport.py | 4 +++- qtpy/QtQml.py | 4 +++- qtpy/QtQuick.py | 4 +++- qtpy/QtQuickWidgets.py | 4 +++- qtpy/QtRemoteObjects.py | 21 +++++++++++++++++++++ qtpy/QtSensors.py | 21 +++++++++++++++++++++ qtpy/QtSerialPort.py | 8 +++++++- qtpy/QtSql.py | 4 +++- qtpy/QtWebChannel.py | 4 +++- qtpy/QtWebEngineCore.py | 21 +++++++++++++++++++++ qtpy/QtWebEngineQuick.py | 17 +++++++++++++++++ qtpy/QtWebEngineWidgets.py | 7 ++++++- qtpy/QtWebSockets.py | 4 +++- qtpy/QtXml.py | 21 +++++++++++++++++++++ qtpy/tests/test_patch_qheaderview.py | 4 ++-- qtpy/tests/test_qt3dcore.py | 4 ++-- qtpy/tests/test_qt3dextras.py | 2 -- qtpy/tests/test_qt3dinput.py | 2 -- qtpy/tests/test_qt3dlogic.py | 2 -- qtpy/tests/test_qt3drender.py | 4 ++-- qtpy/tests/test_qtcharts.py | 4 ++-- qtpy/tests/test_qtcore.py | 4 ++-- qtpy/tests/test_qtdatavisualization.py | 3 --- qtpy/tests/test_qtdbus.py | 12 ++++++++++++ qtpy/tests/test_qtdesigner.py | 4 ++-- qtpy/tests/test_qtmultimedia.py | 6 +++--- qtpy/tests/test_qtnetwork.py | 4 ++-- qtpy/tests/test_qtnetworkauth.py | 13 +++++++++++++ qtpy/tests/test_qtopenglwidgets.py | 9 +++++++++ qtpy/tests/test_qtremoteobjects.py | 13 +++++++++++++ qtpy/tests/test_qtsensors.py | 11 +++++++++++ qtpy/tests/test_qtserialport.py | 4 ++-- qtpy/tests/test_qtsvg.py | 4 ++-- qtpy/tests/test_qtwebenginecore.py | 9 +++++++++ qtpy/tests/test_qtwebenginequick.py | 10 ++++++++++ qtpy/tests/test_qtwebenginewidgets.py | 6 +++--- qtpy/tests/test_qtxml.py | 23 +++++++++++++++++++++++ qtpy/tests/test_qtxmlpatterns.py | 4 ++-- 53 files changed, 371 insertions(+), 55 deletions(-) create mode 100644 qtpy/QtDBus.py create mode 100644 qtpy/QtNetworkAuth.py create mode 100644 qtpy/QtOpenGLWidgets.py create mode 100644 qtpy/QtRemoteObjects.py create mode 100644 qtpy/QtSensors.py create mode 100644 qtpy/QtWebEngineCore.py create mode 100644 qtpy/QtWebEngineQuick.py create mode 100644 qtpy/QtXml.py create mode 100644 qtpy/tests/test_qtdbus.py create mode 100644 qtpy/tests/test_qtnetworkauth.py create mode 100644 qtpy/tests/test_qtopenglwidgets.py create mode 100644 qtpy/tests/test_qtremoteobjects.py create mode 100644 qtpy/tests/test_qtsensors.py create mode 100644 qtpy/tests/test_qtwebenginecore.py create mode 100644 qtpy/tests/test_qtwebenginequick.py create mode 100644 qtpy/tests/test_qtxml.py diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index cb9cc85c..57f1ef1c 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -7,10 +7,14 @@ """Provides Qt3DCore classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.Qt3DCore import * +elif PYQT6: + from PyQt6.Qt3DCore import * +elif PYSIDE6: + from PySide6.Qt3DCore.Qt3DCore import * elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DCore as __temp diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 86b2635a..4397caa1 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -7,10 +7,14 @@ """Provides Qt3DExtras classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.Qt3DExtras import * +elif PYQT6: + from PyQt6.Qt3DExtras import * +elif PYSIDE6: + from PySide6.Qt3DExtras.Qt3DExtras import * elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DExtras as __temp diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 27a4ff77..45ebf2ac 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -7,10 +7,14 @@ """Provides Qt3DInput classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.Qt3DInput import * +elif PYQT6: + from PyQt6.Qt3DInput import * +elif PYSIDE6: + from PySide6.Qt3DInput.Qt3DInput import * elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DInput as __temp diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index e1187da3..2a71d0af 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -7,10 +7,14 @@ """Provides Qt3DLogic classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.Qt3DLogic import * +elif PYQT6: + from PyQt6.Qt3DLogic import * +elif PYSIDE6: + from PySide6.Qt3DLogic.Qt3DLogic import * elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DLogic as __temp diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index 27610e84..4e9ada31 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -7,10 +7,14 @@ """Provides Qt3DRender classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.Qt3DRender import * +elif PYSIDE6: + from PySide6.Qt3DRender.Qt3DRender import * +elif PYQT6: + from PyQt6.Qt3DRender import * elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DRender as __temp diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index af309b13..5b897172 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -7,7 +7,7 @@ """Provides QtChart classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: try: @@ -15,6 +15,10 @@ except ImportError: raise PythonQtError('The QtChart module was not found. ' 'It needs to be installed separately for PyQt5.') +elif PYQT6: + from PyQt6 import QtCharts +elif PYSIDE6: + from PySide6 import QtCharts elif PYSIDE2: from PySide2.QtCharts import * else: diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py new file mode 100644 index 00000000..9499a9f7 --- /dev/null +++ b/qtpy/QtDBus.py @@ -0,0 +1,19 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtDBus classes and functions.""" + +# Local imports +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + from PyQt5.QtDBus import * +elif PYQT6: + from PyQt6.QtDBus import * +elif PYSIDE6: + from PySide6.QtDBus import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index a906f5ab..2b086bbe 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -8,10 +8,14 @@ Provides QtDesigner classes and functions. """ -from . import PYQT5, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtDesigner import * +elif PYQT6: + from PyQt6.QtDesigner import * +elif PYSIDE6: + from PySide6.QtDesigner import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index b53a40e6..2fcfdcc9 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -8,11 +8,15 @@ import warnings -from . import PYQT5, PYSIDE6, PYSIDE2 +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtHelp import * +elif PYQT6: + from PyQt6.QtHelp import * elif PYSIDE6: from PySide6.QtHelp import * elif PYSIDE2: from PySide2.QtHelp import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index 88cbdf10..683c3bb1 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -1,12 +1,16 @@ import warnings -from . import PYQT5 +from . import PYQT5, PYQT6 from . import PYSIDE2 from . import PYSIDE6 if PYQT5: from PyQt5.QtMultimedia import * +elif PYQT6: + from PyQt6.QtMultimedia import * elif PYSIDE6: from PySide6.QtMultimedia import * elif PYSIDE2: from PySide2.QtMultimedia import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtMultimediaWidgets.py b/qtpy/QtMultimediaWidgets.py index 328e166f..ee8db677 100644 --- a/qtpy/QtMultimediaWidgets.py +++ b/qtpy/QtMultimediaWidgets.py @@ -7,11 +7,15 @@ """Provides QtMultimediaWidgets classes and functions.""" # Local imports -from . import PYSIDE2, PYQT5, PythonQtError +from . import PYSIDE2, PYSIDE6, PYQT5, PYQT6, PythonQtError if PYQT5: from PyQt5.QtMultimediaWidgets import * +elif PYQT6: + from PyQt6.QtMultimediaWidgets import * elif PYSIDE2: from PySide2.QtMultimediaWidgets import * +elif PYSIDE6: + from PySide6.QtMultimediaWidgets import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index e2507d64..b053fc41 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -9,11 +9,13 @@ Provides QtNetwork classes and functions. """ -from . import PYQT5, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtNetwork import * +elif PYQT6: + from PyQt6.QtNetwork import * elif PYSIDE6: from PySide6.QtNetwork import * elif PYSIDE2: diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py new file mode 100644 index 00000000..1a377778 --- /dev/null +++ b/qtpy/QtNetworkAuth.py @@ -0,0 +1,19 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtNetworkAuth classes and functions.""" + +# Local imports +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT6: + from PyQt6.QtNetworkAuth import * +elif PYQT5: + from PyQt5.QtNetworkAuth import * +elif PYSIDE6: + from PySide6.QtNetworkAuth import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index fdcb5c71..458da27a 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -11,6 +11,8 @@ if PYQT5: from PyQt5.QtOpenGL import * +elif PYQT6: + from PyQt6.QtOpenGL import * elif PYSIDE6: from PySide6.QtOpenGL import * elif PYSIDE2: diff --git a/qtpy/QtOpenGLWidgets.py b/qtpy/QtOpenGLWidgets.py new file mode 100644 index 00000000..4df07110 --- /dev/null +++ b/qtpy/QtOpenGLWidgets.py @@ -0,0 +1,17 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtOpenGLWidgets classes and functions.""" + +# Local imports +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT6: + from PyQt6.QtOpenGLWidgets import * +elif PYSIDE6: + from PySide6.QtOpenGLWidgets import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 4e512e71..4f1b6d25 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -8,11 +8,13 @@ Provides QtPrintSupport classes and functions. """ -from . import PYQT5, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtPrintSupport import * +elif PYQT6: + from PyQt6.QtPrintSupport import * elif PYSIDE6: from PySide6.QtPrintSupport import * elif PYSIDE2: diff --git a/qtpy/QtQml.py b/qtpy/QtQml.py index 213706e9..ec6934a7 100644 --- a/qtpy/QtQml.py +++ b/qtpy/QtQml.py @@ -7,10 +7,12 @@ """Provides QtQml classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtQml import * +elif PYQT6: + from PyQt6.QtQml import * elif PYSIDE6: from PySide6.QtQml import * elif PYSIDE2: diff --git a/qtpy/QtQuick.py b/qtpy/QtQuick.py index b0697da1..10f6a41c 100644 --- a/qtpy/QtQuick.py +++ b/qtpy/QtQuick.py @@ -7,10 +7,12 @@ """Provides QtQuick classes and functions.""" # Local imports -from . import PYQT5, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtQuick import * +elif PYQT6: + from PyQt6.QtQuick import * elif PYSIDE6: from PySide6.QtQuick import * elif PYSIDE2: diff --git a/qtpy/QtQuickWidgets.py b/qtpy/QtQuickWidgets.py index fb534cff..dfadb2c0 100644 --- a/qtpy/QtQuickWidgets.py +++ b/qtpy/QtQuickWidgets.py @@ -7,10 +7,12 @@ """Provides QtQuickWidgets classes and functions.""" # Local imports -from . import PYQT5, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtQuickWidgets import * +elif PYQT6: + from PyQt6.QtQuickWidgets import * elif PYSIDE6: from PySide6.QtQuickWidgets import * elif PYSIDE2: diff --git a/qtpy/QtRemoteObjects.py b/qtpy/QtRemoteObjects.py new file mode 100644 index 00000000..d1515c57 --- /dev/null +++ b/qtpy/QtRemoteObjects.py @@ -0,0 +1,21 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtRemoteObjects classes and functions.""" + +# Local imports +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + from PyQt5.QtRemoteObjects import * +elif PYQT6: + from PyQt6.QtRemoteObjects import * +elif PYSIDE6: + from PySide6.QtRemoteObjects import * +elif PYSIDE2: + from PySide2.QtRemoteObjects import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtSensors.py b/qtpy/QtSensors.py new file mode 100644 index 00000000..96ac1b12 --- /dev/null +++ b/qtpy/QtSensors.py @@ -0,0 +1,21 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtSensors classes and functions.""" + +# Local imports +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + from PyQt5.QtSensors import * +elif PYQT6: + from PyQt6.QtSensors import * +elif PYSIDE6: + from PySide6.QtSensors import * +elif PYSIDE2: + from PySide2.QtSensors import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtSerialPort.py b/qtpy/QtSerialPort.py index 774005c7..426cb8f5 100644 --- a/qtpy/QtSerialPort.py +++ b/qtpy/QtSerialPort.py @@ -8,9 +8,15 @@ """Provides QtSerialPort classes and functions.""" # Local imports -from . import PYQT5, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtSerialPort import * +elif PYQT6: + from PyQt6.QtSerialPort import * +elif PYSIDE6: + from PySide6.QtSerialPort import * +elif PYSIDE2: + from PySide2.QtSerialPort import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 44f02884..4f5565d2 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -7,10 +7,12 @@ """Provides QtSql classes and functions.""" # Local imports -from . import PYQT5, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: from PyQt5.QtSql import * +elif PYQT6: + from PyQt6.QtSql import * elif PYSIDE6: from PySide6.QtSql import * elif PYSIDE2: diff --git a/qtpy/QtWebChannel.py b/qtpy/QtWebChannel.py index ad4fbea3..efd402c8 100644 --- a/qtpy/QtWebChannel.py +++ b/qtpy/QtWebChannel.py @@ -7,10 +7,12 @@ """Provides QtWebChannel classes and functions.""" # Local imports -from . import PYSIDE2, PYSIDE6, PYQT5, PythonQtError +from . import PYSIDE2, PYSIDE6, PYQT5, PYQT6, PythonQtError if PYQT5: from PyQt5.QtWebChannel import * +elif PYQT6: + from PyQt6.QtWebChannel import * elif PYSIDE6: from PySide6.QtWebChannel import * elif PYSIDE2: diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py new file mode 100644 index 00000000..591b14f2 --- /dev/null +++ b/qtpy/QtWebEngineCore.py @@ -0,0 +1,21 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtWebEngineCore classes and functions.""" + +# Local imports +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + from PyQt5.QtWebEngineCore import * +elif PYQT6: + from PyQt6.QtWebEngineCore import * +elif PYSIDE2: + from PySide2.QtWebEngineCore import * +elif PYSIDE6: + from PySide6.QtWebEngineCore import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebEngineQuick.py b/qtpy/QtWebEngineQuick.py new file mode 100644 index 00000000..e5d9afdc --- /dev/null +++ b/qtpy/QtWebEngineQuick.py @@ -0,0 +1,17 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtWebEngineQuick classes and functions.""" + +# Local imports +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT6: + from PyQt6.QtWebEngineQuick import * +elif PYSIDE6: + from PySide6.QtWebEngineQuick import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index e246ab51..ead018d0 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -9,7 +9,7 @@ Provides QtWebEngineWidgets classes and functions. """ -from . import PYQT5, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError # To test if we are using WebEngine or WebKit @@ -28,6 +28,11 @@ from PyQt5.QtWebKitWidgets import QWebView as QWebEngineView from PyQt5.QtWebKit import QWebSettings as QWebEngineSettings WEBENGINE = False +elif PYQT6: + from PyQt6.QtWebEngineWidgets import * + from PyQt6.QtWebEngineCore import QWebEnginePage + from PyQt6.QtWebEngineCore import QWebEngineSettings + from PyQt6.QtWebEngineCore import QWebEngineProfile elif PYSIDE6: from PySide6.QtWebEngineWidgets import * from PySide6.QtWebEngineCore import QWebEnginePage diff --git a/qtpy/QtWebSockets.py b/qtpy/QtWebSockets.py index 9f5b3731..7b2118b8 100644 --- a/qtpy/QtWebSockets.py +++ b/qtpy/QtWebSockets.py @@ -7,10 +7,12 @@ """Provides QtWebSockets classes and functions.""" # Local imports -from . import PYSIDE2, PYQT5, PythonQtError +from . import PYSIDE2, PYQT5, PYQT6, PythonQtError if PYQT5: from PyQt5.QtWebSockets import * +elif PYQT6: + from PyQt6.QtWebSockets import * elif PYSIDE2: from PySide2.QtWebSockets import * else: diff --git a/qtpy/QtXml.py b/qtpy/QtXml.py new file mode 100644 index 00000000..3c160e60 --- /dev/null +++ b/qtpy/QtXml.py @@ -0,0 +1,21 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtXml classes and functions.""" + +# Local imports +from . import PYSIDE2, PYSIDE6, PYQT5, PYQT6, PythonQtError + +if PYQT5: + from PyQt5.QtXml import * +elif PYQT6: + from PyQt6.QtXml import * +elif PYSIDE6: + from PySide6.QtXml import * +elif PYSIDE2: + from PySide2.QtXml import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index 37fae6ed..42e9e3bd 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -2,7 +2,7 @@ import pytest -from qtpy import PYSIDE2, PYSIDE6, QT_VERSION +from qtpy import PYQT6, PYSIDE2, PYSIDE6, QT_VERSION from qtpy.QtWidgets import QApplication from qtpy.QtWidgets import QHeaderView from qtpy.QtCore import Qt @@ -17,7 +17,7 @@ def get_qapp(icon_path=None): @pytest.mark.skipif( - QT_VERSION.startswith('5.15') or PYSIDE6 or + QT_VERSION.startswith('5.15') or PYSIDE6 or PYQT6 or ((PYSIDE2) and sys.version_info.major == 3 and sys.version_info.minor == 8 and (sys.platform == 'darwin' or sys.platform.startswith('linux')) ), diff --git a/qtpy/tests/test_qt3dcore.py b/qtpy/tests/test_qt3dcore.py index e3ccac8d..b3fdada4 100644 --- a/qtpy/tests/test_qt3dcore.py +++ b/qtpy/tests/test_qt3dcore.py @@ -1,7 +1,7 @@ import pytest -from qtpy import PYQT5, PYSIDE2 +from qtpy import PYQT6 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +@pytest.mark.skipif(PYQT6, reason="Not complete in PyQt6") def test_qt3dcore(): """Test the qtpy.Qt3DCore namespace""" Qt3DCore = pytest.importorskip("qtpy.Qt3DCore") diff --git a/qtpy/tests/test_qt3dextras.py b/qtpy/tests/test_qt3dextras.py index 4a30fcdc..500ee2a6 100644 --- a/qtpy/tests/test_qt3dextras.py +++ b/qtpy/tests/test_qt3dextras.py @@ -1,7 +1,5 @@ import pytest -from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qt3dextras(): """Test the qtpy.Qt3DExtras namespace""" Qt3DExtras = pytest.importorskip("qtpy.Qt3DExtras") diff --git a/qtpy/tests/test_qt3dinput.py b/qtpy/tests/test_qt3dinput.py index 3f6118b0..73ec711a 100644 --- a/qtpy/tests/test_qt3dinput.py +++ b/qtpy/tests/test_qt3dinput.py @@ -1,7 +1,5 @@ import pytest -from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qt3dinput(): """Test the qtpy.Qt3DInput namespace""" Qt3DInput = pytest.importorskip("qtpy.Qt3DInput") diff --git a/qtpy/tests/test_qt3dlogic.py b/qtpy/tests/test_qt3dlogic.py index 8a01c33f..4c2df821 100644 --- a/qtpy/tests/test_qt3dlogic.py +++ b/qtpy/tests/test_qt3dlogic.py @@ -1,7 +1,5 @@ import pytest -from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qt3dlogic(): """Test the qtpy.Qt3DLogic namespace""" Qt3DLogic = pytest.importorskip("qtpy.Qt3DLogic") diff --git a/qtpy/tests/test_qt3drender.py b/qtpy/tests/test_qt3drender.py index 8f8813aa..e0c4e99c 100644 --- a/qtpy/tests/test_qt3drender.py +++ b/qtpy/tests/test_qt3drender.py @@ -1,7 +1,7 @@ import pytest -from qtpy import PYQT5, PYSIDE2 +from qtpy import PYQT6 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +@pytest.mark.skipif(PYQT6, reason="Not complete in PyQt6") def test_qt3drender(): """Test the qtpy.Qt3DRender namespace""" Qt3DRender = pytest.importorskip("qtpy.Qt3DRender") diff --git a/qtpy/tests/test_qtcharts.py b/qtpy/tests/test_qtcharts.py index 3b3f8a14..9d28459e 100644 --- a/qtpy/tests/test_qtcharts.py +++ b/qtpy/tests/test_qtcharts.py @@ -1,8 +1,8 @@ import pytest -from qtpy import PYSIDE2 +from qtpy import PYSIDE2, PYSIDE6 -@pytest.mark.skipif(not PYSIDE2, reason="Only available by default in PySide2") +@pytest.mark.skipif(not (PYSIDE2 or PYSIDE6), reason="Only available by default in PySide") def test_qtcharts(): """Test the qtpy.QtCharts namespace""" from qtpy import QtCharts diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 196f3de2..eff7a661 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,5 +1,5 @@ import pytest -from qtpy import PYQT5, PYSIDE2, QtCore +from qtpy import PYQT5, PYQT6, PYSIDE2, QtCore """Test QtCore.""" @@ -16,7 +16,7 @@ def test_DateTime_toPython(): assert QtCore.QDateTime.toPython is not None -@pytest.mark.skipif(PYSIDE2, +@pytest.mark.skipif(PYSIDE2 or PYQT6, reason="Doesn't seem to be present on PySide2") def test_QtCore_SignalInstance(): class ClassWithSignal(QtCore.QObject): diff --git a/qtpy/tests/test_qtdatavisualization.py b/qtpy/tests/test_qtdatavisualization.py index 4c9968fa..09f19f31 100644 --- a/qtpy/tests/test_qtdatavisualization.py +++ b/qtpy/tests/test_qtdatavisualization.py @@ -1,9 +1,6 @@ import pytest -from qtpy import PYQT5, PYSIDE2 - -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtdatavisualization(): """Test the qtpy.QtDataVisualization namespace""" # Using import skip here since with Python 3 you need to install another package diff --git a/qtpy/tests/test_qtdbus.py b/qtpy/tests/test_qtdbus.py new file mode 100644 index 00000000..2eaf4f5b --- /dev/null +++ b/qtpy/tests/test_qtdbus.py @@ -0,0 +1,12 @@ +import pytest +from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 + +@pytest.mark.skipif(PYSIDE2 or PYSIDE6, reason="Not available in PySide2, not on CI for PySide6") +def test_qtdbus(): + """Test the qtpy.QtDBus namespace""" + from qtpy import QtDBus + + assert QtDBus.QDBusAbstractAdaptor is not None + assert QtDBus.QDBusAbstractInterface is not None + assert QtDBus.QDBusArgument is not None + assert QtDBus.QDBusConnection is not None diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index 4e96b563..254de11f 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -1,7 +1,7 @@ import pytest -from qtpy import PYSIDE6, PYSIDE2 +from qtpy import PYSIDE2 -@pytest.mark.skipif((PYSIDE6 or PYSIDE2), reason="QtDesigner is not avalaible in PySide2/PySide6") +@pytest.mark.skipif(PYSIDE2, reason="QtDesigner is not avalaible in PySide2") def test_qtdesigner(): from qtpy import QtDesigner """Test the qtpy.QtDesigner namespace""" diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 369ca19e..6558b2ba 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -2,10 +2,10 @@ import pytest -from qtpy import PYSIDE6 +from qtpy import PYSIDE6, PYQT6 @pytest.mark.skipif( - sys.platform.startswith('linux') and PYSIDE6, + sys.platform.startswith('linux') and (PYSIDE6 or PYQT6), reason="Needs to setup GStreamer on Linux") def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" @@ -14,7 +14,7 @@ def test_qtmultimedia(): assert QtMultimedia.QAudio is not None assert QtMultimedia.QAudioInput is not None - if not PYSIDE6: + if not (PYSIDE6 or PYQT6): assert QtMultimedia.QAbstractVideoBuffer is not None assert QtMultimedia.QAudioDeviceInfo is not None assert QtMultimedia.QSound is not None diff --git a/qtpy/tests/test_qtnetwork.py b/qtpy/tests/test_qtnetwork.py index 54b3b17a..eb8ec387 100644 --- a/qtpy/tests/test_qtnetwork.py +++ b/qtpy/tests/test_qtnetwork.py @@ -1,5 +1,5 @@ import pytest -from qtpy import PYSIDE2, PYSIDE6, QtNetwork +from qtpy import PYSIDE2, PYSIDE6, PYQT6, QtNetwork def test_qtnetwork(): @@ -15,7 +15,7 @@ def test_qtnetwork(): assert QtNetwork.QNetworkDiskCache is not None assert QtNetwork.QNetworkReply is not None assert QtNetwork.QNetworkRequest is not None - if not PYSIDE6: + if not (PYSIDE6 or PYQT6): assert QtNetwork.QNetworkConfigurationManager is not None assert QtNetwork.QNetworkConfiguration is not None assert QtNetwork.QNetworkSession is not None diff --git a/qtpy/tests/test_qtnetworkauth.py b/qtpy/tests/test_qtnetworkauth.py new file mode 100644 index 00000000..cea1aa78 --- /dev/null +++ b/qtpy/tests/test_qtnetworkauth.py @@ -0,0 +1,13 @@ +import pytest +from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6 + +@pytest.mark.skipif(PYSIDE2 or PYQT5, reason="Not available in CI") +def test_qtnetworkauth(): + """Test the qtpy.QtNetworkAuth namespace""" + from qtpy import QtNetworkAuth + assert QtNetworkAuth.QAbstractOAuth is not None + assert QtNetworkAuth.QAbstractOAuth2 is not None + assert QtNetworkAuth.QAbstractOAuthReplyHandler is not None + assert QtNetworkAuth.QOAuth1 is not None + assert QtNetworkAuth.QOAuth1Signature is not None + assert QtNetworkAuth.QOAuth2AuthorizationCodeFlow is not None diff --git a/qtpy/tests/test_qtopenglwidgets.py b/qtpy/tests/test_qtopenglwidgets.py new file mode 100644 index 00000000..3085c078 --- /dev/null +++ b/qtpy/tests/test_qtopenglwidgets.py @@ -0,0 +1,9 @@ +import pytest +from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 + +@pytest.mark.skipif(PYSIDE2 or PYQT5, reason="Not available in PySide2/PyQt5") +def test_qtopenglwidgets(): + """Test the qtpy.QtOpenGLWidgets namespace""" + from qtpy import QtOpenGLWidgets + + assert QtOpenGLWidgets.QOpenGLWidget is not None diff --git a/qtpy/tests/test_qtremoteobjects.py b/qtpy/tests/test_qtremoteobjects.py new file mode 100644 index 00000000..c7a1d10b --- /dev/null +++ b/qtpy/tests/test_qtremoteobjects.py @@ -0,0 +1,13 @@ +import pytest +from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 + +@pytest.mark.skipif(True, reason="Not available in CI") +def test_qtremoteobjects(): + """Test the qtpy.QtRemoteObjects namespace""" + from qtpy import QtRemoteObjects + + assert QtRemoteObjects.QRemoteObjectAbstractPersistedStore is not None + assert QtRemoteObjects.QRemoteObjectDynamicReplica is not None + assert QtRemoteObjects.QRemoteObjectHost is not None + assert QtRemoteObjects.QRemoteObjectHostBase is not None + assert QtRemoteObjects.QRemoteObjectNode is not None diff --git a/qtpy/tests/test_qtsensors.py b/qtpy/tests/test_qtsensors.py new file mode 100644 index 00000000..b0d1f21f --- /dev/null +++ b/qtpy/tests/test_qtsensors.py @@ -0,0 +1,11 @@ +import pytest +from qtpy import PYSIDE6, PYQT6 + +def test_qtsensors(): + """Test the qtpy.QtSensors namespace""" + from qtpy import QtSensors + + assert QtSensors.QAccelerometer is not None + assert QtSensors.QAccelerometerFilter is not None + assert QtSensors.QAccelerometerReading is not None + diff --git a/qtpy/tests/test_qtserialport.py b/qtpy/tests/test_qtserialport.py index 0d740b2e..cb913427 100644 --- a/qtpy/tests/test_qtserialport.py +++ b/qtpy/tests/test_qtserialport.py @@ -1,7 +1,7 @@ import pytest -from qtpy import PYQT5 +from qtpy import PYSIDE2 -@pytest.mark.skipif(not PYQT5, reason="Only available in Qt5 bindings, but still not in PySide2") +@pytest.mark.skipif(PYSIDE2, reason="Not available in CI") def test_qtserialport(): """Test the qtpy.QtSerialPort namespace""" from qtpy import QtSerialPort diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py index 56d962b1..1eef81e2 100644 --- a/qtpy/tests/test_qtsvg.py +++ b/qtpy/tests/test_qtsvg.py @@ -1,11 +1,11 @@ import pytest -from qtpy import PYSIDE6 +from qtpy import PYSIDE6, PYQT6 def test_qtsvg(): """Test the qtpy.QtSvg namespace""" from qtpy import QtSvg - if not PYSIDE6: + if not (PYSIDE6 or PYQT6): assert QtSvg.QGraphicsSvgItem is not None assert QtSvg.QSvgWidget is not None assert QtSvg.QSvgGenerator is not None diff --git a/qtpy/tests/test_qtwebenginecore.py b/qtpy/tests/test_qtwebenginecore.py new file mode 100644 index 00000000..591b0763 --- /dev/null +++ b/qtpy/tests/test_qtwebenginecore.py @@ -0,0 +1,9 @@ +import pytest +from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6 + +def test_qtwebenginecore(): + """Test the qtpy.QtWebEngineCore namespace""" + from qtpy import QtWebEngineCore + + assert QtWebEngineCore.QWebEngineHttpRequest is not None + diff --git a/qtpy/tests/test_qtwebenginequick.py b/qtpy/tests/test_qtwebenginequick.py new file mode 100644 index 00000000..26074760 --- /dev/null +++ b/qtpy/tests/test_qtwebenginequick.py @@ -0,0 +1,10 @@ +import pytest +from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6 + +@pytest.mark.skipif(PYQT5 or PYSIDE2, reason="Only available in Qt6 bindings") +def test_qtwebenginequick(): + """Test the qtpy.QtWebEngineQuick namespace""" + from qtpy import QtWebEngineQuick + assert QtWebEngineQuick.QtWebEngineQuick is not None + assert QtWebEngineQuick.QQuickWebEngineProfile is not None + diff --git a/qtpy/tests/test_qtwebenginewidgets.py b/qtpy/tests/test_qtwebenginewidgets.py index 276f5ff0..513b6c59 100644 --- a/qtpy/tests/test_qtwebenginewidgets.py +++ b/qtpy/tests/test_qtwebenginewidgets.py @@ -1,9 +1,9 @@ import pytest -from qtpy import PYSIDE6 +from qtpy import PYSIDE6, PYQT6 -@pytest.mark.skipif(PYSIDE6, reason="Only available in Qt<6 bindings") +@pytest.mark.skipif(PYSIDE6 or PYQT6, reason="Only available in Qt<6,>=6.2 bindings") def test_qtwebenginewidgets(): - """Test the qtpy.QtWebSockets namespace""" + """Test the qtpy.QtWebEngineWidget namespace""" QtWebEngineWidgets = pytest.importorskip("qtpy.QtWebEngineWidgets") diff --git a/qtpy/tests/test_qtxml.py b/qtpy/tests/test_qtxml.py new file mode 100644 index 00000000..27d4f235 --- /dev/null +++ b/qtpy/tests/test_qtxml.py @@ -0,0 +1,23 @@ +import pytest + +def test_qtxml(): + """Test the qtpy.QtXml namespace""" + from qtpy import QtXml + + assert QtXml.QDomAttr is not None + assert QtXml.QDomCDATASection is not None + assert QtXml.QDomCharacterData is not None + assert QtXml.QDomComment is not None + assert QtXml.QDomDocument is not None + assert QtXml.QDomDocumentFragment is not None + assert QtXml.QDomDocumentType is not None + assert QtXml.QDomElement is not None + assert QtXml.QDomEntity is not None + assert QtXml.QDomEntityReference is not None + assert QtXml.QDomImplementation is not None + assert QtXml.QDomNamedNodeMap is not None + assert QtXml.QDomNode is not None + assert QtXml.QDomNodeList is not None + assert QtXml.QDomNotation is not None + assert QtXml.QDomProcessingInstruction is not None + assert QtXml.QDomText is not None diff --git a/qtpy/tests/test_qtxmlpatterns.py b/qtpy/tests/test_qtxmlpatterns.py index 4f8da789..a2984879 100644 --- a/qtpy/tests/test_qtxmlpatterns.py +++ b/qtpy/tests/test_qtxmlpatterns.py @@ -1,7 +1,7 @@ import pytest -from qtpy import PYSIDE2, PYSIDE6 +from qtpy import PYSIDE2, PYSIDE6, PYQT6 -@pytest.mark.skipif(PYSIDE6, reason="not available with qt 6.0") +@pytest.mark.skipif((PYSIDE6 or PYQT6), reason="not available with qt 6.0") def test_qtxmlpatterns(): """Test the qtpy.QtXmlPatterns namespace""" from qtpy import QtXmlPatterns From ec048350c130c36b58750aaf7fe369981f010387 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Thu, 21 Oct 2021 10:46:19 -0500 Subject: [PATCH 263/703] Apply missing suggestions from code review --- qtpy/tests/test_qtcore.py | 2 +- qtpy/tests/test_qtremoteobjects.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index eff7a661..e9b22ae7 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -17,7 +17,7 @@ def test_DateTime_toPython(): @pytest.mark.skipif(PYSIDE2 or PYQT6, - reason="Doesn't seem to be present on PySide2") + reason="Doesn't seem to be present on PySide2 and PyQt6") def test_QtCore_SignalInstance(): class ClassWithSignal(QtCore.QObject): signal = QtCore.Signal() diff --git a/qtpy/tests/test_qtremoteobjects.py b/qtpy/tests/test_qtremoteobjects.py index c7a1d10b..88234b9d 100644 --- a/qtpy/tests/test_qtremoteobjects.py +++ b/qtpy/tests/test_qtremoteobjects.py @@ -1,10 +1,9 @@ import pytest from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 -@pytest.mark.skipif(True, reason="Not available in CI") def test_qtremoteobjects(): """Test the qtpy.QtRemoteObjects namespace""" - from qtpy import QtRemoteObjects + QtRemoteObjects = pytest.importorskip("qtpy.QtRemoteObjects") assert QtRemoteObjects.QRemoteObjectAbstractPersistedStore is not None assert QtRemoteObjects.QRemoteObjectDynamicReplica is not None From bc9bddb109a177a5552d1761a9d0bdb57c55f15f Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 22 Oct 2021 10:40:49 -0500 Subject: [PATCH 264/703] QtCharts: Reorganize module import QtNetworkAuth: Add missing skip for QtNetworkAuth test with PyQt6 --- qtpy/QtCharts.py | 16 ++++++++++++---- qtpy/tests/test_qtcharts.py | 3 ++- qtpy/tests/test_qtnetworkauth.py | 6 ++++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 5b897172..94ae52f5 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -11,15 +11,23 @@ if PYQT5: try: - from PyQt5 import QtChart as QtCharts + from PyQt5.QtChart import * except ImportError: raise PythonQtError('The QtChart module was not found. ' 'It needs to be installed separately for PyQt5.') elif PYQT6: - from PyQt6 import QtCharts + try: + from PyQt6.QtCharts import * + except ImportError: + raise PythonQtError('The QtCharts module was not found. ' + 'It needs to be installed separately for PyQt6.') elif PYSIDE6: - from PySide6 import QtCharts + from PySide6.QtCharts import * elif PYSIDE2: - from PySide2.QtCharts import * + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide2.QtCharts as __temp + import inspect + for __name in inspect.getmembers(__temp.QtCharts): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtcharts.py b/qtpy/tests/test_qtcharts.py index 9d28459e..1c7ed425 100644 --- a/qtpy/tests/test_qtcharts.py +++ b/qtpy/tests/test_qtcharts.py @@ -1,4 +1,5 @@ import pytest + from qtpy import PYSIDE2, PYSIDE6 @@ -6,4 +7,4 @@ def test_qtcharts(): """Test the qtpy.QtCharts namespace""" from qtpy import QtCharts - assert QtCharts.QtCharts.QChart is not None + assert QtCharts.QChart is not None diff --git a/qtpy/tests/test_qtnetworkauth.py b/qtpy/tests/test_qtnetworkauth.py index cea1aa78..e41a5db1 100644 --- a/qtpy/tests/test_qtnetworkauth.py +++ b/qtpy/tests/test_qtnetworkauth.py @@ -1,7 +1,9 @@ import pytest -from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6 -@pytest.mark.skipif(PYSIDE2 or PYQT5, reason="Not available in CI") +from qtpy import PYQT5, PYQT6, PYSIDE2 + +@pytest.mark.skipif(PYQT5 or PYQT6 or PYSIDE2, + reason="Not available by default in PyQt. Not available for PySide2") def test_qtnetworkauth(): """Test the qtpy.QtNetworkAuth namespace""" from qtpy import QtNetworkAuth From cd6e3729f2ef48eb7b5843b8d7e3a50ce162d8c3 Mon Sep 17 00:00:00 2001 From: kumattau Date: Sat, 23 Oct 2021 00:54:06 +0900 Subject: [PATCH 265/703] Add QOpenGLWidget class to QtWidgets module (PyQt6) --- qtpy/QtWidgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 912d6b7b..407f48fb 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -15,6 +15,7 @@ if PYQT6: from PyQt6.QtWidgets import * from PyQt6.QtGui import QAction, QActionGroup + from PyQt6.QtOpenGLWidgets import QOpenGLWidget elif PYQT5: from PyQt5.QtWidgets import * elif PYSIDE6: From 12840324f6d265160a25be25435bdd112557da20 Mon Sep 17 00:00:00 2001 From: kumattau Date: Sat, 23 Oct 2021 00:54:06 +0900 Subject: [PATCH 266/703] Add QShortcut class to QtWidgets module (PyQt6) --- qtpy/QtWidgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 407f48fb..56f745c6 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -14,7 +14,7 @@ if PYQT6: from PyQt6.QtWidgets import * - from PyQt6.QtGui import QAction, QActionGroup + from PyQt6.QtGui import QAction, QActionGroup, QShortcut from PyQt6.QtOpenGLWidgets import QOpenGLWidget elif PYQT5: from PyQt5.QtWidgets import * From 1ca2a5a74a2402f369af7e97c9d3f715f04b3878 Mon Sep 17 00:00:00 2001 From: kumattau Date: Sat, 23 Oct 2021 00:54:06 +0900 Subject: [PATCH 267/703] Add exec_ to PyQt6 classes which have exec --- qtpy/QtCore.py | 4 ++++ qtpy/QtGui.py | 2 ++ qtpy/QtPrintSupport.py | 2 ++ qtpy/QtSql.py | 3 +++ qtpy/QtWidgets.py | 3 +++ 5 files changed, 14 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 6f75e397..5b7491a5 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -16,6 +16,10 @@ from PyQt6.QtCore import pyqtSignal as Signal from PyQt6.QtCore import QT_VERSION_STR as __version__ + QCoreApplication.exec_ = QCoreApplication.exec + QEventLoop.exec_ = QEventLoop.exec + QThread.exec_ = QThread.exec + elif PYQT5: from PyQt5.QtCore import * from PyQt5.QtCore import pyqtSignal as Signal diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index c909d1a3..0a7eed51 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -15,6 +15,8 @@ if PYQT6: from PyQt6.QtGui import * + QDrag.exec_ = QDrag.exec + QGuiApplication.exec_ = QGuiApplication.exec elif PYQT5: from PyQt5.QtGui import * elif PYSIDE2: diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 4f1b6d25..49307030 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -15,6 +15,8 @@ from PyQt5.QtPrintSupport import * elif PYQT6: from PyQt6.QtPrintSupport import * + QPageSetupDialog.exec_ = QPageSetupDialog.exec + QPrintDialog.exec_ = QPrintDialog.exec elif PYSIDE6: from PySide6.QtPrintSupport import * elif PYSIDE2: diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 4f5565d2..e567ba3b 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -13,6 +13,9 @@ from PyQt5.QtSql import * elif PYQT6: from PyQt6.QtSql import * + QSqlDatabase.exec_ = QSqlDatabase.exec + QSqlQuery.exec_ = QSqlQuery.exec + QSqlResult.exec_ = QSqlResult.exec elif PYSIDE6: from PySide6.QtSql import * elif PYSIDE2: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 56f745c6..d65567dc 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -16,6 +16,9 @@ from PyQt6.QtWidgets import * from PyQt6.QtGui import QAction, QActionGroup, QShortcut from PyQt6.QtOpenGLWidgets import QOpenGLWidget + QApplication.exec_ = QApplication.exec + QDialog.exec_ = QDialog.exec + QMenu.exec_ = QMenu.exec elif PYQT5: from PyQt5.QtWidgets import * elif PYSIDE6: From cd77e1440065f64acaf6ab7d4b2e60469dff6cf2 Mon Sep 17 00:00:00 2001 From: kumattau Date: Sat, 23 Oct 2021 00:54:06 +0900 Subject: [PATCH 268/703] Add print_ to PyQt6 classes which have print --- qtpy/QtGui.py | 1 + qtpy/QtPrintSupport.py | 1 + qtpy/QtWidgets.py | 2 ++ 3 files changed, 4 insertions(+) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 0a7eed51..da8369e6 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -17,6 +17,7 @@ from PyQt6.QtGui import * QDrag.exec_ = QDrag.exec QGuiApplication.exec_ = QGuiApplication.exec + QTextDocument.print_ = QTextDocument.print elif PYQT5: from PyQt5.QtGui import * elif PYSIDE2: diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 49307030..53253f9e 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -17,6 +17,7 @@ from PyQt6.QtPrintSupport import * QPageSetupDialog.exec_ = QPageSetupDialog.exec QPrintDialog.exec_ = QPrintDialog.exec + QPrintPreviewWidget.print_ = QPrintPreviewWidget.print elif PYSIDE6: from PySide6.QtPrintSupport import * elif PYSIDE2: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index d65567dc..98c5a956 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -19,6 +19,8 @@ QApplication.exec_ = QApplication.exec QDialog.exec_ = QDialog.exec QMenu.exec_ = QMenu.exec + QTextEdit.print_ = QTextEdit.print + QPlainTextEdit.print_ = QPlainTextEdit.print elif PYQT5: from PyQt5.QtWidgets import * elif PYSIDE6: From acf7318a172313e594a0ce64e649ec401733db35 Mon Sep 17 00:00:00 2001 From: kumattau Date: Sat, 23 Oct 2021 16:59:06 +0900 Subject: [PATCH 269/703] [PyQt6] Add missing tabStopWidth/setTabStopWidth same as PySide6 --- qtpy/QtWidgets.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 98c5a956..d1f7b730 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -16,6 +16,10 @@ from PyQt6.QtWidgets import * from PyQt6.QtGui import QAction, QActionGroup, QShortcut from PyQt6.QtOpenGLWidgets import QOpenGLWidget + QTextEdit.setTabStopWidth = QTextEdit.setTabStopDistance + QTextEdit.tabStopWidth = QTextEdit.tabStopDistance + QPlainTextEdit.setTabStopWidth = QPlainTextEdit.setTabStopDistance + QPlainTextEdit.tabStopWidth = QPlainTextEdit.tabStopDistance QApplication.exec_ = QApplication.exec QDialog.exec_ = QDialog.exec QMenu.exec_ = QMenu.exec From ad4ddd3210940f0bfe43a9895670ef2e1091f5e0 Mon Sep 17 00:00:00 2001 From: kumattau Date: Mon, 25 Oct 2021 22:14:18 +0900 Subject: [PATCH 270/703] Remove accidentally-duplicated lines of code in QtCore --- qtpy/QtCore.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 5b7491a5..ac6e78b9 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -43,8 +43,6 @@ # obsolete in qt6 Qt.BackgroundColorRole = Qt.BackgroundRole Qt.TextColorRole = Qt.ForegroundRole - Qt.BackgroundColorRole = Qt.BackgroundRole - Qt.TextColorRole = Qt.ForegroundRole Qt.MidButton = Qt.MiddleButton elif PYSIDE2: From 340d4be14a8e06167358e392e2894338a7b979bc Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 22 Oct 2021 22:59:43 -0500 Subject: [PATCH 271/703] Unify and modernize CI test scripts and runner jobs --- .github/workflows/ci.yml | 167 +++++++++++++++--------------- .github/workflows/coverage.sh | 11 -- .github/workflows/test-pyqt5.sh | 47 --------- .github/workflows/test-pyside2.sh | 23 ---- .github/workflows/test-pyside6.sh | 20 ---- .github/workflows/test.sh | 47 +++++++++ 6 files changed, 131 insertions(+), 184 deletions(-) delete mode 100755 .github/workflows/coverage.sh delete mode 100755 .github/workflows/test-pyqt5.sh delete mode 100755 .github/workflows/test-pyside2.sh delete mode 100755 .github/workflows/test-pyside6.sh create mode 100755 .github/workflows/test.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 358ab04a..82c5936b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,31 +1,86 @@ +# Run the project's test suite name: Tests on: - # This avoids having duplicate builds for a pull request push: branches: - master + - main + - '*.x' pull_request: branches: - master + - main + - '*.x' jobs: - linux: - name: Linux Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} - runs-on: ubuntu-latest + test: + name: Test ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} + runs-on: ${{ matrix.os }} + defaults: + run: + shell: ${{ matrix.special-invocation }}bash -l {0} env: - CI: True - PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} - USE_CONDA: ${{ matrix.USE_CONDA }} + CI: 'True' + PYTHON_VERSION: ${{ matrix.python-version }} + USE_CONDA: ${{ matrix.use-conda }} + PYQT5_VERSION: ${{ matrix.pyqt5-version }} + PYSIDE2_VERSION: ${{ matrix.pyside2-version }} + PYSIDE6_VERSION: ${{ matrix.pyside6-version }} strategy: fail-fast: false matrix: - PYTHON_VERSION: ['3.6', '3.8'] - USE_CONDA: ['Yes', 'No'] + include: + - os: ubuntu-latest + python-version: '3.8' + use-conda: 'Yes' + special-invocation: 'xvfb-run --auto-servernum ' + coverage: 'True' + - os: ubuntu-latest + python-version: '3.8' + use-conda: 'No' + special-invocation: 'xvfb-run --auto-servernum ' + - os: ubuntu-latest + python-version: '3.6' + use-conda: 'Yes' + special-invocation: 'xvfb-run --auto-servernum ' + - os: ubuntu-latest + python-version: '3.6' + use-conda: 'No' + special-invocation: 'xvfb-run --auto-servernum ' + pyside2-version: 5.12.0 # 5.12.1-5.12.6 core dumps on patch test + - os: windows-latest + python-version: '3.8' + use-conda: 'Yes' + - os: windows-latest + python-version: '3.8' + use-conda: 'No' + skip-pyside2: true # No wheel on Windows and Python 3.8 + - os: windows-latest + python-version: '3.6' + use-conda: 'Yes' + pyqt5-version: '5.9' + - os: windows-latest + python-version: '3.6' + use-conda: 'No' + - os: macos-latest + python-version: '3.8' + use-conda: 'Yes' + - os: macos-latest + python-version: '3.8' + use-conda: 'No' + - os: macos-latest + python-version: '3.6' + use-conda: 'Yes' + - os: macos-latest + python-version: '3.6' + use-conda: 'No' steps: - name: Checkout branch uses: actions/checkout@v2 - - name: Install System Packages + - name: Install Linux system packages + if: contains(matrix.os, 'ubuntu') + shell: bash run: | sudo apt update sudo apt install libpulse-dev libegl1-mesa libopengl0 @@ -33,82 +88,28 @@ jobs: uses: conda-incubator/setup-miniconda@v2 with: activate-environment: '' + auto-activate-base: true auto-update-conda: true - auto-activate-base: false - - name: Test PyQt5 + channels: conda-forge + - name: Print Conda info shell: bash -l {0} run: | - eval "$(conda shell.bash hook)" - xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyqt5.sh - - name: Test PySide2 - shell: bash -l {0} - run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside2.sh - - name: Test PySide6 - shell: bash -l {0} - run: xvfb-run --auto-servernum bash -l ./.github/workflows/test-pyside6.sh - - name: Upload coverage - if: matrix.PYTHON_VERSION == '3.8' - shell: bash -l {0} - run: bash -l ./.github/workflows/coverage.sh - - macos: - name: Mac Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} - runs-on: macos-latest - env: - CI: True - PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} - USE_CONDA: ${{ matrix.USE_CONDA }} - strategy: - fail-fast: false - matrix: - PYTHON_VERSION: ['3.6', '3.8'] - USE_CONDA: ['Yes', 'No'] - steps: - - name: Checkout branch - uses: actions/checkout@v2 - - name: Install Conda - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: '' - auto-update-conda: true - auto-activate-base: false - - name: Test PyQt5 - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyqt5.sh - - name: Test PySide2 - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyside2.sh - - name: Test PySide6 - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyside6.sh - - windows: - name: Windows Py${{ matrix.PYTHON_VERSION }} conda=${{ matrix.USE_CONDA }} - runs-on: windows-latest - env: - CI: True - PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }} - USE_CONDA: ${{ matrix.USE_CONDA }} - strategy: - fail-fast: false - matrix: - PYTHON_VERSION: ['3.6', '3.8'] - USE_CONDA: ['Yes', 'No'] - steps: - - name: Checkout branch - uses: actions/checkout@v2 - - name: Install Conda - uses: conda-incubator/setup-miniconda@v2 - with: - activate-environment: '' - auto-update-conda: true - auto-activate-base: true + conda info + conda list - name: Test PyQt5 - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyqt5.sh + if: (! matrix.skip-pyqt5) + run: ./.github/workflows/test.sh pyqt5 - name: Test PySide2 - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyside2.sh + if: always() && (! ((matrix.skip-pyside2) || (matrix.use-conda == 'Yes'))) # No conda packages for Pyside2? + run: ./.github/workflows/test.sh pyside2 - name: Test PySide6 - shell: bash -l {0} - run: bash -l ./.github/workflows/test-pyside6.sh + if: always() && (! ((matrix.skip-pyside6) || (matrix.use-conda == 'Yes'))) # No conda packages yet for Pyside6 + run: ./.github/workflows/test.sh pyside6 + - name: Upload coverage data to coveralls.io + if: matrix.coverage + shell: bash + env: + COVERALLS_REPO_TOKEN: 'xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5' + run: | + python3 -m pip install --upgrade coveralls + python3 -m coveralls --service=github-actions || true diff --git a/.github/workflows/coverage.sh b/.github/workflows/coverage.sh deleted file mode 100755 index f68ecb3e..00000000 --- a/.github/workflows/coverage.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -eval "$(conda shell.bash hook)" -conda deactivate -conda activate test-pyqt5 - -export COVERALLS_REPO_TOKEN="xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5" -coveralls - -# Don't fail at this step -exit 0 diff --git a/.github/workflows/test-pyqt5.sh b/.github/workflows/test-pyqt5.sh deleted file mode 100755 index cdd68b26..00000000 --- a/.github/workflows/test-pyqt5.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash -ex - -# Create conda environment for this test -conda create -n test-pyqt5 -conda activate test-pyqt5 - -# Select build with QtMultimedia -if [ "$(uname)" == "Darwin" ]; then - - if [ "$PYTHON_VERSION" = "3.6" ]; then - export QT_VER=5.12 - else - export QT_VER=5.* - fi - -elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then - - if [ "$PYTHON_VERSION" = "3.6" ]; then - export QT_VER=5.12 - else - export QT_VER=5.* - fi - -else - - if [ "$PYTHON_VERSION" = "3.6" ]; then - export QT_VER=5.9 - else - export QT_VER=5.* - fi - -fi - -if [ "$USE_CONDA" = "Yes" ]; then - conda install -q coveralls pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge - conda install -q qt=$QT_VER pyqt=$QT_VER -c conda-forge -else - # We are getting segfaults in 5.10 - conda install -q coveralls pytest pytest-cov python="$PYTHON_VERSION" -c anaconda - pip install -q pyqt5 PyQtWebEngine -fi - -# Install package -python -m pip install -e . - -# Run tests -python qtpy/tests/runtests.py diff --git a/.github/workflows/test-pyside2.sh b/.github/workflows/test-pyside2.sh deleted file mode 100755 index 46198351..00000000 --- a/.github/workflows/test-pyside2.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -ex - -# Create conda environment for this test -conda create -n test-pyside2 -conda activate test-pyside2 - -if [ "$USE_CONDA" = "Yes" ]; then - # There are no conda packages for PySide2 - exit 0 -elif [ "$PYTHON_VERSION" != "3.6" ] && [ "$RUNNER_OS" = "Windows" ]; then - # There is no wheel for PySide 5.12 on Windows and Python 3.8 - exit 0 -else - # Simple solution to avoid failures with the Qt3D modules - conda install -q coveralls pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge - pip install -q pyside2==5.12 -fi - -# Install package -python -m pip install -e . - -# Run tests -python qtpy/tests/runtests.py diff --git a/.github/workflows/test-pyside6.sh b/.github/workflows/test-pyside6.sh deleted file mode 100755 index eb927888..00000000 --- a/.github/workflows/test-pyside6.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash -ex - -# Create conda environment for this test -conda create -n test-pyside6 -conda activate test-pyside6 - -if [ "$USE_CONDA" = "Yes" ]; then - # There are no conda packages for PySide6 - exit 0 -else - # Simple solution to avoid failures with the Qt3D modules - conda install -q coveralls pytest pytest-cov python="$PYTHON_VERSION" -c conda-forge - pip install -q pyside6==6.2.0 -fi - -# Install package -python -m pip install -e . - -# Run tests -python qtpy/tests/runtests.py diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh new file mode 100755 index 00000000..0be9b431 --- /dev/null +++ b/.github/workflows/test.sh @@ -0,0 +1,47 @@ +#!/bin/bash -ex + +# Activate conda properly +eval "$(conda shell.bash hook)" + +# Set conda channel +if [ "$USE_CONDA" = "No" ]; then + CONDA_CHANNEL_ARG="-c anaconda" +fi + +# Create and activate conda environment for this test +conda create -q -n test-${1} ${CONDA_CHANNEL_ARG} python=${PYTHON_VERSION} pytest pytest-cov +conda activate test-${1} + +if [ "$USE_CONDA" = "Yes" ]; then + + if [ "${1}" = "pyqt5" ]; then + conda install -q pyqt=${PYQT5_VERSION:-"5.12"} + else + exit 1 + fi + +else + + if [ "${1}" = "pyqt5" ]; then + pip install pyqt5==${PYQT5_VERSION:-"5.15"}.* PyQtWebEngine==${PYQT5_VERSION:-"5.15"}.* + elif [ "${1}" = "pyside2" ]; then + pip install pyside2==${PYSIDE2_VERSION:-"5.12"}.* + elif [ "${1}" = "pyside6" ]; then + pip install pyside6==${PYSIDE6_VERSION:-"6.2"}.* + else + exit 1 + fi + +fi + +# Install package +pip install -e . + +# Print environment information +conda list + +# Run tests +python qtpy/tests/runtests.py + +# Deactivate conda env after +conda deactivate From ed4c6eba5c5b368bb013d50dcf8c4d104dfaf77d Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sat, 23 Oct 2021 00:31:35 -0500 Subject: [PATCH 272/703] Use stricter Python and Pytest options & use pytest.ini not runtests.py --- .github/workflows/ci.yml | 2 ++ .github/workflows/test.sh | 4 ++-- pytest.ini | 11 +++++++++++ qtpy/tests/conftest.py | 11 +++-------- qtpy/tests/runtests.py | 25 ------------------------- 5 files changed, 18 insertions(+), 35 deletions(-) create mode 100644 pytest.ini delete mode 100755 qtpy/tests/runtests.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 82c5936b..59bb1d73 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,6 +63,7 @@ jobs: - os: windows-latest python-version: '3.6' use-conda: 'No' + pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on test collection - os: macos-latest python-version: '3.8' use-conda: 'Yes' @@ -75,6 +76,7 @@ jobs: - os: macos-latest python-version: '3.6' use-conda: 'No' + pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on test collection steps: - name: Checkout branch uses: actions/checkout@v2 diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 0be9b431..e62e80c3 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -35,13 +35,13 @@ else fi # Install package -pip install -e . +python -bb -X dev -W error -m pip install -e . # Print environment information conda list # Run tests -python qtpy/tests/runtests.py +python -I -bb -X dev -W error -m pytest qtpy # Deactivate conda env after conda deactivate diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 00000000..19e23c3b --- /dev/null +++ b/pytest.ini @@ -0,0 +1,11 @@ +[pytest] +addopts = --durations=10 -v -r a --color=yes --code-highlight=yes --strict-config --strict-markers --import-mode=importlib --maxfail 10 --cov=qtpy --cov-report=term-missing +empty_parameter_set_mark = fail_at_collect +filterwarnings = + error +log_auto_indent = True +log_level = INFO +minversion = 6.0 +testpaths = + qtpy/tests +xfail_strict = True diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index 4cbeb8dd..9322f263 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -2,9 +2,7 @@ def pytest_configure(config): - """ - This function gets run by py.test at the very start - """ + """Configure the test environment.""" if 'USE_QT_API' in os.environ: os.environ['QT_API'] = os.environ['USE_QT_API'].lower() @@ -15,10 +13,7 @@ def pytest_configure(config): def pytest_report_header(config): - """ - This function is used by py.test to insert a customized header into the - test report. - """ + """Insert a customized header into the test report.""" versions = os.linesep versions += 'PyQt6: ' @@ -67,5 +62,5 @@ def pytest_report_header(config): versions += 'unknown version' versions += os.linesep - + return versions diff --git a/qtpy/tests/runtests.py b/qtpy/tests/runtests.py deleted file mode 100755 index ba43b9e5..00000000 --- a/qtpy/tests/runtests.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# Copyright © 2015- The Spyder Development Team -# -# Licensed under the terms of the MIT License -# ---------------------------------------------------------------------------- - -"""File for running tests programmatically.""" - -# Standard library imports -import sys - -# Third party imports -import qtpy -import pytest - - -def main(): - """Run pytest tests.""" - errno = pytest.main(['-x', 'qtpy', '-v', '-rw', '--durations=10', - '--cov=qtpy', '--cov-report=term-missing']) - sys.exit(errno) - -if __name__ == '__main__': - main() From 7824fd3a330ba9516008e0a25664e350bad709b3 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 25 Oct 2021 16:59:58 -0500 Subject: [PATCH 273/703] Build, install & test from wheel to catch missing files & packaging bugs --- .github/workflows/test.sh | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index e62e80c3..6cabed10 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -34,14 +34,17 @@ else fi -# Install package -python -bb -X dev -W error -m pip install -e . +# Build wheel of package +git clean -xdf +python -bb -X dev -W error setup.py sdist bdist_wheel + +# Install package from build wheel +echo dist/*.whl | xargs -I % python -bb -X dev -W error -m pip install --upgrade % # Print environment information conda list # Run tests -python -I -bb -X dev -W error -m pytest qtpy - -# Deactivate conda env after -conda deactivate +cd qtpy +python -I -bb -X dev -W error -m pytest --cov-config ../.coveragerc +cd .. From 3d4f93db155155f1651969d8fd5ebfae078b9f96 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 25 Oct 2021 17:14:59 -0500 Subject: [PATCH 274/703] Test Pyside2 5.15 on Windows no-conda and try testing Pyside2 on conda --- .github/workflows/ci.yml | 11 +++++++---- .github/workflows/test.sh | 4 +++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59bb1d73..1bee5b78 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,9 @@ jobs: PYQT5_VERSION: ${{ matrix.pyqt5-version }} PYSIDE2_VERSION: ${{ matrix.pyside2-version }} PYSIDE6_VERSION: ${{ matrix.pyside6-version }} + PYQT5_QT_VERSION: ${{ matrix.pyqt5-qt-version }} + PYSIDE2_QT_VERSION: ${{ matrix.pyside2-qt-version }} + PYSIDE6_QT_VERSION: ${{ matrix.pyside6-qt-version }} strategy: fail-fast: false matrix: @@ -55,11 +58,11 @@ jobs: - os: windows-latest python-version: '3.8' use-conda: 'No' - skip-pyside2: true # No wheel on Windows and Python 3.8 + pyside2-version: 5.15 # No 5.12 wheel on Windows and Python 3.8 - os: windows-latest python-version: '3.6' use-conda: 'Yes' - pyqt5-version: '5.9' + pyqt5-qt-version: '5.9' - os: windows-latest python-version: '3.6' use-conda: 'No' @@ -102,10 +105,10 @@ jobs: if: (! matrix.skip-pyqt5) run: ./.github/workflows/test.sh pyqt5 - name: Test PySide2 - if: always() && (! ((matrix.skip-pyside2) || (matrix.use-conda == 'Yes'))) # No conda packages for Pyside2? + if: always() && (! (matrix.skip-pyside2)) run: ./.github/workflows/test.sh pyside2 - name: Test PySide6 - if: always() && (! ((matrix.skip-pyside6) || (matrix.use-conda == 'Yes'))) # No conda packages yet for Pyside6 + if: always() && (! ((matrix.skip-pyside6) || (matrix.use-conda == 'Yes'))) # No conda packages yet for Qt6/Pyside6 run: ./.github/workflows/test.sh pyside6 - name: Upload coverage data to coveralls.io if: matrix.coverage diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 6cabed10..ed1f4d5e 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -15,7 +15,9 @@ conda activate test-${1} if [ "$USE_CONDA" = "Yes" ]; then if [ "${1}" = "pyqt5" ]; then - conda install -q pyqt=${PYQT5_VERSION:-"5.12"} + conda install -q qt=${PYQT5_QT_VERSION:-"5.12"} pyqt=${PYQT5_VERSION:-"5"} + elif [ "${1}" = "pyside2" ]; then + conda install -q qt=${PYSIDE2_QT_VERSION:-"5.12"} pyside2=${PYSIDE2_VERSION:-"5"} else exit 1 fi From 4e42adf6ed15a8cac7eede79540dcf3bf89fd410 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 25 Oct 2021 17:41:19 -0500 Subject: [PATCH 275/703] Raise upper bound Python version to 3.9 in CI matrix --- .github/workflows/ci.yml | 14 +++++++------- qtpy/tests/test_patch_qheaderview.py | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1bee5b78..aebfb9a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,12 +35,12 @@ jobs: matrix: include: - os: ubuntu-latest - python-version: '3.8' + python-version: '3.9' use-conda: 'Yes' special-invocation: 'xvfb-run --auto-servernum ' coverage: 'True' - os: ubuntu-latest - python-version: '3.8' + python-version: '3.9' use-conda: 'No' special-invocation: 'xvfb-run --auto-servernum ' - os: ubuntu-latest @@ -53,12 +53,12 @@ jobs: special-invocation: 'xvfb-run --auto-servernum ' pyside2-version: 5.12.0 # 5.12.1-5.12.6 core dumps on patch test - os: windows-latest - python-version: '3.8' + python-version: '3.9' use-conda: 'Yes' - os: windows-latest - python-version: '3.8' + python-version: '3.9' use-conda: 'No' - pyside2-version: 5.15 # No 5.12 wheel on Windows and Python 3.8 + pyside2-version: 5.15 # No 5.12 wheel on Windows and Python 3.9 - os: windows-latest python-version: '3.6' use-conda: 'Yes' @@ -68,10 +68,10 @@ jobs: use-conda: 'No' pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on test collection - os: macos-latest - python-version: '3.8' + python-version: '3.9' use-conda: 'Yes' - os: macos-latest - python-version: '3.8' + python-version: '3.9' use-conda: 'No' - os: macos-latest python-version: '3.6' diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index 42e9e3bd..6aaf7c8e 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -18,10 +18,10 @@ def get_qapp(icon_path=None): @pytest.mark.skipif( QT_VERSION.startswith('5.15') or PYSIDE6 or PYQT6 or - ((PYSIDE2) and sys.version_info.major == 3 and sys.version_info.minor == 8 and - (sys.platform == 'darwin' or sys.platform.startswith('linux')) + ((PYSIDE2) and sys.version_info.major == 3 and sys.version_info.minor >= 8 + and (sys.platform == 'darwin' or sys.platform.startswith('linux')) ), - reason="It segfaults with Qt 5.15 and fails with PySide2, Python 3.8, on MacOS and Linux") + reason="Segfaults with Qt 5.15; and PySide2/Python 3.8+ on Mac and Linux") def test_patched_qheaderview(): """ This will test whether QHeaderView has the new methods introduced in Qt5. From bdc359fa863b09e07fff4e103da4934eae56b8d9 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 25 Oct 2021 18:40:19 -0500 Subject: [PATCH 276/703] Add PyQt6 support in CI tests --- .github/workflows/ci.yml | 7 ++++ .github/workflows/test.sh | 2 + qtpy/tests/test_qtmultimediawidgets.py | 12 +++--- qtpy/tests/test_qtwinextras.py | 9 ++-- qtpy/tests/test_uic.py | 57 ++++++++++++++++++-------- 5 files changed, 62 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aebfb9a5..36fbb512 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,9 +25,11 @@ jobs: PYTHON_VERSION: ${{ matrix.python-version }} USE_CONDA: ${{ matrix.use-conda }} PYQT5_VERSION: ${{ matrix.pyqt5-version }} + PYQT6_VERSION: ${{ matrix.pyqt6-version }} PYSIDE2_VERSION: ${{ matrix.pyside2-version }} PYSIDE6_VERSION: ${{ matrix.pyside6-version }} PYQT5_QT_VERSION: ${{ matrix.pyqt5-qt-version }} + PYQT6_QT_VERSION: ${{ matrix.pyqt6-qt-version }} PYSIDE2_QT_VERSION: ${{ matrix.pyside2-qt-version }} PYSIDE6_QT_VERSION: ${{ matrix.pyside6-qt-version }} strategy: @@ -51,6 +53,7 @@ jobs: python-version: '3.6' use-conda: 'No' special-invocation: 'xvfb-run --auto-servernum ' + skip-pyqt6: true # No wheels on Py 3.6 Linux CIs pyside2-version: 5.12.0 # 5.12.1-5.12.6 core dumps on patch test - os: windows-latest python-version: '3.9' @@ -79,6 +82,7 @@ jobs: - os: macos-latest python-version: '3.6' use-conda: 'No' + skip-pyqt6: true # No wheels on Py 3.6 macOS CIs pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on test collection steps: - name: Checkout branch @@ -104,6 +108,9 @@ jobs: - name: Test PyQt5 if: (! matrix.skip-pyqt5) run: ./.github/workflows/test.sh pyqt5 + - name: Test PyQt6 + if: always() && (! ((matrix.skip-pyqt6) || (matrix.use-conda == 'Yes'))) # No conda packages yet for Qt6/PyQt6 + run: ./.github/workflows/test.sh pyqt6 - name: Test PySide2 if: always() && (! (matrix.skip-pyside2)) run: ./.github/workflows/test.sh pyside2 diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index ed1f4d5e..295304f0 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -26,6 +26,8 @@ else if [ "${1}" = "pyqt5" ]; then pip install pyqt5==${PYQT5_VERSION:-"5.15"}.* PyQtWebEngine==${PYQT5_VERSION:-"5.15"}.* + elif [ "${1}" = "pyqt6" ]; then + pip install pyqt6==${PYQT6_VERSION:-"6.2"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.2"}.* elif [ "${1}" = "pyside2" ]; then pip install pyside2==${PYSIDE2_VERSION:-"5.12"}.* elif [ "${1}" = "pyside6" ]; then diff --git a/qtpy/tests/test_qtmultimediawidgets.py b/qtpy/tests/test_qtmultimediawidgets.py index dee6f16d..ac1faefe 100644 --- a/qtpy/tests/test_qtmultimediawidgets.py +++ b/qtpy/tests/test_qtmultimediawidgets.py @@ -1,12 +1,14 @@ import os -import sys import pytest + from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") -@pytest.mark.skipif(sys.version_info[0] == 3, - reason="Conda packages don't seem to include QtMultimedia") +@pytest.mark.skipif( + not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") +@pytest.mark.skipif( + os.environ.get('USE_CONDA', 'Yes') == 'Yes', + reason="Conda packages don't seem to include QtMultimedia") def test_qtmultimediawidgets(): """Test the qtpy.QtMultimediaWidgets namespace""" from qtpy import QtMultimediaWidgets @@ -14,4 +16,4 @@ def test_qtmultimediawidgets(): assert QtMultimediaWidgets.QCameraViewfinder is not None assert QtMultimediaWidgets.QGraphicsVideoItem is not None assert QtMultimediaWidgets.QVideoWidget is not None - #assert QtMultimediaWidgets.QVideoWidgetControl is not None + # assert QtMultimediaWidgets.QVideoWidgetControl is not None diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index 261a4875..64b20627 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -2,11 +2,14 @@ import sys import pytest -from qtpy import PYSIDE2, PYSIDE6 +from qtpy import PYQT6, PYSIDE2, PYSIDE6 + +@pytest.mark.skipif( + PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") @pytest.mark.skipif( - sys.platform != "win32" or os.environ.get('USE_CONDA', 'Yes') == 'Yes' or PYSIDE6, - reason="Only available in Qt5 bindings > 5.9 (only available with pip in the current CI setup) and Windows platform") + sys.platform != "win32" or os.environ.get('USE_CONDA', 'Yes') == 'Yes', + reason="Only available in Qt5 bindings > 5.9 with pip on Windows in CIs") def test_qtwinextras(): """Test the qtpy.QtWinExtras namespace""" from qtpy import QtWinExtras diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 8479de39..86b4312d 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -1,9 +1,11 @@ +import contextlib import os import sys -import contextlib +import warnings import pytest -from qtpy import PYQT5, PYSIDE6, PYSIDE2, QtWidgets + +from qtpy import PYSIDE6, PYSIDE2, QtWidgets from qtpy.QtWidgets import QComboBox if PYSIDE2: @@ -46,31 +48,42 @@ def get_qapp(icon_path=None): return qapp -@pytest.mark.skipif(((PYSIDE2 or PYSIDE6 or PYQT5) - and os.environ.get('CI', None) is not None), - reason="It segfaults in our CIs with PYSIDE2/PYSIDE6 or PYQT5") +@pytest.mark.skipif( + os.environ.get('CI', None) is not None + and sys.platform.startswith('linux'), + reason="Segfaults on Linux CIs under all bindings (PYSIDE2/6 & PYQT5/6)") def test_load_ui(): """ Make sure that the patched loadUi function behaves as expected with a simple .ui file. """ app = get_qapp() - ui = loadUi(os.path.join(os.path.dirname(__file__), 'test.ui')) + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", category=DeprecationWarning, message=".*mode.*") + ui = loadUi(os.path.join(os.path.dirname(__file__), 'test.ui')) assert isinstance(ui.pushButton, QtWidgets.QPushButton) assert isinstance(ui.comboBox, QComboBox) -@pytest.mark.skipif(((PYSIDE2 or PYQT5) - and os.environ.get('CI', None) is not None) or PYSIDE6, - reason="It segfaults in our CIs with PYSIDE2 or PYQT5") +@pytest.mark.skipif( + PYSIDE2 or PYSIDE6, + reason="PySide2uic not consistantly installed across platforms/versions") +@pytest.mark.skipif( + os.environ.get('CI', None) is not None + and sys.platform.startswith('linux'), + reason="Segfaults on Linux CIs under all bindings (PYSIDE2/6 & PYQT5/6)") def test_load_ui_type(): """ Make sure that the patched loadUiType function behaves as expected with a simple .ui file. """ app = get_qapp() - ui_type, ui_base_type = loadUiType( - os.path.join(os.path.dirname(__file__), 'test.ui')) + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", category=DeprecationWarning, message=".*mode.*") + ui_type, ui_base_type = loadUiType( + os.path.join(os.path.dirname(__file__), 'test.ui')) assert ui_type.__name__ == 'Ui_Form' class Widget(ui_base_type, ui_type): @@ -84,9 +97,15 @@ def __init__(self): assert isinstance(ui.comboBox, QComboBox) -@pytest.mark.skipif(((PYSIDE2 or PYSIDE6 or PYQT5) - and os.environ.get('CI', None) is not None), - reason="It segfaults in our CIs with PYSIDE2/PYSIDE6 or PYQT5") +@pytest.mark.skipif( + PYSIDE2 and sys.platform == "darwin" + and sys.version_info.major == 3 and sys.version_info.minor == 9 + and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fails on this specific platform, at least on our CIs") +@pytest.mark.skipif( + os.environ.get('CI', None) is not None + and sys.platform.startswith('linux'), + reason="Segfaults on Linux CIs under all bindings (PYSIDE2/6 & PYQT5/6)") def test_load_ui_custom_auto(tmpdir): """ Test that we can load a .ui file with custom widgets without having to @@ -98,14 +117,18 @@ def test_load_ui_custom_auto(tmpdir): with enabled_qcombobox_subclass(tmpdir): from qcombobox_subclass import _QComboBoxSubclass - ui = loadUi(os.path.join(os.path.dirname(__file__), 'test_custom.ui')) + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", category=DeprecationWarning, message=".*mode.*") + ui = loadUi( + os.path.join(os.path.dirname(__file__), 'test_custom.ui')) assert isinstance(ui.pushButton, QtWidgets.QPushButton) assert isinstance(ui.comboBox, _QComboBoxSubclass) -@pytest.mark.skipif(PYSIDE6, reason="unavailable") +@pytest.mark.skipif(PYSIDE6, reason="Unavailable on PySide6") def test_load_full_uic(): - """Test that we load the full uic objects for PyQt5.""" + """Test that we load the full uic objects.""" QT_API = os.environ.get('QT_API', '').lower() if QT_API.startswith('pyside'): assert hasattr(uic, 'loadUi') From 4e4f89ffbd830496c5d2ccae030a62b17786336f Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Thu, 28 Oct 2021 12:24:29 -0500 Subject: [PATCH 277/703] Use modern packaging instead of deprecated distutils for version parse --- qtpy/__init__.py | 18 +++++++++--------- setup.py | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 772fe3d1..e473274a 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -54,7 +54,7 @@ """ -from distutils.version import LooseVersion +from packaging.version import parse import os import platform import sys @@ -121,15 +121,15 @@ class PythonQtWarning(Warning): PYSIDE_VERSION = None if sys.platform == 'darwin': - macos_version = LooseVersion(platform.mac_ver()[0]) - if macos_version < LooseVersion('10.10'): - if LooseVersion(QT_VERSION) >= LooseVersion('5.9'): + macos_version = parse(platform.mac_ver()[0]) + if macos_version < parse('10.10'): + if parse(QT_VERSION) >= parse('5.9'): raise PythonQtError("Qt 5.9 or higher only works in " "macOS 10.10 or higher. Your " "program will fail in this " "system.") - elif macos_version < LooseVersion('10.11'): - if LooseVersion(QT_VERSION) >= LooseVersion('5.11'): + elif macos_version < parse('10.11'): + if parse(QT_VERSION) >= parse('5.11'): raise PythonQtError("Qt 5.11 or higher only works in " "macOS 10.11 or higher. Your " "program will fail in this " @@ -160,9 +160,9 @@ class PythonQtWarning(Warning): PYSIDE2 = True if sys.platform == 'darwin': - macos_version = LooseVersion(platform.mac_ver()[0]) - if macos_version < LooseVersion('10.11'): - if LooseVersion(QT_VERSION) >= LooseVersion('5.11'): + macos_version = parse(platform.mac_ver()[0]) + if macos_version < parse('10.11'): + if parse(QT_VERSION) >= parse('5.11'): raise PythonQtError("Qt 5.11 or higher only works in " "macOS 10.11 or higher. Your " "program will fail in this " diff --git a/setup.py b/setup.py index 0631220f..5aacfb6c 100644 --- a/setup.py +++ b/setup.py @@ -22,6 +22,7 @@ version=version_ns['__version__'], packages=find_packages(exclude=['contrib', 'docs', 'tests*']), python_requires='>=3.6', + install_requires=['packaging'], keywords=["qt PyQt5 PyQt6 PySide2 PySide6"], url='https://github.com/spyder-ide/qtpy', license='MIT', From 7379143c5a6a68bf8d122c01890c2e8ab369f6b8 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Thu, 28 Oct 2021 17:35:55 -0500 Subject: [PATCH 278/703] Refactor job matrix configuration to reduce workflow verbosity --- .github/workflows/ci.yml | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 36fbb512..2d3888cd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,29 +35,23 @@ jobs: strategy: fail-fast: false matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ['3.6', '3.9'] + use-conda: ['Yes', 'No'] include: - os: ubuntu-latest - python-version: '3.9' - use-conda: 'Yes' special-invocation: 'xvfb-run --auto-servernum ' - coverage: 'True' - - os: ubuntu-latest - python-version: '3.9' + - python-version: '3.6' use-conda: 'No' - special-invocation: 'xvfb-run --auto-servernum ' + pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on collection/segfaults on patch test - os: ubuntu-latest - python-version: '3.6' + python-version: '3.9' use-conda: 'Yes' - special-invocation: 'xvfb-run --auto-servernum ' + coverage: 'True' # Collect coverage only from this run, currently - os: ubuntu-latest python-version: '3.6' use-conda: 'No' - special-invocation: 'xvfb-run --auto-servernum ' skip-pyqt6: true # No wheels on Py 3.6 Linux CIs - pyside2-version: 5.12.0 # 5.12.1-5.12.6 core dumps on patch test - - os: windows-latest - python-version: '3.9' - use-conda: 'Yes' - os: windows-latest python-version: '3.9' use-conda: 'No' @@ -65,25 +59,11 @@ jobs: - os: windows-latest python-version: '3.6' use-conda: 'Yes' - pyqt5-qt-version: '5.9' - - os: windows-latest - python-version: '3.6' - use-conda: 'No' - pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on test collection - - os: macos-latest - python-version: '3.9' - use-conda: 'Yes' - - os: macos-latest - python-version: '3.9' - use-conda: 'No' - - os: macos-latest - python-version: '3.6' - use-conda: 'Yes' + pyqt5-qt-version: '5.9' # 5.12 is apparently unreliable here - os: macos-latest python-version: '3.6' use-conda: 'No' skip-pyqt6: true # No wheels on Py 3.6 macOS CIs - pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on test collection steps: - name: Checkout branch uses: actions/checkout@v2 From 82da1b22cbe04bb121f48bb215828b25b069b59d Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 29 Oct 2021 16:18:49 -0500 Subject: [PATCH 279/703] Fix coveralls reporting zero coverage due to not finding report --- .github/workflows/ci.yml | 1 + .github/workflows/test.sh | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2d3888cd..399c9774 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,5 +103,6 @@ jobs: env: COVERALLS_REPO_TOKEN: 'xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5' run: | + cd qtpy # Switch to test working dir per non-src-layout hack python3 -m pip install --upgrade coveralls python3 -m coveralls --service=github-actions || true diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 295304f0..4541f39e 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -40,7 +40,7 @@ fi # Build wheel of package git clean -xdf -python -bb -X dev -W error setup.py sdist bdist_wheel +python -bb -X dev setup.py sdist bdist_wheel # Needs migration to modern PEP 517-based build backend # Install package from build wheel echo dist/*.whl | xargs -I % python -bb -X dev -W error -m pip install --upgrade % @@ -49,6 +49,5 @@ echo dist/*.whl | xargs -I % python -bb -X dev -W error -m pip install --upgrade conda list # Run tests -cd qtpy +cd qtpy # Hack to work around non-src layout pulling in local instead of installed package for cov python -I -bb -X dev -W error -m pytest --cov-config ../.coveragerc -cd .. From c42688a2ffe0709c760ab294c6848b6ccc084f98 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 29 Oct 2021 18:06:19 -0500 Subject: [PATCH 280/703] Improve coverage reporting w/descriptive name and clean up invocation --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 399c9774..cfabbfb1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -101,8 +101,9 @@ jobs: if: matrix.coverage shell: bash env: - COVERALLS_REPO_TOKEN: 'xh75EzxFFMoTEyNPo3wXxXv8OVkul3eE5' + COVERALLS_FLAG_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cd qtpy # Switch to test working dir per non-src-layout hack python3 -m pip install --upgrade coveralls - python3 -m coveralls --service=github-actions || true + python3 -m coveralls --service=github --rcfile="../.coveragerc" || true From 15ba5d551c72f3ce2b58c6e462b16510f681366b Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 29 Oct 2021 19:12:27 -0500 Subject: [PATCH 281/703] Collate test coverage from all bindings --- .github/workflows/ci.yml | 5 +++-- .github/workflows/test.sh | 14 ++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfabbfb1..cdb30ad9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on collection/segfaults on patch test - os: ubuntu-latest python-version: '3.9' - use-conda: 'Yes' + use-conda: 'No' coverage: 'True' # Collect coverage only from this run, currently - os: ubuntu-latest python-version: '3.6' @@ -106,4 +106,5 @@ jobs: run: | cd qtpy # Switch to test working dir per non-src-layout hack python3 -m pip install --upgrade coveralls - python3 -m coveralls --service=github --rcfile="../.coveragerc" || true + python3 -m coveralls --service=github --rcfile="../.coveragerc" --basedir=$(cat qtpy_basepath.txt) + continue-on-error: true diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 4541f39e..99fe2f80 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -8,9 +8,12 @@ if [ "$USE_CONDA" = "No" ]; then CONDA_CHANNEL_ARG="-c anaconda" fi +# Remove any existing env +conda remove -q -n test-env ${CONDA_CHANNEL_ARG} --all || true + # Create and activate conda environment for this test -conda create -q -n test-${1} ${CONDA_CHANNEL_ARG} python=${PYTHON_VERSION} pytest pytest-cov -conda activate test-${1} +conda create -q -n test-env ${CONDA_CHANNEL_ARG} python=${PYTHON_VERSION} pytest pytest-cov +conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then @@ -39,7 +42,7 @@ else fi # Build wheel of package -git clean -xdf +git clean -xdf -e *.coverage python -bb -X dev setup.py sdist bdist_wheel # Needs migration to modern PEP 517-based build backend # Install package from build wheel @@ -50,4 +53,7 @@ conda list # Run tests cd qtpy # Hack to work around non-src layout pulling in local instead of installed package for cov -python -I -bb -X dev -W error -m pytest --cov-config ../.coveragerc +python -I -bb -X dev -W error -m pytest --cov-config ../.coveragerc --cov-append + +# Save QtPy base path for coverage +python -c "from pathlib import Path; import qtpy; print(Path(qtpy.__file__).parent.parent.as_posix())" > qtpy_basepath.txt From 741967a184c1d58254fcacf7fd1917d23d90f97f Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 29 Oct 2021 19:32:14 -0500 Subject: [PATCH 282/703] Upload coverage to coveralls on all runner jobs --- .github/workflows/ci.yml | 15 +++++++-------- .github/workflows/test.sh | 5 +++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cdb30ad9..151a1cfe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,10 +44,6 @@ jobs: - python-version: '3.6' use-conda: 'No' pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on collection/segfaults on patch test - - os: ubuntu-latest - python-version: '3.9' - use-conda: 'No' - coverage: 'True' # Collect coverage only from this run, currently - os: ubuntu-latest python-version: '3.6' use-conda: 'No' @@ -67,6 +63,10 @@ jobs: steps: - name: Checkout branch uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: '3.x' - name: Install Linux system packages if: contains(matrix.os, 'ubuntu') shell: bash @@ -98,13 +98,12 @@ jobs: if: always() && (! ((matrix.skip-pyside6) || (matrix.use-conda == 'Yes'))) # No conda packages yet for Qt6/Pyside6 run: ./.github/workflows/test.sh pyside6 - name: Upload coverage data to coveralls.io - if: matrix.coverage shell: bash env: COVERALLS_FLAG_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cd qtpy # Switch to test working dir per non-src-layout hack - python3 -m pip install --upgrade coveralls - python3 -m coveralls --service=github --rcfile="../.coveragerc" --basedir=$(cat qtpy_basepath.txt) - continue-on-error: true + python -m pip install --upgrade coveralls + cat qtpy_basedir.txt + python -b -X dev -m coveralls --service=github --rcfile="../.coveragerc" --basedir="$(cat qtpy_basedir.txt)" diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 99fe2f80..83a021a2 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -55,5 +55,6 @@ conda list cd qtpy # Hack to work around non-src layout pulling in local instead of installed package for cov python -I -bb -X dev -W error -m pytest --cov-config ../.coveragerc --cov-append -# Save QtPy base path for coverage -python -c "from pathlib import Path; import qtpy; print(Path(qtpy.__file__).parent.parent.as_posix())" > qtpy_basepath.txt +# Save QtPy base dir for coverage +python -c "from pathlib import Path; import qtpy; print(Path(qtpy.__file__).parent.parent.resolve().as_posix())" > qtpy_basedir.txt +cat qtpy_basedir.txt From 71d0e36f5c7ae9ae7d92c9e25e484ee6d6edd478 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 2 Nov 2021 23:35:36 -0500 Subject: [PATCH 283/703] Upgrade deprecated tmpdir test fixture to use modern standard tmp_path --- qtpy/tests/test_uic.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 86b4312d..9ade6d8d 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -22,16 +22,16 @@ class _QComboBoxSubclass(QComboBox): """ @contextlib.contextmanager -def enabled_qcombobox_subclass(tmpdir): +def enabled_qcombobox_subclass(temp_dir_path): """ Context manager that sets up a temporary module with a QComboBox subclass and then removes it once we are done. """ - with open(tmpdir.join('qcombobox_subclass.py').strpath, 'w') as f: + with open(temp_dir_path / 'qcombobox_subclass.py', 'w') as f: f.write(QCOMBOBOX_SUBCLASS) - sys.path.insert(0, tmpdir.strpath) + sys.path.insert(0, str(temp_dir_path)) yield @@ -106,7 +106,7 @@ def __init__(self): os.environ.get('CI', None) is not None and sys.platform.startswith('linux'), reason="Segfaults on Linux CIs under all bindings (PYSIDE2/6 & PYQT5/6)") -def test_load_ui_custom_auto(tmpdir): +def test_load_ui_custom_auto(tmp_path): """ Test that we can load a .ui file with custom widgets without having to explicitly specify a dictionary of custom widgets, even in the case of @@ -115,7 +115,7 @@ def test_load_ui_custom_auto(tmpdir): app = get_qapp() - with enabled_qcombobox_subclass(tmpdir): + with enabled_qcombobox_subclass(tmp_path): from qcombobox_subclass import _QComboBoxSubclass with warnings.catch_warnings(): warnings.filterwarnings( From 53e62f3983e1674c627cc66a96af13d1677fc7b8 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 2 Nov 2021 23:41:29 -0500 Subject: [PATCH 284/703] Declare encoding on open() to avoid bugs and warning on Py3.10+ --- qtpy/tests/test_uic.py | 6 +++++- qtpy/uic.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 9ade6d8d..7befe64a 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -28,7 +28,11 @@ def enabled_qcombobox_subclass(temp_dir_path): and then removes it once we are done. """ - with open(temp_dir_path / 'qcombobox_subclass.py', 'w') as f: + with open( + temp_dir_path / 'qcombobox_subclass.py', + mode='w', + encoding="utf-8", + ) as f: f.write(QCOMBOBOX_SUBCLASS) sys.path.insert(0, str(temp_dir_path)) diff --git a/qtpy/uic.py b/qtpy/uic.py index ba0d2416..23c2f8f8 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -254,7 +254,7 @@ def loadUiType(uifile, from_imports=False): widget_class = ui.find('widget').get('class') form_class = ui.find('class').text - with open(uifile) as fd: + with open(uifile, encoding="utf-8") as fd: code_stream = StringIO() frame = {} From 8256c91f2ccca7f6fe246d02eb6d93dc888e6288 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 3 Nov 2021 00:04:34 -0500 Subject: [PATCH 285/703] Fix bad indents, raise from, bare except and misplaced docstrings --- qtpy/QtCharts.py | 16 ++++++++++------ qtpy/QtCore.py | 18 +++++++++--------- qtpy/__init__.py | 6 ++---- qtpy/tests/test_qtcore.py | 4 ++-- qtpy/tests/test_qtdesigner.py | 2 +- qtpy/tests/test_qtsvg.py | 4 ++-- qtpy/tests/test_uic.py | 2 +- qtpy/uic.py | 8 +++++--- 8 files changed, 32 insertions(+), 28 deletions(-) diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 94ae52f5..b041b0cd 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -12,15 +12,19 @@ if PYQT5: try: from PyQt5.QtChart import * - except ImportError: - raise PythonQtError('The QtChart module was not found. ' - 'It needs to be installed separately for PyQt5.') + except ImportError as error: + raise PythonQtError( + 'The QtChart module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error elif PYQT6: try: from PyQt6.QtCharts import * - except ImportError: - raise PythonQtError('The QtCharts module was not found. ' - 'It needs to be installed separately for PyQt6.') + except ImportError as error: + raise PythonQtError( + 'The QtCharts module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error elif PYSIDE6: from PySide6.QtCharts import * elif PYSIDE2: diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index ac6e78b9..6d0b3f83 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -36,21 +36,21 @@ del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR elif PYSIDE6: - from PySide6.QtCore import * - import PySide6.QtCore - __version__ = PySide6.QtCore.__version__ + from PySide6.QtCore import * + import PySide6.QtCore + __version__ = PySide6.QtCore.__version__ - # obsolete in qt6 - Qt.BackgroundColorRole = Qt.BackgroundRole - Qt.TextColorRole = Qt.ForegroundRole - Qt.MidButton = Qt.MiddleButton + # obsolete in qt6 + Qt.BackgroundColorRole = Qt.BackgroundRole + Qt.TextColorRole = Qt.ForegroundRole + Qt.MidButton = Qt.MiddleButton elif PYSIDE2: from PySide2.QtCore import * - try: # may be limited to PySide-5.11a1 only + try: # may be limited to PySide-5.11a1 only from PySide2.QtGui import QStringListModel - except: + except Exception: pass import PySide2.QtCore diff --git a/qtpy/__init__.py b/qtpy/__init__.py index e473274a..7ac6e66c 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -65,13 +65,11 @@ class PythonQtError(RuntimeError): - """Error raise if no bindings could be selected.""" - pass + """Error raised if no bindings could be selected.""" class PythonQtWarning(Warning): """Warning if some features are not implemented in a binding.""" - pass # Qt API environment variable name @@ -110,7 +108,7 @@ class PythonQtWarning(Warning): elif 'PyQt5' in sys.modules: API = initial_api if initial_api in PYQT5_API else 'pyqt5' elif 'PySide6' in sys.modules: - API = initial_api if initial_api in PYSIDE6_API else 'pyside6' + API = initial_api if initial_api in PYSIDE6_API else 'pyside6' elif 'PySide2' in sys.modules: API = initial_api if initial_api in PYSIDE2_API else 'pyside2' diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index e9b22ae7..7e9864b3 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,8 +1,8 @@ +"""Test QtCore.""" + import pytest from qtpy import PYQT5, PYQT6, PYSIDE2, QtCore -"""Test QtCore.""" - def test_qtmsghandler(): """Test qtpy.QtMsgHandler""" diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index 254de11f..04b1374e 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -3,8 +3,8 @@ @pytest.mark.skipif(PYSIDE2, reason="QtDesigner is not avalaible in PySide2") def test_qtdesigner(): + """Test the qtpy.QtDesigner namespace.""" from qtpy import QtDesigner - """Test the qtpy.QtDesigner namespace""" assert QtDesigner.QAbstractExtensionFactory is not None assert QtDesigner.QAbstractExtensionManager is not None assert QtDesigner.QDesignerActionEditorInterface is not None diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py index 1eef81e2..4b2a0bb9 100644 --- a/qtpy/tests/test_qtsvg.py +++ b/qtpy/tests/test_qtsvg.py @@ -6,7 +6,7 @@ def test_qtsvg(): from qtpy import QtSvg if not (PYSIDE6 or PYQT6): - assert QtSvg.QGraphicsSvgItem is not None - assert QtSvg.QSvgWidget is not None + assert QtSvg.QGraphicsSvgItem is not None + assert QtSvg.QSvgWidget is not None assert QtSvg.QSvgGenerator is not None assert QtSvg.QSvgRenderer is not None diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 7befe64a..db9f660b 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -140,4 +140,4 @@ def test_load_full_uic(): else: objects = ['compileUi', 'compileUiDir', 'loadUi', 'loadUiType', 'widgetPluginPath'] - assert all([hasattr(uic, o) for o in objects]) + assert all((hasattr(uic, o) for o in objects)) diff --git a/qtpy/uic.py b/qtpy/uic.py index 23c2f8f8..0650d45d 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -152,9 +152,11 @@ def createWidget(self, class_name, parent=None, name=''): # customWidgets is empty. try: widget = self.customWidgets[class_name](parent) - except KeyError: - raise Exception('No custom widget ' + class_name + ' ' - 'found in customWidgets') + except KeyError as error: + raise Exception( + f'No custom widget {class_name} ' + 'found in customWidgets' + ) from error if self.baseinstance: # set an attribute for the new child widget on the base From 943c622342bdcd80f3f5b0225eb0ee3601269d5a Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 4 Nov 2021 12:06:07 -0500 Subject: [PATCH 286/703] PyQt6: Add support for initial unscoped enum access and tests --- qtpy/QtCore.py | 7 ++--- qtpy/QtGui.py | 5 ++-- qtpy/QtWidgets.py | 10 +++++--- qtpy/compat.py | 4 +-- qtpy/enums_compat.py | 50 ++++++++++++++++++++++++++++++++++++ qtpy/sip.py | 15 +++++++++++ qtpy/tests/test_qtcore.py | 17 +++++++++++- qtpy/tests/test_qtgui.py | 33 ++++++++++++++++++++++++ qtpy/tests/test_qtwidgets.py | 45 ++++++++++++++++++++++++++++++++ 9 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 qtpy/enums_compat.py create mode 100644 qtpy/sip.py create mode 100644 qtpy/tests/test_qtgui.py create mode 100644 qtpy/tests/test_qtwidgets.py diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index ac6e78b9..322b87d0 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -8,18 +8,19 @@ """ Provides QtCore classes and functions. """ - from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError + if PYQT6: + from PyQt6 import QtCore from PyQt6.QtCore import * from PyQt6.QtCore import pyqtSignal as Signal from PyQt6.QtCore import QT_VERSION_STR as __version__ - QCoreApplication.exec_ = QCoreApplication.exec QEventLoop.exec_ = QEventLoop.exec QThread.exec_ = QThread.exec - + from .enums_compat import promote_enums + promote_enums(QtCore) elif PYQT5: from PyQt5.QtCore import * from PyQt5.QtCore import pyqtSignal as Signal diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index da8369e6..9bc3ee94 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -8,16 +8,17 @@ """ Provides QtGui classes and functions. """ -import warnings - from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError if PYQT6: + from PyQt6 import QtGui from PyQt6.QtGui import * QDrag.exec_ = QDrag.exec QGuiApplication.exec_ = QGuiApplication.exec QTextDocument.print_ = QTextDocument.print + from .enums_compat import promote_enums + promote_enums(QtGui) elif PYQT5: from PyQt5.QtGui import * elif PYSIDE2: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index d1f7b730..be82b060 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -8,23 +8,25 @@ """ Provides widget classes and functions. """ - from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -from ._patch.qheaderview import introduce_renamed_methods_qheaderview + if PYQT6: + from PyQt6 import QtWidgets from PyQt6.QtWidgets import * from PyQt6.QtGui import QAction, QActionGroup, QShortcut from PyQt6.QtOpenGLWidgets import QOpenGLWidget QTextEdit.setTabStopWidth = QTextEdit.setTabStopDistance QTextEdit.tabStopWidth = QTextEdit.tabStopDistance + QTextEdit.print_ = QTextEdit.print QPlainTextEdit.setTabStopWidth = QPlainTextEdit.setTabStopDistance QPlainTextEdit.tabStopWidth = QPlainTextEdit.tabStopDistance + QPlainTextEdit.print_ = QPlainTextEdit.print QApplication.exec_ = QApplication.exec QDialog.exec_ = QDialog.exec QMenu.exec_ = QMenu.exec - QTextEdit.print_ = QTextEdit.print - QPlainTextEdit.print_ = QPlainTextEdit.print + from .enums_compat import promote_enums + promote_enums(QtWidgets) elif PYQT5: from PyQt5.QtWidgets import * elif PYSIDE6: diff --git a/qtpy/compat.py b/qtpy/compat.py index 3a51c921..7494862f 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -5,8 +5,6 @@ """ Compatibility functions """ - -from collections.abc import Callable import sys from .QtWidgets import QFileDialog @@ -53,7 +51,7 @@ def from_qvariant(qobj=None, pytype=None): # analysis:ignore # Wrappers around QFileDialog static methods # ============================================================================= def getexistingdirectory(parent=None, caption='', basedir='', - options=QFileDialog.ShowDirsOnly): + options=QFileDialog.Option.ShowDirsOnly): """Wrapper around QtGui.QFileDialog.getExistingDirectory static method Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" # Calling QFileDialog static method diff --git a/qtpy/enums_compat.py b/qtpy/enums_compat.py new file mode 100644 index 00000000..b0a52939 --- /dev/null +++ b/qtpy/enums_compat.py @@ -0,0 +1,50 @@ +# +# Copyright © 2009- The Spyder Development Team +# Licensed under the terms of the MIT License +""" +Compatibility functions for scoped and unscoped enums access +""" +from . import PYQT6 + +if PYQT6: + import enum + + from . import sip + + + def promote_specific_enums(base_class, enum_classes_list, inclusion_criteria): + """ + Allow access for the given enumeration classes values at base class level. + + Based on: + https://github.com/pyqtgraph/pyqtgraph/blob/pyqtgraph-0.12.1/pyqtgraph/Qt.py#L331-L377 + """ + for enum_class_name in enum_classes_list: + klass = getattr(base_class, enum_class_name) + attrib_names = [x for x in dir(klass) if inclusion_criteria(x)] + for attrib_name in attrib_names: + attrib = getattr(klass, attrib_name) + if not isinstance(attrib, (enum.Enum)): + continue + setattr(base_class, attrib.name, attrib) + + + def promote_enums(module): + """ + Search enums in the given module and allow unscoped access. + + Taken from: + https://github.com/pyqtgraph/pyqtgraph/blob/pyqtgraph-0.12.1/pyqtgraph/Qt.py#L331-L377 + """ + class_names = [x for x in dir(module) if x.startswith('Q')] + for class_name in class_names: + klass = getattr(module, class_name) + if not isinstance(klass, sip.wrappertype): + continue + attrib_names = [x for x in dir(klass) if x[0].isupper()] + for attrib_name in attrib_names: + attrib = getattr(klass, attrib_name) + if not isinstance(attrib, enum.EnumMeta): + continue + for e in attrib: + setattr(klass, e.name, e) \ No newline at end of file diff --git a/qtpy/sip.py b/qtpy/sip.py new file mode 100644 index 00000000..b0237c8e --- /dev/null +++ b/qtpy/sip.py @@ -0,0 +1,15 @@ + +# +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) + +from . import PYQT6, PYQT5,PythonQtError + +if PYQT6: + from PyQt6.sip import * +elif PYQT5: + from PyQt5.sip import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index e9b22ae7..2b434daf 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,5 +1,5 @@ import pytest -from qtpy import PYQT5, PYQT6, PYSIDE2, QtCore +from qtpy import PYQT5, PYQT6, PYSIDE2, PYQT_VERSION, QtCore """Test QtCore.""" @@ -25,3 +25,18 @@ class ClassWithSignal(QtCore.QObject): instance = ClassWithSignal() assert isinstance(instance.signal, QtCore.SignalInstance) + + +@pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), + reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" + "to work with scoped enum access") +def test_enum_access(): + """ + Test scoped and unscoped enum access for qtpy.QtCore.*. + """ + assert QtCore.QAbstractAnimation.Stopped == QtCore.QAbstractAnimation.State.Stopped + assert QtCore.QEvent.ActionAdded == QtCore.QEvent.Type.ActionAdded + assert QtCore.Qt.AlignLeft == QtCore.Qt.AlignmentFlag.AlignLeft + assert QtCore.Qt.Key_Return == QtCore.Qt.Key.Key_Return + assert QtCore.Qt.transparent == QtCore.Qt.GlobalColor.transparent + assert QtCore.Qt.Widget == QtCore.Qt.WindowType.Widget diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py new file mode 100644 index 00000000..d563e832 --- /dev/null +++ b/qtpy/tests/test_qtgui.py @@ -0,0 +1,33 @@ +"""Test QtGui.""" +import pytest + +from qtpy import PYQT5, PYQT_VERSION, QtGui + + +def test_qdrag_functions(): + """Test functions mapping for QtGui.QDrag.""" + assert QtGui.QDrag.exec_ + + +def test_qguiapplication_functions(): + """Test functions mapping for QtGui.QGuiApplication.""" + assert QtGui.QGuiApplication.exec_ + + +def test_qtextdocument_functions(): + """Test functions mapping for QtGui.QTextDocument.""" + assert QtGui.QTextDocument.print_ + + +@pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), + reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" + "to work with scoped enum access") +def test_enum_access(): + """ + Test scoped and unscoped enum access for qtpy.QtWidgets.*. + """ + assert QtGui.QColor.Rgb == QtGui.QColor.Spec.Rgb + assert QtGui.QFont.AllUppercase == QtGui.QFont.Capitalization.AllUppercase + assert QtGui.QIcon.Normal == QtGui.QIcon.Mode.Normal + assert QtGui.QImage.Format_Invalid == QtGui.QImage.Format.Format_Invalid + \ No newline at end of file diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py new file mode 100644 index 00000000..5432c93d --- /dev/null +++ b/qtpy/tests/test_qtwidgets.py @@ -0,0 +1,45 @@ +"""Test QtWidgets.""" +import pytest + +from qtpy import PYQT5, PYQT_VERSION, QtWidgets + + +def test_qtextedit_functions(): + """Test functions mapping for QtWidgets.QTextEdit.""" + assert QtWidgets.QTextEdit.setTabStopWidth + assert QtWidgets.QTextEdit.tabStopWidth + assert QtWidgets.QTextEdit.print_ + + +def test_qplaintextedit_functions(): + """Test functions mapping for QtWidgets.QPlainTextEdit.""" + assert QtWidgets.QPlainTextEdit.setTabStopWidth + assert QtWidgets.QPlainTextEdit.tabStopWidth + assert QtWidgets.QPlainTextEdit.print_ + + +def test_qapplication_functions(): + """Test functions mapping for QtWidgets.QApplication.""" + assert QtWidgets.QApplication.exec_ + + +def test_qdialog_functions(): + """Test functions mapping for QtWidgets.QDialog.""" + assert QtWidgets.QDialog.exec_ + + +def test_qmenu_functions(): + """Test functions mapping for QtWidgets.QDialog.""" + assert QtWidgets.QMenu.exec_ + + +@pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), + reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" + "to work with scoped enum access") +def test_enum_access(): + """ + Test scoped and unscoped enum access for qtpy.QtWidgets.*. + """ + assert QtWidgets.QFileDialog.AcceptOpen == QtWidgets.QFileDialog.AcceptMode.AcceptOpen + assert QtWidgets.QMessageBox.InvalidRole == QtWidgets.QMessageBox.ButtonRole.InvalidRole + assert QtWidgets.QStyle.State_None == QtWidgets.QStyle.StateFlag.State_None \ No newline at end of file From 6d75a4ab0b275bdd75a7ffff059222ac59e179b5 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 4 Nov 2021 13:35:20 -0500 Subject: [PATCH 287/703] PyQt6: Missing aliases for QtCore. Add comments for patches --- qtpy/QtCore.py | 10 ++++++++++ qtpy/QtGui.py | 4 ++++ qtpy/QtWidgets.py | 6 ++++++ 3 files changed, 20 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 322b87d0..98c25b03 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -15,10 +15,20 @@ from PyQt6 import QtCore from PyQt6.QtCore import * from PyQt6.QtCore import pyqtSignal as Signal + from PyQt6.QtCore import pyqtBoundSignal as SignalInstance + from PyQt6.QtCore import pyqtSlot as Slot + from PyQt5.QtCore import pyqtProperty as Property from PyQt6.QtCore import QT_VERSION_STR as __version__ + + # Map missing methods QCoreApplication.exec_ = QCoreApplication.exec QEventLoop.exec_ = QEventLoop.exec QThread.exec_ = QThread.exec + + # Those are imported from `import *` + del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR + + # Allow unscoped access for enums inside the QtCore module from .enums_compat import promote_enums promote_enums(QtCore) elif PYQT5: diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 9bc3ee94..ec861f7c 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -14,9 +14,13 @@ if PYQT6: from PyQt6 import QtGui from PyQt6.QtGui import * + + # Map missing/renamed methods QDrag.exec_ = QDrag.exec QGuiApplication.exec_ = QGuiApplication.exec QTextDocument.print_ = QTextDocument.print + + # Allow unscoped access for enums inside the QtGui module from .enums_compat import promote_enums promote_enums(QtGui) elif PYQT5: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index be82b060..fee8ffb6 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -16,6 +16,8 @@ from PyQt6.QtWidgets import * from PyQt6.QtGui import QAction, QActionGroup, QShortcut from PyQt6.QtOpenGLWidgets import QOpenGLWidget + + # Map missing/renamed methods QTextEdit.setTabStopWidth = QTextEdit.setTabStopDistance QTextEdit.tabStopWidth = QTextEdit.tabStopDistance QTextEdit.print_ = QTextEdit.print @@ -25,6 +27,8 @@ QApplication.exec_ = QApplication.exec QDialog.exec_ = QDialog.exec QMenu.exec_ = QMenu.exec + + # Allow unscoped access for enums inside the QtWidgets module from .enums_compat import promote_enums promote_enums(QtWidgets) elif PYQT5: @@ -33,6 +37,8 @@ from PySide6.QtWidgets import * from PySide6.QtGui import QAction, QActionGroup, QShortcut from PySide6.QtOpenGLWidgets import QOpenGLWidget + + # Map missing/renamed methods QTextEdit.setTabStopWidth = QTextEdit.setTabStopDistance QTextEdit.tabStopWidth = QTextEdit.tabStopDistance QPlainTextEdit.setTabStopWidth = QPlainTextEdit.setTabStopDistance From dc86e18055181ca71b05febe3070636b7322b209 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Fri, 5 Nov 2021 10:27:23 -0500 Subject: [PATCH 288/703] PyQt6: Apply suggestions from code review for enum access Co-authored-by: CAM Gerlach --- qtpy/QtCore.py | 2 +- qtpy/enums_compat.py | 10 +++++----- qtpy/sip.py | 4 ++-- qtpy/tests/test_qtcore.py | 5 ++--- qtpy/tests/test_qtgui.py | 7 ++----- qtpy/tests/test_qtwidgets.py | 6 ++---- 6 files changed, 14 insertions(+), 20 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 98c25b03..3972ef6d 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -17,7 +17,7 @@ from PyQt6.QtCore import pyqtSignal as Signal from PyQt6.QtCore import pyqtBoundSignal as SignalInstance from PyQt6.QtCore import pyqtSlot as Slot - from PyQt5.QtCore import pyqtProperty as Property + from PyQt6.QtCore import pyqtProperty as Property from PyQt6.QtCore import QT_VERSION_STR as __version__ # Map missing methods diff --git a/qtpy/enums_compat.py b/qtpy/enums_compat.py index b0a52939..218acb6b 100644 --- a/qtpy/enums_compat.py +++ b/qtpy/enums_compat.py @@ -2,7 +2,7 @@ # Copyright © 2009- The Spyder Development Team # Licensed under the terms of the MIT License """ -Compatibility functions for scoped and unscoped enums access +Compatibility functions for scoped and unscoped enum access. """ from . import PYQT6 @@ -36,15 +36,15 @@ def promote_enums(module): Taken from: https://github.com/pyqtgraph/pyqtgraph/blob/pyqtgraph-0.12.1/pyqtgraph/Qt.py#L331-L377 """ - class_names = [x for x in dir(module) if x.startswith('Q')] + class_names = [name for name in dir(module) if name.startswith('Q')] for class_name in class_names: klass = getattr(module, class_name) if not isinstance(klass, sip.wrappertype): continue - attrib_names = [x for x in dir(klass) if x[0].isupper()] + attrib_names = [name for name in dir(klass) if name[0].isupper()] for attrib_name in attrib_names: attrib = getattr(klass, attrib_name) if not isinstance(attrib, enum.EnumMeta): continue - for e in attrib: - setattr(klass, e.name, e) \ No newline at end of file + for enum_obj in attrib: + setattr(klass, enum_obj.name, enum_obj) diff --git a/qtpy/sip.py b/qtpy/sip.py index b0237c8e..ffb1d334 100644 --- a/qtpy/sip.py +++ b/qtpy/sip.py @@ -1,4 +1,3 @@ - # # Copyright © 2009- The Spyder Development Team # @@ -12,4 +11,5 @@ elif PYQT5: from PyQt5.sip import * else: - raise PythonQtError('No Qt bindings could be found') + raise PythonQtError( + 'Currently selected Qt binding does not support this module') diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 2b434daf..d8068a5b 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,4 +1,5 @@ import pytest + from qtpy import PYQT5, PYQT6, PYSIDE2, PYQT_VERSION, QtCore """Test QtCore.""" @@ -31,9 +32,7 @@ class ClassWithSignal(QtCore.QObject): reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" "to work with scoped enum access") def test_enum_access(): - """ - Test scoped and unscoped enum access for qtpy.QtCore.*. - """ + """Test scoped and unscoped enum access for qtpy.QtCore.*.""" assert QtCore.QAbstractAnimation.Stopped == QtCore.QAbstractAnimation.State.Stopped assert QtCore.QEvent.ActionAdded == QtCore.QEvent.Type.ActionAdded assert QtCore.Qt.AlignLeft == QtCore.Qt.AlignmentFlag.AlignLeft diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index d563e832..09798955 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -23,11 +23,8 @@ def test_qtextdocument_functions(): reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" "to work with scoped enum access") def test_enum_access(): - """ - Test scoped and unscoped enum access for qtpy.QtWidgets.*. - """ + """Test scoped and unscoped enum access for qtpy.QtWidgets.*.""" assert QtGui.QColor.Rgb == QtGui.QColor.Spec.Rgb assert QtGui.QFont.AllUppercase == QtGui.QFont.Capitalization.AllUppercase assert QtGui.QIcon.Normal == QtGui.QIcon.Mode.Normal - assert QtGui.QImage.Format_Invalid == QtGui.QImage.Format.Format_Invalid - \ No newline at end of file + assert QtGui.QImage.Format_Invalid == QtGui.QImage.Format.Format_Invalid \ No newline at end of file diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 5432c93d..68e8192c 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -37,9 +37,7 @@ def test_qmenu_functions(): reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" "to work with scoped enum access") def test_enum_access(): - """ - Test scoped and unscoped enum access for qtpy.QtWidgets.*. - """ + """Test scoped and unscoped enum access for qtpy.QtWidgets.*.""" assert QtWidgets.QFileDialog.AcceptOpen == QtWidgets.QFileDialog.AcceptMode.AcceptOpen assert QtWidgets.QMessageBox.InvalidRole == QtWidgets.QMessageBox.ButtonRole.InvalidRole - assert QtWidgets.QStyle.State_None == QtWidgets.QStyle.StateFlag.State_None \ No newline at end of file + assert QtWidgets.QStyle.State_None == QtWidgets.QStyle.StateFlag.State_None From 8ef3b5d2b12f6983e9846f129db1a948e1661a68 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 5 Nov 2021 10:29:45 -0500 Subject: [PATCH 289/703] PyQt6: Remove unused promote_specific_enums function --- qtpy/enums_compat.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/qtpy/enums_compat.py b/qtpy/enums_compat.py index 218acb6b..220968db 100644 --- a/qtpy/enums_compat.py +++ b/qtpy/enums_compat.py @@ -12,23 +12,6 @@ from . import sip - def promote_specific_enums(base_class, enum_classes_list, inclusion_criteria): - """ - Allow access for the given enumeration classes values at base class level. - - Based on: - https://github.com/pyqtgraph/pyqtgraph/blob/pyqtgraph-0.12.1/pyqtgraph/Qt.py#L331-L377 - """ - for enum_class_name in enum_classes_list: - klass = getattr(base_class, enum_class_name) - attrib_names = [x for x in dir(klass) if inclusion_criteria(x)] - for attrib_name in attrib_names: - attrib = getattr(klass, attrib_name) - if not isinstance(attrib, (enum.Enum)): - continue - setattr(base_class, attrib.name, attrib) - - def promote_enums(module): """ Search enums in the given module and allow unscoped access. From 589d09c19708c4a39d8bcd62c4dffd2419696f08 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 5 Nov 2021 10:48:02 -0500 Subject: [PATCH 290/703] PyQt6: Update copyright string --- qtpy/enums_compat.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qtpy/enums_compat.py b/qtpy/enums_compat.py index 220968db..a8e71981 100644 --- a/qtpy/enums_compat.py +++ b/qtpy/enums_compat.py @@ -1,5 +1,9 @@ # # Copyright © 2009- The Spyder Development Team +# Copyright © 2012- University of North Carolina at Chapel Hill +# Luke Campagnola ('luke.campagnola@%s.com' % 'gmail') +# Ogi Moore ('ognyan.moore@%s.com' % 'gmail') +# KIU Shueng Chuan ('nixchuan@%s.com' % 'gmail') # Licensed under the terms of the MIT License """ Compatibility functions for scoped and unscoped enum access. From 8590108c50877084b2033e13d7c9128216f34328 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 5 Nov 2021 12:38:32 -0500 Subject: [PATCH 291/703] Compat: Restore use of QFileDialog.ShowDirsOnly (unscoped enum access) --- qtpy/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/compat.py b/qtpy/compat.py index 7494862f..dbac4396 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -51,7 +51,7 @@ def from_qvariant(qobj=None, pytype=None): # analysis:ignore # Wrappers around QFileDialog static methods # ============================================================================= def getexistingdirectory(parent=None, caption='', basedir='', - options=QFileDialog.Option.ShowDirsOnly): + options=QFileDialog.ShowDirsOnly): """Wrapper around QtGui.QFileDialog.getExistingDirectory static method Compatible with PyQt >=v4.4 (API #1 and #2) and PySide >=v1.0""" # Calling QFileDialog static method From e4d59687a77362bd5ba7b64abfa1f52662e51c1f Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 7 Nov 2021 19:40:25 -0500 Subject: [PATCH 292/703] fix regression in force_qt_qpi --- qtpy/__init__.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 7ac6e66c..2c5558ef 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -100,9 +100,8 @@ class PythonQtWarning(Warning): PYQT5 = True PYQT6 = PYSIDE2 = PYSIDE6 = False -# When `FORCE_QT_API` is set, we disregard -# any previously imported python bindings. -if 'FORCE_QT_API' in os.environ: +# Unless `FORCE_QT_API` is set, we use previousl imported python bindings. +if not os.environ.get('FORCE_QT_API'): if 'PyQt6' in sys.modules: API = initial_api if initial_api in PYQT6_API else 'pyqt6' elif 'PyQt5' in sys.modules: From e60d456de8eb2c19c79001c02a020e48e6731739 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 7 Nov 2021 20:00:17 -0500 Subject: [PATCH 293/703] Update qtpy/__init__.py --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 2c5558ef..33da3639 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -100,7 +100,7 @@ class PythonQtWarning(Warning): PYQT5 = True PYQT6 = PYSIDE2 = PYSIDE6 = False -# Unless `FORCE_QT_API` is set, we use previousl imported python bindings. +# Unless `FORCE_QT_API` is set, we use previously imported python bindings. if not os.environ.get('FORCE_QT_API'): if 'PyQt6' in sys.modules: API = initial_api if initial_api in PYQT6_API else 'pyqt6' From 2c903ea146e73849fe606397267254174f1a1567 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 7 Nov 2021 20:15:08 -0500 Subject: [PATCH 294/703] Update qtpy/__init__.py --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 33da3639..6a548dc3 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -100,7 +100,7 @@ class PythonQtWarning(Warning): PYQT5 = True PYQT6 = PYSIDE2 = PYSIDE6 = False -# Unless `FORCE_QT_API` is set, we use previously imported python bindings. +# Unless `FORCE_QT_API` is set, use previously imported Qt Python bindings if not os.environ.get('FORCE_QT_API'): if 'PyQt6' in sys.modules: API = initial_api if initial_api in PYQT6_API else 'pyqt6' From bfa5cbfc642ddadef1d968cbaa46ac3ea79e9a6d Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 8 Nov 2021 12:57:40 -0500 Subject: [PATCH 295/703] PyQt6: Add missing map for QDateTime.toPython Co-authored-by: Talley Lambert --- qtpy/QtCore.py | 4 ++++ qtpy/tests/test_qtcore.py | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 3972ef6d..e65d01f8 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -20,6 +20,10 @@ from PyQt6.QtCore import pyqtProperty as Property from PyQt6.QtCore import QT_VERSION_STR as __version__ + # For issue #153 + from PyQt6.QtCore import QDateTime + QDateTime.toPython = QDateTime.toPyDateTime + # Map missing methods QCoreApplication.exec_ = QCoreApplication.exec QEventLoop.exec_ = QEventLoop.exec diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index d8068a5b..423b2f2b 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -10,8 +10,6 @@ def test_qtmsghandler(): assert QtCore.qInstallMessageHandler is not None -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), - reason="Targeted to PyQt5 or PySide2") def test_DateTime_toPython(): """Test QDateTime.toPython""" assert QtCore.QDateTime.toPython is not None From 82f8a7157a2092319c8773101bd65d3e352663da Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 8 Nov 2021 17:22:17 -0600 Subject: [PATCH 296/703] Add back legacy PYQT4 and PYSIDE package-level constants for compat --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 6a548dc3..29a5fc7d 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -98,7 +98,7 @@ class PythonQtWarning(Warning): is_old_pyqt = is_pyqt46 = False PYQT5 = True -PYQT6 = PYSIDE2 = PYSIDE6 = False +PYQT4 = PYQT6 = PYSIDE = PYSIDE2 = PYSIDE6 = False # Unless `FORCE_QT_API` is set, use previously imported Qt Python bindings if not os.environ.get('FORCE_QT_API'): From 62944de027f925188bc68452c339fafaa2719151 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sun, 7 Nov 2021 20:09:08 -0600 Subject: [PATCH 297/703] Sync standardized gitignore and gitattributes from other projects --- .gitattributes | 304 +++++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 183 ++++++++++++++++++++++++----- LICENSE.txt | 43 ++++--- 3 files changed, 481 insertions(+), 49 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..8b521664 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,304 @@ +# Standard gitattributes config + +# Set the default behavior, in case people don't have core.autocrlf set. +* text eol=lf + +# Explicitly declare text files you want to always be normalized and converted +# to native line endings on checkout. + +*.txt text + +# Source code +*.bash text eol=lf +*.c text +*.cpp text +*.csh text eol=lf +*.fish text eol=lf +*.inc text +*.ipynb text +*.h text +*.ksh text eol=lf +*.ps1 text +*.pxd text diff=python +*.py text diff=python +*.py3 text diff=python +*.pyi text diff=python +*.pyw text diff=python +*.pyx text diff=python +*.qss text +*.r text +*.R text +*.rb text +*.rmd text +*.Rmd text +*.rnw text +*.Rnw text +*.sh text eol=lf +*.zsh text eol=lf + +# Documentation +*.adoc text +*.latex text +*.LaTeX text +*.markdown text +*.md text +*.po text +*.pot text +*.rd text +*.Rd text +*.rst text +*.tex text +*.TeX text +*.tmpl text +*.tpl text + +# Web +*.atom text +*.css text +*.htm text +*.html text +*.js text +*.jsx text +*.json text +*.php text +*.pl text +*.rss text +*.sass text +*.scss text +*.xht text +*.xhtml text + +# Configuration +*.cfg text +*.cnf text +*.conf text +*.config text +*.desktop text +*.inf text +*.ini text +*.plist text +*.toml text +*.xml text +*.yml text +*.yaml text + +# Plain text data +*.cdl text +*.csv text +*.dif text +*.geojson text +*.gml text +*.kml text +*.sql text +*.tab text +*.tsv text +*.wkt text + +# Other text files +*.diff -text +*.patch -text + +# Special files +.*rc text +.checkignore text +.ciocheck text +.ciocopyright text +.editorconfig text +.gitattributes export-ignore +.gitconfig export-ignore +.gitignore export-ignore +.gitmodules export-ignore +.gitkeep export-ignore +*.lektorproject text +.nojekyll text +.project text + +AUTHORS text +CHANGELOG text +CHANGES text +CONTRIBUTING text +INSTALL text +license text +LICENSE text +NEWS text +NOTICES text +readme text +*README* text +RELEASE text +TODO text + +browserslist text +contents.lr text +makefile text +Makefile text +MANIFEST.in text + + +# Declare files that will always have CRLF line endings on checkout. +*.bat text eol=crlf +*.cmd text eol=crlf +*.vbs text eol=crlf +*.vb text eol=crlf + + +# Denote all files that are truly binary and should not be modified. + +# Executable +*.app binary +*.bin binary +*.deb binary +*.dll binary +*.dylib binary +*.elf binary +*.exe binary +*.ko binary +*.lib binary +*.msi binary +*.o binary +*.obj binary +*.pyc binary +*.pyd binary +*.pyo binary +*.rdb binary +*.Rdb binary +*.rdx binary +*.Rdx binary +*.rpm binary +*.so binary +*.sys binary + +# Data +*.cdf binary +*.db binary +*.dta binary +*.feather binary +*.fit binary +*.fits binary +*.fts binary +*.fods binary +*.geotiff binary +*.gpkg binary +*.h4 binary +*.h5 binary +*.hdf binary +*.hdf4 binary +*.hdf5 binary +*.mat binary +*.nc binary +*.npy binary +*.npz binary +*.odb binary +*.ods binary +*.p binary +*.parquet binary +*.pickle binary +*.pkl binary +*.rdata binary +*.Rdata binary +*.RData binary +*.rda binary +*.Rda binary +*.rds binary +*.Rds binary +*.sav binary +*.sqlite binary +*.wkb binary +*.xls binary +*.XLS binary +*.xlsx binary +*.XLSX binary + +# Documents +*.doc binary +*.DOC binary +*.docx binary +*.DOCX binary +*.epub binary +*.fodp binary +*.fodt binary +*.odp binary +*.odt binary +*.pdf binary +*.PDF binary +*.ppt binary +*.PPT binary +*.pptx binary +*.PPTX binary +*.rtf binary +*.RTF binary + +# Graphics +*.ai binary +*.bmp binary +*.eps binary +*.fodg binary +*.gif binary +*.icns binary +*.ico binary +*.jp2 binary +*.jpeg binary +*.jpg binary +*.mo binary +*.pdn binary +*.png binary +*.PNG binary +*.psd binary +*.odg binary +*.svg binary +*.svgz binary +*.tif binary +*.tiff binary +*.webp binary +*.xcf binary + +# Fonts +*.eot binary +*.otc binary +*.otf binary +*.ttc binary +*.ttf binary +*.woff binary +*.woff2 binary + +# Audio/Video +*.aac binary +*.flac binary +*.mka binary +*.mkv binary +*.mp3 binary +*.mp4 binary +*.oga binary +*.ogg binary +*.ogv binary +*.opus binary +*.wav binary +*.webm binary + + +# Archives +*.7z binary +*.bz2 binary +*.dmg binary +*.gz binary +*.lz binary +*.lzma binary +*.pyz binary +*.rar binary +*.sz binary +*.tar binary +*.tbz2 binary +*.tgz binary +*.tlz binary +*.txz binary +*.xz binary +*.zip binary + +# Spyder-related +*.results binary +*.spydata binary + +# Other +*.bak binary +*.lnk binary +*.temp binary +*.tmp binary diff --git a/.gitignore b/.gitignore index 2aa984af..26e37b2b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,171 @@ -# Python compiled files -*.py[ocd] +# Standard gitignore list -# C extensions -*.so +# Archive files +*.7z +*.bz2 +*.gz +*.lzma +*.lzma2 +*.tar +*.xz +*.zip -# Kate -.directory +# Temporary / backup files +*.bak +*.swp +*.tmp +*.temp -# PyPi configuration -.pypirc +# OS-specific files +.DS_Store +Thumbs.db -# Packages +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging *.egg -*.egg-info -dist -build -eggs -parts -bin -var -sdist -develop-eggs +*.egg-info/ +.eggs/ .installed.cfg -lib -lib64 +.Python +bin/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +lib/ +lib64/ +MANIFEST +parts/ +pip-wheel-metadata/ +sdist/ +share/python-wheels/ +var/ +wheels/ + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +.cache +*.cover +.coverage +.coverage.* +.hypothesis/ +.nox/ +.pytest_cache/ +.tox/ +coverage.xml +htmlcov/ +nosetests.xml # Translations *.mo -# Gedit files +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +doc/_build/ +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# Celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +ENV/ +venv/ +VENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject +spyder_crash.log + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Pylint +.pylint.d/ + +# gedit files *~ -# Other files -toread.md -.chache +# Notepad++ files +nppBackup/ + +# git .orig files +*.orig + +# IDEA project settings .idea/ -# Macos -*.DS_Store +# VSCode files +.vscode/ + +# Kate +.directory +.swp.* +*.swp + +# PyPi configuration +.pypirc -# End of File +# Other files +toread.md diff --git a/LICENSE.txt b/LICENSE.txt index 4a791817..b9173e4e 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,22 +1,21 @@ -The MIT License (MIT) - -Copyright (c) The Spyder Development Team - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - +The MIT License (MIT) + +Copyright (c) The Spyder Development Team + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 4f468ab3fe977d11d510a9d4d22393cb621f3111 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sun, 7 Nov 2021 20:11:00 -0600 Subject: [PATCH 298/703] Add Contributing Guide based on other Spyder projects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Althviz Moré --- CONTRIBUTING.md | 189 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..f21dd64f --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,189 @@ +# Contributing Guide + +QtPy is part of the Spyder IDE Github org, and is developed with standard Github flow. + +If you're not comfortable with at least the basics of ``git`` and GitHub, we recommend reading beginner tutorials such as [GitHub's Git Guide](https://github.com/git-guides/), its [introduction to basic Git commands](https://guides.github.com/introduction/git-handbook/#basic-git) and its [guide to the fork workflow](https://guides.github.com/activities/forking/), or (if you prefer) their [video equivalents](https://www.youtube.com/githubguides). +However, this contributing guide should fill you in on most of the basics you need to know. + +Let us know if you have any further questions, and we look forward to your contributions! + + +## Reporting Issues + +Discover a bug? +Want a new feature? +[Open](https://github.com/spyder-ide/qtpy/issues/new/choose) an [issue](https://github.com/spyder-ide/qtpy/issues)! +Make sure to describe the bug or feature in detail, with reproducible examples and references if possible, what you are looking to have fixed/added. +While we can't promise we'll fix everything you might find, we'll certainly take it into consideration, and typically welcome pull requests to resolve accepted issues. + + + +## Setting Up a Development Environment + +**Note**: You may need to substitute ``python3`` for ``python`` in the commands below on some Linux distros where ``python`` isn't mapped to ``python3`` (yet). + +### Fork and clone the repo + +First, navigate to the [project repository](https://github.com/spyder-ide/qtpy) in your web browser and press the ``Fork`` button to make a personal copy of the repository on your own Github account. +Then, click the ``Clone or Download`` button on your repository, copy the link and run the following on the command line to clone the repo: + +```bash +git clone +``` + +Finally, set the upstream remote to the official QtPy repo with: + +```bash +git remote add upstream https://github.com/spyder-ide/qtpy.git +``` + + +### Create and activate a fresh environment + +Particularly for development installs, we highly recommend you create and activate a virtual environment to avoid any conflicts with other packages on your system or causing any other issues. +Of course, you're free to use any environment management tool of your choice (conda, virtualenvwrapper, pyenv, etc). + +To do so with Conda (recommended), simply execute the following: + +```bash +conda create -c conda-forge -n qtpy-env python=3.9 +``` + +And activate it with + +```bash +conda activate qtpy-env +``` + +With pip/venv, you can create a virtual environment with + +```bash +python -m venv qtpy-env +``` + +And activate it with the following on Linux and macOS, + +```bash +source qtpy-env/bin/activate +``` + +or on Windows (cmd), + +```cmd +.\qtpy-env\Scripts\activate.bat +``` + +Regardless of the tool you use, make sure to remember to always activate your environment before using it. + + +### Install a Python Qt binding + +Before installing QtPy itself, make sure you have the Qt binding(s) you wish to develop against. +For example, for PyQt5 on Conda, you'd run: + +```bash +conda install -c conda-forge pyqt=5 +``` + +Or for the same using pip, you'd execute: + +```bash +python -m pip install pyqt5==5.* PyQtWebEngine==5.* +``` + +While having separate environments for each binding is recommended, you can install multiple in one environment and select between them using the ``QT_API`` environment variable, as described in the [Readme](https://github.com/spyder-ide/qtpy/blob/master/README.md) (for example, setting it to ``pyqt5`` to select PyQt5, if it is installed). + + +### Install QtPy in editable mode + +Finally, to install the QtPy package itself in editable ("development") mode, where updates to the source files will be reflected in the installed package, and include any additional dependencies used for development, run + +```bash +python -m pip install -e .[test] +``` + +You can then import and use QtPy as normal. +When you make changes in your local copy of the git repository, they will be reflected in your installed copy as soon as you re-run Python. + + + +## Deciding Which Branch to Use + +When you start to work on a new pull request (PR), you need to be sure that your work is done on top of the correct branch, and that you base your PR on Github against it. + +To guide you, issues on Github are marked with a milestone that indicates the correct branch to use. +If not, follow these guidelines: + +* Use the latest release branch (e.g. ``1.x``) to fix security issues and critical bugs only (if in any doubt, ask first) +* Use ``master`` branch for anything else, particularly introducing new features or breaking compatibility with previous versions + +Of course, if a bug is only present in ``master``, please base bugfixes on that branch. + + + +## Making Your Changes + +To start working on a new PR, you need to execute these commands, filling in the branch names where appropriate (```` is the branch you're basing your work against, e.g. ``master``, while ```` is the branch you'll be creating to store your changes, e.g. ``fix-startup-bug`` or ``add-widget-support``: + +```bash +git checkout +git pull upstream +git checkout -b +``` + +Once you've made and tested your changes, commit them with a descriptive message of 74 characters or less written in the imperative tense, with a capitalized first letter and no period at the end. +For example: + +```bash +git commit -am "Fix bug importing package on Windows" +``` + + + +## Running the Tests + +Once you've made your changes (or ideally, before), you'll want to run the full test suite and write new tests of your own, if you haven't already done so. + +This package uses the [Pytest](https://pytest.org) framework for its unit and integration tests, which are located inside the package alongside the tested code, in the ``tests/`` subdirectory. +We **strongly** suggest you run the full test suite before every commit (it should only take a few seconds to run on most machines). + +In general, any new major functionality should come with tests, and we welcome contributing to expand our coverage, increase reliability, and ensure we don't experience any regressions. +If you need help writing tests, please let us know, and we'll be happy to guide you. + +To run the tests, install the development dependencies as above, and then simply execute + +```bash +pytest +``` + +The ``pytest.ini`` config file configures a variety of settings and command line options for you, so you shouldn't need to pass any further options to pytest unless you have a specific use case. +For a more rigorous run mirroring what is executed on our CIs, execute the following: + +```bash +cd qtpy +python -bb -X dev -W error -m pytest +cd .. +``` + + + +## Pushing your Changes + +Now that your changes are ready to go, you'll need to push them to the appropriate remote. +All contributors, including core developers, should push to their personal fork and submit a PR from there, to avoid cluttering the upstream repo with feature branches. +To do so, run: + +```bash +git push -u origin +``` + +Where ```` is the name of your feature branch, e.g. ``fix-startup-bug``. + + + +## Submitting a Pull Request + +Finally, create a pull request to the [spyder-ide/qtpy repository](https://github.com/spyder-ide/qtpy/) on Github. +Make sure to set the target branch to the one you based your PR off of (``master`` or ``X.x``). + +Thanks for taking the time to read and follow this guide, and we look forward to your contributions! From 6e56484495513d289f5551757d776366919a3611 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 9 Nov 2021 13:29:32 -0600 Subject: [PATCH 299/703] Add further detail to contributing guide regarding commits and reviews --- CONTRIBUTING.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f21dd64f..739c3257 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -131,13 +131,19 @@ git pull upstream git checkout -b ``` -Once you've made and tested your changes, commit them with a descriptive message of 74 characters or less written in the imperative tense, with a capitalized first letter and no period at the end. +Once you've made and tested your changes, commit them with a descriptive, unique message of 74 characters or less written in the imperative tense, with a capitalized first letter and no period at the end. +Try to make your commit message understandable on its own, giving the reader a high-level idea of what your changes accomplished without having to dig into the diffs. For example: ```bash -git commit -am "Fix bug importing package on Windows" +git commit -am "Fix bug reading env variable when importing package on Windows" ``` +If your changes are complex (more than a few dozen lines) and can be broken into discrete steps/parts, its often a good idea to make multiple commits as you work. +On the other hand, if your changes are fairly small (less than a dozen lines), its usually better to make them as a single commit, and then use the ``git -a --amend`` (followed by ``git push -f``, if you've already pushed your work) if you spot a bug or a reviewer requests a change. + +These aren't hard and fast rules, so just use your best judgment, and if there does happen to be a significant issue we'll be happy to help. + ## Running the Tests @@ -186,4 +192,6 @@ Where ```` is the name of your feature branch, e.g. ``fix-startu Finally, create a pull request to the [spyder-ide/qtpy repository](https://github.com/spyder-ide/qtpy/) on Github. Make sure to set the target branch to the one you based your PR off of (``master`` or ``X.x``). +We'll then review your changes, and after they're ready to go, your work will become an official part of QtPy. + Thanks for taking the time to read and follow this guide, and we look forward to your contributions! From 3f19aa3a2707392e96a91b3fa87c6fa38ab794e1 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 17 Nov 2021 10:40:34 -0500 Subject: [PATCH 300/703] QtDataVisualization: Catch 'PythonQtError' when trying to do alias Version: Correct dev version to reflect being 1.x --- qtpy/__init__.py | 2 +- qtpy/_version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index ccb7d417..4393ee6d 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -235,5 +235,5 @@ class PythonQtWarning(Warning): # QtDataVisualization backward compatibility (QtDataVisualization vs. QtDatavisualization) # Only available for Qt5 bindings > 5.9 on Windows from . import QtDataVisualization as QtDatavisualization -except ImportError: +except (ImportError, PythonQtError): pass \ No newline at end of file diff --git a/qtpy/_version.py b/qtpy/_version.py index cb9f17f0..2f5af178 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (2, 0, 0, 'dev0') +version_info = (1, 12, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From c8fd4b6101c2745f029160fc81f0a87da2c7c379 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 17 Nov 2021 17:52:22 -0600 Subject: [PATCH 301/703] Add a warning for users still running legacy Qt4-based APIs to fix #261 --- .github/workflows/ci.yml | 2 ++ qtpy/__init__.py | 16 +++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6b17fee8..c1ece0d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,11 @@ on: push: branches: - master + - '*.x' pull_request: branches: - master + - '*.x' jobs: linux: diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 4393ee6d..aebaa2d7 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -101,6 +101,9 @@ class PythonQtWarning(Warning): # Names of the expected PySide2 api PYSIDE2_API = ['pyside2'] +# Names of the legacy APIs that we should warn users about +LEGACY_APIS = PYQT4_API + PYSIDE_API + # Detecting if a binding was specified by the user binding_specified = QT_API in os.environ @@ -236,4 +239,15 @@ class PythonQtWarning(Warning): # Only available for Qt5 bindings > 5.9 on Windows from . import QtDataVisualization as QtDatavisualization except (ImportError, PythonQtError): - pass \ No newline at end of file + pass + +# Warn if using a legacy, soon to be unsupported Qt API/binding +if API in LEGACY_APIS or initial_api in LEGACY_APIS: + warnings.warn( + "A deprecated Qt4-based binding (PyQt4/PySide) was installed, " + "imported or set via the 'QT_API' environment variable. " + "To ensure your application is still supported in QtPy 2.0, " + "please make sure it doesn't depend upon, import or " + "set the 'QT_API' env var to 'pyqt', 'pyqt4' or 'pyside'.", + DeprecationWarning, + ) From 0c5c2667ef98db0fef3eec4c8e60a463ffb43a5b Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 18 Nov 2021 12:13:23 -0500 Subject: [PATCH 302/703] Update README sponsors section --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 59691109..36d3b2d2 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,10 @@ Everyone is welcome to contribute! ## Sponsors -Become a sponsor to get your logo on our README on Github. +QtPy is funded thanks to the generous support of + +[![Quansight](https://pbs.twimg.com/profile_banners/945906846715580416/1607461712/300x100)](https://www.quansight.com/) + +and the donations we have received from our users around the world through [Open Collective](https://opencollective.com/spyder/): [![Sponsors](https://opencollective.com/spyder/sponsors.svg)](https://opencollective.com/spyder#support) From b1688920b9834e6a804f5cc6de1b1d1a0edcc1d3 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 18 Nov 2021 13:10:01 -0500 Subject: [PATCH 303/703] Add Numfocus logo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36d3b2d2..7b0f8c09 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Everyone is welcome to contribute! QtPy is funded thanks to the generous support of -[![Quansight](https://pbs.twimg.com/profile_banners/945906846715580416/1607461712/300x100)](https://www.quansight.com/) +[![Quansight](https://pbs.twimg.com/profile_banners/945906846715580416/1607461712/300x100)](https://www.quansight.com/)[![Numfocus](https://i2.wp.com/numfocus.org/wp-content/uploads/2017/07/NumFocus_LRG.png?fit=320%2C148&ssl=1)](https://numfocus.org/) and the donations we have received from our users around the world through [Open Collective](https://opencollective.com/spyder/): From 01b838e2f520c5a97a615fef3874e4d9f133cfa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Thu, 18 Nov 2021 13:47:49 -0500 Subject: [PATCH 304/703] Update Quansight logo --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7b0f8c09..a9274c9a 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,8 @@ Everyone is welcome to contribute! QtPy is funded thanks to the generous support of -[![Quansight](https://pbs.twimg.com/profile_banners/945906846715580416/1607461712/300x100)](https://www.quansight.com/)[![Numfocus](https://i2.wp.com/numfocus.org/wp-content/uploads/2017/07/NumFocus_LRG.png?fit=320%2C148&ssl=1)](https://numfocus.org/) + +[![Quansight](https://user-images.githubusercontent.com/16781833/142477716-53152d43-99a0-470c-a70b-c04bbfa97dd4.png)](https://www.quansight.com/)[![Numfocus](https://i2.wp.com/numfocus.org/wp-content/uploads/2017/07/NumFocus_LRG.png?fit=320%2C148&ssl=1)](https://numfocus.org/) and the donations we have received from our users around the world through [Open Collective](https://opencollective.com/spyder/): From eb505dccd6956be0dc78f8fe1675906c41c7b753 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Sat, 20 Nov 2021 12:20:41 -0800 Subject: [PATCH 305/703] py3compat: import Callable and MulableMapping for py2 compat The PY2 code path was not importing Callable, which breaks the compat module when it tries to import Callable from py3compat. Signed-off-by: David Aguilar --- qtpy/py3compat.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/py3compat.py b/qtpy/py3compat.py index cc4bdbf5..e92871a0 100644 --- a/qtpy/py3compat.py +++ b/qtpy/py3compat.py @@ -46,6 +46,7 @@ if PY2: # Python 2 import __builtin__ as builtins + from collections import Callable, MutableMapping import ConfigParser as configparser try: import _winreg as winreg From e1abbd573ab8cfab917cc86691ebf07b2ba9c336 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 1 Nov 2021 13:33:15 -0500 Subject: [PATCH 306/703] Add pyproject.toml for proper PEP 517 build support --- .github/workflows/test.sh | 5 +++-- RELEASE.md | 4 +--- pyproject.toml | 6 ++++++ 3 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 pyproject.toml diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 83a021a2..84d869b0 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -43,9 +43,10 @@ fi # Build wheel of package git clean -xdf -e *.coverage -python -bb -X dev setup.py sdist bdist_wheel # Needs migration to modern PEP 517-based build backend +pip install --upgrade build +python -bb -X dev -W error -W ignore:::pyparsing -m build -# Install package from build wheel +# Install package from built wheel echo dist/*.whl | xargs -I % python -bb -X dev -W error -m pip install --upgrade % # Print environment information diff --git a/RELEASE.md b/RELEASE.md index c9ca3799..f3d434ce 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -14,9 +14,7 @@ To release a new version of qtpy on PyPI: * git add . && git commit -m 'Release X.X.X' -* python setup.py sdist - -* python setup.py bdist_wheel +* python -m build * twine check dist/* diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..f6c16894 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel", +] +build-backend = "setuptools.build_meta" From e908508edd05d8b52902b3d44e4f21da439182b3 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 1 Nov 2021 14:34:59 -0500 Subject: [PATCH 307/703] Port setup metadata from legacy setup.py to declarative setup.cfg --- RELEASE.md | 4 ++-- qtpy/__init__.py | 2 +- qtpy/_version.py | 2 -- setup.cfg | 42 ++++++++++++++++++++++++++++++++++++++ setup.py | 53 +++++------------------------------------------- 5 files changed, 50 insertions(+), 53 deletions(-) delete mode 100644 qtpy/_version.py create mode 100644 setup.cfg diff --git a/RELEASE.md b/RELEASE.md index f3d434ce..dca29916 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -10,7 +10,7 @@ To release a new version of qtpy on PyPI: loghub -m vX.X.X spyder-ide/qtpy -* Update `_version.py` (set release version, remove 'dev0') +* Update `__version__` in `__init__.py` (set release version, remove 'dev0') * git add . && git commit -m 'Release X.X.X' @@ -22,7 +22,7 @@ To release a new version of qtpy on PyPI: * git tag -a vX.X.X -m 'Release X.X.X' -* Update `_version.py` (add 'dev0' and increment minor) +* Update `__version__` in `__init__.py` (add 'dev0' and increment minor) * git add . && git commit -m 'Back to work' diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 28623701..11d53512 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -from ._version import __version__ +__version__ = '2.0.0.dev0' class PythonQtError(RuntimeError): diff --git a/qtpy/_version.py b/qtpy/_version.py deleted file mode 100644 index cb9f17f0..00000000 --- a/qtpy/_version.py +++ /dev/null @@ -1,2 +0,0 @@ -version_info = (2, 0, 0, 'dev0') -__version__ = '.'.join(map(str, version_info)) diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 00000000..b2fb08e3 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,42 @@ +[metadata] +name = QtPy +version = attr: qtpy.__version__ +description = Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6) and additional custom QWidgets. +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/spyder-ide/qtpy +author = Colin Duquesnoy and the Spyder Development Team +author_email = spyder.python@gmail.com +maintainer = Spyder Development Team and QtPy Contributors +maintainer_email = spyder.python@gmail.com +license = MIT +license_file = LICENSE.txt +license_files = + AUTHORS.md + LICENSE.txt +classifiers = + Development Status :: 5 - Production/Stable + Environment :: Win32 (MS Windows) + Environment :: X11 Applications :: Qt + Intended Audience :: Developers + License :: OSI Approved :: MIT License + Operating System :: OS Independent + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 +keywords = qt PyQt5 PyQt6 PySide2 PySide6 + +[options] +packages = find: +install_requires = + packaging +python_requires = >=3.6 +zip_safe = False + +[options.packages.find] +exclude = + contrib + docs + tests* diff --git a/setup.py b/setup.py index 5aacfb6c..de11cffa 100644 --- a/setup.py +++ b/setup.py @@ -1,51 +1,8 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -""" -Setup script for qtpy -""" +"""Stub setup.py for use with legacy build tooling.""" -import os +import setuptools -from setuptools import setup, find_packages - -HERE = os.path.abspath(os.path.dirname(__file__)) - -version_ns = {} -with open(os.path.join(HERE, 'qtpy', '_version.py')) as f: - exec(f.read(), {}, version_ns) - -with open(os.path.join(HERE, 'README.md'), encoding='utf-8') as f: - LONG_DESCRIPTION = f.read() - -setup( - name='QtPy', - version=version_ns['__version__'], - packages=find_packages(exclude=['contrib', 'docs', 'tests*']), - python_requires='>=3.6', - install_requires=['packaging'], - keywords=["qt PyQt5 PyQt6 PySide2 PySide6"], - url='https://github.com/spyder-ide/qtpy', - license='MIT', - author='Colin Duquesnoy and the Spyder Development Team', - author_email='spyder.python@gmail.com', - maintainer='Spyder Development Team and QtPy Contributors', - maintainer_email='spyder.python@gmail.com', - description='Provides an abstraction layer on top of the various Qt ' - 'bindings (PyQt5/6 and PySide2/6) and additional custom ' - 'QWidgets.', - long_description=LONG_DESCRIPTION, - long_description_content_type='text/markdown', - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: X11 Applications :: Qt', - 'Environment :: Win32 (MS Windows)', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - ] -) +if __name__ == "__main__": + setuptools.setup() From 3f15808aad58a6f8c241838ee82193232cd7b2fc Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 1 Nov 2021 21:18:37 -0500 Subject: [PATCH 308/703] Add some additional useful modern metadata to the setup.cfg --- setup.cfg | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/setup.cfg b/setup.cfg index b2fb08e3..0beb4fbe 100644 --- a/setup.cfg +++ b/setup.cfg @@ -16,23 +16,33 @@ license_files = LICENSE.txt classifiers = Development Status :: 5 - Production/Stable + Environment :: MacOS X Environment :: Win32 (MS Windows) Environment :: X11 Applications :: Qt Intended Audience :: Developers License :: OSI Approved :: MIT License Operating System :: OS Independent Programming Language :: Python :: 3 + Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Topic :: Software Development :: Libraries + Topic :: Software Development :: User Interfaces + Topic :: Software Development :: Widget Sets keywords = qt PyQt5 PyQt6 PySide2 PySide6 +project_urls = + Github = https://github.com/spyder-ide/qtpy + Bug Tracker = https://github.com/spyder-ide/qtpy/issues + Parent Project = https://www.spyder-ide.org/ [options] packages = find: install_requires = packaging python_requires = >=3.6 +include_package_data = True zip_safe = False [options.packages.find] @@ -40,3 +50,8 @@ exclude = contrib docs tests* + +[options.extras_require] +test = + pytest>=6.0.0,<7.0 + pytest-cov>=2.11.0 From 194b265f3f5734974ae59b94616022878273e3df Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 1 Nov 2021 22:00:43 -0500 Subject: [PATCH 309/703] Update RELEASE.md to follow best current packaging practices --- RELEASE.md | 59 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 16 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index dca29916..0eed4606 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,31 +1,58 @@ -To release a new version of qtpy on PyPI: +# Release Procedure -* Close Github milestone +To release a new version of QtPy on PyPI: -* git fetch upstream && git merge upstream/master -* git clean -xfdi +## Prepare -* Update CHANGELOG.md using `loghub` to generate the list of issues and PRs merged to add at the top of the file +* Close Github milestone and ensure all issues are resolved/moved - loghub -m vX.X.X spyder-ide/qtpy +* `git switch master && git pull upstream master` (assuming `master` is the release branch) -* Update `__version__` in `__init__.py` (set release version, remove 'dev0') +* Check `MANIFEST.in` and `setup.cfg` to ensure they are up to date -* git add . && git commit -m 'Release X.X.X' -* python -m build +## Commit -* twine check dist/* +* `pip install --upgrade loghub` -* twine upload dist/* +* Update `CHANGELOG.md` using `loghub` to generate the list of issues and PRs merged to add at the top of the file: -* git tag -a vX.X.X -m 'Release X.X.X' + ```bash + loghub -m vX.Y.Z spyder-ide/qtpy + ``` -* Update `__version__` in `__init__.py` (add 'dev0' and increment minor) +* Update `__version__` in `__init__.py` (set release version, remove `.dev0`) -* git add . && git commit -m 'Back to work' +* `git commit -am 'Release X.Y.Z'` -* git push -* git push --tags +## Build + +**Note**: We use `pip` instead of `conda` here even on Conda installs, to ensure we always get the latest upstream versions of the build dependencies. + +* `git clean -xfdi` + +* `python -m pip install --upgrade-strategy eager --upgrade build pip setuptools twine wheel` + +* `python -bb -X dev -W error -m build` + + +## Upload + +* `twine check --strict dist/*` + +* `twine upload dist/*` + +* `git tag -a vX.Y.Z -m 'Release X.Y.Z'` + + +## Cleanup + +* Update `__version__` in `__init__.py` (add `.dev0` and increment minor) + +* `git commit -am 'Back to work'` + +* `git push upstream --follow-tags` + +* Create a GitHub release from the tag From 759a35010b60e435014b5377b63e454d5b9cb204 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 8 Nov 2021 00:02:19 -0600 Subject: [PATCH 310/703] Add pip check & twine check to CIs --- .github/workflows/ci.yml | 5 +++-- .github/workflows/test.sh | 7 ++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 151a1cfe..51f409de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: CI: 'True' PYTHON_VERSION: ${{ matrix.python-version }} USE_CONDA: ${{ matrix.use-conda }} + SKIP_PIP_CHECK: ${{ matrix.skip-pip-check }} PYQT5_VERSION: ${{ matrix.pyqt5-version }} PYQT6_VERSION: ${{ matrix.pyqt6-version }} PYSIDE2_VERSION: ${{ matrix.pyside2-version }} @@ -43,6 +44,7 @@ jobs: special-invocation: 'xvfb-run --auto-servernum ' - python-version: '3.6' use-conda: 'No' + skip-pip-check: 'true' # Pytest packaging issue on Python 3.6 defaults channel pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on collection/segfaults on patch test - os: ubuntu-latest python-version: '3.6' @@ -104,6 +106,5 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cd qtpy # Switch to test working dir per non-src-layout hack - python -m pip install --upgrade coveralls cat qtpy_basedir.txt - python -b -X dev -m coveralls --service=github --rcfile="../.coveragerc" --basedir="$(cat qtpy_basedir.txt)" + pipx run coveralls --service=github --rcfile="../.coveragerc" --basedir="$(cat qtpy_basedir.txt)" diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 84d869b0..683da29c 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -43,7 +43,7 @@ fi # Build wheel of package git clean -xdf -e *.coverage -pip install --upgrade build +python -m pip install --upgrade build python -bb -X dev -W error -W ignore:::pyparsing -m build # Install package from built wheel @@ -59,3 +59,8 @@ python -I -bb -X dev -W error -m pytest --cov-config ../.coveragerc --cov-append # Save QtPy base dir for coverage python -c "from pathlib import Path; import qtpy; print(Path(qtpy.__file__).parent.parent.resolve().as_posix())" > qtpy_basedir.txt cat qtpy_basedir.txt +cd .. + +# Check package and environment +pipx run twine check --strict dist/* +pip check -v || ${SKIP_PIP_CHECK:-false} From 61c34788765b547461bd321d3aaa2e00a9112ad8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Mon, 22 Nov 2021 10:05:20 -0500 Subject: [PATCH 311/703] PyQt6: Apply suggestions from code review for enum access Co-authored-by: Carlos Cordoba --- qtpy/enums_compat.py | 4 ++-- qtpy/sip.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qtpy/enums_compat.py b/qtpy/enums_compat.py index a8e71981..1340fa36 100644 --- a/qtpy/enums_compat.py +++ b/qtpy/enums_compat.py @@ -1,13 +1,14 @@ -# # Copyright © 2009- The Spyder Development Team # Copyright © 2012- University of North Carolina at Chapel Hill # Luke Campagnola ('luke.campagnola@%s.com' % 'gmail') # Ogi Moore ('ognyan.moore@%s.com' % 'gmail') # KIU Shueng Chuan ('nixchuan@%s.com' % 'gmail') # Licensed under the terms of the MIT License + """ Compatibility functions for scoped and unscoped enum access. """ + from . import PYQT6 if PYQT6: @@ -15,7 +16,6 @@ from . import sip - def promote_enums(module): """ Search enums in the given module and allow unscoped access. diff --git a/qtpy/sip.py b/qtpy/sip.py index ffb1d334..64e71e66 100644 --- a/qtpy/sip.py +++ b/qtpy/sip.py @@ -4,7 +4,7 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) -from . import PYQT6, PYQT5,PythonQtError +from . import PYQT6, PYQT5, PythonQtError if PYQT6: from PyQt6.sip import * From b02425b2f884ccefaca25d904f5d61bae14756a4 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 22 Nov 2021 10:18:34 -0500 Subject: [PATCH 312/703] PyQt6: Remove imported QtCore, QtGui and QtWidgets after promoting enums --- qtpy/QtCore.py | 1 + qtpy/QtGui.py | 1 + qtpy/QtWidgets.py | 1 + 3 files changed, 3 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index e65d01f8..1a66d88e 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -35,6 +35,7 @@ # Allow unscoped access for enums inside the QtCore module from .enums_compat import promote_enums promote_enums(QtCore) + del QtCore elif PYQT5: from PyQt5.QtCore import * from PyQt5.QtCore import pyqtSignal as Signal diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index ec861f7c..62344572 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -23,6 +23,7 @@ # Allow unscoped access for enums inside the QtGui module from .enums_compat import promote_enums promote_enums(QtGui) + del QtGui elif PYQT5: from PyQt5.QtGui import * elif PYSIDE2: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index fee8ffb6..2734e780 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -31,6 +31,7 @@ # Allow unscoped access for enums inside the QtWidgets module from .enums_compat import promote_enums promote_enums(QtWidgets) + del QtWidgets elif PYQT5: from PyQt5.QtWidgets import * elif PYSIDE6: From 1142c546aaf153d5c81604b1700c234a15086d47 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sun, 7 Nov 2021 20:37:35 -0600 Subject: [PATCH 313/703] Update license and authors files to be more descriptive --- AUTHORS.md | 17 ++++++++++------- LICENSE.txt | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index cb19d7b5..bec8765c 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -1,17 +1,20 @@ # Authors -## Maintainer +## Original Authors -Spyder Development Team ([Spyder-IDE](http://github.com/spyder-ide)) +* [pyqode.qt](https://github.com/pyQode/pyqode.qt): Colin Duquesnoy ([@ColinDuquesnoy](https://github.com/ColinDuquesnoy)) +* [spyderlib.qt](https://github.com/spyder-ide/spyder/commits/2.3/spyderlib/qt): Pierre Raybaut ([@PierreRaybaut](https://github.com/PierreRaybaut)) +* [qt-helpers](https://github.com/glue-viz/qt-helpers): Thomas Robitaille ([@astrofrog](https://www.github.com/astrofrog)) -## Main Authors +## Current Maintainers -* Colin Duquesnoy ([@ColinDuquesnoy](http://github.com/ColinDuquesnoy)) - -* [The QtPy Contributors](https://github.com/spyder-ide/qtpy/graphs/contributors) +* Daniel Althviz ([@dalthviz](https://github.com/dalthviz)) +* Carlos Cordoba ([@ccordoba12](https://github.com/ccordoba12)) +* C.A.M. Gerlach ([@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* Spyder Development Team ([Spyder-IDE](https://github.com/spyder-ide)) ## Contributors -* Thomas Robitaille ([@astrofrog](http://www.github.com/astrofrog)) +* [The QtPy Contributors](https://github.com/spyder-ide/qtpy/graphs/contributors) diff --git a/LICENSE.txt b/LICENSE.txt index b9173e4e..fed9ab7d 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) The Spyder Development Team +Copyright (c) 2011- QtPy contributors and others (see AUTHORS.md) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 876c074f0346cf3d3d8d817984b0fbef07d61ec2 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sun, 7 Nov 2021 20:44:01 -0600 Subject: [PATCH 314/703] Add basic security policy to repo --- MANIFEST.in | 1 + SECURITY.md | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 SECURITY.md diff --git a/MANIFEST.in b/MANIFEST.in index 2019e338..1d7cd43a 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,4 +2,5 @@ include AUTHORS.md include CHANGELOG.md include LICENSE.txt include README.md +include SECURITY.md recursive-include qtpy/tests *.py *.ui diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..e746a8a8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,24 @@ +# Security Policy + + +## Supported Versions + +The following summarizes the support status of recent QtPy versions. + +| Version | Supported | +| -------- | ------------------ | +| 2.0.x | :heavy_check_mark: | +| 1.11.x | :heavy_check_mark: | +| <=1.10.x | :x: | + + + +## Reporting a Vulnerability + +If you believe you've discovered a security vulnerability in Sub Manager, please contact the project maintainers, the Spyder development team, at spyder.python@gmail.com . +Please be sure to carefully document the vulnerability, including a summary, describing the impacts, identifying the line(s) of code affected, stating the conditions under which it is exploitable and including a minimal reproducible test case. +Further information and advice or patches on how to mitigate it is always welcome. +You can usually expect to hear back within 1 week, at which point we'll inform you of our evaluation of the vulnerability and what steps we plan to take, and will reach out if we need further clarification from you. +Once its patched, we'll send a followup email letting you know, and are happy to update you on its status should you further inquire. +While this is a volunteer project and we don't have financial compensation to offer, we can certainly publicly thank you for your help if you would like. +Thanks! From eafc078d2885493a27ada7bc584eea94118e7b80 Mon Sep 17 00:00:00 2001 From: kumattau Date: Fri, 26 Nov 2021 23:03:32 +0900 Subject: [PATCH 315/703] Map exec_ to their non-deprecated alternatives --- qtpy/QtCore.py | 6 ++++++ qtpy/QtGui.py | 6 +++++- qtpy/QtPrintSupport.py | 3 +++ qtpy/QtSql.py | 4 ++++ qtpy/QtWidgets.py | 5 +++++ 5 files changed, 23 insertions(+), 1 deletion(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index f5487d0e..b195d8c5 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -61,6 +61,12 @@ Qt.TextColorRole = Qt.ForegroundRole Qt.MidButton = Qt.MiddleButton + # Map DeprecationWarning methods + QCoreApplication.exec_ = QCoreApplication.exec + QEventLoop.exec_ = QEventLoop.exec + QThread.exec_ = QThread.exec + QTextStreamManipulator.exec_ = QTextStreamManipulator.exec + elif PYSIDE2: from PySide2.QtCore import * diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 62344572..253925c5 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -12,7 +12,7 @@ if PYQT6: - from PyQt6 import QtGui + from PyQt6 import QtGui from PyQt6.QtGui import * # Map missing/renamed methods @@ -31,5 +31,9 @@ elif PYSIDE6: from PySide6.QtGui import * QFontMetrics.width = QFontMetrics.horizontalAdvance + + # Map DeprecationWarning methods + QDrag.exec_ = QDrag.exec + QGuiApplication.exec_ = QGuiApplication.exec else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 53253f9e..85cbb981 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -20,6 +20,9 @@ QPrintPreviewWidget.print_ = QPrintPreviewWidget.print elif PYSIDE6: from PySide6.QtPrintSupport import * + # Map DeprecationWarning methods + QPageSetupDialog.exec_ = QPageSetupDialog.exec + QPrintDialog.exec_ = QPrintDialog.exec elif PYSIDE2: from PySide2.QtPrintSupport import * else: diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index e567ba3b..3cdaaf33 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -18,6 +18,10 @@ QSqlResult.exec_ = QSqlResult.exec elif PYSIDE6: from PySide6.QtSql import * + # Map DeprecationWarning methods + QSqlDatabase.exec_ = QSqlDatabase.exec + QSqlQuery.exec_ = QSqlQuery.exec + QSqlResult.exec_ = QSqlResult.exec elif PYSIDE2: from PySide2.QtSql import * else: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 2734e780..dfdff016 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -44,6 +44,11 @@ QTextEdit.tabStopWidth = QTextEdit.tabStopDistance QPlainTextEdit.setTabStopWidth = QPlainTextEdit.setTabStopDistance QPlainTextEdit.tabStopWidth = QPlainTextEdit.tabStopDistance + + # Map DeprecationWarning methods + QApplication.exec_ = QApplication.exec + QDialog.exec_ = QDialog.exec + QMenu.exec_ = QMenu.exec elif PYSIDE2: from PySide2.QtWidgets import * else: From d6f935e7dfcc7b0c5292802d9fdbaf8d5eaad6c1 Mon Sep 17 00:00:00 2001 From: kumattau Date: Wed, 1 Dec 2021 01:20:48 +0900 Subject: [PATCH 316/703] Add QtTextToSpeech Module --- qtpy/QtTextToSpeech.py | 16 ++++++++++++++++ qtpy/tests/test_qttexttospeech.py | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 qtpy/QtTextToSpeech.py create mode 100644 qtpy/tests/test_qttexttospeech.py diff --git a/qtpy/QtTextToSpeech.py b/qtpy/QtTextToSpeech.py new file mode 100644 index 00000000..e02e037a --- /dev/null +++ b/qtpy/QtTextToSpeech.py @@ -0,0 +1,16 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- +"""Provides QtTextToSpeech classes and functions.""" + +from . import PYQT5, PYSIDE2, PythonQtError + +if PYQT5: + from PyQt5.QtTextToSpeech import * +elif PYSIDE2: + from PySide2.QtTextToSpeech import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qttexttospeech.py b/qtpy/tests/test_qttexttospeech.py new file mode 100644 index 00000000..4cc0daa1 --- /dev/null +++ b/qtpy/tests/test_qttexttospeech.py @@ -0,0 +1,16 @@ +import pytest +from packaging import version +from qtpy import PYQT5, PYSIDE2, PYQT_VERSION + + +@pytest.mark.skipif(not ((PYQT5 and version.parse(PYQT_VERSION) >= version.parse('5.15.1')) or PYSIDE2), + reason="Only available in Qt5 bindings (PyQt5 >= 5.15.1 or PySide2)") +def test_qttexttospeech(): + """Test the qtpy.QtTextToSpeech namespace.""" + from qtpy import QtTextToSpeech + + assert QtTextToSpeech.QTextToSpeech is not None + assert QtTextToSpeech.QVoice is not None + + if PYSIDE2: + assert QtTextToSpeech.QTextToSpeechEngine is not None From dc9fd14806ea4b9be49e09385b7ddcde2f1c2f3a Mon Sep 17 00:00:00 2001 From: kumattau Date: Tue, 30 Nov 2021 23:50:37 +0900 Subject: [PATCH 317/703] Import all classes in QtTest module. --- qtpy/QtTest.py | 14 ++++++++++---- qtpy/tests/test_qttest.py | 17 ++++++++++++++++- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 7db1e14c..557d1c74 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -12,12 +12,18 @@ from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT6: - from PyQt6.QtTest import QTest + from PyQt6 import QtTest + from PyQt6.QtTest import * + + # Allow unscoped access for enums inside the QtTest module + from .enums_compat import promote_enums + promote_enums(QtTest) + del QtTest elif PYQT5: - from PyQt5.QtTest import QTest + from PyQt5.QtTest import * elif PYSIDE6: - from PySide6.QtTest import QTest + from PySide6.QtTest import * elif PYSIDE2: - from PySide2.QtTest import QTest + from PySide2.QtTest import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qttest.py b/qtpy/tests/test_qttest.py index 64fa0d4d..64554916 100644 --- a/qtpy/tests/test_qttest.py +++ b/qtpy/tests/test_qttest.py @@ -1,7 +1,22 @@ import pytest -from qtpy import QtTest +from packaging import version +from qtpy import QtTest, PYQT5, PYQT6, PYSIDE6, PYQT_VERSION def test_qttest(): """Test the qtpy.QtTest namespace""" assert QtTest.QTest is not None + + if PYQT5 or PYQT6 or PYSIDE6: + assert QtTest.QSignalSpy is not None + + if (PYQT5 and version.parse(PYQT_VERSION) >= version.parse('5.11')) or PYQT6 or PYSIDE6: + assert QtTest.QAbstractItemModelTester is not None + + +@pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), + reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" + "to work with scoped enum access") +def test_enum_access(): + """Test scoped and unscoped enum access for qtpy.QtTest.*.""" + assert QtTest.QTest.Click == QtTest.QTest.KeyAction.Click From aa44fea99120e6c793c2fdbff9df99be951b074a Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 29 Nov 2021 15:02:46 -0600 Subject: [PATCH 318/703] Add warning for deprecated/EoL Qt5 & PyQt5/PySide2 versions to fix #284 --- qtpy/__init__.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index aebaa2d7..6d978ae3 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -104,6 +104,11 @@ class PythonQtWarning(Warning): # Names of the legacy APIs that we should warn users about LEGACY_APIS = PYQT4_API + PYSIDE_API +# Minimum fully supported versions of Qt and the bindings +PYQT_VERSION_MIN = '5.9.0' +PYSIDE_VERSION_MIN = '5.12.0' +QT_VERSION_MIN = '5.9.0' + # Detecting if a binding was specified by the user binding_specified = QT_API in os.environ @@ -241,6 +246,17 @@ class PythonQtWarning(Warning): except (ImportError, PythonQtError): pass + +def _warn_old_minor_version(name, old_version, min_version): + warning_message = ( + "{name} version {old_version} is unsupported upstream and " + "deprecated by QtPy. To ensure your application is still supported " + "in QtPy 2.0, please make sure it doesn't depend upon {name} versions " + "older than {min_version}.".format( + name=name, old_version=old_version, min_version=min_version)) + warnings.warn(warning_message, DeprecationWarning) + + # Warn if using a legacy, soon to be unsupported Qt API/binding if API in LEGACY_APIS or initial_api in LEGACY_APIS: warnings.warn( @@ -251,3 +267,12 @@ class PythonQtWarning(Warning): "set the 'QT_API' env var to 'pyqt', 'pyqt4' or 'pyside'.", DeprecationWarning, ) +else: + if LooseVersion(QT_VERSION) < LooseVersion(QT_VERSION_MIN): + _warn_old_minor_version('Qt', QT_VERSION, QT_VERSION_MIN) + if PYQT_VERSION and (LooseVersion(PYQT_VERSION) + < LooseVersion(PYQT_VERSION_MIN)): + _warn_old_minor_version('PyQt', PYQT_VERSION, PYQT_VERSION_MIN) + elif PYSIDE_VERSION and (LooseVersion(PYSIDE_VERSION) + < LooseVersion(PYSIDE_VERSION_MIN)): + _warn_old_minor_version('PySide', PYSIDE_VERSION, PYSIDE_VERSION_MIN) From 1c7f098e3c89e5ae9c6883c8c337ee7e2971ecc9 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 1 Dec 2021 16:53:44 -0600 Subject: [PATCH 319/703] Ensure versions are defined before checking for outdated ones --- qtpy/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 079380c0..07172048 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -105,6 +105,10 @@ class PythonQtWarning(Warning): PYQT5 = True PYQT4 = PYQT6 = PYSIDE = PYSIDE2 = PYSIDE6 = False +PYQT_VERSION = None +PYSIDE_VERSION = None +QT_VERSION = None + # Unless `FORCE_QT_API` is set, use previously imported Qt Python bindings if not os.environ.get('FORCE_QT_API'): if 'PyQt6' in sys.modules: @@ -120,7 +124,6 @@ class PythonQtWarning(Warning): try: from PyQt5.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt5.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore - PYSIDE_VERSION = None if sys.platform == 'darwin': macos_version = parse(platform.mac_ver()[0]) @@ -145,7 +148,6 @@ class PythonQtWarning(Warning): try: from PyQt6.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt6.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore - PYSIDE_VERSION = None PYQT5 = False PYQT6 = True except ImportError: @@ -157,7 +159,6 @@ class PythonQtWarning(Warning): from PySide2 import __version__ as PYSIDE_VERSION # analysis:ignore from PySide2.QtCore import __version__ as QT_VERSION # analysis:ignore - PYQT_VERSION = None PYQT5 = False PYSIDE2 = True @@ -179,7 +180,6 @@ class PythonQtWarning(Warning): from PySide6 import __version__ as PYSIDE_VERSION # analysis:ignore from PySide6.QtCore import __version__ as QT_VERSION # analysis:ignore - PYQT_VERSION = None PYQT5 = False PYSIDE6 = True @@ -215,7 +215,7 @@ def _warn_old_minor_version(name, old_version, min_version): # Warn if using an End of Life, unsupported Qt API/binding -if parse(QT_VERSION) < parse(QT_VERSION_MIN): +if QT_VERSION and parse(QT_VERSION) < parse(QT_VERSION_MIN): _warn_old_minor_version('Qt', QT_VERSION, QT_VERSION_MIN) if PYQT_VERSION and parse(PYQT_VERSION) < parse(PYQT_VERSION_MIN): _warn_old_minor_version('PyQt', PYQT_VERSION, PYQT_VERSION_MIN) From d731e2e1c10656ab38c27867a0e8cab838525d94 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 1 Dec 2021 18:21:43 -0600 Subject: [PATCH 320/703] Use temporary dir instead of package dir in CIs to avoid shadowing --- .github/workflows/ci.yml | 2 +- .github/workflows/test.sh | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51f409de..f161066e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -105,6 +105,6 @@ jobs: COVERALLS_FLAG_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - cd qtpy # Switch to test working dir per non-src-layout hack + cd temp_test_dir # Switch to test working dir per non-src-layout hack cat qtpy_basedir.txt pipx run coveralls --service=github --rcfile="../.coveragerc" --basedir="$(cat qtpy_basedir.txt)" diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 683da29c..bcaffe96 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -53,13 +53,14 @@ echo dist/*.whl | xargs -I % python -bb -X dev -W error -m pip install --upgrade conda list # Run tests -cd qtpy # Hack to work around non-src layout pulling in local instead of installed package for cov -python -I -bb -X dev -W error -m pytest --cov-config ../.coveragerc --cov-append +mkdir -p temp_test_dir +pushd temp_test_dir # Hack to work around non-src layout pulling in local instead of installed package for cov +python -I -bb -X dev -W error -m pytest ../qtpy --cov-config ../.coveragerc --cov-append # Save QtPy base dir for coverage python -c "from pathlib import Path; import qtpy; print(Path(qtpy.__file__).parent.parent.resolve().as_posix())" > qtpy_basedir.txt cat qtpy_basedir.txt -cd .. +popd # Check package and environment pipx run twine check --strict dist/* From 0a53012ea2283f859a67373d0c6f3644507410f1 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 2 Dec 2021 10:09:35 -0500 Subject: [PATCH 321/703] Update RELEASE.md for 1.x version --- RELEASE.md | 68 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 834408bf..80a22188 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,29 +1,71 @@ -To release a new version of qtpy on PyPI: +## PyPI -* Close Github milestone +To release a new version of qtpy on PyPI (replacing `X.Y.Z` for the corresponding version when needed): -* git fetch upstream && git merge upstream/master +* Close [GitHub milestone](https://github.com/spyder-ide/qtpy/milestones) -* git clean -xfdi +* Update local repo with -* Update CHANGELOG.md + git checkout 1.x && git fetch upstream && git merge upstream/1.x + +* Clean local repo with + + git clean -xfdi + +* Update `CHANGELOG.md` with + + loghub spyder-ide/qtpy -m vX.Y.Z * Update `_version.py` (set release version, remove 'dev0') -* git add and git commit +* Create release commit with + + git add . && git commit -m "Release X.Y.Z" + +* Update the most important release packages with + + pip install -U pip setuptools twine wheel + +* Create source distribution with + + python setup.py sdist + +* Create wheel with -* python setup.py sdist + python setup.py bdist_wheel -* python setup.py bdist_wheel +* Check release files with -* twine upload dist/* + twine check dist/* -* git tag -a vX.X.X -m 'comment' +* Upload to PyPI package files with + + twine upload dist/* + +* Create release tag with + + git tag -a vX.Y.Z -m "Release X.Y.Z" * Update `_version.py` (add 'dev0' and increment minor) -* git add and git commit +* Create `Back to work` commit with + + git add . && git commit -m "Back to work" + +* Merge new release commits on master with + + git checkout master && git fetch upstream && git merge upstream/master + git merge 1.x + git commit -m "Merge from 1.x: Release X.Y.Z" + +* Update remote repository with + + git push upstream master + git push upstream 1.x + git push upstream --tags + +## Conda-forge -* git push +To release a new version of qtpy on Conda-forge -* git push --tags +* After the release on PyPI an automatic PR in the [conda-forge feedstock repo for qtpy](https://github.com/conda-forge/qtpy-feedstock/pulls) should open. Merging this PR will update the respective conda-forge package. \ No newline at end of file From a7e5f1425c7bb7453e62dfecce68949ad27f92fe Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Thu, 2 Dec 2021 18:10:56 -0600 Subject: [PATCH 322/703] Sync release instructions with improvements from master --- RELEASE.md | 123 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 86 insertions(+), 37 deletions(-) diff --git a/RELEASE.md b/RELEASE.md index 80a22188..1cbe8386 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,71 +1,120 @@ +# Release Procedure + +In the commands below, replace `X.Y.Z` with the release version when needed. + +**Note**: We use `pip` instead of `conda` here even on Conda installs, to ensure we always get the latest upstream versions of the build dependencies. + + ## PyPI -To release a new version of qtpy on PyPI (replacing `X.Y.Z` for the corresponding version when needed): +To release a new version of QtPy on PyPI: + + +### Prepare + +* Close [GitHub milestone](https://github.com/spyder-ide/qtpy/milestones) and ensure all issues are resolved/moved + +* Update local repo + + ```bash + git restore . && git switch 1.x && git pull upstream 1.x + ``` + +* Clean local repo + + ```bash + git clean -xfdi + ``` + + +### Commit + +* Install/upgrade Loghub + + ```bash + pip install --upgrade loghub + ``` -* Close [GitHub milestone](https://github.com/spyder-ide/qtpy/milestones) +* Update `CHANGELOG.md` using Loghub to generate the list of issues and PRs merged to add at the top of the file -* Update local repo with + ```bash + loghub -m vX.Y.Z spyder-ide/qtpy + ``` - git checkout 1.x && git fetch upstream && git merge upstream/1.x +* Update `qtpy/_version.py` (set release version, remove `dev0`) -* Clean local repo with +* Create release commit - git clean -xfdi + ```bash + git commit -am "Release X.Y.Z" + ``` -* Update `CHANGELOG.md` with - loghub spyder-ide/qtpy -m vX.Y.Z +### Build -* Update `_version.py` (set release version, remove 'dev0') +* Update the packaging stack -* Create release commit with + ```bash + python -m pip install --upgrade pip + pip install --upgrade --upgrade-strategy eager setuptools twine wheel + ``` - git add . && git commit -m "Release X.Y.Z" +* Build source distribution and wheel -* Update the most important release packages with + ```bash + python -bb -X dev -W error setup.py sdist bdist_wheel + ``` - pip install -U pip setuptools twine wheel +* Check distribution archives -* Create source distribution with + ```bash + twine check --strict dist/* + ``` - python setup.py sdist -* Create wheel with +### Release - python setup.py bdist_wheel +* Upload distribution packages to PyPI -* Check release files with + ```bash + twine upload dist/* + ``` - twine check dist/* +* Create release tag -* Upload to PyPI package files with + ```bash + git tag -a vX.Y.Z -m "Release X.Y.Z" + ``` - twine upload dist/* -* Create release tag with +### Finalize - git tag -a vX.Y.Z -m "Release X.Y.Z" +* Update `qtpy/_version.py` (add `dev0` and increment minor) -* Update `_version.py` (add 'dev0' and increment minor) +* Create `Back to work` commit -* Create `Back to work` commit with + ```bash + git commit -am "Back to work" + ``` - git add . && git commit -m "Back to work" +* Push new release commits and tags to `1.x` -* Merge new release commits on master with + ```bash + git push upsteam 1.x --follow-tags + ``` - git checkout master && git fetch upstream && git merge upstream/master - git merge 1.x - git commit -m "Merge from 1.x: Release X.Y.Z" +* Merge new release commits to `master` -* Update remote repository with + ```bash + git switch master && git pull upstream master + git merge 1.x -m "Merge from 1.x: Release X.Y.Z" + git push upstream master + ``` - git push upstream master - git push upstream 1.x - git push upstream --tags -## Conda-forge +## Conda-Forge -To release a new version of qtpy on Conda-forge +To release a new version of QtPy on Conda-Forge: -* After the release on PyPI an automatic PR in the [conda-forge feedstock repo for qtpy](https://github.com/conda-forge/qtpy-feedstock/pulls) should open. Merging this PR will update the respective conda-forge package. \ No newline at end of file +* After the release on PyPI, an automatic PR in the [Conda-Forge feedstock repo for QtPy](https://github.com/conda-forge/qtpy-feedstock/pulls) should open. + Merging this PR will update the respective Conda-Forge package. From fd79fc7f28e0bc18c362c1d621db244be5208d45 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 3 Dec 2021 12:16:40 -0500 Subject: [PATCH 323/703] Release 1.11.3 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ qtpy/_version.py | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4141eb32..2379a228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # History of changes +## Version 1.11.3 (2021/12/03) + +### Issues Closed + +* [Issue 284](https://github.com/spyder-ide/qtpy/issues/284) - Warn if using a deprecated and/or unsupported Qt 5 version ([PR 289](https://github.com/spyder-ide/qtpy/pull/289) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 280](https://github.com/spyder-ide/qtpy/issues/280) - import qtpy raise except when only have PyQt4 ([PR 281](https://github.com/spyder-ide/qtpy/pull/281) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 261](https://github.com/spyder-ide/qtpy/issues/261) - Add a deprecation warning for unsupported `Qt` versions and bindings (at least `Qt4`: `PyQt4` and `PySide`) ([PR 283](https://github.com/spyder-ide/qtpy/pull/283) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) + +In this release 3 issues were closed. + +### Pull Requests Merged + +* [PR 293](https://github.com/spyder-ide/qtpy/pull/293) - PR: Update RELEASE.md for 1.x version, by [@dalthviz](https://github.com/dalthviz) +* [PR 289](https://github.com/spyder-ide/qtpy/pull/289) - PR: Add warning for deprecated/EoL Qt5 & PyQt5/PySide2 versions to fix #284, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([284](https://github.com/spyder-ide/qtpy/issues/284)) +* [PR 285](https://github.com/spyder-ide/qtpy/pull/285) - PR: Import Callable and MutableMapping in py3compat for Python 2 compat, by [@davvid](https://github.com/davvid) +* [PR 283](https://github.com/spyder-ide/qtpy/pull/283) - PR: Add a warning for developers still running legacy Qt4-based APIs, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([261](https://github.com/spyder-ide/qtpy/issues/261)) +* [PR 281](https://github.com/spyder-ide/qtpy/pull/281) - PR: Catch `PythonQtError` when trying to do alias for `QtDataVisualization` and dev version correction, by [@dalthviz](https://github.com/dalthviz) ([280](https://github.com/spyder-ide/qtpy/issues/280)) + +In this release 5 pull requests were closed. + + +---- + + ## Version 1.11.2 (2021-09-23) ### Issues Closed diff --git a/qtpy/_version.py b/qtpy/_version.py index 2f5af178..310a76d6 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 12, 0, 'dev0') +version_info = (1, 11, 3) __version__ = '.'.join(map(str, version_info)) From a4adaee7340137b18e28f4502fc22abc1224998d Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 3 Dec 2021 12:23:26 -0500 Subject: [PATCH 324/703] Back to work --- qtpy/_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/_version.py b/qtpy/_version.py index 310a76d6..2f5af178 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 11, 3) +version_info = (1, 12, 0, 'dev0') __version__ = '.'.join(map(str, version_info)) From 92e2b86d8486ed557af19ed9d5240a7c5167d803 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 3 Dec 2021 13:26:54 -0600 Subject: [PATCH 325/703] Add minimum version check for Qt6 >=6.2.0 --- qtpy/__init__.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 07172048..02c9a28f 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -87,9 +87,13 @@ class PythonQtWarning(Warning): PYSIDE6_API = ['pyside6'] # Minimum supported versions of Qt and the bindings -PYQT_VERSION_MIN = '5.9.0' -PYSIDE_VERSION_MIN = '5.12.0' -QT_VERSION_MIN = '5.9.0' +QT5_VERSION_MIN = PYQT5_VERSION_MIN = '5.9.0' +PYSIDE2_VERSION_MIN = '5.12.0' +QT6_VERSION_MIN = PYQT6_VERSION_MIN = PYSIDE6_VERSION_MIN = '6.2.0' + +QT_VERSION_MIN = QT5_VERSION_MIN +PYQT_VERSION_MIN = PYQT5_VERSION_MIN +PYSIDE_VERISION_MIN = PYSIDE2_VERSION_MIN # Detecting if a binding was specified by the user binding_specified = QT_API in os.environ @@ -207,17 +211,26 @@ class PythonQtWarning(Warning): def _warn_old_minor_version(name, old_version, min_version): """Warn if using a Qt or binding version no longer supported by QtPy.""" warning_message = ( - "{name} version {old_version} is no longer supported upstream or " + "{name} version {old_version} is not supported upstream or " "by QtPy 2.0. To ensure your application works correctly with QtPy, " "please upgrade to {name} {min_version} or later.".format( name=name, old_version=old_version, min_version=min_version)) warnings.warn(warning_message, PythonQtWarning) -# Warn if using an End of Life, unsupported Qt API/binding -if QT_VERSION and parse(QT_VERSION) < parse(QT_VERSION_MIN): - _warn_old_minor_version('Qt', QT_VERSION, QT_VERSION_MIN) -if PYQT_VERSION and parse(PYQT_VERSION) < parse(PYQT_VERSION_MIN): - _warn_old_minor_version('PyQt', PYQT_VERSION, PYQT_VERSION_MIN) -elif PYSIDE_VERSION and parse(PYSIDE_VERSION) < parse(PYSIDE_VERSION_MIN): - _warn_old_minor_version('PySide', PYSIDE_VERSION, PYSIDE_VERSION_MIN) +# Warn if using an End of Life or unsupported Qt API/binding minor version +if QT_VERSION: + if parse(QT_VERSION) < parse(QT5_VERSION_MIN): + _warn_old_minor_version('Qt5', QT_VERSION, QT5_VERSION_MIN) + elif parse('6') <= parse(QT_VERSION) < parse(QT6_VERSION_MIN): + _warn_old_minor_version('Qt6', QT_VERSION, QT6_VERSION_MIN) +if PYQT_VERSION: + if parse(PYQT_VERSION) < parse(PYQT5_VERSION_MIN): + _warn_old_minor_version('PyQt5', PYQT_VERSION, PYQT5_VERSION_MIN) + elif parse('6') <= parse(PYQT_VERSION) < parse(PYQT6_VERSION_MIN): + _warn_old_minor_version('PyQt6', PYQT_VERSION, PYQT6_VERSION_MIN) +elif PYSIDE_VERSION: + if parse(PYSIDE_VERSION) < parse(PYSIDE2_VERSION_MIN): + _warn_old_minor_version('PySide2', PYSIDE_VERSION, PYSIDE2_VERSION_MIN) + elif parse('6') <= parse(PYSIDE_VERSION) < parse(PYSIDE6_VERSION_MIN): + _warn_old_minor_version('PySide6', PYSIDE_VERSION, PYSIDE6_VERSION_MIN) From b794ad950dff364436b2b779988242fa876b49bb Mon Sep 17 00:00:00 2001 From: CAM Gerlach Date: Mon, 6 Dec 2021 16:38:13 -0600 Subject: [PATCH 326/703] Add public constants to __init__ for version check and simplify warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Althviz Moré --- qtpy/__init__.py | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 02c9a28f..bb1f7301 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -106,8 +106,8 @@ class PythonQtWarning(Warning): assert API in (PYQT5_API + PYQT6_API + PYSIDE2_API + PYSIDE6_API) is_old_pyqt = is_pyqt46 = False -PYQT5 = True -PYQT4 = PYQT6 = PYSIDE = PYSIDE2 = PYSIDE6 = False +QT5 = PYQT5 = True +QT4 = QT6 = PYQT4 = PYQT6 = PYSIDE = PYSIDE2 = PYSIDE6 = False PYQT_VERSION = None PYSIDE_VERSION = None @@ -129,6 +129,8 @@ class PythonQtWarning(Warning): from PyQt5.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt5.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore + QT5 = PYQT5 = True + if sys.platform == 'darwin': macos_version = parse(platform.mac_ver()[0]) if macos_version < parse('10.10'): @@ -152,8 +154,10 @@ class PythonQtWarning(Warning): try: from PyQt6.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore from PyQt6.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore - PYQT5 = False - PYQT6 = True + + QT5 = PYQT5 = False + QT6 = PYQT6 = True + except ImportError: API = os.environ['QT_API'] = 'pyside2' @@ -164,7 +168,7 @@ class PythonQtWarning(Warning): from PySide2.QtCore import __version__ as QT_VERSION # analysis:ignore PYQT5 = False - PYSIDE2 = True + QT5 = PYSIDE2 = True if sys.platform == 'darwin': macos_version = parse(platform.mac_ver()[0]) @@ -184,8 +188,8 @@ class PythonQtWarning(Warning): from PySide6 import __version__ as PYSIDE_VERSION # analysis:ignore from PySide6.QtCore import __version__ as QT_VERSION # analysis:ignore - PYQT5 = False - PYSIDE6 = True + QT5 = PYQT5 = False + QT6 = PYSIDE6 = True except ImportError: API = os.environ['QT_API'] = 'pyqt5' @@ -203,7 +207,7 @@ class PythonQtWarning(Warning): try: # QtDataVisualization backward compatibility (QtDataVisualization vs. QtDatavisualization) # Only available for Qt5 bindings > 5.9 on Windows - from . import QtDataVisualization as QtDatavisualization + from . import QtDataVisualization as QtDatavisualization # analysis:ignore except (ImportError, PythonQtError): pass @@ -211,8 +215,8 @@ class PythonQtWarning(Warning): def _warn_old_minor_version(name, old_version, min_version): """Warn if using a Qt or binding version no longer supported by QtPy.""" warning_message = ( - "{name} version {old_version} is not supported upstream or " - "by QtPy 2.0. To ensure your application works correctly with QtPy, " + "{name} version {old_version} is not supported by QtPy. " + "To ensure your application works correctly with QtPy, " "please upgrade to {name} {min_version} or later.".format( name=name, old_version=old_version, min_version=min_version)) warnings.warn(warning_message, PythonQtWarning) @@ -220,17 +224,18 @@ def _warn_old_minor_version(name, old_version, min_version): # Warn if using an End of Life or unsupported Qt API/binding minor version if QT_VERSION: - if parse(QT_VERSION) < parse(QT5_VERSION_MIN): + if QT5 and (parse(QT_VERSION) < parse(QT5_VERSION_MIN)): _warn_old_minor_version('Qt5', QT_VERSION, QT5_VERSION_MIN) - elif parse('6') <= parse(QT_VERSION) < parse(QT6_VERSION_MIN): + elif QT6 and (parse(QT_VERSION) < parse(QT6_VERSION_MIN)): _warn_old_minor_version('Qt6', QT_VERSION, QT6_VERSION_MIN) + if PYQT_VERSION: - if parse(PYQT_VERSION) < parse(PYQT5_VERSION_MIN): + if PYQT5 and (parse(PYQT_VERSION) < parse(PYQT5_VERSION_MIN)): _warn_old_minor_version('PyQt5', PYQT_VERSION, PYQT5_VERSION_MIN) - elif parse('6') <= parse(PYQT_VERSION) < parse(PYQT6_VERSION_MIN): + elif PYQT6 and (parse(PYQT_VERSION) < parse(PYQT6_VERSION_MIN)): _warn_old_minor_version('PyQt6', PYQT_VERSION, PYQT6_VERSION_MIN) elif PYSIDE_VERSION: - if parse(PYSIDE_VERSION) < parse(PYSIDE2_VERSION_MIN): + if PYSIDE2 and (parse(PYSIDE_VERSION) < parse(PYSIDE2_VERSION_MIN)): _warn_old_minor_version('PySide2', PYSIDE_VERSION, PYSIDE2_VERSION_MIN) - elif parse('6') <= parse(PYSIDE_VERSION) < parse(PYSIDE6_VERSION_MIN): + elif PYSIDE6 and (parse(PYSIDE_VERSION) < parse(PYSIDE6_VERSION_MIN)): _warn_old_minor_version('PySide6', PYSIDE_VERSION, PYSIDE6_VERSION_MIN) From 005641217fbece5e2844ffb844596c90ae3850b6 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 8 Dec 2021 19:45:23 -0600 Subject: [PATCH 327/703] Add support for Python 3.10 on CIs and packaging --- .github/workflows/ci.yml | 25 +++++++++++++------------ .github/workflows/test.sh | 13 ++++--------- qtpy/tests/test_uic.py | 2 +- setup.cfg | 3 ++- 4 files changed, 20 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f161066e..4a82f14f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,23 +37,24 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.6', '3.9'] + python-version: ['3.6', '3.10'] use-conda: ['Yes', 'No'] include: - os: ubuntu-latest - special-invocation: 'xvfb-run --auto-servernum ' + special-invocation: 'xvfb-run --auto-servernum ' # Needed for GUI tests to work + - python-version: '3.10' + skip-pyside2: true # Skip Pyside2 on all Python 3.10 builds until it supports it + - use-conda: 'Yes' # No conda packages yet for Qt6 + skip-pyqt6: true + skip-pyside6: true - python-version: '3.6' use-conda: 'No' skip-pip-check: 'true' # Pytest packaging issue on Python 3.6 defaults channel - pyside2-version: 5.12.0 # 5.12.1-5.12.6 fails on collection/segfaults on patch test - - os: ubuntu-latest - python-version: '3.6' - use-conda: 'No' - skip-pyqt6: true # No wheels on Py 3.6 Linux CIs + pyside2-version: '5.12.0' # 5.12.1-5.12.6 fails on collection/segfaults on patch test - os: windows-latest - python-version: '3.9' + python-version: '3.10' use-conda: 'No' - pyside2-version: 5.15 # No 5.12 wheel on Windows and Python 3.9 + pyside2-version: '5.15' # No 5.12 wheel on Windows and Python 3.10 - os: windows-latest python-version: '3.6' use-conda: 'Yes' @@ -61,7 +62,7 @@ jobs: - os: macos-latest python-version: '3.6' use-conda: 'No' - skip-pyqt6: true # No wheels on Py 3.6 macOS CIs + skip-pyside6: true # Py3.6 wheels apparently still don't work on latest macOS steps: - name: Checkout branch uses: actions/checkout@v2 @@ -91,13 +92,13 @@ jobs: if: (! matrix.skip-pyqt5) run: ./.github/workflows/test.sh pyqt5 - name: Test PyQt6 - if: always() && (! ((matrix.skip-pyqt6) || (matrix.use-conda == 'Yes'))) # No conda packages yet for Qt6/PyQt6 + if: always() && (! (matrix.skip-pyqt6)) run: ./.github/workflows/test.sh pyqt6 - name: Test PySide2 if: always() && (! (matrix.skip-pyside2)) run: ./.github/workflows/test.sh pyside2 - name: Test PySide6 - if: always() && (! ((matrix.skip-pyside6) || (matrix.use-conda == 'Yes'))) # No conda packages yet for Qt6/Pyside6 + if: always() && (! (matrix.skip-pyside6)) run: ./.github/workflows/test.sh pyside6 - name: Upload coverage data to coveralls.io shell: bash diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index bcaffe96..04c030f6 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -3,16 +3,11 @@ # Activate conda properly eval "$(conda shell.bash hook)" -# Set conda channel -if [ "$USE_CONDA" = "No" ]; then - CONDA_CHANNEL_ARG="-c anaconda" -fi - # Remove any existing env -conda remove -q -n test-env ${CONDA_CHANNEL_ARG} --all || true +conda remove -q -n test-env --all || true # Create and activate conda environment for this test -conda create -q -n test-env ${CONDA_CHANNEL_ARG} python=${PYTHON_VERSION} pytest pytest-cov +conda create -q -n test-env python=${PYTHON_VERSION} pytest pytest-cov conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then @@ -44,10 +39,10 @@ fi # Build wheel of package git clean -xdf -e *.coverage python -m pip install --upgrade build -python -bb -X dev -W error -W ignore:::pyparsing -m build +python -bb -X dev -W error -m build # Install package from built wheel -echo dist/*.whl | xargs -I % python -bb -X dev -W error -m pip install --upgrade % +echo dist/*.whl | xargs -I % python -bb -X dev -W error -W "ignore::DeprecationWarning:pip._internal.locations._distutils" -W "ignore::DeprecationWarning:distutils.command.install" -m pip install --upgrade % # Print environment information conda list diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index db9f660b..a4a32ef7 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -140,4 +140,4 @@ def test_load_full_uic(): else: objects = ['compileUi', 'compileUiDir', 'loadUi', 'loadUiType', 'widgetPluginPath'] - assert all((hasattr(uic, o) for o in objects)) + assert all(hasattr(uic, o) for o in objects) diff --git a/setup.cfg b/setup.cfg index 0beb4fbe..a8048465 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [metadata] name = QtPy version = attr: qtpy.__version__ -description = Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6) and additional custom QWidgets. +description = Provides an abstraction layer on top of the various Qt bindings (PyQt5/6 and PySide2/6). long_description = file: README.md long_description_content_type = text/markdown url = https://github.com/spyder-ide/qtpy @@ -28,6 +28,7 @@ classifiers = Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Topic :: Software Development :: Libraries Topic :: Software Development :: User Interfaces Topic :: Software Development :: Widget Sets From b860606c2ce654044131228ddfb741c517ab282e Mon Sep 17 00:00:00 2001 From: stonebig Date: Sat, 11 Dec 2021 20:27:00 +0100 Subject: [PATCH 328/703] make QLibraryInfo.location works (found while trying to patch Pyzo for PyQt6-6.2.2) --- qtpy/QtCore.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index b195d8c5..c5b9b602 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -28,6 +28,8 @@ QCoreApplication.exec_ = QCoreApplication.exec QEventLoop.exec_ = QEventLoop.exec QThread.exec_ = QThread.exec + + QLibraryInfo.location = QLibraryInfo.path # Those are imported from `import *` del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR From f434884e6aa6f5ec20fc12f763e5d68306f73cb1 Mon Sep 17 00:00:00 2001 From: stonebig Date: Sat, 11 Dec 2021 20:36:39 +0100 Subject: [PATCH 329/703] 'width' need for PyQt6 same as PySide6 --- qtpy/QtGui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 253925c5..8faa724a 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -14,6 +14,7 @@ if PYQT6: from PyQt6 import QtGui from PyQt6.QtGui import * + QFontMetrics.width = QFontMetrics.horizontalAdvance # Map missing/renamed methods QDrag.exec_ = QDrag.exec From a6783036b310335c9187c171f28728f0d7f0718d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Thu, 16 Dec 2021 16:37:17 -0500 Subject: [PATCH 330/703] Update README to describe QtPy usefulness when migrating between Qt bindings/versions (#301) * Update README.md to add a line for QtPy usefulness when migration between Qt bindings/versions * Apply suggestions from code review to README.md Co-authored-by: CAM Gerlach Co-authored-by: Carlos Cordoba --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a9274c9a..bd272131 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,10 @@ write applications using a single API call to either PyQt or PySide. It provides support for PyQt5, PyQt6, PySide6, PySide2 using the Qt5 layout (where the QtGui module has been split into QtGui and QtWidgets). -Basically, you can write your code as if you were using PySide2 -but import Qt modules from `qtpy` instead of `PySide2` (or `PyQt5`) +Basically, you can write your code as if you were using PyQt or PySide directly, +but import Qt modules from `qtpy` instead of `PyQt5`, `PyQt6`, `PySide2`, or `PySide6`. +Accordingly, when porting code between different Qt bindings (PyQt vs PySide) or Qt versions (Qt5 vs Qt6), QtPy makes this much more painless, and allows you to easily and incrementally transition between them. QtPy handles incompatibilities and differences between bindings or Qt versions for you while keeping your project running, so you can focus more on your own code and less on keeping track of supporting every Qt version and binding. Furthermore, when you do want to upgrade or support new bindings, it allows you to update your project module by module rather than all at once. You can check out examples of this approach in projects using QtPy, like [git-cola](https://github.com/git-cola/git-cola/issues/232). ### Attribution and acknowledgments From 43060912604b305e428a63b5d53f217f277d06c7 Mon Sep 17 00:00:00 2001 From: Almar Klein Date: Tue, 21 Dec 2021 12:25:12 +0100 Subject: [PATCH 331/703] Fix QFileSystemModel for PyQt6 For some reason they moved it to QtGui. --- qtpy/QtWidgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index dfdff016..84639330 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -14,7 +14,7 @@ if PYQT6: from PyQt6 import QtWidgets from PyQt6.QtWidgets import * - from PyQt6.QtGui import QAction, QActionGroup, QShortcut + from PyQt6.QtGui import QAction, QActionGroup, QShortcut, QFileSystemModel from PyQt6.QtOpenGLWidgets import QOpenGLWidget # Map missing/renamed methods From ee67d2af1f08d1d0bebe1ab58da8e19356c2e60e Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 22 Dec 2021 12:10:16 -0500 Subject: [PATCH 332/703] Release 2.0.0 --- CHANGELOG.md | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ qtpy/__init__.py | 2 +- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2379a228..852bc64d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,69 @@ # History of changes +## Version 2.0.0 (2021-12-22) + +### Issues Closed + +* [Issue 300](https://github.com/spyder-ide/qtpy/issues/300) - Release QtPy 2.0.0 +* [Issue 286](https://github.com/spyder-ide/qtpy/issues/286) - PySide6 DeprecationWarning of exec_ ([PR 287](https://github.com/spyder-ide/qtpy/pull/287) by [@kumattau](https://github.com/kumattau)) +* [Issue 274](https://github.com/spyder-ide/qtpy/issues/274) - SignalInstance, Slot, and Property for Qt6 QtCore +* [Issue 270](https://github.com/spyder-ide/qtpy/issues/270) - Port packaging from deprecated legacy builder to current PEP 517 standards ([PR 272](https://github.com/spyder-ide/qtpy/pull/272) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 269](https://github.com/spyder-ide/qtpy/issues/269) - Add CI testing and official support for Python 3.10 ([PR 296](https://github.com/spyder-ide/qtpy/pull/296) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 267](https://github.com/spyder-ide/qtpy/issues/267) - Coveralls shows 0 coverage despite coverage report and runner output looking fine ([PR 268](https://github.com/spyder-ide/qtpy/pull/268) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 258](https://github.com/spyder-ide/qtpy/issues/258) - Compatibility with `QtWidgets.QOpenGLWidget` vs `QtOpenGLWidgets.QOpenGLWidget` ([PR 259](https://github.com/spyder-ide/qtpy/pull/259) by [@kumattau](https://github.com/kumattau)) +* [Issue 257](https://github.com/spyder-ide/qtpy/issues/257) - Check import behavior for QtCharts module ([PR 260](https://github.com/spyder-ide/qtpy/pull/260) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 253](https://github.com/spyder-ide/qtpy/issues/253) - Declare and test support up to Python 3.9 and PyQt6 ([PR 262](https://github.com/spyder-ide/qtpy/pull/262) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 250](https://github.com/spyder-ide/qtpy/issues/250) - Drop Python 2 support ([PR 251](https://github.com/spyder-ide/qtpy/pull/251) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 234](https://github.com/spyder-ide/qtpy/issues/234) - distutils.LooseVersion is being deprecated ([PR 266](https://github.com/spyder-ide/qtpy/pull/266) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 233](https://github.com/spyder-ide/qtpy/issues/233) - PyQt6 support ([PR 294](https://github.com/spyder-ide/qtpy/pull/294) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 232](https://github.com/spyder-ide/qtpy/issues/232) - Add support for `QtTextToSpeech` +* [Issue 197](https://github.com/spyder-ide/qtpy/issues/197) - QtTest imports are incomplete ([PR 290](https://github.com/spyder-ide/qtpy/pull/290) by [@kumattau](https://github.com/kumattau)) +* [Issue 70](https://github.com/spyder-ide/qtpy/issues/70) - Add QHeaderView.setSectionResize for Qt4 layer +* [Issue 66](https://github.com/spyder-ide/qtpy/issues/66) - Add documentation for how to run tests ([PR 277](https://github.com/spyder-ide/qtpy/pull/277) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 62](https://github.com/spyder-ide/qtpy/issues/62) - Add to the readme the importance of qtpy when migrating an application ([PR 301](https://github.com/spyder-ide/qtpy/pull/301) by [@dalthviz](https://github.com/dalthviz)) + +In this release 17 issues were closed. + +### Pull Requests Merged + +* [PR 303](https://github.com/spyder-ide/qtpy/pull/303) - PR: Fix QFileSystemModel for PyQt6, by [@almarklein](https://github.com/almarklein) +* [PR 301](https://github.com/spyder-ide/qtpy/pull/301) - PR: Update README.md adding a line for QtPy usefulness when migrating between Qt bindings/versions, by [@dalthviz](https://github.com/dalthviz) ([62](https://github.com/spyder-ide/qtpy/issues/62)) +* [PR 299](https://github.com/spyder-ide/qtpy/pull/299) - PR: 'width' needed for PyQt6, by [@stonebig](https://github.com/stonebig) +* [PR 298](https://github.com/spyder-ide/qtpy/pull/298) - PR: Make QLibraryInfo.location work for PyQt6, by [@stonebig](https://github.com/stonebig) +* [PR 296](https://github.com/spyder-ide/qtpy/pull/296) - PR: Add support for Python 3.10 on CIs and packaging, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([269](https://github.com/spyder-ide/qtpy/issues/269)) +* [PR 294](https://github.com/spyder-ide/qtpy/pull/294) - PR: Add minimum version check for Qt6 >=6.2.0, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([233](https://github.com/spyder-ide/qtpy/issues/233)) +* [PR 292](https://github.com/spyder-ide/qtpy/pull/292) - PR: Define Qt/binding versions at top level, fix warnings if versions not found, and fix test dir on CIs, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) +* [PR 291](https://github.com/spyder-ide/qtpy/pull/291) - PR: Add QtTextToSpeech Module, by [@kumattau](https://github.com/kumattau) +* [PR 290](https://github.com/spyder-ide/qtpy/pull/290) - PR: Import all classes in QtTest module., by [@kumattau](https://github.com/kumattau) ([197](https://github.com/spyder-ide/qtpy/issues/197)) +* [PR 287](https://github.com/spyder-ide/qtpy/pull/287) - PR: Map exec_ to their non-deprecated alternatives, by [@kumattau](https://github.com/kumattau) ([286](https://github.com/spyder-ide/qtpy/issues/286)) +* [PR 282](https://github.com/spyder-ide/qtpy/pull/282) - PR: Update README sponsors section, by [@dalthviz](https://github.com/dalthviz) +* [PR 279](https://github.com/spyder-ide/qtpy/pull/279) - PR: Add back legacy PYQT4 and PYSIDE package-level constants for compat, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) +* [PR 278](https://github.com/spyder-ide/qtpy/pull/278) - PR: Update gitignore, gitattributes, license, authors and security policy, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) +* [PR 277](https://github.com/spyder-ide/qtpy/pull/277) - PR: Add Contributing Guide based on other Spyder projects, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([66](https://github.com/spyder-ide/qtpy/issues/66)) +* [PR 276](https://github.com/spyder-ide/qtpy/pull/276) - PR: Fix regression in FORCE_QT_API behavior from merging PySide6 support, by [@tlambert03](https://github.com/tlambert03) +* [PR 273](https://github.com/spyder-ide/qtpy/pull/273) - PR: Avoid future deprecations and decrease general technical debt, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) +* [PR 272](https://github.com/spyder-ide/qtpy/pull/272) - PR: Upgrade packaging to avoid legacy/deprecated behavior and follow PEP 517, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([270](https://github.com/spyder-ide/qtpy/issues/270)) +* [PR 271](https://github.com/spyder-ide/qtpy/pull/271) - PR: Unscoped enums access for PyQt6 and other missing PyQt6 compatibility changes, by [@dalthviz](https://github.com/dalthviz) +* [PR 268](https://github.com/spyder-ide/qtpy/pull/268) - PR: Fix and improve Coveralls reporting, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([267](https://github.com/spyder-ide/qtpy/issues/267)) +* [PR 266](https://github.com/spyder-ide/qtpy/pull/266) - PR: Use modern packaging instead of deprecated distutils for version parse, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([235](https://github.com/spyder-ide/qtpy/issues/235), [234](https://github.com/spyder-ide/qtpy/issues/234)) +* [PR 264](https://github.com/spyder-ide/qtpy/pull/264) - PR: [PyQt6] Add missing tabStopWidth/setTabStopWidth same as PySide6, by [@kumattau](https://github.com/kumattau) +* [PR 263](https://github.com/spyder-ide/qtpy/pull/263) - PR: Remove accidentally-duplicated lines of code in QtCore, by [@kumattau](https://github.com/kumattau) +* [PR 262](https://github.com/spyder-ide/qtpy/pull/262) - PR: Unify and cleanup CI infra, improve robustness and test Python 3.9 and PyQt6, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([253](https://github.com/spyder-ide/qtpy/issues/253)) +* [PR 260](https://github.com/spyder-ide/qtpy/pull/260) - PR: Reorganize `QtCharts` module import and add missing skip validation for `QtNetworkAuth` test with `PyQt6`, by [@dalthviz](https://github.com/dalthviz) ([257](https://github.com/spyder-ide/qtpy/issues/257)) +* [PR 259](https://github.com/spyder-ide/qtpy/pull/259) - PR: [PyQt6] Add exec_/print_, and add QOpenGLWidget/QShortcut to QtWidgets, by [@kumattau](https://github.com/kumattau) ([258](https://github.com/spyder-ide/qtpy/issues/258)) +* [PR 256](https://github.com/spyder-ide/qtpy/pull/256) - PR: Fix fallback API, by [@benoit-pierre](https://github.com/benoit-pierre) +* [PR 255](https://github.com/spyder-ide/qtpy/pull/255) - PR: Add additional Qt modules and more support and tests for PyQt6/PySide6, by [@jschueller](https://github.com/jschueller) +* [PR 254](https://github.com/spyder-ide/qtpy/pull/254) - PR: Add QShortcut class to QtWidgets module, by [@kumattau](https://github.com/kumattau) +* [PR 252](https://github.com/spyder-ide/qtpy/pull/252) - PR: Remove Qt4 support, by [@jschueller](https://github.com/jschueller) +* [PR 251](https://github.com/spyder-ide/qtpy/pull/251) - PR: Drop Python 2, by [@dalthviz](https://github.com/dalthviz) ([250](https://github.com/spyder-ide/qtpy/issues/250)) +* [PR 225](https://github.com/spyder-ide/qtpy/pull/225) - PR: Add support for PySide6, by [@jschueller](https://github.com/jschueller) + +In this release 31 pull requests were closed. + + +---- + + ## Version 1.11.3 (2021/12/03) ### Issues Closed diff --git a/qtpy/__init__.py b/qtpy/__init__.py index bb1f7301..a01740e5 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.0.0.dev0' +__version__ = '2.0.0' class PythonQtError(RuntimeError): From e0628d82de5757ea9d17199e0e2bd38c4e4052bf Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 22 Dec 2021 12:19:29 -0500 Subject: [PATCH 333/703] Back to work --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index a01740e5..33842484 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.0.0' +__version__ = '2.1.0.dev0' class PythonQtError(RuntimeError): From 31cf7a949ca5ba1949ecb6d5184948690e115184 Mon Sep 17 00:00:00 2001 From: kumattau Date: Sat, 25 Dec 2021 01:25:47 +0900 Subject: [PATCH 334/703] Set CI job's timeout to 10 min to force a stalled test to terminate --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a82f14f..4c6a9c1b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: test: name: Test ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} runs-on: ${{ matrix.os }} + timeout-minutes: 10 defaults: run: shell: ${{ matrix.special-invocation }}bash -l {0} From 7f00c2913e572f319f2325638199fe1e925fc82b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Thu, 23 Dec 2021 10:41:40 +0100 Subject: [PATCH 335/703] add missing import in QtOpenGl --- qtpy/QtOpenGL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 458da27a..354984fd 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -7,7 +7,7 @@ """Provides QtOpenGL classes and functions.""" # Local imports -from . import PYQT5, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtOpenGL import * From 707d478069eea35ff9ab56652fed2fa8b52d8334 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Thu, 23 Dec 2021 15:41:43 +0100 Subject: [PATCH 336/703] adds a test for QtOpenGL --- qtpy/tests/test_qtopengl.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 qtpy/tests/test_qtopengl.py diff --git a/qtpy/tests/test_qtopengl.py b/qtpy/tests/test_qtopengl.py new file mode 100644 index 00000000..2b4f131d --- /dev/null +++ b/qtpy/tests/test_qtopengl.py @@ -0,0 +1,26 @@ +import pytest +from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 + +def test_qtopengl(): + """Test the qtpy.QtOpenGL namespace""" + from qtpy import QtOpenGL + + assert QtOpenGL.QOpenGLBuffer is not None + assert QtOpenGL.QOpenGLContext is not None + assert QtOpenGL.QOpenGLContextGroup is not None + assert QtOpenGL.QOpenGLDebugLogger is not None + assert QtOpenGL.QOpenGLDebugMessage is not None + assert QtOpenGL.QOpenGLFramebufferObject is not None + assert QtOpenGL.QOpenGLFramebufferObjectFormat is not None + assert QtOpenGL.QOpenGLPixelTransferOptions is not None + assert QtOpenGL.QOpenGLShader is not None + assert QtOpenGL.QOpenGLShaderProgram is not None + assert QtOpenGL.QOpenGLTexture is not None + assert QtOpenGL.QOpenGLTextureBlitter is not None + assert QtOpenGL.QOpenGLTimeMonitor is not None + assert QtOpenGL.QOpenGLTimerQuery is not None + assert QtOpenGL.QOpenGLVersionProfile is not None + assert QtOpenGL.QOpenGLVertexArrayObject is not None + assert QtOpenGL.QOpenGLWindow is not None + + From 0af7220042e5524cfd38362e4ae0b29406b0dec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Wed, 12 Jan 2022 09:33:07 +0100 Subject: [PATCH 337/703] [qtgui] pull in opengl module contents for pyside6/pyqt6 --- qtpy/QtGui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 8faa724a..f949fe45 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -14,6 +14,7 @@ if PYQT6: from PyQt6 import QtGui from PyQt6.QtGui import * + from PyQt6.QtOpenGL import * QFontMetrics.width = QFontMetrics.horizontalAdvance # Map missing/renamed methods @@ -31,6 +32,7 @@ from PySide2.QtGui import * elif PYSIDE6: from PySide6.QtGui import * + from PySide6.QtOpenGL import * QFontMetrics.width = QFontMetrics.horizontalAdvance # Map DeprecationWarning methods From 39a6a31a66ad06136bfb9c1b844858c0727aa271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Wed, 12 Jan 2022 09:34:20 +0100 Subject: [PATCH 338/703] [qtopengl] mirror qtgui imports from pyqt5 to pyside2 --- qtpy/QtOpenGL.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 354984fd..59103901 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -17,6 +17,8 @@ from PySide6.QtOpenGL import * elif PYSIDE2: from PySide2.QtOpenGL import * + from PySide2.QtGui import QOpenGLBuffer, QOpenGLFramebufferObject, QOpenGLFramebufferObjectFormat, QOpenGLShader, \ + QOpenGLShaderProgram else: raise PythonQtError('No Qt bindings could be found') From 76a89ba89aa74f5c002e49a21c63e0434891e7c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= Date: Wed, 12 Jan 2022 10:29:30 +0100 Subject: [PATCH 339/703] [qtopengl] harmonize QtGui pull-ins --- qtpy/QtOpenGL.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 59103901..653f71a9 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -11,14 +11,24 @@ if PYQT5: from PyQt5.QtOpenGL import * + from PyQt5.QtGui import (QOpenGLBuffer, QOpenGLFramebufferObject, QOpenGLFramebufferObjectFormat, QOpenGLShader, + QOpenGLShaderProgram, QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, + QOpenGLDebugMessage,QOpenGLPixelTransferOptions, QOpenGLTexture, QOpenGLTextureBlitter, + QOpenGLTimeMonitor, QOpenGLTimerQuery, QOpenGLVersionProfile, QOpenGLVertexArrayObject, + QOpenGLWindow) elif PYQT6: from PyQt6.QtOpenGL import * + from PySide6.QtGui import (QOpenGLContext, QOpenGLContextGroup) elif PYSIDE6: from PySide6.QtOpenGL import * + from PySide6.QtGui import (QOpenGLContext, QOpenGLContextGroup) elif PYSIDE2: from PySide2.QtOpenGL import * - from PySide2.QtGui import QOpenGLBuffer, QOpenGLFramebufferObject, QOpenGLFramebufferObjectFormat, QOpenGLShader, \ - QOpenGLShaderProgram + from PySide2.QtGui import (QOpenGLBuffer, QOpenGLFramebufferObject, QOpenGLFramebufferObjectFormat, QOpenGLShader, + QOpenGLShaderProgram, QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, + QOpenGLDebugMessage,QOpenGLPixelTransferOptions, QOpenGLTexture, QOpenGLTextureBlitter, + QOpenGLTimeMonitor, QOpenGLTimerQuery, QOpenGLVersionProfile, QOpenGLVertexArrayObject, + QOpenGLWindow) else: raise PythonQtError('No Qt bindings could be found') From be9b79ee9d3d4506f7e879f8051261f8d8072c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Fritze?= <47802+renefritze@users.noreply.github.com> Date: Wed, 12 Jan 2022 16:14:08 +0100 Subject: [PATCH 340/703] [qtopengl] Fix copy+paste error in qtgui imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Althviz Moré --- qtpy/QtOpenGL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 653f71a9..19da7e63 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -18,7 +18,7 @@ QOpenGLWindow) elif PYQT6: from PyQt6.QtOpenGL import * - from PySide6.QtGui import (QOpenGLContext, QOpenGLContextGroup) + from PyQt6.QtGui import (QOpenGLContext, QOpenGLContextGroup) elif PYSIDE6: from PySide6.QtOpenGL import * from PySide6.QtGui import (QOpenGLContext, QOpenGLContextGroup) From 40cd82a2168f1a772bd4ba73cd303d1c68c1ebe4 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 12 Jan 2022 12:41:44 -0500 Subject: [PATCH 341/703] Add "New features" section for version 2.0 to Changelog This was missing from the release. --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 852bc64d..ecd28806 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Version 2.0.0 (2021-12-22) +### New features + +* Add support for PyQt6 and PySide6 with Qt 6 >= 6.2.0 +* Add support for QtTextToSpeech. +* Drop support for PyQt4, PySide and Python 2. + ### Issues Closed * [Issue 300](https://github.com/spyder-ide/qtpy/issues/300) - Release QtPy 2.0.0 From 8f941fe4ede60bf1e4faab6f2c87413de1d1b710 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Wed, 12 Jan 2022 12:45:13 -0500 Subject: [PATCH 342/703] Make lines fit in 79 columns --- qtpy/QtOpenGL.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 19da7e63..1fe48fbf 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -11,11 +11,14 @@ if PYQT5: from PyQt5.QtOpenGL import * - from PyQt5.QtGui import (QOpenGLBuffer, QOpenGLFramebufferObject, QOpenGLFramebufferObjectFormat, QOpenGLShader, - QOpenGLShaderProgram, QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, - QOpenGLDebugMessage,QOpenGLPixelTransferOptions, QOpenGLTexture, QOpenGLTextureBlitter, - QOpenGLTimeMonitor, QOpenGLTimerQuery, QOpenGLVersionProfile, QOpenGLVertexArrayObject, - QOpenGLWindow) + from PyQt5.QtGui import ( + QOpenGLBuffer, QOpenGLFramebufferObject, + QOpenGLFramebufferObjectFormat, QOpenGLShader, QOpenGLShaderProgram, + QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, + QOpenGLDebugMessage, QOpenGLPixelTransferOptions, QOpenGLTexture, + QOpenGLTextureBlitter, QOpenGLTimeMonitor, QOpenGLTimerQuery, + QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow + ) elif PYQT6: from PyQt6.QtOpenGL import * from PyQt6.QtGui import (QOpenGLContext, QOpenGLContextGroup) @@ -24,11 +27,14 @@ from PySide6.QtGui import (QOpenGLContext, QOpenGLContextGroup) elif PYSIDE2: from PySide2.QtOpenGL import * - from PySide2.QtGui import (QOpenGLBuffer, QOpenGLFramebufferObject, QOpenGLFramebufferObjectFormat, QOpenGLShader, - QOpenGLShaderProgram, QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, - QOpenGLDebugMessage,QOpenGLPixelTransferOptions, QOpenGLTexture, QOpenGLTextureBlitter, - QOpenGLTimeMonitor, QOpenGLTimerQuery, QOpenGLVersionProfile, QOpenGLVertexArrayObject, - QOpenGLWindow) + from PySide2.QtGui import ( + QOpenGLBuffer, QOpenGLFramebufferObject, + QOpenGLFramebufferObjectFormat, QOpenGLShader, QOpenGLShaderProgram, + QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, + QOpenGLDebugMessage, QOpenGLPixelTransferOptions, QOpenGLTexture, + QOpenGLTextureBlitter, QOpenGLTimeMonitor, QOpenGLTimerQuery, + QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow + ) else: raise PythonQtError('No Qt bindings could be found') From b9c123c5fb37a5041c7016f494003bb71f0f91f5 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 13 Jan 2022 10:43:17 -0500 Subject: [PATCH 343/703] QtCore: Add missing QtGui utility function to QtCore.Qt --- qtpy/QtCore.py | 10 ++++++++++ qtpy/tests/test_qtcore.py | 11 +++++++++++ 2 files changed, 21 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index c5b9b602..3347f6dc 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -58,6 +58,11 @@ import PySide6.QtCore __version__ = PySide6.QtCore.__version__ + # Missing QtGui utility functions on Qt + if getattr(Qt, 'mightBeRichText', None) is None: + from PySide6.QtGui import Qt as guiQt + Qt.mightBeRichText = guiQt.mightBeRichText + # obsolete in qt6 Qt.BackgroundColorRole = Qt.BackgroundRole Qt.TextColorRole = Qt.ForegroundRole @@ -79,5 +84,10 @@ import PySide2.QtCore __version__ = PySide2.QtCore.__version__ + + # Missing QtGui utility functions on Qt + if getattr(Qt, 'mightBeRichText', None) is None: + from PySide2.QtGui import Qt as guiQt + Qt.mightBeRichText = guiQt.mightBeRichText else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 3f5183fa..ceb3298d 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -37,3 +37,14 @@ def test_enum_access(): assert QtCore.Qt.Key_Return == QtCore.Qt.Key.Key_Return assert QtCore.Qt.transparent == QtCore.Qt.GlobalColor.transparent assert QtCore.Qt.Widget == QtCore.Qt.WindowType.Widget + + +@pytest.mark.skipif(PYQT5 or PYQT6, + reason="Unavailable by default for PyQt") +def test_qtgui_namespace_mightBeRichText(): + """ + Test included elements (mightBeRichText) from module QtGui. + + See: https://doc.qt.io/qt-5/qt-sub-qtgui.html + """ + assert QtCore.Qt.mightBeRichText is not None From 93904bdb06c875c899d729966a770f74ef49b449 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 13 Jan 2022 11:11:30 -0500 Subject: [PATCH 344/703] QtCore: Enable QtGui/Qt namespace test for PyQt --- qtpy/QtCore.py | 9 +++++++++ qtpy/tests/test_qtcore.py | 2 -- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 3347f6dc..e7ed562d 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -24,6 +24,15 @@ from PyQt6.QtCore import QDateTime QDateTime.toPython = QDateTime.toPyDateTime + # For issue #311 + # Seems like there is an error with sip. Without first + # trying to import `PyQt6.QtGui.Qt` + # Some functions like `PyQt6.QtCore.Qt.mightBeRichText` are missing + try: + from PyQt6.QtGui import Qt + except ImportError: + pass + # Map missing methods QCoreApplication.exec_ = QCoreApplication.exec QEventLoop.exec_ = QEventLoop.exec diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index ceb3298d..ac4c6fc8 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -39,8 +39,6 @@ def test_enum_access(): assert QtCore.Qt.Widget == QtCore.Qt.WindowType.Widget -@pytest.mark.skipif(PYQT5 or PYQT6, - reason="Unavailable by default for PyQt") def test_qtgui_namespace_mightBeRichText(): """ Test included elements (mightBeRichText) from module QtGui. From 1f7232166ec4afa56a9cd20d287e7cdd1df20d21 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 13 Jan 2022 12:39:26 -0500 Subject: [PATCH 345/703] Update testing and prevent errors with PySide2 5.12 --- .github/workflows/ci.yml | 2 +- .github/workflows/test.sh | 2 +- qtpy/QtCore.py | 8 ++++++-- qtpy/tests/test_qtcore.py | 4 +++- setup.cfg | 2 +- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c6a9c1b..023b87cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: test: name: Test ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} runs-on: ${{ matrix.os }} - timeout-minutes: 10 + timeout-minutes: 12 defaults: run: shell: ${{ matrix.special-invocation }}bash -l {0} diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 04c030f6..989f5d47 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -7,7 +7,7 @@ eval "$(conda shell.bash hook)" conda remove -q -n test-env --all || true # Create and activate conda environment for this test -conda create -q -n test-env python=${PYTHON_VERSION} pytest pytest-cov +conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index e7ed562d..0bb6a743 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -96,7 +96,11 @@ # Missing QtGui utility functions on Qt if getattr(Qt, 'mightBeRichText', None) is None: - from PySide2.QtGui import Qt as guiQt - Qt.mightBeRichText = guiQt.mightBeRichText + try: + from PySide2.QtGui import Qt as guiQt + Qt.mightBeRichText = guiQt.mightBeRichText + except ImportError: + # Fails with PySide2 5.12.0 + pass else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index ac4c6fc8..8b287b08 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -2,7 +2,7 @@ import pytest -from qtpy import PYQT5, PYQT6, PYSIDE2, PYQT_VERSION, QtCore +from qtpy import PYQT5, PYQT6, PYSIDE2, PYQT_VERSION, PYSIDE_VERSION, QtCore def test_qtmsghandler(): @@ -39,6 +39,8 @@ def test_enum_access(): assert QtCore.Qt.Widget == QtCore.Qt.WindowType.Widget +@pytest.mark.skipif(PYSIDE2 and PYSIDE_VERSION.startswith('5.12.0'), + reason="Utility functions unavailable for PySide2 5.12.0") def test_qtgui_namespace_mightBeRichText(): """ Test included elements (mightBeRichText) from module QtGui. diff --git a/setup.cfg b/setup.cfg index a8048465..f331b7be 100644 --- a/setup.cfg +++ b/setup.cfg @@ -55,4 +55,4 @@ exclude = [options.extras_require] test = pytest>=6.0.0,<7.0 - pytest-cov>=2.11.0 + pytest-cov>=3.0.0 From f94cecafdab4f3de367742744b84a46de7286fb2 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 13 Jan 2022 12:41:20 -0500 Subject: [PATCH 346/703] QtCore: Remove guiQt after utility functions assignment --- qtpy/QtCore.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 0bb6a743..343896c9 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -71,6 +71,7 @@ if getattr(Qt, 'mightBeRichText', None) is None: from PySide6.QtGui import Qt as guiQt Qt.mightBeRichText = guiQt.mightBeRichText + del guiQt # obsolete in qt6 Qt.BackgroundColorRole = Qt.BackgroundRole @@ -99,6 +100,7 @@ try: from PySide2.QtGui import Qt as guiQt Qt.mightBeRichText = guiQt.mightBeRichText + del guiQt except ImportError: # Fails with PySide2 5.12.0 pass From b817223d26b908df751507584dfbd26e412bae40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Mon, 17 Jan 2022 09:33:04 -0500 Subject: [PATCH 347/703] QtCore: Apply suggestions from code review Improve comment punctuation/caps Co-authored-by: CAM Gerlach --- qtpy/QtCore.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 343896c9..b6f2c906 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -26,8 +26,8 @@ # For issue #311 # Seems like there is an error with sip. Without first - # trying to import `PyQt6.QtGui.Qt` - # Some functions like `PyQt6.QtCore.Qt.mightBeRichText` are missing + # trying to import `PyQt6.QtGui.Qt`, some functions like + # `PyQt6.QtCore.Qt.mightBeRichText` are missing. try: from PyQt6.QtGui import Qt except ImportError: From 03baeee8c233409074bc127339126ecde9cce719 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 17 Jan 2022 09:39:03 -0500 Subject: [PATCH 348/703] CI: Update timeout to 15 min --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 023b87cf..8736b2c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: test: name: Test ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} runs-on: ${{ matrix.os }} - timeout-minutes: 12 + timeout-minutes: 15 defaults: run: shell: ${{ matrix.special-invocation }}bash -l {0} From fdf6eb824d63f14dfb9b67de1c8c08b9a4d98c54 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 14 Jan 2022 13:10:00 -0500 Subject: [PATCH 349/703] QtCore: Add mapping for missing enum values aliases --- qtpy/QtCore.py | 11 ++++++++++- qtpy/tests/test_qtcore.py | 4 ++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index b6f2c906..b184e0f8 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -47,6 +47,15 @@ from .enums_compat import promote_enums promote_enums(QtCore) del QtCore + + # Map missing unscoped access enum values + Qt.BackButton = Qt.XButton1 + Qt.ForwardButton = Qt.XButton2 + + # Map Qt6 removed obsolete ItemDataRole enum values + Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole + Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole + elif PYQT5: from PyQt5.QtCore import * from PyQt5.QtCore import pyqtSignal as Signal @@ -73,7 +82,7 @@ Qt.mightBeRichText = guiQt.mightBeRichText del guiQt - # obsolete in qt6 + # Map Qt6 removed obsolete ItemDataRole enum values Qt.BackgroundColorRole = Qt.BackgroundRole Qt.TextColorRole = Qt.ForegroundRole Qt.MidButton = Qt.MiddleButton diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 8b287b08..a1eac6e1 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -37,6 +37,10 @@ def test_enum_access(): assert QtCore.Qt.Key_Return == QtCore.Qt.Key.Key_Return assert QtCore.Qt.transparent == QtCore.Qt.GlobalColor.transparent assert QtCore.Qt.Widget == QtCore.Qt.WindowType.Widget + assert QtCore.Qt.BackButton == QtCore.Qt.MouseButton.XButton1 + assert QtCore.Qt.XButton1 == QtCore.Qt.MouseButton.XButton1 + assert QtCore.Qt.BackgroundColorRole == QtCore.Qt.ItemDataRole.BackgroundColorRole + assert QtCore.Qt.TextColorRole == QtCore.Qt.ItemDataRole.TextColorRole @pytest.mark.skipif(PYSIDE2 and PYSIDE_VERSION.startswith('5.12.0'), From d02c5d4eac5984d3335e75450b87a6f5a18b9cb1 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 14 Jan 2022 13:28:41 -0500 Subject: [PATCH 350/703] QtCore: Update PySide6 mapping for ItemDataRole enum values --- qtpy/QtCore.py | 6 +++--- qtpy/tests/test_qtcore.py | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index b184e0f8..16dedd8e 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -83,9 +83,9 @@ del guiQt # Map Qt6 removed obsolete ItemDataRole enum values - Qt.BackgroundColorRole = Qt.BackgroundRole - Qt.TextColorRole = Qt.ForegroundRole - Qt.MidButton = Qt.MiddleButton + Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole + Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole + Qt.MidButton = Qt.MouseButton.MiddleButton = Qt.MiddleButton # Map DeprecationWarning methods QCoreApplication.exec_ = QCoreApplication.exec diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index a1eac6e1..4c8a8f58 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -2,7 +2,7 @@ import pytest -from qtpy import PYQT5, PYQT6, PYSIDE2, PYQT_VERSION, PYSIDE_VERSION, QtCore +from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PYQT_VERSION, PYSIDE_VERSION, QtCore def test_qtmsghandler(): @@ -41,6 +41,8 @@ def test_enum_access(): assert QtCore.Qt.XButton1 == QtCore.Qt.MouseButton.XButton1 assert QtCore.Qt.BackgroundColorRole == QtCore.Qt.ItemDataRole.BackgroundColorRole assert QtCore.Qt.TextColorRole == QtCore.Qt.ItemDataRole.TextColorRole + if PYSIDE2 or PYSIDE6: + assert QtCore.Qt.MidButton == QtCore.Qt.MouseButton.MiddleButton @pytest.mark.skipif(PYSIDE2 and PYSIDE_VERSION.startswith('5.12.0'), From 734951dd2997b1184e8476b533dcfcd2f6c0d864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Mon, 17 Jan 2022 09:35:06 -0500 Subject: [PATCH 351/703] QtCore: Apply suggestions from code review Improve comments and import order Co-authored-by: CAM Gerlach --- qtpy/QtCore.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 16dedd8e..f4d53bda 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -52,7 +52,7 @@ Qt.BackButton = Qt.XButton1 Qt.ForwardButton = Qt.XButton2 - # Map Qt6 removed obsolete ItemDataRole enum values + # Alias deprecated ItemDataRole enum values removed in Qt6 Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole @@ -82,7 +82,7 @@ Qt.mightBeRichText = guiQt.mightBeRichText del guiQt - # Map Qt6 removed obsolete ItemDataRole enum values + # Alias deprecated ItemDataRole enum values removed in Qt6 Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole Qt.MidButton = Qt.MouseButton.MiddleButton = Qt.MiddleButton From 715153e4cc5ba4cdfa5c4ed480743fc9d29cd8c8 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 17 Jan 2022 10:09:24 -0500 Subject: [PATCH 352/703] QtCharts: Add alias for backward compatibility with 1.x --- qtpy/__init__.py | 3 +++ qtpy/tests/test_qtcharts.py | 1 + 2 files changed, 4 insertions(+) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 33842484..9a4ba7a5 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -211,6 +211,9 @@ class PythonQtWarning(Warning): except (ImportError, PythonQtError): pass +# QtCharts backward compatibility with QtPy 1.x +from . import QtCharts +QtCharts.QtCharts = QtCharts def _warn_old_minor_version(name, old_version, min_version): """Warn if using a Qt or binding version no longer supported by QtPy.""" diff --git a/qtpy/tests/test_qtcharts.py b/qtpy/tests/test_qtcharts.py index 1c7ed425..0531b169 100644 --- a/qtpy/tests/test_qtcharts.py +++ b/qtpy/tests/test_qtcharts.py @@ -8,3 +8,4 @@ def test_qtcharts(): """Test the qtpy.QtCharts namespace""" from qtpy import QtCharts assert QtCharts.QChart is not None + assert QtCharts.QtCharts.QChart is not None From 3b6735d04f414958f52cec876583cfdc2cfe8a6c Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 18 Jan 2022 10:00:07 -0500 Subject: [PATCH 353/703] QtCharts: Catch errors when applying backward compat logic --- qtpy/__init__.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 9a4ba7a5..4cbee88f 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -211,9 +211,13 @@ class PythonQtWarning(Warning): except (ImportError, PythonQtError): pass -# QtCharts backward compatibility with QtPy 1.x -from . import QtCharts -QtCharts.QtCharts = QtCharts +try: + # QtCharts backward compatibility with QtPy 1.x + from . import QtCharts + QtCharts.QtCharts = QtCharts +except (ImportError, PythonQtError): + pass + def _warn_old_minor_version(name, old_version, min_version): """Warn if using a Qt or binding version no longer supported by QtPy.""" From 67d40503f1ec4f0579cc0ddf85e1a554092b52bf Mon Sep 17 00:00:00 2001 From: kumattau Date: Wed, 19 Jan 2022 00:27:03 +0900 Subject: [PATCH 354/703] Fix mappings of instance method and slot alias (PyQt6 and PySide6) --- qtpy/QtCore.py | 16 ++++++++-------- qtpy/QtGui.py | 10 +++++----- qtpy/QtPrintSupport.py | 10 +++++----- qtpy/QtSql.py | 12 ++++++------ qtpy/QtWidgets.py | 28 ++++++++++++++-------------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index b6f2c906..9267d5a3 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -22,7 +22,7 @@ # For issue #153 from PyQt6.QtCore import QDateTime - QDateTime.toPython = QDateTime.toPyDateTime + QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) # For issue #311 # Seems like there is an error with sip. Without first @@ -35,10 +35,10 @@ # Map missing methods QCoreApplication.exec_ = QCoreApplication.exec - QEventLoop.exec_ = QEventLoop.exec - QThread.exec_ = QThread.exec - - QLibraryInfo.location = QLibraryInfo.path + QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + + QLibraryInfo.location = QLibraryInfo.path # Those are imported from `import *` del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR @@ -80,9 +80,9 @@ # Map DeprecationWarning methods QCoreApplication.exec_ = QCoreApplication.exec - QEventLoop.exec_ = QEventLoop.exec - QThread.exec_ = QThread.exec - QTextStreamManipulator.exec_ = QTextStreamManipulator.exec + QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) elif PYSIDE2: from PySide2.QtCore import * diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index f949fe45..1bb9a743 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -15,12 +15,12 @@ from PyQt6 import QtGui from PyQt6.QtGui import * from PyQt6.QtOpenGL import * - QFontMetrics.width = QFontMetrics.horizontalAdvance + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) # Map missing/renamed methods - QDrag.exec_ = QDrag.exec + QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QGuiApplication.exec_ = QGuiApplication.exec - QTextDocument.print_ = QTextDocument.print + QTextDocument.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) # Allow unscoped access for enums inside the QtGui module from .enums_compat import promote_enums @@ -33,10 +33,10 @@ elif PYSIDE6: from PySide6.QtGui import * from PySide6.QtOpenGL import * - QFontMetrics.width = QFontMetrics.horizontalAdvance + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) # Map DeprecationWarning methods - QDrag.exec_ = QDrag.exec + QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QGuiApplication.exec_ = QGuiApplication.exec else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 85cbb981..4fcec32b 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -15,14 +15,14 @@ from PyQt5.QtPrintSupport import * elif PYQT6: from PyQt6.QtPrintSupport import * - QPageSetupDialog.exec_ = QPageSetupDialog.exec - QPrintDialog.exec_ = QPrintDialog.exec - QPrintPreviewWidget.print_ = QPrintPreviewWidget.print + QPageSetupDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QPrintDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QPrintPreviewWidget.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) elif PYSIDE6: from PySide6.QtPrintSupport import * # Map DeprecationWarning methods - QPageSetupDialog.exec_ = QPageSetupDialog.exec - QPrintDialog.exec_ = QPrintDialog.exec + QPageSetupDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QPrintDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) elif PYSIDE2: from PySide2.QtPrintSupport import * else: diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 3cdaaf33..038c34db 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -13,15 +13,15 @@ from PyQt5.QtSql import * elif PYQT6: from PyQt6.QtSql import * - QSqlDatabase.exec_ = QSqlDatabase.exec - QSqlQuery.exec_ = QSqlQuery.exec - QSqlResult.exec_ = QSqlResult.exec + QSqlDatabase.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QSqlQuery.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QSqlResult.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) elif PYSIDE6: from PySide6.QtSql import * # Map DeprecationWarning methods - QSqlDatabase.exec_ = QSqlDatabase.exec - QSqlQuery.exec_ = QSqlQuery.exec - QSqlResult.exec_ = QSqlResult.exec + QSqlDatabase.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QSqlQuery.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QSqlResult.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) elif PYSIDE2: from PySide2.QtSql import * else: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 84639330..70996ae7 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -18,15 +18,15 @@ from PyQt6.QtOpenGLWidgets import QOpenGLWidget # Map missing/renamed methods - QTextEdit.setTabStopWidth = QTextEdit.setTabStopDistance - QTextEdit.tabStopWidth = QTextEdit.tabStopDistance - QTextEdit.print_ = QTextEdit.print - QPlainTextEdit.setTabStopWidth = QPlainTextEdit.setTabStopDistance - QPlainTextEdit.tabStopWidth = QPlainTextEdit.tabStopDistance - QPlainTextEdit.print_ = QPlainTextEdit.print + QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) + QTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) + QTextEdit.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) + QPlainTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) + QPlainTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) + QPlainTextEdit.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) QApplication.exec_ = QApplication.exec - QDialog.exec_ = QDialog.exec - QMenu.exec_ = QMenu.exec + QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) # Allow unscoped access for enums inside the QtWidgets module from .enums_compat import promote_enums @@ -40,15 +40,15 @@ from PySide6.QtOpenGLWidgets import QOpenGLWidget # Map missing/renamed methods - QTextEdit.setTabStopWidth = QTextEdit.setTabStopDistance - QTextEdit.tabStopWidth = QTextEdit.tabStopDistance - QPlainTextEdit.setTabStopWidth = QPlainTextEdit.setTabStopDistance - QPlainTextEdit.tabStopWidth = QPlainTextEdit.tabStopDistance + QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) + QTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) + QPlainTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) + QPlainTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) # Map DeprecationWarning methods QApplication.exec_ = QApplication.exec - QDialog.exec_ = QDialog.exec - QMenu.exec_ = QMenu.exec + QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) elif PYSIDE2: from PySide2.QtWidgets import * else: From c0f73b92f2d69ecc20449d8afa32e985e9fc947f Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 19 Jan 2022 10:14:28 -0500 Subject: [PATCH 355/703] QtCharts: Update handling for module layout --- qtpy/QtCharts.py | 4 ++++ qtpy/__init__.py | 7 ------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index b041b0cd..164be471 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -12,6 +12,7 @@ if PYQT5: try: from PyQt5.QtChart import * + from PyQt5 import QtChart as QtCharts except ImportError as error: raise PythonQtError( 'The QtChart module was not found. ' @@ -20,6 +21,7 @@ elif PYQT6: try: from PyQt6.QtCharts import * + from PyQt6 import QtCharts except ImportError as error: raise PythonQtError( 'The QtCharts module was not found. ' @@ -27,7 +29,9 @@ ) from error elif PYSIDE6: from PySide6.QtCharts import * + from PySide6 import QtCharts elif PYSIDE2: + from PySide2.QtCharts import * # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.QtCharts as __temp import inspect diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 4cbee88f..33842484 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -211,13 +211,6 @@ class PythonQtWarning(Warning): except (ImportError, PythonQtError): pass -try: - # QtCharts backward compatibility with QtPy 1.x - from . import QtCharts - QtCharts.QtCharts = QtCharts -except (ImportError, PythonQtError): - pass - def _warn_old_minor_version(name, old_version, min_version): """Warn if using a Qt or binding version no longer supported by QtPy.""" From 13718adf22cd0b5e7d17500f9e051f8449f516af Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 25 Jan 2022 09:30:41 -0500 Subject: [PATCH 356/703] QtCore/QtGui: Add testing for aliased methods --- .github/workflows/test.sh | 7 ++++++- qtpy/QtCore.py | 10 ++++++--- qtpy/QtGui.py | 4 ++++ qtpy/tests/test_qtcore.py | 35 ++++++++++++++++++++++++++++++- qtpy/tests/test_qtgui.py | 33 +++++++++++++++++++++++++---- qtpy/tests/test_qtprintsupport.py | 2 -- setup.cfg | 1 + 7 files changed, 81 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 989f5d47..6c9c6e96 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -7,7 +7,12 @@ eval "$(conda shell.bash hook)" conda remove -q -n test-env --all || true # Create and activate conda environment for this test -conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' +if [ "$PYQT5_QT_VERSION" = "5.9" ] || [ "$PYSIDE2_QT_VERSION" = "5.9" ] ; then + conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' 'pytest-qt==3.3.0' +else + conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' 'pytest-qt' +fi + conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index c29441a5..d6c617a1 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -20,7 +20,7 @@ from PyQt6.QtCore import pyqtProperty as Property from PyQt6.QtCore import QT_VERSION_STR as __version__ - # For issue #153 + # For issue #153 and updated for issue #305 from PyQt6.QtCore import QDateTime QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) @@ -37,6 +37,7 @@ QCoreApplication.exec_ = QCoreApplication.exec QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QLibraryInfo.location = QLibraryInfo.path @@ -64,9 +65,12 @@ from PyQt5.QtCore import pyqtProperty as Property from PyQt5.QtCore import QT_VERSION_STR as __version__ - # For issue #153 + # For issue #153 and updated for issue #305 from PyQt5.QtCore import QDateTime - QDateTime.toPython = QDateTime.toPyDateTime + QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) + + # Map missing methods on PyQt5 5.12 + QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) # Those are imported from `import *` del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 1bb9a743..a887833d 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -30,6 +30,10 @@ from PyQt5.QtGui import * elif PYSIDE2: from PySide2.QtGui import * + if hasattr(QFontMetrics, 'horizontalAdvance'): + # Needed to prevent raising a DeprecationWarning when using QFontMetrics.width + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) + elif PYSIDE6: from PySide6.QtGui import * from PySide6.QtOpenGL import * diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 4c8a8f58..0e34c47e 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,5 +1,9 @@ """Test QtCore.""" +from datetime import datetime +import os +import sys + import pytest from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PYQT_VERSION, PYSIDE_VERSION, QtCore @@ -10,9 +14,38 @@ def test_qtmsghandler(): assert QtCore.qInstallMessageHandler is not None -def test_DateTime_toPython(): +def test_qdatetime_toPython(): """Test QDateTime.toPython""" + q_date = QtCore.QDateTime.currentDateTime() assert QtCore.QDateTime.toPython is not None + py_date = q_date.toPython() + assert isinstance(py_date, datetime) + + +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fatal Python error: Aborted on Linux CI when not using conda") +def test_qeventloop_exec_(qtbot): + """Test QEventLoop.exec_""" + assert QtCore.QEventLoop.exec_ is not None + event_loop = QtCore.QEventLoop(None) + QtCore.QTimer.singleShot(1000, event_loop.quit) + event_loop.exec_() + + +def test_qthread_exec_(): + """Test QThread.exec_""" + assert QtCore.QThread.exec_ is not None + + +def test_qlibraryinfo_location(): + """Test QLibraryInfo.location""" + assert QtCore.QLibraryInfo.location is not None + assert QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PrefixPath) is not None + + +def test_qtextstreammanipulator_exec_(): + """Test QTextStreamManipulator.exec_""" + QtCore.QTextStreamManipulator.exec_ is not None @pytest.mark.skipif(PYSIDE2 or PYQT6, diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 09798955..b069823e 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -1,22 +1,47 @@ """Test QtGui.""" +import os +import sys + import pytest from qtpy import PYQT5, PYQT_VERSION, QtGui -def test_qdrag_functions(): +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fatal Python error: Aborted on Linux CI when not using conda") +def test_qfontmetrics_width(qtbot): + """Test QFontMetrics width""" + assert QtGui.QFontMetrics.width is not None + font = QtGui.QFont("times", 24) + font_metrics = QtGui.QFontMetrics(font) + width = font_metrics.width("Test") + assert width in range(40, 62) + + +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fatal Python error: Aborted on Linux CI when not using conda") +def test_qdrag_functions(qtbot): """Test functions mapping for QtGui.QDrag.""" - assert QtGui.QDrag.exec_ + assert QtGui.QDrag.exec_ is not None + drag = QtGui.QDrag(None) + drag.exec_() def test_qguiapplication_functions(): """Test functions mapping for QtGui.QGuiApplication.""" - assert QtGui.QGuiApplication.exec_ + assert QtGui.QGuiApplication.exec_ is not None +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Segmentation fault/Aborted on Linux CI when not using conda") def test_qtextdocument_functions(): """Test functions mapping for QtGui.QTextDocument.""" - assert QtGui.QTextDocument.print_ + assert QtGui.QTextDocument.print_ is not None + text_document = QtGui.QTextDocument("Test") + print_device = QtGui.QPdfWriter('test.pdf') + text_document.print_(print_device) + assert os.path.exists('test.pdf') + os.remove('test.pdf') @pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py index cf3b1052..dfc0d298 100644 --- a/qtpy/tests/test_qtprintsupport.py +++ b/qtpy/tests/test_qtprintsupport.py @@ -12,5 +12,3 @@ def test_qtprintsupport(): assert QtPrintSupport.QPrinter is not None assert QtPrintSupport.QPrinterInfo is not None assert QtPrintSupport.QPrintPreviewWidget is not None - - diff --git a/setup.cfg b/setup.cfg index f331b7be..0333558b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -56,3 +56,4 @@ exclude = test = pytest>=6.0.0,<7.0 pytest-cov>=3.0.0 + pytest-qt From 16dd533f32b3efdbbe9c2f7c6e5e3f42fe6c6b1d Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 25 Jan 2022 10:44:50 -0500 Subject: [PATCH 357/703] QtPrintSupport: Add tests for aliased methods --- qtpy/tests/test_qtprintsupport.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py index dfc0d298..3d14575c 100644 --- a/qtpy/tests/test_qtprintsupport.py +++ b/qtpy/tests/test_qtprintsupport.py @@ -1,4 +1,9 @@ +"""Test QtPrintSupport.""" +import os +import sys + import pytest + from qtpy import QtPrintSupport @@ -12,3 +17,22 @@ def test_qtprintsupport(): assert QtPrintSupport.QPrinter is not None assert QtPrintSupport.QPrinterInfo is not None assert QtPrintSupport.QPrintPreviewWidget is not None + + +def test_qpagesetupdialog_exec_(): + """Test qtpy.QtPrintSupport.QPageSetupDialog exec_""" + assert QtPrintSupport.QPageSetupDialog.exec_ is not None + + +def test_qprintdialog_exec_(): + """Test qtpy.QtPrintSupport.QPrintDialog exec_""" + assert QtPrintSupport.QPrintDialog.exec_ is not None + + +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fatal Python error: Aborted on Linux CI when not using conda") +def test_qprintpreviewwidget_print_(qtbot): + """Test qtpy.QtPrintSupport.QPrintPreviewWidget print_""" + assert QtPrintSupport.QPrintPreviewWidget.print_ is not None + preview_widget = QtPrintSupport.QPrintPreviewWidget() + preview_widget.print_() From 2658e3f34ac20f1f98115fb7f25dffa20ed2d2c2 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 25 Jan 2022 11:41:43 -0500 Subject: [PATCH 358/703] QtSql: Add tests for aliased methods --- qtpy/tests/test_qtsql.py | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index 12e8d920..b32dfe1e 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -1,6 +1,9 @@ +"""Test QtSql.""" import pytest + from qtpy import QtSql + def test_qtsql(): """Test the qtpy.QtSql namespace""" assert QtSql.QSqlDatabase is not None @@ -20,3 +23,49 @@ def test_qtsql(): # Following modules are not (yet) part of any wrapper: # QSqlDriverCreator, QSqlDriverPlugin + + +def test_qtsql_members_aliases(): + """ + Test aliased methods over qtpy.QtSql members including: + + * qtpy.QtSql.QSqlDatabase.exec_ + * qtpy.QtSql.QSqlQuery.exec_ + * qtpy.QtSql.QSqlResult.exec_ + """ + assert QtSql.QSqlDatabase.exec_ is not None + assert QtSql.QSqlQuery.exec_ is not None + assert QtSql.QSqlResult.exec_ is not None + + connection = QtSql.QSqlDatabase.addDatabase("QSQLITE") + assert connection.open() + connection.setDatabaseName("test.sqlite") + QtSql.QSqlDatabase.exec_( + connection, + """ + CREATE TABLE test ( + id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, + name VARCHAR(40) NOT NULL + ) + """ + ) + # Created table 'test' and 'sqlite_sequence' + assert len(connection.tables()) == 2 + + insert_table_query = QtSql.QSqlQuery() + assert insert_table_query.exec_( + """ + INSERT INTO test (name) VALUES ( + "TESTING" + ) + """ + ) + select_table_query = QtSql.QSqlQuery() + select_table_query.prepare( + """ + SELECT * FROM test + """) + select_table_query.exec_() + record = select_table_query.record() + assert not record.isEmpty() + connection.close() From 8152e44fd55ff9d5a386406576fdb02fffb566fb Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 25 Jan 2022 12:11:54 -0500 Subject: [PATCH 359/703] QtWidgets: Add tests for aliased methods --- qtpy/tests/test_qtwidgets.py | 38 ++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 68e8192c..08cf8d3e 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -1,21 +1,38 @@ """Test QtWidgets.""" +import os +import sys + import pytest -from qtpy import PYQT5, PYQT_VERSION, QtWidgets +from qtpy import PYQT5, PYQT_VERSION, QtCore, QtWidgets, QtPrintSupport -def test_qtextedit_functions(): +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fatal Python error: Aborted on Linux CI when not using conda") +def test_qtextedit_functions(qtbot): """Test functions mapping for QtWidgets.QTextEdit.""" assert QtWidgets.QTextEdit.setTabStopWidth assert QtWidgets.QTextEdit.tabStopWidth assert QtWidgets.QTextEdit.print_ + textedit_widget = QtWidgets.QTextEdit(None) + textedit_widget.setTabStopWidth(90) + assert textedit_widget.tabStopWidth() == 90 + printer = QtPrintSupport.QPrinter() + textedit_widget.print_(printer) -def test_qplaintextedit_functions(): +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fatal Python error: Aborted on Linux CI when not using conda") +def test_qplaintextedit_functions(qtbot): """Test functions mapping for QtWidgets.QPlainTextEdit.""" assert QtWidgets.QPlainTextEdit.setTabStopWidth assert QtWidgets.QPlainTextEdit.tabStopWidth assert QtWidgets.QPlainTextEdit.print_ + plaintextedit_widget = QtWidgets.QPlainTextEdit(None) + plaintextedit_widget.setTabStopWidth(90) + assert plaintextedit_widget.tabStopWidth() == 90 + printer = QtPrintSupport.QPrinter() + plaintextedit_widget.print_(printer) def test_qapplication_functions(): @@ -23,15 +40,24 @@ def test_qapplication_functions(): assert QtWidgets.QApplication.exec_ -def test_qdialog_functions(): +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fatal Python error: Aborted on Linux CI when not using conda") +def test_qdialog_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QDialog.exec_ + dialog = QtWidgets.QDialog(None) + QtCore.QTimer.singleShot(100, dialog.accept) + dialog.exec_() -def test_qmenu_functions(): +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fatal Python error: Aborted on Linux CI when not using conda") +def test_qmenu_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QMenu.exec_ - + menu = QtWidgets.QMenu(None) + QtCore.QTimer.singleShot(100, menu.close) + menu.exec_() @pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" From 51f46b86d44f9d7d47d7c86d608cf7e98994b06c Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 26 Jan 2022 09:53:27 -0500 Subject: [PATCH 360/703] QtWidgets/QtSql: Fix/Skip some tests regarding using a printer and SQLite on Windows --- .github/workflows/test.sh | 4 ++-- qtpy/tests/test_qtcore.py | 2 +- qtpy/tests/test_qtsql.py | 7 +++++-- qtpy/tests/test_qtwidgets.py | 20 ++++++++++++++------ 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 6c9c6e96..92f33c27 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -7,10 +7,10 @@ eval "$(conda shell.bash hook)" conda remove -q -n test-env --all || true # Create and activate conda environment for this test -if [ "$PYQT5_QT_VERSION" = "5.9" ] || [ "$PYSIDE2_QT_VERSION" = "5.9" ] ; then +if [ "$PYQT5_QT_VERSION" = "5.9" -a "${1}" = "pyqt5" ] || [ "$PYSIDE2_QT_VERSION" = "5.9" -a "${1}" = "pyside2" ] ; then conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' 'pytest-qt==3.3.0' else - conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' 'pytest-qt' + conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' pytest-qt fi conda activate test-env diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 0e34c47e..aa5964ca 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -28,7 +28,7 @@ def test_qeventloop_exec_(qtbot): """Test QEventLoop.exec_""" assert QtCore.QEventLoop.exec_ is not None event_loop = QtCore.QEventLoop(None) - QtCore.QTimer.singleShot(1000, event_loop.quit) + QtCore.QTimer.singleShot(100, event_loop.quit) event_loop.exec_() diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index b32dfe1e..4caa44ea 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -1,7 +1,9 @@ """Test QtSql.""" +import os + import pytest -from qtpy import QtSql +from qtpy import PYSIDE2, PYSIDE_VERSION, QtSql def test_qtsql(): @@ -24,7 +26,8 @@ def test_qtsql(): # Following modules are not (yet) part of any wrapper: # QSqlDriverCreator, QSqlDriverPlugin - +@pytest.mark.skipif(os.name == 'nt' and PYSIDE2 and PYSIDE_VERSION.startswith('5.13.2'), + reason="SQLite driver unavailable on PySide 5.13.2 with Windows") def test_qtsql_members_aliases(): """ Test aliased methods over qtpy.QtSql members including: diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 08cf8d3e..a2265294 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -4,7 +4,7 @@ import pytest -from qtpy import PYQT5, PYQT_VERSION, QtCore, QtWidgets, QtPrintSupport +from qtpy import PYQT5, PYQT_VERSION, QtCore, QtGui, QtWidgets @pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', @@ -14,11 +14,14 @@ def test_qtextedit_functions(qtbot): assert QtWidgets.QTextEdit.setTabStopWidth assert QtWidgets.QTextEdit.tabStopWidth assert QtWidgets.QTextEdit.print_ - textedit_widget = QtWidgets.QTextEdit(None) + textedit_widget = QtWidgets.QTextEdit(None) + qtbot.addWidget(textedit_widget) textedit_widget.setTabStopWidth(90) assert textedit_widget.tabStopWidth() == 90 - printer = QtPrintSupport.QPrinter() - textedit_widget.print_(printer) + print_device = QtGui.QPdfWriter('test.pdf') + textedit_widget.print_(print_device) + assert os.path.exists('test.pdf') + os.remove('test.pdf') @pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', @@ -29,10 +32,13 @@ def test_qplaintextedit_functions(qtbot): assert QtWidgets.QPlainTextEdit.tabStopWidth assert QtWidgets.QPlainTextEdit.print_ plaintextedit_widget = QtWidgets.QPlainTextEdit(None) + qtbot.addWidget(plaintextedit_widget) plaintextedit_widget.setTabStopWidth(90) assert plaintextedit_widget.tabStopWidth() == 90 - printer = QtPrintSupport.QPrinter() - plaintextedit_widget.print_(printer) + print_device = QtGui.QPdfWriter('test.pdf') + plaintextedit_widget.print_(print_device) + assert os.path.exists('test.pdf') + os.remove('test.pdf') def test_qapplication_functions(): @@ -46,6 +52,7 @@ def test_qdialog_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QDialog.exec_ dialog = QtWidgets.QDialog(None) + qtbot.addWidget(dialog) QtCore.QTimer.singleShot(100, dialog.accept) dialog.exec_() @@ -56,6 +63,7 @@ def test_qmenu_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QMenu.exec_ menu = QtWidgets.QMenu(None) + qtbot.addWidget(menu) QtCore.QTimer.singleShot(100, menu.close) menu.exec_() From 5159f4c28a5b39a7eacfb48274e36c1f72e58eb7 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 26 Jan 2022 10:58:25 -0500 Subject: [PATCH 361/703] QtWidgets: Skip stalled test for QDialog and QMenu on MacOS/conda/Python 3.6 --- qtpy/tests/test_qtwidgets.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index a2265294..6ef197cb 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -48,6 +48,9 @@ def test_qapplication_functions(): @pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif(sys.platform == 'darwin' and os.environ.get('USE_CONDA', 'Yes') == 'Yes' and + sys.version_info.major == 3 and sys.version_info.minor == 6, + reason="Stalls on MacOS CI when using conda and Python 3.6") def test_qdialog_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QDialog.exec_ @@ -59,6 +62,9 @@ def test_qdialog_functions(qtbot): @pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif(sys.platform == 'darwin' and os.environ.get('USE_CONDA', 'Yes') == 'Yes' and + sys.version_info.major == 3 and sys.version_info.minor == 6, + reason="Stalls on MacOS CI when using conda and Python 3.6") def test_qmenu_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QMenu.exec_ From d69796d4f377883a3559009483d9cff1291bed66 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 26 Jan 2022 11:40:59 -0500 Subject: [PATCH 362/703] QtWidgets: Add test for QDialog subclass. Remove qtbot.addWidget calls --- qtpy/tests/test_qtwidgets.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 6ef197cb..72c704e0 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -14,8 +14,7 @@ def test_qtextedit_functions(qtbot): assert QtWidgets.QTextEdit.setTabStopWidth assert QtWidgets.QTextEdit.tabStopWidth assert QtWidgets.QTextEdit.print_ - textedit_widget = QtWidgets.QTextEdit(None) - qtbot.addWidget(textedit_widget) + textedit_widget = QtWidgets.QTextEdit(None) textedit_widget.setTabStopWidth(90) assert textedit_widget.tabStopWidth() == 90 print_device = QtGui.QPdfWriter('test.pdf') @@ -32,7 +31,6 @@ def test_qplaintextedit_functions(qtbot): assert QtWidgets.QPlainTextEdit.tabStopWidth assert QtWidgets.QPlainTextEdit.print_ plaintextedit_widget = QtWidgets.QPlainTextEdit(None) - qtbot.addWidget(plaintextedit_widget) plaintextedit_widget.setTabStopWidth(90) assert plaintextedit_widget.tabStopWidth() == 90 print_device = QtGui.QPdfWriter('test.pdf') @@ -55,7 +53,24 @@ def test_qdialog_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QDialog.exec_ dialog = QtWidgets.QDialog(None) - qtbot.addWidget(dialog) + QtCore.QTimer.singleShot(100, dialog.accept) + dialog.exec_() + + +@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', + reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif(sys.platform == 'darwin' and os.environ.get('USE_CONDA', 'Yes') == 'Yes' and + sys.version_info.major == 3 and sys.version_info.minor == 6, + reason="Stalls on MacOS CI when using conda and Python 3.6") +def test_qdialog_subclass(qtbot): + """Test functions mapping for QtWidgets.QDialog when using a subclass""" + assert QtWidgets.QDialog.exec_ + class CustomDialog(QtWidgets.QDialog): + def __init__(self): + super().__init__(None) + self.setWindowTitle("Testing") + assert CustomDialog.exec_ + dialog = CustomDialog() QtCore.QTimer.singleShot(100, dialog.accept) dialog.exec_() @@ -69,7 +84,6 @@ def test_qmenu_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QMenu.exec_ menu = QtWidgets.QMenu(None) - qtbot.addWidget(menu) QtCore.QTimer.singleShot(100, menu.close) menu.exec_() From 49c0dbd661503da974616708ded93bb68082aa3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Thu, 27 Jan 2022 09:26:05 -0500 Subject: [PATCH 363/703] Testing: Apply suggestions from code review * Change skip constrains definition (`os.name` vs `sys.platform`) * `pytest` version constraints * Syntaxis to select `pytest-qt` version to install on the CIs Co-authored-by: CAM Gerlach --- .github/workflows/test.sh | 9 +++------ qtpy/tests/test_qtsql.py | 7 ++++--- setup.cfg | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 92f33c27..e28e901a 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -7,12 +7,9 @@ eval "$(conda shell.bash hook)" conda remove -q -n test-env --all || true # Create and activate conda environment for this test -if [ "$PYQT5_QT_VERSION" = "5.9" -a "${1}" = "pyqt5" ] || [ "$PYSIDE2_QT_VERSION" = "5.9" -a "${1}" = "pyside2" ] ; then - conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' 'pytest-qt==3.3.0' -else - conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' pytest-qt -fi - +QT_VERSION_VAR=${1^^}_QT_VERSION +if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; fi # pytest-qt >=4 doesn't support Qt >=5.9 +conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' pytest-qt${PYTESTQT_VERSION:-} conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index 4caa44ea..0c1f8004 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -1,5 +1,5 @@ """Test QtSql.""" -import os +import sys import pytest @@ -26,8 +26,9 @@ def test_qtsql(): # Following modules are not (yet) part of any wrapper: # QSqlDriverCreator, QSqlDriverPlugin -@pytest.mark.skipif(os.name == 'nt' and PYSIDE2 and PYSIDE_VERSION.startswith('5.13.2'), - reason="SQLite driver unavailable on PySide 5.13.2 with Windows") +@pytest.mark.skipif( + sys.platform == 'win32' and PYSIDE2 and PYSIDE_VERSION.startswith('5.13'), + reason="SQLite driver unavailable on PySide 5.13.2 with Windows") def test_qtsql_members_aliases(): """ Test aliased methods over qtpy.QtSql members including: diff --git a/setup.cfg b/setup.cfg index 0333558b..9e749739 100644 --- a/setup.cfg +++ b/setup.cfg @@ -54,6 +54,6 @@ exclude = [options.extras_require] test = - pytest>=6.0.0,<7.0 + pytest>=6.0.0 pytest-cov>=3.0.0 pytest-qt From aa015843f63ddcde5a41fe713cb091646e3e220d Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 27 Jan 2022 13:53:25 -0500 Subject: [PATCH 364/703] Testing: Fix test.sh on macOS and add fixtures --- .github/workflows/test.sh | 3 ++- qtpy/tests/conftest.py | 12 ++++++++++++ qtpy/tests/test_qtgui.py | 7 +++---- qtpy/tests/test_qtsql.py | 20 +++++++++++++------- qtpy/tests/test_qtwidgets.py | 15 +++++++-------- 5 files changed, 37 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index e28e901a..3acd4fbb 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -7,7 +7,8 @@ eval "$(conda shell.bash hook)" conda remove -q -n test-env --all || true # Create and activate conda environment for this test -QT_VERSION_VAR=${1^^}_QT_VERSION +BINDING=$(echo "$1" | tr '[:lower:]' '[:upper:]') +QT_VERSION_VAR=${BINDING}_QT_VERSION if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; fi # pytest-qt >=4 doesn't support Qt >=5.9 conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' pytest-qt${PYTESTQT_VERSION:-} conda activate test-env diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index 9322f263..ea1fd190 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -1,5 +1,7 @@ import os +import pytest + def pytest_configure(config): """Configure the test environment.""" @@ -64,3 +66,13 @@ def pytest_report_header(config): versions += os.linesep return versions + + +@pytest.fixture +def pdf_writer(qtbot): + from pathlib import Path + from qtpy import QtGui + output_path = Path('test.pdf') + device = QtGui.QPdfWriter(str(output_path)) + yield device, output_path + output_path.unlink() diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index b069823e..1e4af95f 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -34,14 +34,13 @@ def test_qguiapplication_functions(): @pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', reason="Segmentation fault/Aborted on Linux CI when not using conda") -def test_qtextdocument_functions(): +def test_qtextdocument_functions(pdf_writer): """Test functions mapping for QtGui.QTextDocument.""" assert QtGui.QTextDocument.print_ is not None text_document = QtGui.QTextDocument("Test") - print_device = QtGui.QPdfWriter('test.pdf') + print_device, output_path = pdf_writer text_document.print_(print_device) - assert os.path.exists('test.pdf') - os.remove('test.pdf') + assert os.path.exists(output_path) @pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index 0c1f8004..7c7b45ce 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -6,6 +6,14 @@ from qtpy import PYSIDE2, PYSIDE_VERSION, QtSql +@pytest.fixture +def database_connection(): + """Create a database connection""" + connection = QtSql.QSqlDatabase.addDatabase("QSQLITE") + yield connection + connection.close() + + def test_qtsql(): """Test the qtpy.QtSql namespace""" assert QtSql.QSqlDatabase is not None @@ -29,7 +37,7 @@ def test_qtsql(): @pytest.mark.skipif( sys.platform == 'win32' and PYSIDE2 and PYSIDE_VERSION.startswith('5.13'), reason="SQLite driver unavailable on PySide 5.13.2 with Windows") -def test_qtsql_members_aliases(): +def test_qtsql_members_aliases(database_connection): """ Test aliased methods over qtpy.QtSql members including: @@ -41,11 +49,10 @@ def test_qtsql_members_aliases(): assert QtSql.QSqlQuery.exec_ is not None assert QtSql.QSqlResult.exec_ is not None - connection = QtSql.QSqlDatabase.addDatabase("QSQLITE") - assert connection.open() - connection.setDatabaseName("test.sqlite") + assert database_connection.open() + database_connection.setDatabaseName("test.sqlite") QtSql.QSqlDatabase.exec_( - connection, + database_connection, """ CREATE TABLE test ( id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, @@ -54,7 +61,7 @@ def test_qtsql_members_aliases(): """ ) # Created table 'test' and 'sqlite_sequence' - assert len(connection.tables()) == 2 + assert len(database_connection.tables()) == 2 insert_table_query = QtSql.QSqlQuery() assert insert_table_query.exec_( @@ -72,4 +79,3 @@ def test_qtsql_members_aliases(): select_table_query.exec_() record = select_table_query.record() assert not record.isEmpty() - connection.close() diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 72c704e0..98633eb2 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -9,7 +9,7 @@ @pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', reason="Fatal Python error: Aborted on Linux CI when not using conda") -def test_qtextedit_functions(qtbot): +def test_qtextedit_functions(qtbot, pdf_writer): """Test functions mapping for QtWidgets.QTextEdit.""" assert QtWidgets.QTextEdit.setTabStopWidth assert QtWidgets.QTextEdit.tabStopWidth @@ -17,15 +17,14 @@ def test_qtextedit_functions(qtbot): textedit_widget = QtWidgets.QTextEdit(None) textedit_widget.setTabStopWidth(90) assert textedit_widget.tabStopWidth() == 90 - print_device = QtGui.QPdfWriter('test.pdf') + print_device, output_path = pdf_writer textedit_widget.print_(print_device) - assert os.path.exists('test.pdf') - os.remove('test.pdf') + assert os.path.exists(output_path) @pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', reason="Fatal Python error: Aborted on Linux CI when not using conda") -def test_qplaintextedit_functions(qtbot): +def test_qplaintextedit_functions(qtbot, pdf_writer): """Test functions mapping for QtWidgets.QPlainTextEdit.""" assert QtWidgets.QPlainTextEdit.setTabStopWidth assert QtWidgets.QPlainTextEdit.tabStopWidth @@ -33,10 +32,9 @@ def test_qplaintextedit_functions(qtbot): plaintextedit_widget = QtWidgets.QPlainTextEdit(None) plaintextedit_widget.setTabStopWidth(90) assert plaintextedit_widget.tabStopWidth() == 90 - print_device = QtGui.QPdfWriter('test.pdf') + print_device, output_path = pdf_writer plaintextedit_widget.print_(print_device) - assert os.path.exists('test.pdf') - os.remove('test.pdf') + assert os.path.exists(output_path) def test_qapplication_functions(): @@ -87,6 +85,7 @@ def test_qmenu_functions(qtbot): QtCore.QTimer.singleShot(100, menu.close) menu.exec_() + @pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" "to work with scoped enum access") From f09c0068def9d2cec646bda4f351eff04ea5df07 Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Fri, 28 Jan 2022 11:33:51 +0100 Subject: [PATCH 365/703] enum_compat: allow unscoped access to aliased enum values A number of enum values in Qt are actually aliases and one need to inspect the enum members to find the names under which some values are aliased. --- qtpy/enums_compat.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/qtpy/enums_compat.py b/qtpy/enums_compat.py index 1340fa36..70263fa2 100644 --- a/qtpy/enums_compat.py +++ b/qtpy/enums_compat.py @@ -21,7 +21,9 @@ def promote_enums(module): Search enums in the given module and allow unscoped access. Taken from: - https://github.com/pyqtgraph/pyqtgraph/blob/pyqtgraph-0.12.1/pyqtgraph/Qt.py#L331-L377 + https://github.com/pyqtgraph/pyqtgraph/blob/pyqtgraph-0.12.1/pyqtgraph/Qt.py#L331-L377 + and adapted to also copy enum values aliased under different names. + """ class_names = [name for name in dir(module) if name.startswith('Q')] for class_name in class_names: @@ -33,5 +35,5 @@ def promote_enums(module): attrib = getattr(klass, attrib_name) if not isinstance(attrib, enum.EnumMeta): continue - for enum_obj in attrib: - setattr(klass, enum_obj.name, enum_obj) + for name, value in attrib.__members__.items(): + setattr(klass, name, value) From 79a2adf8b8cd68936dc16655ff407083eb5ca179 Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Fri, 28 Jan 2022 11:36:24 +0100 Subject: [PATCH 366/703] tests: check we can access enum values using an alias name --- qtpy/tests/test_qtwidgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 68e8192c..ef67d2ec 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -41,3 +41,4 @@ def test_enum_access(): assert QtWidgets.QFileDialog.AcceptOpen == QtWidgets.QFileDialog.AcceptMode.AcceptOpen assert QtWidgets.QMessageBox.InvalidRole == QtWidgets.QMessageBox.ButtonRole.InvalidRole assert QtWidgets.QStyle.State_None == QtWidgets.QStyle.StateFlag.State_None + assert QtWidgets.QStyle.SC_SliderGroove == QtWidgets.QStyle.SubControl.SC_SliderGroove From dc90a7d26fede0768b2aa91fbcacdd32c765a99a Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Fri, 28 Jan 2022 11:49:45 +0100 Subject: [PATCH 367/703] tests: test for aliases using TicksLeft which is a documented alias --- qtpy/tests/test_qtwidgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index ef67d2ec..d8392f26 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -41,4 +41,5 @@ def test_enum_access(): assert QtWidgets.QFileDialog.AcceptOpen == QtWidgets.QFileDialog.AcceptMode.AcceptOpen assert QtWidgets.QMessageBox.InvalidRole == QtWidgets.QMessageBox.ButtonRole.InvalidRole assert QtWidgets.QStyle.State_None == QtWidgets.QStyle.StateFlag.State_None + assert QtWidgets.QSlider.TicksLeft == QtWidgets.QSlider.TickPosition.TicksAbove assert QtWidgets.QStyle.SC_SliderGroove == QtWidgets.QStyle.SubControl.SC_SliderGroove From 2e4d7dd2f3dda0e8f47305a07e791de162250287 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Fri, 28 Jan 2022 10:18:23 -0500 Subject: [PATCH 368/703] Testing: Apply suggestions from code review * Improve test files docstrings and add blank lines at the end of files * Use `Path.exists` instead of `os.path.exists` Co-authored-by: Carlos Cordoba Co-authored-by: CAM Gerlach --- qtpy/tests/test_qtgui.py | 3 ++- qtpy/tests/test_qtprintsupport.py | 1 + qtpy/tests/test_qtsql.py | 3 +++ qtpy/tests/test_qtwidgets.py | 5 +++-- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 1e4af95f..59f7a3c5 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -1,4 +1,5 @@ """Test QtGui.""" + import os import sys @@ -40,7 +41,7 @@ def test_qtextdocument_functions(pdf_writer): text_document = QtGui.QTextDocument("Test") print_device, output_path = pdf_writer text_document.print_(print_device) - assert os.path.exists(output_path) + assert output_path.exists() @pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py index 3d14575c..41a02f25 100644 --- a/qtpy/tests/test_qtprintsupport.py +++ b/qtpy/tests/test_qtprintsupport.py @@ -1,4 +1,5 @@ """Test QtPrintSupport.""" + import os import sys diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index 7c7b45ce..f69e6c55 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -1,4 +1,5 @@ """Test QtSql.""" + import sys import pytest @@ -60,6 +61,7 @@ def test_qtsql_members_aliases(database_connection): ) """ ) + # Created table 'test' and 'sqlite_sequence' assert len(database_connection.tables()) == 2 @@ -71,6 +73,7 @@ def test_qtsql_members_aliases(database_connection): ) """ ) + select_table_query = QtSql.QSqlQuery() select_table_query.prepare( """ diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 98633eb2..3165e8fd 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -1,4 +1,5 @@ """Test QtWidgets.""" + import os import sys @@ -19,7 +20,7 @@ def test_qtextedit_functions(qtbot, pdf_writer): assert textedit_widget.tabStopWidth() == 90 print_device, output_path = pdf_writer textedit_widget.print_(print_device) - assert os.path.exists(output_path) + assert output_path.exists() @pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', @@ -34,7 +35,7 @@ def test_qplaintextedit_functions(qtbot, pdf_writer): assert plaintextedit_widget.tabStopWidth() == 90 print_device, output_path = pdf_writer plaintextedit_widget.print_(print_device) - assert os.path.exists(output_path) + assert output_path.exists() def test_qapplication_functions(): From 348710cd0c953c171b9e74686bf6a02df06ff8ab Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 28 Jan 2022 10:51:09 -0500 Subject: [PATCH 369/703] Testing: Create utils module for USE_CONDA validations --- qtpy/tests/test_qtcore.py | 4 ++-- qtpy/tests/test_qtgui.py | 8 ++++---- qtpy/tests/test_qtmultimediawidgets.py | 5 +++-- qtpy/tests/test_qtprintsupport.py | 5 ++--- qtpy/tests/test_qtwidgets.py | 18 +++++++++--------- qtpy/tests/test_qtwinextras.py | 6 ++++-- qtpy/tests/test_uic.py | 2 +- qtpy/tests/utils.py | 11 +++++++++++ 8 files changed, 36 insertions(+), 23 deletions(-) create mode 100644 qtpy/tests/utils.py diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index aa5964ca..97c56b97 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,12 +1,12 @@ """Test QtCore.""" from datetime import datetime -import os import sys import pytest from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PYQT_VERSION, PYSIDE_VERSION, QtCore +from qtpy.tests.utils import not_using_conda def test_qtmsghandler(): @@ -22,7 +22,7 @@ def test_qdatetime_toPython(): assert isinstance(py_date, datetime) -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qeventloop_exec_(qtbot): """Test QEventLoop.exec_""" diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 59f7a3c5..7b75bdbb 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -1,14 +1,14 @@ """Test QtGui.""" -import os import sys import pytest from qtpy import PYQT5, PYQT_VERSION, QtGui +from qtpy.tests.utils import not_using_conda -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qfontmetrics_width(qtbot): """Test QFontMetrics width""" @@ -19,7 +19,7 @@ def test_qfontmetrics_width(qtbot): assert width in range(40, 62) -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qdrag_functions(qtbot): """Test functions mapping for QtGui.QDrag.""" @@ -33,7 +33,7 @@ def test_qguiapplication_functions(): assert QtGui.QGuiApplication.exec_ is not None -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), reason="Segmentation fault/Aborted on Linux CI when not using conda") def test_qtextdocument_functions(pdf_writer): """Test functions mapping for QtGui.QTextDocument.""" diff --git a/qtpy/tests/test_qtmultimediawidgets.py b/qtpy/tests/test_qtmultimediawidgets.py index ac1faefe..4a7fca9a 100644 --- a/qtpy/tests/test_qtmultimediawidgets.py +++ b/qtpy/tests/test_qtmultimediawidgets.py @@ -1,13 +1,14 @@ -import os +"""Test QtMultimediaWidgets.""" import pytest from qtpy import PYQT5, PYSIDE2 +from qtpy.tests.utils import using_conda @pytest.mark.skipif( not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") @pytest.mark.skipif( - os.environ.get('USE_CONDA', 'Yes') == 'Yes', + using_conda(), reason="Conda packages don't seem to include QtMultimedia") def test_qtmultimediawidgets(): """Test the qtpy.QtMultimediaWidgets namespace""" diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py index 41a02f25..427d0bfa 100644 --- a/qtpy/tests/test_qtprintsupport.py +++ b/qtpy/tests/test_qtprintsupport.py @@ -1,12 +1,11 @@ """Test QtPrintSupport.""" -import os import sys import pytest from qtpy import QtPrintSupport - +from qtpy.tests.utils import not_using_conda def test_qtprintsupport(): """Test the qtpy.QtPrintSupport namespace""" @@ -30,7 +29,7 @@ def test_qprintdialog_exec_(): assert QtPrintSupport.QPrintDialog.exec_ is not None -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda, reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qprintpreviewwidget_print_(qtbot): """Test qtpy.QtPrintSupport.QPrintPreviewWidget print_""" diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 3165e8fd..31b58702 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -1,14 +1,14 @@ """Test QtWidgets.""" -import os import sys import pytest from qtpy import PYQT5, PYQT_VERSION, QtCore, QtGui, QtWidgets +from qtpy.tests.utils import using_conda, not_using_conda -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qtextedit_functions(qtbot, pdf_writer): """Test functions mapping for QtWidgets.QTextEdit.""" @@ -23,7 +23,7 @@ def test_qtextedit_functions(qtbot, pdf_writer): assert output_path.exists() -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qplaintextedit_functions(qtbot, pdf_writer): """Test functions mapping for QtWidgets.QPlainTextEdit.""" @@ -43,9 +43,9 @@ def test_qapplication_functions(): assert QtWidgets.QApplication.exec_ -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") -@pytest.mark.skipif(sys.platform == 'darwin' and os.environ.get('USE_CONDA', 'Yes') == 'Yes' and +@pytest.mark.skipif(sys.platform == 'darwin' and using_conda() and sys.version_info.major == 3 and sys.version_info.minor == 6, reason="Stalls on MacOS CI when using conda and Python 3.6") def test_qdialog_functions(qtbot): @@ -56,9 +56,9 @@ def test_qdialog_functions(qtbot): dialog.exec_() -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") -@pytest.mark.skipif(sys.platform == 'darwin' and os.environ.get('USE_CONDA', 'Yes') == 'Yes' and +@pytest.mark.skipif(sys.platform == 'darwin' and using_conda() and sys.version_info.major == 3 and sys.version_info.minor == 6, reason="Stalls on MacOS CI when using conda and Python 3.6") def test_qdialog_subclass(qtbot): @@ -74,9 +74,9 @@ def __init__(self): dialog.exec_() -@pytest.mark.skipif(sys.platform.startswith('linux') and os.environ.get('USE_CONDA', 'No') == 'No', +@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") -@pytest.mark.skipif(sys.platform == 'darwin' and os.environ.get('USE_CONDA', 'Yes') == 'Yes' and +@pytest.mark.skipif(sys.platform == 'darwin' and using_conda() and sys.version_info.major == 3 and sys.version_info.minor == 6, reason="Stalls on MacOS CI when using conda and Python 3.6") def test_qmenu_functions(qtbot): diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index 64b20627..ec220104 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -1,14 +1,16 @@ -import os +"""Test QtWinExtras.""" + import sys import pytest from qtpy import PYQT6, PYSIDE2, PYSIDE6 +from qtpy.tests.utils import using_conda @pytest.mark.skipif( PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") @pytest.mark.skipif( - sys.platform != "win32" or os.environ.get('USE_CONDA', 'Yes') == 'Yes', + sys.platform != "win32" or using_conda(), reason="Only available in Qt5 bindings > 5.9 with pip on Windows in CIs") def test_qtwinextras(): """Test the qtpy.QtWinExtras namespace""" diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index a4a32ef7..ecd50985 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -104,7 +104,7 @@ def __init__(self): @pytest.mark.skipif( PYSIDE2 and sys.platform == "darwin" and sys.version_info.major == 3 and sys.version_info.minor == 9 - and os.environ.get('USE_CONDA', 'No') == 'No', + and not_using_conda(), reason="Fails on this specific platform, at least on our CIs") @pytest.mark.skipif( os.environ.get('CI', None) is not None diff --git a/qtpy/tests/utils.py b/qtpy/tests/utils.py new file mode 100644 index 00000000..f9e36ddc --- /dev/null +++ b/qtpy/tests/utils.py @@ -0,0 +1,11 @@ +"""Utility functions for tests.""" + +import os + + +def using_conda(): + return os.environ.get('USE_CONDA', 'Yes') == 'Yes' + + +def not_using_conda(): + return os.environ.get('USE_CONDA', 'No') == 'No' From 8c73dcb88b37cf16191a5a87420bcee2a60877d4 Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Fri, 28 Jan 2022 18:45:10 +0100 Subject: [PATCH 370/703] QtCore: remove manual aliases and update tests --- qtpy/QtCore.py | 4 ---- qtpy/tests/test_qtcore.py | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index c29441a5..7be1dc3c 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -48,10 +48,6 @@ promote_enums(QtCore) del QtCore - # Map missing unscoped access enum values - Qt.BackButton = Qt.XButton1 - Qt.ForwardButton = Qt.XButton2 - # Alias deprecated ItemDataRole enum values removed in Qt6 Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 4c8a8f58..4d2199a4 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -37,7 +37,7 @@ def test_enum_access(): assert QtCore.Qt.Key_Return == QtCore.Qt.Key.Key_Return assert QtCore.Qt.transparent == QtCore.Qt.GlobalColor.transparent assert QtCore.Qt.Widget == QtCore.Qt.WindowType.Widget - assert QtCore.Qt.BackButton == QtCore.Qt.MouseButton.XButton1 + assert QtCore.Qt.BackButton == QtCore.Qt.MouseButton.BackButton assert QtCore.Qt.XButton1 == QtCore.Qt.MouseButton.XButton1 assert QtCore.Qt.BackgroundColorRole == QtCore.Qt.ItemDataRole.BackgroundColorRole assert QtCore.Qt.TextColorRole == QtCore.Qt.ItemDataRole.TextColorRole From 328f82a825b96a8211949e58acef02705bcf1c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Mon, 31 Jan 2022 09:59:05 -0500 Subject: [PATCH 371/703] Testing: Apply suggestions from code review * Improve skip statements code style Co-authored-by: Carlos Cordoba Co-authored-by: CAM Gerlach --- qtpy/tests/test_qtcore.py | 5 ++-- qtpy/tests/test_qtgui.py | 15 ++++++---- qtpy/tests/test_qtprintsupport.py | 5 ++-- qtpy/tests/test_qtwidgets.py | 46 ++++++++++++++++++------------- 4 files changed, 42 insertions(+), 29 deletions(-) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 97c56b97..fe0ec039 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -22,8 +22,9 @@ def test_qdatetime_toPython(): assert isinstance(py_date, datetime) -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qeventloop_exec_(qtbot): """Test QEventLoop.exec_""" assert QtCore.QEventLoop.exec_ is not None diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 7b75bdbb..0aafe6a9 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -8,8 +8,9 @@ from qtpy.tests.utils import not_using_conda -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qfontmetrics_width(qtbot): """Test QFontMetrics width""" assert QtGui.QFontMetrics.width is not None @@ -19,8 +20,9 @@ def test_qfontmetrics_width(qtbot): assert width in range(40, 62) -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qdrag_functions(qtbot): """Test functions mapping for QtGui.QDrag.""" assert QtGui.QDrag.exec_ is not None @@ -33,8 +35,9 @@ def test_qguiapplication_functions(): assert QtGui.QGuiApplication.exec_ is not None -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), - reason="Segmentation fault/Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Segmentation fault/Aborted on Linux CI when not using conda") def test_qtextdocument_functions(pdf_writer): """Test functions mapping for QtGui.QTextDocument.""" assert QtGui.QTextDocument.print_ is not None diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py index 427d0bfa..45770042 100644 --- a/qtpy/tests/test_qtprintsupport.py +++ b/qtpy/tests/test_qtprintsupport.py @@ -29,8 +29,9 @@ def test_qprintdialog_exec_(): assert QtPrintSupport.QPrintDialog.exec_ is not None -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda, - reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qprintpreviewwidget_print_(qtbot): """Test qtpy.QtPrintSupport.QPrintPreviewWidget print_""" assert QtPrintSupport.QPrintPreviewWidget.print_ is not None diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 31b58702..c305331f 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -8,8 +8,9 @@ from qtpy.tests.utils import using_conda, not_using_conda -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qtextedit_functions(qtbot, pdf_writer): """Test functions mapping for QtWidgets.QTextEdit.""" assert QtWidgets.QTextEdit.setTabStopWidth @@ -23,8 +24,9 @@ def test_qtextedit_functions(qtbot, pdf_writer): assert output_path.exists() -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qplaintextedit_functions(qtbot, pdf_writer): """Test functions mapping for QtWidgets.QPlainTextEdit.""" assert QtWidgets.QPlainTextEdit.setTabStopWidth @@ -43,11 +45,13 @@ def test_qapplication_functions(): assert QtWidgets.QApplication.exec_ -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") -@pytest.mark.skipif(sys.platform == 'darwin' and using_conda() and - sys.version_info.major == 3 and sys.version_info.minor == 6, - reason="Stalls on MacOS CI when using conda and Python 3.6") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform == 'darwin' and using_conda() + and sys.version_info[:2] == (3, 6), + reason="Stalls on macOS CI when using Conda and Python 3.6") def test_qdialog_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QDialog.exec_ @@ -56,11 +60,13 @@ def test_qdialog_functions(qtbot): dialog.exec_() -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") -@pytest.mark.skipif(sys.platform == 'darwin' and using_conda() and - sys.version_info.major == 3 and sys.version_info.minor == 6, - reason="Stalls on MacOS CI when using conda and Python 3.6") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform == 'darwin' and using_conda() + and sys.version_info[:2] == (3, 6), + reason="Stalls on macOS CI when using Conda and Python 3.6") def test_qdialog_subclass(qtbot): """Test functions mapping for QtWidgets.QDialog when using a subclass""" assert QtWidgets.QDialog.exec_ @@ -74,11 +80,13 @@ def __init__(self): dialog.exec_() -@pytest.mark.skipif(sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") -@pytest.mark.skipif(sys.platform == 'darwin' and using_conda() and - sys.version_info.major == 3 and sys.version_info.minor == 6, - reason="Stalls on MacOS CI when using conda and Python 3.6") +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform == 'darwin' and using_conda() + and sys.version_info[:2] == (3, 6), + reason="Stalls on macOS CI when using Conda and Python 3.6") def test_qmenu_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QMenu.exec_ From 4a29566115a22c16899fd70aa2c2e97d08251643 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 31 Jan 2022 11:23:57 -0500 Subject: [PATCH 372/703] Testing: Add restriction for pip version with Python 3.6 to prevent error with the dataclasses module when building wheel --- .github/workflows/test.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 3acd4fbb..7bce53c3 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -10,7 +10,8 @@ conda remove -q -n test-env --all || true BINDING=$(echo "$1" | tr '[:lower:]' '[:upper:]') QT_VERSION_VAR=${BINDING}_QT_VERSION if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; fi # pytest-qt >=4 doesn't support Qt >=5.9 -conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' pytest-qt${PYTESTQT_VERSION:-} +if [ "${PYTHON_VERSION}" = "3.6" ]; then PIP_VERSION="=21.3.1"; fi # pip >21.3.1 depends on the 'dataclasses' module (unavailable by default with Python 3.6) +conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' pytest-qt${PYTESTQT_VERSION:-} pip${PIP_VERSION:-} conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then From 96dbec3b7bcdbcb3d0695623e104084de601d5d6 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 2 Feb 2022 10:59:16 -0500 Subject: [PATCH 373/703] Release 2.0.1 --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ qtpy/__init__.py | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecd28806..49465431 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,37 @@ # History of changes +## Version 2.0.1 (2022-02-02) + +### Issues Closed + +* [Issue 320](https://github.com/spyder-ide/qtpy/issues/320) - Release QtPy 2.0.1 +* [Issue 316](https://github.com/spyder-ide/qtpy/issues/316) - Tests for instance methods alias mapping fix (exec_ vs exec and others) ([PR 317](https://github.com/spyder-ide/qtpy/pull/317) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 311](https://github.com/spyder-ide/qtpy/issues/311) - `QtCore.Qt.mightBeRichText` undefined in PySide ([PR 313](https://github.com/spyder-ide/qtpy/pull/313) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 306](https://github.com/spyder-ide/qtpy/issues/306) - Qt6: missing unscoped enum values ([PR 314](https://github.com/spyder-ide/qtpy/pull/314) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 305](https://github.com/spyder-ide/qtpy/issues/305) - Qt6: exec_ vs. exec ([PR 308](https://github.com/spyder-ide/qtpy/pull/308) by [@kumattau](https://github.com/kumattau)) +* [Issue 304](https://github.com/spyder-ide/qtpy/issues/304) - Qt6: QtCharts namespace incompatibility ([PR 315](https://github.com/spyder-ide/qtpy/pull/315) by [@dalthviz](https://github.com/dalthviz)) + +In this release 6 issues were closed. + +### Pull Requests Merged + +* [PR 319](https://github.com/spyder-ide/qtpy/pull/319) - PR: Promote enum aliases, by [@MatthieuDartiailh](https://github.com/MatthieuDartiailh) +* [PR 317](https://github.com/spyder-ide/qtpy/pull/317) - PR: Add missing tests for aliased methods, by [@dalthviz](https://github.com/dalthviz) ([316](https://github.com/spyder-ide/qtpy/issues/316)) +* [PR 315](https://github.com/spyder-ide/qtpy/pull/315) - PR: Add `QtCharts` alias for backward compatibility with 1.x, by [@dalthviz](https://github.com/dalthviz) ([304](https://github.com/spyder-ide/qtpy/issues/304)) +* [PR 314](https://github.com/spyder-ide/qtpy/pull/314) - PR: Add mapping for missing enum values aliases on `QtCore.Qt`, by [@dalthviz](https://github.com/dalthviz) ([306](https://github.com/spyder-ide/qtpy/issues/306)) +* [PR 313](https://github.com/spyder-ide/qtpy/pull/313) - PR: Add missing `QtGui` utility function to `QtCore.Qt` for PySide bindings, by [@dalthviz](https://github.com/dalthviz) ([311](https://github.com/spyder-ide/qtpy/issues/311)) +* [PR 312](https://github.com/spyder-ide/qtpy/pull/312) - PR: Add "New features" section for version 2.0 to changelog, by [@ccordoba12](https://github.com/ccordoba12) +* [PR 309](https://github.com/spyder-ide/qtpy/pull/309) - PR: Set CI job's timeout to 10 min to force a stalled test to terminate, by [@kumattau](https://github.com/kumattau) +* [PR 308](https://github.com/spyder-ide/qtpy/pull/308) - PR: Fix mappings of instance method and slot alias (PyQt6 and PySide6), by [@kumattau](https://github.com/kumattau) ([305](https://github.com/spyder-ide/qtpy/issues/305)) +* [PR 307](https://github.com/spyder-ide/qtpy/pull/307) - PR: Add missing imports in QtOpenGL, by [@renefritze](https://github.com/renefritze) + +In this release 9 pull requests were closed. + + +---- + + + ## Version 2.0.0 (2021-12-22) ### New features diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 33842484..d849764d 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.1.0.dev0' +__version__ = '2.0.1' class PythonQtError(RuntimeError): From 7b27f767c8b5805be44437318f0a1376e65aefb8 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 2 Feb 2022 11:03:18 -0500 Subject: [PATCH 374/703] Back to work --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index d849764d..33842484 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.0.1' +__version__ = '2.1.0.dev0' class PythonQtError(RuntimeError): From d9dc0be440fb6bf416373cc473b5f2d42a7eb010 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 25 Feb 2022 23:28:51 -0600 Subject: [PATCH 375/703] Disable --importmode=importlib to work around pytest-dev/pytest#9645 --- .github/workflows/ci.yml | 2 +- .github/workflows/test.sh | 2 +- MANIFEST.in | 11 ++++++----- setup.cfg | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8736b2c4..6e3719e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: test: name: Test ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} runs-on: ${{ matrix.os }} - timeout-minutes: 15 + timeout-minutes: 20 defaults: run: shell: ${{ matrix.special-invocation }}bash -l {0} diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 7bce53c3..8d632927 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -11,7 +11,7 @@ BINDING=$(echo "$1" | tr '[:lower:]' '[:upper:]') QT_VERSION_VAR=${BINDING}_QT_VERSION if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; fi # pytest-qt >=4 doesn't support Qt >=5.9 if [ "${PYTHON_VERSION}" = "3.6" ]; then PIP_VERSION="=21.3.1"; fi # pip >21.3.1 depends on the 'dataclasses' module (unavailable by default with Python 3.6) -conda create -q -n test-env python=${PYTHON_VERSION} pytest 'pytest-cov>=3.0.0' pytest-qt${PYTESTQT_VERSION:-} pip${PIP_VERSION:-} +conda create -q -n test-env python=${PYTHON_VERSION} "pytest>=6,!=7.0.0,!=7.0.1" "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} pip${PIP_VERSION:-} conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then diff --git a/MANIFEST.in b/MANIFEST.in index 1d7cd43a..c089429f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,7 @@ -include AUTHORS.md -include CHANGELOG.md -include LICENSE.txt -include README.md -include SECURITY.md +include AUTHORS* +include CHANGELOG* +include LICENSE* +include README* +include SECURITY* +include pytest.ini recursive-include qtpy/tests *.py *.ui diff --git a/setup.cfg b/setup.cfg index 9e749739..545bf92a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -54,6 +54,6 @@ exclude = [options.extras_require] test = - pytest>=6.0.0 + pytest>=6,!=7.0.0,!=7.0.1 pytest-cov>=3.0.0 pytest-qt From 544c39b58c254c6521b0c837c4c2aab22d285146 Mon Sep 17 00:00:00 2001 From: Nicolas Elie <40382614+n-elie@users.noreply.github.com> Date: Tue, 1 Mar 2022 15:07:45 +0100 Subject: [PATCH 376/703] Monkey patch UIParser for Python 3.9 compatibility --- qtpy/uic.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qtpy/uic.py b/qtpy/uic.py index 0650d45d..5ab8fbc9 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -86,6 +86,15 @@ from PySide2.QtUiTools import QUiLoader try: from pyside2uic import compileUi + from pyside2uic.uiparser import UIParser + from xml.etree.ElementTree import Element + class ElemPatched(Element): + def getiterator(self, *args, **kwargs): + return self.iter(*args, **kwargs) + def readResources(self, elem): + return self._readResources(ElemPatched(elem)) + UIParser._readResources = UIParser.readResources + UIParser.readResources = readResources except ImportError: pass @@ -247,6 +256,7 @@ def loadUiType(uifile, from_imports=False): import sys from io import StringIO from xml.etree.ElementTree import ElementTree + from . import QtWidgets # Parse the UI file From 296dee3da8aba381b3cf17da34a6d17626e50357 Mon Sep 17 00:00:00 2001 From: Nicolas Elie <40382614+n-elie@users.noreply.github.com> Date: Tue, 1 Mar 2022 16:12:13 +0100 Subject: [PATCH 377/703] Comment why patching UIParser is needed --- qtpy/uic.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qtpy/uic.py b/qtpy/uic.py index 5ab8fbc9..6f53e4f5 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -86,6 +86,9 @@ from PySide2.QtUiTools import QUiLoader try: from pyside2uic import compileUi + # Patch UIParser as xml.etree.Elementree.Element.getiterator + # was deprecated since Python 3.2 and removed in Python 3.9 + # https://docs.python.org/3.9/whatsnew/3.9.html#removed from pyside2uic.uiparser import UIParser from xml.etree.ElementTree import Element class ElemPatched(Element): From fa5139b84c30971e6560f9ab837045c80af769ff Mon Sep 17 00:00:00 2001 From: Martin Israel Date: Tue, 1 Mar 2022 22:20:13 +0100 Subject: [PATCH 378/703] Add missing QWebEngineScript import for PyQt5 and PySide2 --- qtpy/QtWebEngineWidgets.py | 2 ++ qtpy/tests/test_qtwebenginewidgets.py | 1 + 2 files changed, 3 insertions(+) diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index ead018d0..58a3a003 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -21,6 +21,7 @@ from PyQt5.QtWebEngineWidgets import QWebEnginePage from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtWebEngineWidgets import QWebEngineSettings + from PyQt5.QtWebEngineWidgets import QWebEngineScript # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 from PyQt5.QtWebEngineWidgets import QWebEngineProfile except ImportError: @@ -42,6 +43,7 @@ from PySide2.QtWebEngineWidgets import QWebEnginePage from PySide2.QtWebEngineWidgets import QWebEngineView from PySide2.QtWebEngineWidgets import QWebEngineSettings + from PySide2.QtWebEngineWidgets import QWebEngineScript # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 from PySide2.QtWebEngineWidgets import QWebEngineProfile else: diff --git a/qtpy/tests/test_qtwebenginewidgets.py b/qtpy/tests/test_qtwebenginewidgets.py index 513b6c59..f9b3ac48 100644 --- a/qtpy/tests/test_qtwebenginewidgets.py +++ b/qtpy/tests/test_qtwebenginewidgets.py @@ -10,3 +10,4 @@ def test_qtwebenginewidgets(): assert QtWebEngineWidgets.QWebEnginePage is not None assert QtWebEngineWidgets.QWebEngineView is not None assert QtWebEngineWidgets.QWebEngineSettings is not None + assert QtWebEngineWidgets.QWebEngineScript is not None From 8c684e4037c470460e895d2311353e4502512844 Mon Sep 17 00:00:00 2001 From: Martin Israel Date: Wed, 2 Mar 2022 00:33:40 +0100 Subject: [PATCH 379/703] adds support for QWebEngineScript in PyQt6 and PySide6 --- qtpy/QtWebEngineWidgets.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 58a3a003..5bcaebf0 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -34,11 +34,14 @@ from PyQt6.QtWebEngineCore import QWebEnginePage from PyQt6.QtWebEngineCore import QWebEngineSettings from PyQt6.QtWebEngineCore import QWebEngineProfile + from PyQt6.QtWebEngineCore import QWebEngineScript + elif PYSIDE6: from PySide6.QtWebEngineWidgets import * from PySide6.QtWebEngineCore import QWebEnginePage from PySide6.QtWebEngineCore import QWebEngineSettings from PySide6.QtWebEngineCore import QWebEngineProfile + from PySide6.QtWebEngineCore import QWebEngineScript elif PYSIDE2: from PySide2.QtWebEngineWidgets import QWebEnginePage from PySide2.QtWebEngineWidgets import QWebEngineView From 248f9090e230896cb051fa7e6f650485a4c46508 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 24 Mar 2022 14:29:14 -0500 Subject: [PATCH 380/703] QtCore: Add missing Qt.MidButton on PyQt6 --- qtpy/QtCore.py | 2 ++ qtpy/tests/test_qtcore.py | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index c9337f7e..3b36d7a0 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -52,6 +52,8 @@ # Alias deprecated ItemDataRole enum values removed in Qt6 Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole + # Alias for MiddleButton removed in PyQt6 but available in PyQt5, PySide2 and PySide6 + Qt.MidButton = Qt.MiddleButton elif PYQT5: from PyQt5.QtCore import * diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 14c57e2d..158d6863 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -75,8 +75,7 @@ def test_enum_access(): assert QtCore.Qt.XButton1 == QtCore.Qt.MouseButton.XButton1 assert QtCore.Qt.BackgroundColorRole == QtCore.Qt.ItemDataRole.BackgroundColorRole assert QtCore.Qt.TextColorRole == QtCore.Qt.ItemDataRole.TextColorRole - if PYSIDE2 or PYSIDE6: - assert QtCore.Qt.MidButton == QtCore.Qt.MouseButton.MiddleButton + assert QtCore.Qt.MidButton == QtCore.Qt.MouseButton.MiddleButton @pytest.mark.skipif(PYSIDE2 and PYSIDE_VERSION.startswith('5.12.0'), From e74c23d81615d7cd76a2ca28ca222c9f85c457f7 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 25 Mar 2022 14:17:36 -0500 Subject: [PATCH 381/703] Drop support for Python 3.6 --- .github/workflows/ci.yml | 13 +++++-------- qtpy/tests/test_patch_qheaderview.py | 11 ++++++----- qtpy/tests/test_qtwidgets.py | 12 ++++++------ setup.cfg | 3 +-- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e3719e7..3d29af49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.6', '3.10'] + python-version: ['3.7', '3.10'] use-conda: ['Yes', 'No'] include: - os: ubuntu-latest @@ -48,22 +48,19 @@ jobs: - use-conda: 'Yes' # No conda packages yet for Qt6 skip-pyqt6: true skip-pyside6: true - - python-version: '3.6' + - python-version: '3.7' use-conda: 'No' - skip-pip-check: 'true' # Pytest packaging issue on Python 3.6 defaults channel - pyside2-version: '5.12.0' # 5.12.1-5.12.6 fails on collection/segfaults on patch test - os: windows-latest python-version: '3.10' use-conda: 'No' pyside2-version: '5.15' # No 5.12 wheel on Windows and Python 3.10 - os: windows-latest - python-version: '3.6' + python-version: '3.7' use-conda: 'Yes' - pyqt5-qt-version: '5.9' # 5.12 is apparently unreliable here + pyqt5-qt-version: '5.9' - os: macos-latest - python-version: '3.6' + python-version: '3.7' use-conda: 'No' - skip-pyside6: true # Py3.6 wheels apparently still don't work on latest macOS steps: - name: Checkout branch uses: actions/checkout@v2 diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index 6aaf7c8e..b2357240 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -17,11 +17,12 @@ def get_qapp(icon_path=None): @pytest.mark.skipif( - QT_VERSION.startswith('5.15') or PYSIDE6 or PYQT6 or - ((PYSIDE2) and sys.version_info.major == 3 and sys.version_info.minor >= 8 - and (sys.platform == 'darwin' or sys.platform.startswith('linux')) - ), - reason="Segfaults with Qt 5.15; and PySide2/Python 3.8+ on Mac and Linux") + QT_VERSION.startswith('5.15') or PYSIDE6 or PYQT6, + reason="Segfaults with Qt >=5.15") +@pytest.mark.skipif( + (PYSIDE2) and sys.version_info.major == 3 and sys.version_info.minor >= 7 + and (sys.platform == 'darwin' or sys.platform.startswith('linux')), + reason="PySide2/Python 3.7+ on Mac and Linux") def test_patched_qheaderview(): """ This will test whether QHeaderView has the new methods introduced in Qt5. diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 8f60b0c6..d1a9c142 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -50,8 +50,8 @@ def test_qapplication_functions(): reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( sys.platform == 'darwin' and using_conda() - and sys.version_info[:2] == (3, 6), - reason="Stalls on macOS CI when using Conda and Python 3.6") + and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI when using Conda and Python 3.7") def test_qdialog_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QDialog.exec_ @@ -65,8 +65,8 @@ def test_qdialog_functions(qtbot): reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( sys.platform == 'darwin' and using_conda() - and sys.version_info[:2] == (3, 6), - reason="Stalls on macOS CI when using Conda and Python 3.6") + and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI when using Conda and Python 3.7") def test_qdialog_subclass(qtbot): """Test functions mapping for QtWidgets.QDialog when using a subclass""" assert QtWidgets.QDialog.exec_ @@ -85,8 +85,8 @@ def __init__(self): reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( sys.platform == 'darwin' and using_conda() - and sys.version_info[:2] == (3, 6), - reason="Stalls on macOS CI when using Conda and Python 3.6") + and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI when using Conda and Python 3.7") def test_qmenu_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QMenu.exec_ diff --git a/setup.cfg b/setup.cfg index 545bf92a..1183a59a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -24,7 +24,6 @@ classifiers = Operating System :: OS Independent Programming Language :: Python :: 3 Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 @@ -42,7 +41,7 @@ project_urls = packages = find: install_requires = packaging -python_requires = >=3.6 +python_requires = >=3.7 include_package_data = True zip_safe = False From d7c3c40290953986306456d2ee71c729becc4bf8 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 25 Mar 2022 15:43:37 -0500 Subject: [PATCH 382/703] Update a few additional instances of Python 3.6-specific code --- .github/workflows/test.sh | 4 ++-- qtpy/tests/test_qtpositioning.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 8d632927..5f233445 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -9,8 +9,8 @@ conda remove -q -n test-env --all || true # Create and activate conda environment for this test BINDING=$(echo "$1" | tr '[:lower:]' '[:upper:]') QT_VERSION_VAR=${BINDING}_QT_VERSION -if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; fi # pytest-qt >=4 doesn't support Qt >=5.9 -if [ "${PYTHON_VERSION}" = "3.6" ]; then PIP_VERSION="=21.3.1"; fi # pip >21.3.1 depends on the 'dataclasses' module (unavailable by default with Python 3.6) +# pytest-qt >=4 doesn't support Qt >=5.9 +if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; fi conda create -q -n test-env python=${PYTHON_VERSION} "pytest>=6,!=7.0.0,!=7.0.1" "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} pip${PIP_VERSION:-} conda activate test-env diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py index 080ef4c5..5c691864 100644 --- a/qtpy/tests/test_qtpositioning.py +++ b/qtpy/tests/test_qtpositioning.py @@ -12,7 +12,7 @@ def test_qtpositioning(): assert QtPositioning.QGeoCoordinate is not None assert QtPositioning.QGeoLocation is not None assert QtPositioning.QGeoPath is not None - # CI for 3.6 uses Qt 5.9 + # CI for 3.7 uses Qt 5.9 # assert QtPositioning.QGeoPolygon is not None # New in Qt 5.10 assert QtPositioning.QGeoPositionInfo is not None assert QtPositioning.QGeoPositionInfoSource is not None From d2343217cdf829d38b4171599be686ee5c322274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Mon, 28 Mar 2022 12:43:05 -0500 Subject: [PATCH 383/703] Testing: Apply suggestions from code review * Improve skip `test_patched_qheaderview` reason string * Remove unnecessary config for CI jobs Co-authored-by: CAM Gerlach --- .github/workflows/ci.yml | 4 ---- qtpy/tests/test_patch_qheaderview.py | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3d29af49..a9cf33ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,8 +48,6 @@ jobs: - use-conda: 'Yes' # No conda packages yet for Qt6 skip-pyqt6: true skip-pyside6: true - - python-version: '3.7' - use-conda: 'No' - os: windows-latest python-version: '3.10' use-conda: 'No' @@ -59,8 +57,6 @@ jobs: use-conda: 'Yes' pyqt5-qt-version: '5.9' - os: macos-latest - python-version: '3.7' - use-conda: 'No' steps: - name: Checkout branch uses: actions/checkout@v2 diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py index b2357240..57a60c53 100644 --- a/qtpy/tests/test_patch_qheaderview.py +++ b/qtpy/tests/test_patch_qheaderview.py @@ -20,9 +20,9 @@ def get_qapp(icon_path=None): QT_VERSION.startswith('5.15') or PYSIDE6 or PYQT6, reason="Segfaults with Qt >=5.15") @pytest.mark.skipif( - (PYSIDE2) and sys.version_info.major == 3 and sys.version_info.minor >= 7 + PYSIDE2 and sys.version_info.major == 3 and sys.version_info.minor >= 7 and (sys.platform == 'darwin' or sys.platform.startswith('linux')), - reason="PySide2/Python 3.7+ on Mac and Linux") + reason="Segfaults with PySide2 in Python 3.7+ on Mac and Linux") def test_patched_qheaderview(): """ This will test whether QHeaderView has the new methods introduced in Qt5. From ad51d684bcef8751d1d5dac83f299795d6a5b62c Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 28 Mar 2022 14:15:05 -0500 Subject: [PATCH 384/703] QtWidgets: Skip .exec_ related test on macOS with Python 3.7 --- qtpy/tests/test_qtwidgets.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index d1a9c142..0b08158e 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -49,9 +49,8 @@ def test_qapplication_functions(): sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( - sys.platform == 'darwin' and using_conda() - and sys.version_info[:2] == (3, 7), - reason="Stalls on macOS CI when using Conda and Python 3.7") + sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7") def test_qdialog_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QDialog.exec_ @@ -64,9 +63,8 @@ def test_qdialog_functions(qtbot): sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( - sys.platform == 'darwin' and using_conda() - and sys.version_info[:2] == (3, 7), - reason="Stalls on macOS CI when using Conda and Python 3.7") + sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7") def test_qdialog_subclass(qtbot): """Test functions mapping for QtWidgets.QDialog when using a subclass""" assert QtWidgets.QDialog.exec_ @@ -84,9 +82,8 @@ def __init__(self): sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( - sys.platform == 'darwin' and using_conda() - and sys.version_info[:2] == (3, 7), - reason="Stalls on macOS CI when using Conda and Python 3.7") + sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7") def test_qmenu_functions(qtbot): """Test functions mapping for QtWidgets.QDialog.""" assert QtWidgets.QMenu.exec_ From 1452b2c24de924d61ea46a4219db38bcc21bcb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Tue, 29 Mar 2022 10:13:44 -0500 Subject: [PATCH 385/703] Testing: Apply suggestions from code review Skip PySide6 on macOS with Python 3.7 Co-authored-by: CAM Gerlach --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9cf33ad..7ab6a322 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,6 +57,9 @@ jobs: use-conda: 'Yes' pyqt5-qt-version: '5.9' - os: macos-latest + python-version: '3.7' + use-conda: 'No' + skip-pyside6: true # pytest-qt crashes on pytest startup under this environment steps: - name: Checkout branch uses: actions/checkout@v2 From 16b356b03659d0fcd8f2ff2ad3d423f8cee48ad9 Mon Sep 17 00:00:00 2001 From: Edwin Yllanes Date: Wed, 30 Mar 2022 16:05:48 -0500 Subject: [PATCH 386/703] Fix typo --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 33842484..f9a020b6 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -93,7 +93,7 @@ class PythonQtWarning(Warning): QT_VERSION_MIN = QT5_VERSION_MIN PYQT_VERSION_MIN = PYQT5_VERSION_MIN -PYSIDE_VERISION_MIN = PYSIDE2_VERSION_MIN +PYSIDE_VERSION_MIN = PYSIDE2_VERSION_MIN # Detecting if a binding was specified by the user binding_specified = QT_API in os.environ From 1d69167f117ff11e0ce1520e3929770c531235c0 Mon Sep 17 00:00:00 2001 From: frmdstryr Date: Tue, 5 Apr 2022 11:09:23 -0400 Subject: [PATCH 387/703] Use QFileDialog.Option to be compatiable with Qt6 --- qtpy/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/compat.py b/qtpy/compat.py index dbac4396..45e11338 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -75,7 +75,7 @@ def getexistingdirectory(parent=None, caption='', basedir='', def _qfiledialog_wrapper(attr, parent=None, caption='', basedir='', filters='', selectedfilter='', options=None): if options is None: - options = QFileDialog.Options(0) + options = QFileDialog.Option(0) func = getattr(QFileDialog, attr) From 4531fe5a279cecfb85955d0fb25087f130b1b2a6 Mon Sep 17 00:00:00 2001 From: Julian Gilbey Date: Thu, 7 Apr 2022 20:11:04 +0100 Subject: [PATCH 388/703] Skip import of QOpenGLTime* on architectures where not available --- qtpy/QtOpenGL.py | 14 ++++++++++++-- qtpy/tests/test_qtopengl.py | 5 +++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 1fe48fbf..c62789d2 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -16,9 +16,14 @@ QOpenGLFramebufferObjectFormat, QOpenGLShader, QOpenGLShaderProgram, QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, QOpenGLDebugMessage, QOpenGLPixelTransferOptions, QOpenGLTexture, - QOpenGLTextureBlitter, QOpenGLTimeMonitor, QOpenGLTimerQuery, + QOpenGLTextureBlitter, QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow ) + # These are not present on some architectures + try: + from PyQt5.QtGui import QOpenGLTimeMonitor, QOpenGLTimerQuery + except ImportError: + pass elif PYQT6: from PyQt6.QtOpenGL import * from PyQt6.QtGui import (QOpenGLContext, QOpenGLContextGroup) @@ -32,9 +37,14 @@ QOpenGLFramebufferObjectFormat, QOpenGLShader, QOpenGLShaderProgram, QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, QOpenGLDebugMessage, QOpenGLPixelTransferOptions, QOpenGLTexture, - QOpenGLTextureBlitter, QOpenGLTimeMonitor, QOpenGLTimerQuery, + QOpenGLTextureBlitter, QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow ) + # These are not present on some architectures + try: + from PySide2.QtGui import QOpenGLTimeMonitor, QOpenGLTimerQuery + except ImportError: + pass else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtopengl.py b/qtpy/tests/test_qtopengl.py index 2b4f131d..3c0517a3 100644 --- a/qtpy/tests/test_qtopengl.py +++ b/qtpy/tests/test_qtopengl.py @@ -17,8 +17,9 @@ def test_qtopengl(): assert QtOpenGL.QOpenGLShaderProgram is not None assert QtOpenGL.QOpenGLTexture is not None assert QtOpenGL.QOpenGLTextureBlitter is not None - assert QtOpenGL.QOpenGLTimeMonitor is not None - assert QtOpenGL.QOpenGLTimerQuery is not None + # These are not present on some architectures + # assert QtOpenGL.QOpenGLTimeMonitor is not None + # assert QtOpenGL.QOpenGLTimerQuery is not None assert QtOpenGL.QOpenGLVersionProfile is not None assert QtOpenGL.QOpenGLVertexArrayObject is not None assert QtOpenGL.QOpenGLWindow is not None From 06d98438f9535a0477cdc85acf4c001fa7657b26 Mon Sep 17 00:00:00 2001 From: Julian Gilbey Date: Fri, 8 Apr 2022 05:41:14 +0100 Subject: [PATCH 389/703] Remove commented code and replace by improved comment --- qtpy/tests/test_qtopengl.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qtpy/tests/test_qtopengl.py b/qtpy/tests/test_qtopengl.py index 3c0517a3..984c7b99 100644 --- a/qtpy/tests/test_qtopengl.py +++ b/qtpy/tests/test_qtopengl.py @@ -17,11 +17,9 @@ def test_qtopengl(): assert QtOpenGL.QOpenGLShaderProgram is not None assert QtOpenGL.QOpenGLTexture is not None assert QtOpenGL.QOpenGLTextureBlitter is not None - # These are not present on some architectures - # assert QtOpenGL.QOpenGLTimeMonitor is not None - # assert QtOpenGL.QOpenGLTimerQuery is not None assert QtOpenGL.QOpenGLVersionProfile is not None assert QtOpenGL.QOpenGLVertexArrayObject is not None assert QtOpenGL.QOpenGLWindow is not None - + # We do not test for QOpenGLTimeMonitor or QOpenGLTimerQuery as + # they are not present on some architectures From e982161ea36b4fa6f447d3a07b35007cfed12d62 Mon Sep 17 00:00:00 2001 From: Julian Gilbey Date: Fri, 8 Apr 2022 18:22:36 +0100 Subject: [PATCH 390/703] Give an example of an architecture where QOpenGLTime* is not present --- qtpy/QtOpenGL.py | 4 ++-- qtpy/tests/test_qtopengl.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index c62789d2..b4cb5a93 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -19,7 +19,7 @@ QOpenGLTextureBlitter, QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow ) - # These are not present on some architectures + # These are not present on some architectures such as armhf try: from PyQt5.QtGui import QOpenGLTimeMonitor, QOpenGLTimerQuery except ImportError: @@ -40,7 +40,7 @@ QOpenGLTextureBlitter, QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow ) - # These are not present on some architectures + # These are not present on some architectures such as armhf try: from PySide2.QtGui import QOpenGLTimeMonitor, QOpenGLTimerQuery except ImportError: diff --git a/qtpy/tests/test_qtopengl.py b/qtpy/tests/test_qtopengl.py index 984c7b99..b4b25120 100644 --- a/qtpy/tests/test_qtopengl.py +++ b/qtpy/tests/test_qtopengl.py @@ -21,5 +21,5 @@ def test_qtopengl(): assert QtOpenGL.QOpenGLVertexArrayObject is not None assert QtOpenGL.QOpenGLWindow is not None # We do not test for QOpenGLTimeMonitor or QOpenGLTimerQuery as - # they are not present on some architectures + # they are not present on some architectures such as armhf From 3b29ee2d46a18940fea4b65fe8dfca647fb03d7f Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 8 Apr 2022 13:26:17 -0500 Subject: [PATCH 391/703] Patch: Remove QHeaderView patch related files --- qtpy/_patch/__init__.py | 0 qtpy/_patch/qheaderview.py | 95 ---------------------------- qtpy/tests/test_patch_qheaderview.py | 86 ------------------------- 3 files changed, 181 deletions(-) delete mode 100644 qtpy/_patch/__init__.py delete mode 100644 qtpy/_patch/qheaderview.py delete mode 100644 qtpy/tests/test_patch_qheaderview.py diff --git a/qtpy/_patch/__init__.py b/qtpy/_patch/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/qtpy/_patch/qheaderview.py b/qtpy/_patch/qheaderview.py deleted file mode 100644 index 9dee5712..00000000 --- a/qtpy/_patch/qheaderview.py +++ /dev/null @@ -1,95 +0,0 @@ -# -# Copyright © The Spyder Development Team -# -# Licensed under the terms of the MIT License -# (see LICENSE.txt for details) -import warnings - -def introduce_renamed_methods_qheaderview(QHeaderView): - - _isClickable = QHeaderView.isClickable - def sectionsClickable(self): - """ - QHeaderView.sectionsClickable() -> bool - """ - return _isClickable(self) - QHeaderView.sectionsClickable = sectionsClickable - def isClickable(self): - warnings.warn('isClickable is only available in Qt4. Use ' - 'sectionsClickable instead.', stacklevel=2) - return _isClickable(self) - QHeaderView.isClickable = isClickable - - - _isMovable = QHeaderView.isMovable - def sectionsMovable(self): - """ - QHeaderView.sectionsMovable() -> bool - """ - return _isMovable(self) - QHeaderView.sectionsMovable = sectionsMovable - def isMovable(self): - warnings.warn('isMovable is only available in Qt4. Use ' - 'sectionsMovable instead.', stacklevel=2) - return _isMovable(self) - QHeaderView.isMovable = isMovable - - - _resizeMode = QHeaderView.resizeMode - def sectionResizeMode(self, logicalIndex): - """ - QHeaderView.sectionResizeMode(int) -> QHeaderView.ResizeMode - """ - return _resizeMode(self, logicalIndex) - QHeaderView.sectionResizeMode = sectionResizeMode - def resizeMode(self, logicalIndex): - warnings.warn('resizeMode is only available in Qt4. Use ' - 'sectionResizeMode instead.', stacklevel=2) - return _resizeMode(self, logicalIndex) - QHeaderView.resizeMode = resizeMode - - _setClickable = QHeaderView.setClickable - def setSectionsClickable(self, clickable): - """ - QHeaderView.setSectionsClickable(bool) - """ - return _setClickable(self, clickable) - QHeaderView.setSectionsClickable = setSectionsClickable - def setClickable(self, clickable): - warnings.warn('setClickable is only available in Qt4. Use ' - 'setSectionsClickable instead.', stacklevel=2) - return _setClickable(self, clickable) - QHeaderView.setClickable = setClickable - - - _setMovable = QHeaderView.setMovable - def setSectionsMovable(self, movable): - """ - QHeaderView.setSectionsMovable(bool) - """ - return _setMovable(self, movable) - QHeaderView.setSectionsMovable = setSectionsMovable - def setMovable(self, movable): - warnings.warn('setMovable is only available in Qt4. Use ' - 'setSectionsMovable instead.', stacklevel=2) - return _setMovable(self, movable) - QHeaderView.setMovable = setMovable - - - _setResizeMode = QHeaderView.setResizeMode - def setSectionResizeMode(self, *args): - """ - QHeaderView.setSectionResizeMode(QHeaderView.ResizeMode) - QHeaderView.setSectionResizeMode(int, QHeaderView.ResizeMode) - """ - _setResizeMode(self, *args) - QHeaderView.setSectionResizeMode = setSectionResizeMode - def setResizeMode(self, *args): - warnings.warn('setResizeMode is only available in Qt4. Use ' - 'setSectionResizeMode instead.', stacklevel=2) - _setResizeMode(self, *args) - QHeaderView.setResizeMode = setResizeMode - - - - diff --git a/qtpy/tests/test_patch_qheaderview.py b/qtpy/tests/test_patch_qheaderview.py deleted file mode 100644 index 57a60c53..00000000 --- a/qtpy/tests/test_patch_qheaderview.py +++ /dev/null @@ -1,86 +0,0 @@ -import sys - -import pytest - -from qtpy import PYQT6, PYSIDE2, PYSIDE6, QT_VERSION -from qtpy.QtWidgets import QApplication -from qtpy.QtWidgets import QHeaderView -from qtpy.QtCore import Qt -from qtpy.QtCore import QAbstractListModel - - -def get_qapp(icon_path=None): - qapp = QApplication.instance() - if qapp is None: - qapp = QApplication(['']) - return qapp - - -@pytest.mark.skipif( - QT_VERSION.startswith('5.15') or PYSIDE6 or PYQT6, - reason="Segfaults with Qt >=5.15") -@pytest.mark.skipif( - PYSIDE2 and sys.version_info.major == 3 and sys.version_info.minor >= 7 - and (sys.platform == 'darwin' or sys.platform.startswith('linux')), - reason="Segfaults with PySide2 in Python 3.7+ on Mac and Linux") -def test_patched_qheaderview(): - """ - This will test whether QHeaderView has the new methods introduced in Qt5. - It will then create an instance of QHeaderView and test that no exceptions - are raised and that some basic behaviour works. - """ - assert QHeaderView.sectionsClickable is not None - assert QHeaderView.sectionsMovable is not None - assert QHeaderView.sectionResizeMode is not None - assert QHeaderView.setSectionsClickable is not None - assert QHeaderView.setSectionsMovable is not None - assert QHeaderView.setSectionResizeMode is not None - - # setup a model and add it to a headerview - qapp = get_qapp() - headerview = QHeaderView(Qt.Horizontal) - # Fails here on PySide 2 and Python 3.8 due a bug: https://bugreports.qt.io/browse/PYSIDE-1140 - class Model(QAbstractListModel): - pass - model = Model() - headerview.setModel(model) - assert headerview.count() == 1 - - # test it - assert isinstance(headerview.sectionsClickable(), bool) - assert isinstance(headerview.sectionsMovable(), bool) - if PYSIDE2: - assert isinstance(headerview.sectionResizeMode(0), - QHeaderView.ResizeMode) - else: - assert isinstance(headerview.sectionResizeMode(0), int) - - headerview.setSectionsClickable(True) - assert headerview.sectionsClickable() == True - headerview.setSectionsClickable(False) - assert headerview.sectionsClickable() == False - - headerview.setSectionsMovable(True) - assert headerview.sectionsMovable() == True - headerview.setSectionsMovable(False) - assert headerview.sectionsMovable() == False - - headerview.setSectionResizeMode(QHeaderView.Interactive) - assert headerview.sectionResizeMode(0) == QHeaderView.Interactive - headerview.setSectionResizeMode(QHeaderView.Fixed) - assert headerview.sectionResizeMode(0) == QHeaderView.Fixed - headerview.setSectionResizeMode(QHeaderView.Stretch) - assert headerview.sectionResizeMode(0) == QHeaderView.Stretch - headerview.setSectionResizeMode(QHeaderView.ResizeToContents) - assert headerview.sectionResizeMode(0) == QHeaderView.ResizeToContents - - headerview.setSectionResizeMode(0, QHeaderView.Interactive) - assert headerview.sectionResizeMode(0) == QHeaderView.Interactive - headerview.setSectionResizeMode(0, QHeaderView.Fixed) - assert headerview.sectionResizeMode(0) == QHeaderView.Fixed - headerview.setSectionResizeMode(0, QHeaderView.Stretch) - assert headerview.sectionResizeMode(0) == QHeaderView.Stretch - headerview.setSectionResizeMode(0, QHeaderView.ResizeToContents) - assert headerview.sectionResizeMode(0) == QHeaderView.ResizeToContents - - From 07f285b130303cf8af15f8ef0485cea0460ebbd8 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 11 Apr 2022 12:56:17 -0500 Subject: [PATCH 392/703] uic: Improve skip conditions for uic load* related tests --- qtpy/tests/test_uic.py | 52 ++++++++++++------------------------------ 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index ecd50985..f873f1d2 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -12,7 +12,7 @@ pytest.importorskip("pyside2uic", reason="pyside2uic not installed") from qtpy import uic -from qtpy.uic import loadUi, loadUiType +from qtpy.tests.utils import not_using_conda QCOMBOBOX_SUBCLASS = """ @@ -42,30 +42,19 @@ def enabled_qcombobox_subclass(temp_dir_path): sys.path.pop(0) -def get_qapp(icon_path=None): - """ - Helper function to return a QApplication instance - """ - qapp = QtWidgets.QApplication.instance() - if qapp is None: - qapp = QtWidgets.QApplication(['']) - return qapp - - @pytest.mark.skipif( - os.environ.get('CI', None) is not None - and sys.platform.startswith('linux'), - reason="Segfaults on Linux CIs under all bindings (PYSIDE2/6 & PYQT5/6)") -def test_load_ui(): + sys.platform.startswith('linux') and not_using_conda(), + reason="Segfaults on Linux CI when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") +def test_load_ui(qtbot): """ Make sure that the patched loadUi function behaves as expected with a simple .ui file. """ - app = get_qapp() with warnings.catch_warnings(): warnings.filterwarnings( "ignore", category=DeprecationWarning, message=".*mode.*") - ui = loadUi(os.path.join(os.path.dirname(__file__), 'test.ui')) + ui = uic.loadUi(os.path.join(os.path.dirname(__file__), 'test.ui')) + assert isinstance(ui.pushButton, QtWidgets.QPushButton) assert isinstance(ui.comboBox, QComboBox) @@ -74,19 +63,17 @@ def test_load_ui(): PYSIDE2 or PYSIDE6, reason="PySide2uic not consistantly installed across platforms/versions") @pytest.mark.skipif( - os.environ.get('CI', None) is not None - and sys.platform.startswith('linux'), - reason="Segfaults on Linux CIs under all bindings (PYSIDE2/6 & PYQT5/6)") -def test_load_ui_type(): + sys.platform.startswith('linux') and not_using_conda(), + reason="Segfaults on Linux CI when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") +def test_load_ui_type(qtbot): """ Make sure that the patched loadUiType function behaves as expected with a simple .ui file. """ - app = get_qapp() with warnings.catch_warnings(): warnings.filterwarnings( "ignore", category=DeprecationWarning, message=".*mode.*") - ui_type, ui_base_type = loadUiType( + ui_type, ui_base_type = uic.loadUiType( os.path.join(os.path.dirname(__file__), 'test.ui')) assert ui_type.__name__ == 'Ui_Form' @@ -102,35 +89,26 @@ def __init__(self): @pytest.mark.skipif( - PYSIDE2 and sys.platform == "darwin" - and sys.version_info.major == 3 and sys.version_info.minor == 9 - and not_using_conda(), - reason="Fails on this specific platform, at least on our CIs") -@pytest.mark.skipif( - os.environ.get('CI', None) is not None - and sys.platform.startswith('linux'), - reason="Segfaults on Linux CIs under all bindings (PYSIDE2/6 & PYQT5/6)") -def test_load_ui_custom_auto(tmp_path): + sys.platform.startswith('linux') and not_using_conda(), + reason="Segfaults on Linux CI when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") +def test_load_ui_custom_auto(qtbot, tmp_path): """ Test that we can load a .ui file with custom widgets without having to explicitly specify a dictionary of custom widgets, even in the case of PySide. """ - - app = get_qapp() - with enabled_qcombobox_subclass(tmp_path): from qcombobox_subclass import _QComboBoxSubclass with warnings.catch_warnings(): warnings.filterwarnings( "ignore", category=DeprecationWarning, message=".*mode.*") - ui = loadUi( + ui = uic.loadUi( os.path.join(os.path.dirname(__file__), 'test_custom.ui')) assert isinstance(ui.pushButton, QtWidgets.QPushButton) assert isinstance(ui.comboBox, _QComboBoxSubclass) -@pytest.mark.skipif(PYSIDE6, reason="Unavailable on PySide6") + def test_load_full_uic(): """Test that we load the full uic objects.""" QT_API = os.environ.get('QT_API', '').lower() From 62dc126d9d7c0522c8a9c5ae180b26b4e409b4d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Mon, 18 Apr 2022 10:26:04 -0500 Subject: [PATCH 393/703] Apply suggestions from code review Co-authored-by: CAM Gerlach --- qtpy/tests/test_uic.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index f873f1d2..a715dcbe 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -44,7 +44,7 @@ def enabled_qcombobox_subclass(temp_dir_path): @pytest.mark.skipif( sys.platform.startswith('linux') and not_using_conda(), - reason="Segfaults on Linux CI when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") + reason="Segfaults on Linux when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") def test_load_ui(qtbot): """ Make sure that the patched loadUi function behaves as expected with a @@ -64,7 +64,7 @@ def test_load_ui(qtbot): reason="PySide2uic not consistantly installed across platforms/versions") @pytest.mark.skipif( sys.platform.startswith('linux') and not_using_conda(), - reason="Segfaults on Linux CI when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") + reason="Segfaults on Linux when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") def test_load_ui_type(qtbot): """ Make sure that the patched loadUiType function behaves as expected with a @@ -90,7 +90,7 @@ def __init__(self): @pytest.mark.skipif( sys.platform.startswith('linux') and not_using_conda(), - reason="Segfaults on Linux CI when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") + reason="Segfaults on Linux when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") def test_load_ui_custom_auto(qtbot, tmp_path): """ Test that we can load a .ui file with custom widgets without having to From fab767aaa6e2e4c44835cb6cc2f1995e44847854 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 18 Apr 2022 10:57:40 -0500 Subject: [PATCH 394/703] Test: Increase default version for PyQt 6 to 6.3 --- .github/workflows/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 5f233445..98b7c76c 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -29,7 +29,7 @@ else if [ "${1}" = "pyqt5" ]; then pip install pyqt5==${PYQT5_VERSION:-"5.15"}.* PyQtWebEngine==${PYQT5_VERSION:-"5.15"}.* elif [ "${1}" = "pyqt6" ]; then - pip install pyqt6==${PYQT6_VERSION:-"6.2"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.2"}.* + pip install pyqt6==${PYQT6_VERSION:-"6.3"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.3"}.* elif [ "${1}" = "pyside2" ]; then pip install pyside2==${PYSIDE2_VERSION:-"5.12"}.* elif [ "${1}" = "pyside6" ]; then From d39d4d0f6d6014567e6f6a36f6b5bf77e3d318d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Tue, 19 Apr 2022 10:49:52 -0500 Subject: [PATCH 395/703] Apply suggestions from code review Co-authored-by: CAM Gerlach --- .github/workflows/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 98b7c76c..d86f790c 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -29,7 +29,7 @@ else if [ "${1}" = "pyqt5" ]; then pip install pyqt5==${PYQT5_VERSION:-"5.15"}.* PyQtWebEngine==${PYQT5_VERSION:-"5.15"}.* elif [ "${1}" = "pyqt6" ]; then - pip install pyqt6==${PYQT6_VERSION:-"6.3"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.3"}.* + pip install pyqt6==${PYQT6_VERSION:-"6.3"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.3"}.* PyQt6-Qt6==${PYQT6_QT_VERSION:-"6.3"}.* elif [ "${1}" = "pyside2" ]; then pip install pyside2==${PYSIDE2_VERSION:-"5.12"}.* elif [ "${1}" = "pyside6" ]; then From 6893baf646b0e227a7cf0bd376179bce8c1fd6a0 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 19 Apr 2022 11:38:38 -0500 Subject: [PATCH 396/703] CI: Enable testing PyQt6/PySide6 6.3 --- .github/workflows/ci.yml | 2 ++ .github/workflows/test.sh | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ab6a322..e19231c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,10 +52,12 @@ jobs: python-version: '3.10' use-conda: 'No' pyside2-version: '5.15' # No 5.12 wheel on Windows and Python 3.10 + pyside6-version: '6.3' - os: windows-latest python-version: '3.7' use-conda: 'Yes' pyqt5-qt-version: '5.9' + pyqt6-qt-version: '6.2' - os: macos-latest python-version: '3.7' use-conda: 'No' diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index d86f790c..adcd1002 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -33,7 +33,11 @@ else elif [ "${1}" = "pyside2" ]; then pip install pyside2==${PYSIDE2_VERSION:-"5.12"}.* elif [ "${1}" = "pyside6" ]; then - pip install pyside6==${PYSIDE6_VERSION:-"6.2"}.* + if [ "${PYSIDE6_VERSION:-"6.3":0:3}" = "6.3" ]; then + pip install pyside6==${PYSIDE6_VERSION:-"6.3"}.* pyside6-addons==${PYSIDE6_VERSION:-"6.3"}.* pyside6-essentials==${PYSIDE6_VERSION:-"6.3"}.* + else + pip install pyside6==${PYSIDE6_VERSION:-"6.2"}.* + fi else exit 1 fi From 1d4131fb379ea13bf4806f857f1a6f499827ce17 Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Wed, 8 Jul 2020 15:46:54 -0400 Subject: [PATCH 397/703] Preliminary cli for mypy options --- MANIFEST.in | 1 + README.md | 19 +++++++++++ qtpy/__init__.py | 9 +++-- qtpy/__main__.py | 9 +++++ qtpy/cli.py | 77 ++++++++++++++++++++++++++++++++++++++++++ qtpy/py.typed | 0 qtpy/tests/test_cli.py | 45 ++++++++++++++++++++++++ setup.cfg | 4 +++ 8 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 qtpy/__main__.py create mode 100644 qtpy/cli.py create mode 100644 qtpy/py.typed create mode 100644 qtpy/tests/test_cli.py diff --git a/MANIFEST.in b/MANIFEST.in index c089429f..3c312e0c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,3 +5,4 @@ include README* include SECURITY* include pytest.ini recursive-include qtpy/tests *.py *.ui +include qtpy/py.typed diff --git a/README.md b/README.md index bd272131..a752f5df 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,25 @@ conda install qtpy ``` +### mypy + +A CLI is offered to help with usage of QtPy. Presently, the only feature +is to generate 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. + +``` +--always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6 +``` + +If using bash or similar, this can be integrated into the Mypy command line +as follows. + +```console +$ env/bin/mypy --package mypackage $(env/bin/qtpy mypy-args) +``` + + ## Contributing Everyone is welcome to contribute! diff --git a/qtpy/__init__.py b/qtpy/__init__.py index f9a020b6..f4fd0b6d 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -101,9 +101,11 @@ class PythonQtWarning(Warning): # Setting a default value for QT_API os.environ.setdefault(QT_API, 'pyqt5') +API_NAMES = {'pyqt5': 'PyQt5', 'pyqt6': 'PyQt6', + 'pyside2':'PySide2', 'pyside6': 'PySide6'} API = os.environ[QT_API].lower() initial_api = API -assert API in (PYQT5_API + PYQT6_API + PYSIDE2_API + PYSIDE6_API) +assert API in API_NAMES is_old_pyqt = is_pyqt46 = False QT5 = PYQT5 = True @@ -201,8 +203,9 @@ class PythonQtWarning(Warning): warnings.warn('Selected binding "{}" could not be found, ' 'using "{}"'.format(initial_api, API), RuntimeWarning) -API_NAME = {'pyqt6': 'PyQt6', 'pyqt5': 'PyQt5', - 'pyside2':'PySide2', 'pyside6': 'PySide6'}[API] + +# Set display name of the Qt API +API_NAME = API_NAMES[API] try: # QtDataVisualization backward compatibility (QtDataVisualization vs. QtDatavisualization) diff --git a/qtpy/__main__.py b/qtpy/__main__.py new file mode 100644 index 00000000..f4bc9027 --- /dev/null +++ b/qtpy/__main__.py @@ -0,0 +1,9 @@ +import qtpy.cli + + +def main(): + return qtpy.cli.cli() + + +if __name__ == "__main__": + main() diff --git a/qtpy/cli.py b/qtpy/cli.py new file mode 100644 index 00000000..1a289758 --- /dev/null +++ b/qtpy/cli.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +# ----------------------------------------------------------------------------- +# Copyright © 2009- The QtPy Contributors +# +# Released under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Provide a CLI to allow configuring developer settings, including mypy.""" + +# Standard library imports +import argparse +import sys +import textwrap + + +class RawDescriptionArgumentDefaultsHelpFormatter( + argparse.RawDescriptionHelpFormatter, + argparse.ArgumentDefaultsHelpFormatter, +): + pass + + +def cli(args=sys.argv[1:]): + parser = argparse.ArgumentParser( + description="Features in support of development with QtPy.", + formatter_class=RawDescriptionArgumentDefaultsHelpFormatter, + ) + + parser.set_defaults(func=parser.print_help) + + cli_subparsers = parser.add_subparsers() + + mypy_args_parser = cli_subparsers.add_parser( + name='mypy-args', + description=textwrap.dedent( + """\ + Generate command line arguments for using mypy with QtPy. + + This will generate strings similar to the following which help guide mypy + through which library QtPy would have used so that mypy can get the proper + underlying type hints. + + --always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6 + + Use such as: + + env/bin/mypy --package mypackage $(env/bin/qtpy mypy-args) + """ + ), + formatter_class=RawDescriptionArgumentDefaultsHelpFormatter, + ) + mypy_args_parser.set_defaults(func=mypy_args) + + arguments = parser.parse_args(args=args) + + reserved_parameters = {'func'} + cleaned = { + k: v + for k, v in vars(arguments).items() + if k not in reserved_parameters + } + + arguments.func(**cleaned) + + +def mypy_args(): + options = {False: '--always-false', True: '--always-true'} + + import qtpy + + apis_active = {name: qtpy.API == name for name in qtpy.API_NAMES} + print(' '.join( + f'{options[is_active]}={name.upper()}' + for name, is_active + in apis_active.items() + )) diff --git a/qtpy/py.typed b/qtpy/py.typed new file mode 100644 index 00000000..e69de29b diff --git a/qtpy/tests/test_cli.py b/qtpy/tests/test_cli.py new file mode 100644 index 00000000..f89f2cb2 --- /dev/null +++ b/qtpy/tests/test_cli.py @@ -0,0 +1,45 @@ +from __future__ import absolute_import + +import subprocess +import sys + +import pytest + +import qtpy + + +subcommands = [ + ['mypy'], + ['mypy', 'args'], +] + + +@pytest.mark.parametrize( + argnames=['subcommand'], + argvalues=[[subcommand] for subcommand in subcommands], + ids=[' '.join(subcommand) for subcommand in subcommands], +) +def test_cli_help_does_not_fail(subcommand): + # .check_call() over .run(..., check=True) because of py2 + subprocess.check_call( + [sys.executable, '-m', 'qtpy', *subcommand, '--help'], + ) + + +def test_cli_mypy_args(): + output = subprocess.check_output( + [sys.executable, '-m', 'qtpy', 'mypy', 'args'], + ) + + if qtpy.PYQT5: + expected = b'--always-true=PYQT5 --always-false=PYQT6 --always-false=PYSIDE2 --always-false=PYSIDE6\n' + elif qtpy.PYQT6: + expected = b'--always-false=PYQT5 --always-true=PYQT6 --always-false=PYSIDE2 --always-false=PYSIDE6\n' + elif qtpy.PYSIDE2: + expected = b'--always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6\n' + elif qtpy.PYSIDE6: + expected = b'--always-false=PYQT5 --always-false=PYQT6 --always-false=PYSIDE2 --always-true=PYSIDE6\n' + else: + assert False, 'No valid API to test' + + assert output == expected diff --git a/setup.cfg b/setup.cfg index 1183a59a..63b88881 100644 --- a/setup.cfg +++ b/setup.cfg @@ -56,3 +56,7 @@ test = pytest>=6,!=7.0.0,!=7.0.1 pytest-cov>=3.0.0 pytest-qt + +[options.entry_points] +console_scripts = + qtpy = qtpy.__main__:main From 013926680f7ec0cc89eabb779142f40807949fdc Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 19 Apr 2022 00:58:43 -0500 Subject: [PATCH 398/703] Fix, clean up and improve new CLI tests --- qtpy/tests/test_cli.py | 54 +++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/qtpy/tests/test_cli.py b/qtpy/tests/test_cli.py index f89f2cb2..5abff746 100644 --- a/qtpy/tests/test_cli.py +++ b/qtpy/tests/test_cli.py @@ -1,4 +1,4 @@ -from __future__ import absolute_import +"""Test the QtPy CLI.""" import subprocess import sys @@ -8,38 +8,60 @@ import qtpy -subcommands = [ - ['mypy'], - ['mypy', 'args'], +SUBCOMMANDS = [ + [], + ['mypy-args'], ] @pytest.mark.parametrize( argnames=['subcommand'], - argvalues=[[subcommand] for subcommand in subcommands], - ids=[' '.join(subcommand) for subcommand in subcommands], + argvalues=[[subcommand] for subcommand in SUBCOMMANDS], + ids=[' '.join(subcommand) for subcommand in SUBCOMMANDS], ) def test_cli_help_does_not_fail(subcommand): - # .check_call() over .run(..., check=True) because of py2 - subprocess.check_call( - [sys.executable, '-m', 'qtpy', *subcommand, '--help'], + subprocess.run( + [sys.executable, '-m', 'qtpy', *subcommand, '--help'], check=True, ) def test_cli_mypy_args(): - output = subprocess.check_output( - [sys.executable, '-m', 'qtpy', 'mypy', 'args'], + output = subprocess.run( + [sys.executable, '-m', 'qtpy', 'mypy-args'], + capture_output=True, + check=True, + encoding='utf-8', ) if qtpy.PYQT5: - expected = b'--always-true=PYQT5 --always-false=PYQT6 --always-false=PYSIDE2 --always-false=PYSIDE6\n' + expected = ' '.join([ + '--always-true=PYQT5', + '--always-false=PYQT6', + '--always-false=PYSIDE2', + '--always-false=PYSIDE6', + ]) elif qtpy.PYQT6: - expected = b'--always-false=PYQT5 --always-true=PYQT6 --always-false=PYSIDE2 --always-false=PYSIDE6\n' + expected = ' '.join([ + '--always-false=PYQT5', + '--always-true=PYQT6', + '--always-false=PYSIDE2', + '--always-false=PYSIDE6', + ]) elif qtpy.PYSIDE2: - expected = b'--always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6\n' + expected = ' '.join([ + '--always-false=PYQT5', + '--always-false=PYQT6', + '--always-true=PYSIDE2', + '--always-false=PYSIDE6', + ]) elif qtpy.PYSIDE6: - expected = b'--always-false=PYQT5 --always-false=PYQT6 --always-false=PYSIDE2 --always-true=PYSIDE6\n' + expected = ' '.join([ + '--always-false=PYQT5', + '--always-false=PYQT6', + '--always-false=PYSIDE2', + '--always-true=PYSIDE6', + ]) else: assert False, 'No valid API to test' - assert output == expected + assert output.stdout.strip() == expected.strip() From f341da213a6123cfa38a2cea40a89ec3a7154b83 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 19 Apr 2022 01:42:22 -0500 Subject: [PATCH 399/703] Simpifly and improve CLI for Mypy --- qtpy/__main__.py | 2 +- qtpy/cli.py | 77 ++++++++++++++++++++++++------------------------ 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/qtpy/__main__.py b/qtpy/__main__.py index f4bc9027..17e972cc 100644 --- a/qtpy/__main__.py +++ b/qtpy/__main__.py @@ -2,7 +2,7 @@ def main(): - return qtpy.cli.cli() + return qtpy.cli.main() if __name__ == "__main__": diff --git a/qtpy/cli.py b/qtpy/cli.py index 1a289758..3cbb660a 100644 --- a/qtpy/cli.py +++ b/qtpy/cli.py @@ -10,36 +10,49 @@ # Standard library imports import argparse -import sys import textwrap -class RawDescriptionArgumentDefaultsHelpFormatter( - argparse.RawDescriptionHelpFormatter, - argparse.ArgumentDefaultsHelpFormatter, -): - pass +def generate_mypy_args(): + """Generate a string with always-true/false args to pass to mypy.""" + options = {False: '--always-false', True: '--always-true'} + import qtpy -def cli(args=sys.argv[1:]): - parser = argparse.ArgumentParser( - description="Features in support of development with QtPy.", - formatter_class=RawDescriptionArgumentDefaultsHelpFormatter, + apis_active = {name: qtpy.API == name for name in qtpy.API_NAMES} + mypy_args = ' '.join( + f'{options[is_active]}={name.upper()}' + for name, is_active in apis_active.items() ) + return mypy_args - parser.set_defaults(func=parser.print_help) - cli_subparsers = parser.add_subparsers() +def print_mypy_args(): + """Print the generated mypy args to stdout.""" + print(generate_mypy_args()) + +def generate_arg_parser(): + """Generate the argument parser for the dev CLI for QtPy.""" + parser = argparse.ArgumentParser( + description='Features to support development with QtPy.', + ) + parser.set_defaults(func=parser.print_help) + cli_subparsers = parser.add_subparsers( + title='Subcommands', help='Subcommand to run', metavar='Subcommand') + + # Parser for the MyPy args subcommand mypy_args_parser = cli_subparsers.add_parser( name='mypy-args', + help='Generate command line arguments for using mypy with QtPy.', + formatter_class=argparse.RawTextHelpFormatter, description=textwrap.dedent( - """\ + """ Generate command line arguments for using mypy with QtPy. - This will generate strings similar to the following which help guide mypy - through which library QtPy would have used so that mypy can get the proper - underlying type hints. + This will generate strings similar to the following + which help guide mypy through which library QtPy would have used + so that mypy can get the proper underlying type hints. --always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6 @@ -48,30 +61,18 @@ def cli(args=sys.argv[1:]): env/bin/mypy --package mypackage $(env/bin/qtpy mypy-args) """ ), - formatter_class=RawDescriptionArgumentDefaultsHelpFormatter, ) - mypy_args_parser.set_defaults(func=mypy_args) - - arguments = parser.parse_args(args=args) + mypy_args_parser.set_defaults(func=print_mypy_args) - reserved_parameters = {'func'} - cleaned = { - k: v - for k, v in vars(arguments).items() - if k not in reserved_parameters - } + return parser - arguments.func(**cleaned) +def main(args=None): + """Run the development CLI for QtPy.""" + parser = generate_arg_parser() + parsed_args = parser.parse_args(args=args) -def mypy_args(): - options = {False: '--always-false', True: '--always-true'} - - import qtpy - - apis_active = {name: qtpy.API == name for name in qtpy.API_NAMES} - print(' '.join( - f'{options[is_active]}={name.upper()}' - for name, is_active - in apis_active.items() - )) + reserved_params = {'func'} + cleaned_args = {key: value for key, value in vars(parsed_args).items() + if key not in reserved_params} + parsed_args.func(**cleaned_args) From 200f5f2bf9f7162bcf322d6cf123042377d358c2 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 19 Apr 2022 01:48:22 -0500 Subject: [PATCH 400/703] Update and improve Readme and help text for mypy-args CLI command --- README.md | 20 ++++++++++---------- qtpy/cli.py | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a752f5df..5b50e0c4 100644 --- a/README.md +++ b/README.md @@ -71,22 +71,22 @@ conda install qtpy ``` -### mypy +### Mypy -A CLI is offered to help with usage of QtPy. Presently, the only feature -is to generate 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. +A CLI is offered to help with usage of QtPy. +Presently, the only feature is to generate 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. -``` +```text --always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6 ``` -If using bash or similar, this can be integrated into the Mypy command line -as follows. +If using Bash or a similar shell, this can be injected into +the Mypy command line invocation as follows: -```console -$ env/bin/mypy --package mypackage $(env/bin/qtpy mypy-args) +```bash +mypy --package mypackage $(qtpy mypy-args) ``` diff --git a/qtpy/cli.py b/qtpy/cli.py index 3cbb660a..ae0a855a 100644 --- a/qtpy/cli.py +++ b/qtpy/cli.py @@ -56,9 +56,9 @@ def generate_arg_parser(): --always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6 - Use such as: + It can be used as follows on Bash or a similar shell: - env/bin/mypy --package mypackage $(env/bin/qtpy mypy-args) + mypy --package mypackage $(qtpy mypy-args) """ ), ) From 83f3cb6a8e09cab730bc20f698d07c142b684027 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Tue, 19 Apr 2022 02:03:36 -0500 Subject: [PATCH 401/703] Add version option to QtPy CLI --- qtpy/cli.py | 11 +++++++++++ qtpy/tests/test_cli.py | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/qtpy/cli.py b/qtpy/cli.py index ae0a855a..89ebaf78 100644 --- a/qtpy/cli.py +++ b/qtpy/cli.py @@ -13,6 +13,12 @@ import textwrap +def print_version(): + """Print the current version of the package.""" + import qtpy + print('QtPy version', qtpy.__version__) + + def generate_mypy_args(): """Generate a string with always-true/false args to pass to mypy.""" options = {False: '--always-false', True: '--always-true'} @@ -38,6 +44,11 @@ def generate_arg_parser(): description='Features to support development with QtPy.', ) parser.set_defaults(func=parser.print_help) + + parser.add_argument( + '--version', action='store_const', dest='func', const=print_version, + help='If passed, will print the version and exit') + cli_subparsers = parser.add_subparsers( title='Subcommands', help='Subcommand to run', metavar='Subcommand') diff --git a/qtpy/tests/test_cli.py b/qtpy/tests/test_cli.py index 5abff746..42a10f33 100644 --- a/qtpy/tests/test_cli.py +++ b/qtpy/tests/test_cli.py @@ -25,6 +25,16 @@ def test_cli_help_does_not_fail(subcommand): ) +def test_cli_version(): + output = subprocess.run( + [sys.executable, '-m', 'qtpy', '--version'], + capture_output=True, + check=True, + encoding='utf-8', + ) + assert output.stdout.strip().split()[-1] == qtpy.__version__ + + def test_cli_mypy_args(): output = subprocess.run( [sys.executable, '-m', 'qtpy', 'mypy-args'], From c18fcf327585ff7a52e388925b97ef14b3616ffa Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 20 Apr 2022 23:30:12 -0500 Subject: [PATCH 402/703] Further improve Mypy section in Readme & add license header/docstring --- README.md | 16 ++++++++++++++-- qtpy/__main__.py | 9 +++++++++ qtpy/cli.py | 1 - 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5b50e0c4..fd8ddca8 100644 --- a/README.md +++ b/README.md @@ -71,15 +71,27 @@ conda install qtpy ``` -### Mypy +### Mypy integration A CLI is offered to help with usage of QtPy. Presently, the only feature is to generate 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. +If you run + +```bash +qtpy mypy-args +``` + +QtPy will output a string of Mypy CLI args that will reflect the currently +selected Qt API. +For example, in an environment where `PYQT5` would be selected +(or the default fallback, if no binding can be found in the environment), +this would output the following: + ```text ---always-false=PYQT5 --always-false=PYQT6 --always-true=PYSIDE2 --always-false=PYSIDE6 +--always-true=PYQT5 --always-false=PYQT6 --always-false=PYSIDE2 --always-false=PYSIDE6 ``` If using Bash or a similar shell, this can be injected into diff --git a/qtpy/__main__.py b/qtpy/__main__.py index 17e972cc..a8f993c0 100644 --- a/qtpy/__main__.py +++ b/qtpy/__main__.py @@ -1,3 +1,12 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The QtPy Contributors +# +# Released under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Dev CLI entry point for QtPy, a compat layer for the Python Qt bindings.""" + import qtpy.cli diff --git a/qtpy/cli.py b/qtpy/cli.py index 89ebaf78..3a629970 100644 --- a/qtpy/cli.py +++ b/qtpy/cli.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # ----------------------------------------------------------------------------- # Copyright © 2009- The QtPy Contributors # From cec7c821841808a468094c7d0187fc976fa93907 Mon Sep 17 00:00:00 2001 From: CAM Gerlach Date: Fri, 22 Apr 2022 20:28:03 -0500 Subject: [PATCH 403/703] Further refine Mypy CLI in readme from reviewer feedback Co-authored-by: Carlos Cordoba --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index fd8ddca8..a4ed6c3e 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,8 @@ conda install qtpy ### Mypy integration -A CLI is offered to help with usage of QtPy. -Presently, the only feature is to generate command line arguments for Mypy +A Command Line Interface (CLI) is offered to help with usage of QtPy. +Presently, its only feature is to generate 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. @@ -86,7 +86,7 @@ qtpy mypy-args QtPy will output a string of Mypy CLI args that will reflect the currently selected Qt API. -For example, in an environment where `PYQT5` would be selected +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: @@ -94,7 +94,7 @@ this would output the following: --always-true=PYQT5 --always-false=PYQT6 --always-false=PYSIDE2 --always-false=PYSIDE6 ``` -If using Bash or a similar shell, this can be injected into +Using Bash or a similar shell, this can be injected into the Mypy command line invocation as follows: ```bash From ac977ccf9b487d36b475dc62596078ce697fbdb8 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 26 Apr 2022 16:03:38 -0400 Subject: [PATCH 404/703] BUG: Fix bug with environ handling --- qtpy/__init__.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index f4fd0b6d..44a53b36 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -98,14 +98,13 @@ class PythonQtWarning(Warning): # Detecting if a binding was specified by the user binding_specified = QT_API in os.environ -# Setting a default value for QT_API -os.environ.setdefault(QT_API, 'pyqt5') - API_NAMES = {'pyqt5': 'PyQt5', 'pyqt6': 'PyQt6', 'pyside2':'PySide2', 'pyside6': 'PySide6'} -API = os.environ[QT_API].lower() +API = os.environ.get(QT_API, 'pyqt5').lower() initial_api = API -assert API in API_NAMES +if API not in API_NAMES: + raise ValueError( + f'Specified QT_API={repr(QT_API)} not in valid options: {API_NAMES}') is_old_pyqt = is_pyqt46 = False QT5 = PYQT5 = True @@ -150,7 +149,9 @@ class PythonQtWarning(Warning): del macos_version except ImportError: - API = os.environ['QT_API'] = 'pyqt6' + API = 'pyqt6' + else: + os.environ['QT_API'] = API if API in PYQT6_API: try: @@ -161,7 +162,9 @@ class PythonQtWarning(Warning): QT6 = PYQT6 = True except ImportError: - API = os.environ['QT_API'] = 'pyside2' + API = 'pyside2' + else: + os.environ['QT_API'] = API if API in PYSIDE2_API: @@ -183,7 +186,9 @@ class PythonQtWarning(Warning): del macos_version except ImportError: - API = os.environ['QT_API'] = 'pyside6' + API = 'pyside6' + else: + os.environ['QT_API'] = API if API in PYSIDE6_API: try: @@ -194,7 +199,9 @@ class PythonQtWarning(Warning): QT6 = PYSIDE6 = True except ImportError: - API = os.environ['QT_API'] = 'pyqt5' + API = 'pyqt5' + else: + os.environ['QT_API'] = API # If a correct API name is passed to QT_API and it could not be found, From bd6eb932c7fe204047586875164df222fa426795 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 27 Apr 2022 13:09:25 -0400 Subject: [PATCH 405/703] TST: Add tests --- qtpy/__init__.py | 11 +++++++--- qtpy/tests/conftest.py | 5 +++-- qtpy/tests/test_main.py | 45 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 44a53b36..521001e6 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -72,6 +72,10 @@ class PythonQtWarning(Warning): """Warning if some features are not implemented in a binding.""" +class PythonQtValueError(ValueError): + """Error raised if an invalid QT_API is specified.""" + + # Qt API environment variable name QT_API = 'QT_API' @@ -99,12 +103,13 @@ class PythonQtWarning(Warning): binding_specified = QT_API in os.environ API_NAMES = {'pyqt5': 'PyQt5', 'pyqt6': 'PyQt6', - 'pyside2':'PySide2', 'pyside6': 'PySide6'} + 'pyside2': 'PySide2', 'pyside6': 'PySide6'} API = os.environ.get(QT_API, 'pyqt5').lower() initial_api = API if API not in API_NAMES: - raise ValueError( - f'Specified QT_API={repr(QT_API)} not in valid options: {API_NAMES}') + raise PythonQtValueError( + f'Specified QT_API={repr(QT_API.lower())} not in valid options: ' + f'{API_NAMES}') is_old_pyqt = is_pyqt46 = False QT5 = PYQT5 = True diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index ea1fd190..a24f664f 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -21,8 +21,9 @@ def pytest_report_header(config): versions += 'PyQt6: ' try: - from PyQt6 import Qt - versions += f"PyQt: {Qt.PYQT_VERSION_STR} - Qt: {Qt.QT_VERSION_STR}" + from PyQt6 import QtCore + versions += \ + f"PyQt: {QtCore.PYQT_VERSION_STR} - Qt: {QtCore.QT_VERSION_STR}" except ImportError: versions += 'not installed' except AttributeError: diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index 86aeb5d2..3539cfa0 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -1,6 +1,10 @@ import os +import sys +import subprocess -from qtpy import QtCore, QtGui, QtWidgets +import pytest + +from qtpy import QtCore, QtGui, QtWidgets, API_NAMES, PythonQtValueError try: # removed in qt 6.0 from qtpy import QtWebEngineWidgets @@ -16,6 +20,7 @@ def assert_pyside2(): assert QtGui.QPainter is PySide2.QtGui.QPainter assert QtWidgets.QWidget is PySide2.QtWidgets.QWidget assert QtWebEngineWidgets.QWebEnginePage is PySide2.QtWebEngineWidgets.QWebEnginePage + assert os.environ['QT_API'] == 'PySide2' def assert_pyside6(): """ @@ -27,6 +32,7 @@ def assert_pyside6(): assert QtWidgets.QWidget is PySide6.QtWidgets.QWidget # Only valid for qt>=6.2 # assert QtWebEngineWidgets.QWebEnginePage is PySide6.QtWebEngineCore.QWebEnginePage + assert os.environ['QT_API'] == 'PySide6' def assert_pyqt5(): """ @@ -40,6 +46,7 @@ def assert_pyqt5(): assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebEngineWidgets.QWebEnginePage else: assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebKitWidgets.QWebPage + assert os.environ['QT_API'] == 'PyQt5' def assert_pyqt6(): """ @@ -83,3 +90,39 @@ def test_qt_api(): assert_pyqt6() else: assert_pyqt5() + + +@pytest.mark.parametrize('api', API_NAMES.values()) +def test_qt_api_environ(api): + """ + If no QT_API is specified but some Qt is imported, ensure environ is set properly + """ + mod = f'{api}.QtCore' + pytest.importorskip(mod, reason=f'Requires {api}') + # clean env + env = os.environ.copy() + for key in ('QT_API', 'USE_QT_API'): + if key in env: + del env[key] + cmd = f""" +import {mod} +from qtpy import API +import os +print(API) +print(os.environ['QT_API']) +""" + output = subprocess.check_output([sys.executable, '-c', cmd], env=env) + got_api, env_qt_api = output.strip().decode('utf-8').splitlines() + assert got_api == api.lower() + assert env_qt_api == api.lower() + # Also ensure we raise a nice error + env['QT_API'] = 'bad' + cmd = """ +try: + import qtpy +except ValueError as exc: + assert 'Specified QT_API' in str(exc), str(exc) +else: + raise AssertionError('QtPy imported despite bad QT_API') +""" + subprocess.check_call([sys.executable, '-Oc', cmd], env=env) From 9a5559e1a1b7b5b269ab45f6d15066e0f6d073f0 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 27 Apr 2022 13:12:26 -0400 Subject: [PATCH 406/703] FIX: Use QT_API --- qtpy/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 521001e6..14008d00 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -156,7 +156,7 @@ class PythonQtValueError(ValueError): except ImportError: API = 'pyqt6' else: - os.environ['QT_API'] = API + os.environ[QT_API] = API if API in PYQT6_API: try: @@ -169,7 +169,7 @@ class PythonQtValueError(ValueError): except ImportError: API = 'pyside2' else: - os.environ['QT_API'] = API + os.environ[QT_API] = API if API in PYSIDE2_API: @@ -193,7 +193,7 @@ class PythonQtValueError(ValueError): except ImportError: API = 'pyside6' else: - os.environ['QT_API'] = API + os.environ[QT_API] = API if API in PYSIDE6_API: try: @@ -206,7 +206,7 @@ class PythonQtValueError(ValueError): except ImportError: API = 'pyqt5' else: - os.environ['QT_API'] = API + os.environ[QT_API] = API # If a correct API name is passed to QT_API and it could not be found, From 74741c91c59c61848662745406dcdfa79e1b1cf9 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 27 Apr 2022 14:05:34 -0400 Subject: [PATCH 407/703] FIX: Cap --- qtpy/tests/test_main.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index 3539cfa0..57bc0201 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -20,7 +20,7 @@ def assert_pyside2(): assert QtGui.QPainter is PySide2.QtGui.QPainter assert QtWidgets.QWidget is PySide2.QtWidgets.QWidget assert QtWebEngineWidgets.QWebEnginePage is PySide2.QtWebEngineWidgets.QWebEnginePage - assert os.environ['QT_API'] == 'PySide2' + assert os.environ['QT_API'] == 'pyside2' def assert_pyside6(): """ @@ -32,7 +32,7 @@ def assert_pyside6(): assert QtWidgets.QWidget is PySide6.QtWidgets.QWidget # Only valid for qt>=6.2 # assert QtWebEngineWidgets.QWebEnginePage is PySide6.QtWebEngineCore.QWebEnginePage - assert os.environ['QT_API'] == 'PySide6' + assert os.environ['QT_API'] == 'pyside6' def assert_pyqt5(): """ @@ -46,7 +46,7 @@ def assert_pyqt5(): assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebEngineWidgets.QWebEnginePage else: assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebKitWidgets.QWebPage - assert os.environ['QT_API'] == 'PyQt5' + assert os.environ['QT_API'] == 'pyqt5' def assert_pyqt6(): """ @@ -56,6 +56,7 @@ def assert_pyqt6(): assert QtCore.QEvent is PyQt6.QtCore.QEvent assert QtGui.QPainter is PyQt6.QtGui.QPainter assert QtWidgets.QWidget is PyQt6.QtWidgets.QWidget + assert os.environ['QT_API'] == 'pyqt6' def test_qt_api(): From 2a98aadd1e8fd6c6144ef71ce46a9abe7e92393f Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Wed, 27 Apr 2022 15:40:10 -0400 Subject: [PATCH 408/703] Apply suggestions from code review Co-authored-by: Carlos Cordoba --- qtpy/__init__.py | 2 +- qtpy/tests/test_main.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 14008d00..d0ce4449 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -108,7 +108,7 @@ class PythonQtValueError(ValueError): initial_api = API if API not in API_NAMES: raise PythonQtValueError( - f'Specified QT_API={repr(QT_API.lower())} not in valid options: ' + f'Specified QT_API={repr(QT_API.lower())} is not in valid options: ' f'{API_NAMES}') is_old_pyqt = is_pyqt46 = False diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index 57bc0201..000ddf10 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -5,6 +5,7 @@ import pytest from qtpy import QtCore, QtGui, QtWidgets, API_NAMES, PythonQtValueError + try: # removed in qt 6.0 from qtpy import QtWebEngineWidgets @@ -96,7 +97,7 @@ def test_qt_api(): @pytest.mark.parametrize('api', API_NAMES.values()) def test_qt_api_environ(api): """ - If no QT_API is specified but some Qt is imported, ensure environ is set properly + If no QT_API is specified but some Qt is imported, ensure QT_API is set properly. """ mod = f'{api}.QtCore' pytest.importorskip(mod, reason=f'Requires {api}') From 4a00ec1ddaac2714af5f2b479f441d006d16a8ac Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Wed, 27 Apr 2022 21:03:21 +0200 Subject: [PATCH 409/703] pyside2: Add workaround for `mode` argument in QTextCursor.movePosition() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PySide2 does not accept the `mode` keyword argument in QTextCursor.movePosition() even though it is a valid optional argument as per C++ API. Fix this by monkeypatching. Notes: * The `mode` argument is called `arg__2` in PySide2 as per QTextCursor.movePosition.__doc__ and __signature__. Using `arg__2` as keyword argument works as intended, so does using a positional argument. Tested with PySide2 5.15.0, 5.15.2.1 and 5.15.3; older version, down to PySide 1, are probably affected as well [1]. * PySide2 5.15.0 and 5.15.2.1 silently ignore invalid keyword arguments, i.e. passing the `mode` keyword argument has no effect and doesn’t raise an exception. Older versions, down to PySide 1, are probably affected as well [1]. PySide2 5.15.3 raises an exception when `mode`or any other invalid keyword argument is passed. [1] https://bugreports.qt.io/browse/PYSIDE-185 --- qtpy/QtGui.py | 26 ++++++++++++++++++++++++++ qtpy/tests/test_qtgui.py | 23 +++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index a887833d..8bce3a19 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -34,6 +34,32 @@ # Needed to prevent raising a DeprecationWarning when using QFontMetrics.width QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) + # PySide2 does not accept the `mode` keyword argument in + # QTextCursor.movePosition() even though it is a valid optional argument + # as per C++ API. Fix this by monkeypatching. + # + # Notes: + # + # * The `mode` argument is called `arg__2` in PySide2 as per + # QTextCursor.movePosition.__doc__ and __signature__. Using `arg__2` as + # keyword argument works as intended, so does using a positional + # argument. Tested with PySide2 5.15.0, 5.15.2.1 and 5.15.3; older + # version, down to PySide 1, are probably affected as well [1]. + # + # * PySide2 5.15.0 and 5.15.2.1 silently ignore invalid keyword arguments, + # i.e. passing the `mode` keyword argument has no effect and doesn’t + # raise an exception. Older versions, down to PySide 1, are probably + # affected as well [1]. PySide2 5.15.3 raises an exception when `mode`or + # any other invalid keyword argument is passed. + movePosition = QTextCursor.movePosition + def movePositionPatched( + self, + operation: QTextCursor.MoveOperation, + mode: QTextCursor.MoveMode = QTextCursor.MoveAnchor, + n: int = 1, + ) -> bool: + return movePosition(self, operation, mode, n) + QTextCursor.movePosition = movePositionPatched elif PYSIDE6: from PySide6.QtGui import * from PySide6.QtOpenGL import * diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 0aafe6a9..6b097120 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -4,7 +4,7 @@ import pytest -from qtpy import PYQT5, PYQT_VERSION, QtGui +from qtpy import PYQT5, PYQT_VERSION, PYSIDE2, QtGui from qtpy.tests.utils import not_using_conda @@ -55,4 +55,23 @@ def test_enum_access(): assert QtGui.QColor.Rgb == QtGui.QColor.Spec.Rgb assert QtGui.QFont.AllUppercase == QtGui.QFont.Capitalization.AllUppercase assert QtGui.QIcon.Normal == QtGui.QIcon.Mode.Normal - assert QtGui.QImage.Format_Invalid == QtGui.QImage.Format.Format_Invalid \ No newline at end of file + assert QtGui.QImage.Format_Invalid == QtGui.QImage.Format.Format_Invalid + +@pytest.mark.skipif(not PYSIDE2, reason="PySide2 specific test") +def test_qtextcursor_moveposition(): + """Test monkeypatched QTextCursor.movePosition""" + doc = QtGui.QTextDocument("foo bar baz") + cursor = QtGui.QTextCursor(doc) + + assert not cursor.movePosition(QtGui.QTextCursor.Start) + assert cursor.movePosition(QtGui.QTextCursor.EndOfWord, mode=QtGui.QTextCursor.KeepAnchor) + assert cursor.selectedText() == "foo" + + assert cursor.movePosition(QtGui.QTextCursor.Start) + assert cursor.movePosition(QtGui.QTextCursor.WordRight, n=2, mode=QtGui.QTextCursor.KeepAnchor) + assert cursor.selectedText() == "foo bar " + + assert cursor.movePosition(QtGui.QTextCursor.Start) + assert cursor.position() == cursor.anchor() + assert cursor.movePosition(QtGui.QTextCursor.NextWord, QtGui.QTextCursor.KeepAnchor, 3) + assert cursor.selectedText() == "foo bar baz" From 411bd7172c0becddfa07d6d3e9381fa8cf1dadd7 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 28 Apr 2022 12:11:47 -0500 Subject: [PATCH 410/703] Change bindings 'try order' to PyQt5, PySide2, PyQt6, PySide6 --- README.md | 14 +++++----- qtpy/__init__.py | 61 ++++++++++++++++++++--------------------- qtpy/tests/conftest.py | 22 +++++++-------- qtpy/tests/test_cli.py | 16 +++++------ qtpy/tests/test_main.py | 19 ++++++++----- 5 files changed, 68 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index a4ed6c3e..78b4473e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# QtPy: Abstraction layer for PyQt5/PyQt6/PySide2/PySide6 +# QtPy: Abstraction layer for PyQt5/PySide2/PyQt6/PySide6 [![license](https://img.shields.io/pypi/l/qtpy.svg)](./LICENSE) [![pypi version](https://img.shields.io/pypi/v/qtpy.svg)](https://pypi.org/project/QtPy/) @@ -10,7 +10,7 @@ [![Github build status](https://github.com/spyder-ide/qtpy/workflows/Tests/badge.svg)](https://github.com/spyder-ide/qtpy/actions) [![Coverage Status](https://coveralls.io/repos/github/spyder-ide/qtpy/badge.svg?branch=master)](https://coveralls.io/github/spyder-ide/qtpy?branch=master) -*Copyright © 2009–2021 The Spyder Development Team* +*Copyright © 2009–2022 The Spyder Development Team* ## Description @@ -22,7 +22,7 @@ It provides support for PyQt5, PyQt6, PySide6, PySide2 using the Qt5 layout (where the QtGui module has been split into QtGui and QtWidgets). Basically, you can write your code as if you were using PyQt or PySide directly, -but import Qt modules from `qtpy` instead of `PyQt5`, `PyQt6`, `PySide2`, or `PySide6`. +but import Qt modules from `qtpy` instead of `PyQt5`, `PySide2`, `PyQt6` or `PySide6`. Accordingly, when porting code between different Qt bindings (PyQt vs PySide) or Qt versions (Qt5 vs Qt6), QtPy makes this much more painless, and allows you to easily and incrementally transition between them. QtPy handles incompatibilities and differences between bindings or Qt versions for you while keeping your project running, so you can focus more on your own code and less on keeping track of supporting every Qt version and binding. Furthermore, when you do want to upgrade or support new bindings, it allows you to update your project module by module rather than all at once. You can check out examples of this approach in projects using QtPy, like [git-cola](https://github.com/git-cola/git-cola/issues/232). @@ -46,16 +46,16 @@ This project is released under the MIT license. ### Requirements -You need PyQt5, PyQt6, PySide2 or PySide6 installed in your system to make use +You need PyQt5, PySide2, PyQt6 or PySide6 installed in your system to make use of QtPy. If several of these packages are found, PyQt5 is used by default unless you set the `QT_API` environment variable. `QT_API` can take the following values: * `pyqt5` (to use PyQt5). +* `pyside2` (to use PySide2). * `pyqt6` (to use PyQt6). -* `pyside6` (to use PySide6) -* `pyside2` (to use PySide2) +* `pyside6` (to use PySide6). ### Installation @@ -91,7 +91,7 @@ For example, in an environment where PyQt5 is installed and selected this would output the following: ```text ---always-true=PYQT5 --always-false=PYQT6 --always-false=PYSIDE2 --always-false=PYSIDE6 +--always-true=PYQT5 --always-false=PYSIDE2 --always-false=PYQT6 --always-false=PYSIDE6 ``` Using Bash or a similar shell, this can be injected into diff --git a/qtpy/__init__.py b/qtpy/__init__.py index d0ce4449..a37505aa 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -11,8 +11,8 @@ If one of the APIs has already been imported, then it will be used. -Otherwise, the shim will automatically select the first available API (PyQt5, PyQt6, -PySide2 and PySide6); in that case, you can force the use of one +Otherwise, the shim will automatically select the first available API (PyQt5, PySide2, +PyQt6 and PySide6); in that case, you can force the use of one specific bindings (e.g. if your application is using one specific bindings and you need to use library that use QtPy) by setting up the ``QT_API`` environment variable. @@ -25,14 +25,6 @@ >>> from qtpy import QtGui, QtWidgets, QtCore >>> print(QtWidgets.QWidget) -PyQt6 -===== - - >>> import os - >>> os.environ['QT_API'] = 'pyqt6' - >>> from qtpy import QtGui, QtWidgets, QtCore - >>> print(QtWidgets.QWidget) - PySide2 ====== @@ -44,6 +36,14 @@ >>> from qtpy import QtGui, QtWidgets, QtCore >>> print(QtWidgets.QWidget) +PyQt6 +===== + + >>> import os + >>> os.environ['QT_API'] = 'pyqt6' + >>> from qtpy import QtGui, QtWidgets, QtCore + >>> print(QtWidgets.QWidget) + PySide6 ======= @@ -102,8 +102,8 @@ class PythonQtValueError(ValueError): # Detecting if a binding was specified by the user binding_specified = QT_API in os.environ -API_NAMES = {'pyqt5': 'PyQt5', 'pyqt6': 'PyQt6', - 'pyside2': 'PySide2', 'pyside6': 'PySide6'} +API_NAMES = {'pyqt5': 'PyQt5', 'pyside2': 'PySide2', + 'pyqt6': 'PyQt6', 'pyside6': 'PySide6'} API = os.environ.get(QT_API, 'pyqt5').lower() initial_api = API if API not in API_NAMES: @@ -121,14 +121,14 @@ class PythonQtValueError(ValueError): # Unless `FORCE_QT_API` is set, use previously imported Qt Python bindings if not os.environ.get('FORCE_QT_API'): - if 'PyQt6' in sys.modules: - API = initial_api if initial_api in PYQT6_API else 'pyqt6' - elif 'PyQt5' in sys.modules: + if 'PyQt5' in sys.modules: API = initial_api if initial_api in PYQT5_API else 'pyqt5' - elif 'PySide6' in sys.modules: - API = initial_api if initial_api in PYSIDE6_API else 'pyside6' elif 'PySide2' in sys.modules: API = initial_api if initial_api in PYSIDE2_API else 'pyside2' + elif 'PyQt6' in sys.modules: + API = initial_api if initial_api in PYQT6_API else 'pyqt6' + elif 'PySide6' in sys.modules: + API = initial_api if initial_api in PYSIDE6_API else 'pyside6' if API in PYQT5_API: try: @@ -153,25 +153,11 @@ class PythonQtValueError(ValueError): "system.") del macos_version - except ImportError: - API = 'pyqt6' - else: - os.environ[QT_API] = API - -if API in PYQT6_API: - try: - from PyQt6.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore - from PyQt6.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore - - QT5 = PYQT5 = False - QT6 = PYQT6 = True - except ImportError: API = 'pyside2' else: os.environ[QT_API] = API - if API in PYSIDE2_API: try: from PySide2 import __version__ as PYSIDE_VERSION # analysis:ignore @@ -190,6 +176,19 @@ class PythonQtValueError(ValueError): "system.") del macos_version + except ImportError: + API = 'pyqt6' + else: + os.environ[QT_API] = API + +if API in PYQT6_API: + try: + from PyQt6.QtCore import PYQT_VERSION_STR as PYQT_VERSION # analysis:ignore + from PyQt6.QtCore import QT_VERSION_STR as QT_VERSION # analysis:ignore + + QT5 = PYQT5 = False + QT6 = PYQT6 = True + except ImportError: API = 'pyside6' else: diff --git a/qtpy/tests/conftest.py b/qtpy/tests/conftest.py index a24f664f..f9b06f2b 100644 --- a/qtpy/tests/conftest.py +++ b/qtpy/tests/conftest.py @@ -18,35 +18,35 @@ def pytest_report_header(config): """Insert a customized header into the test report.""" versions = os.linesep - versions += 'PyQt6: ' + versions += 'PyQt5: ' try: - from PyQt6 import QtCore - versions += \ - f"PyQt: {QtCore.PYQT_VERSION_STR} - Qt: {QtCore.QT_VERSION_STR}" + from PyQt5 import Qt + versions += f"PyQt: {Qt.PYQT_VERSION_STR} - Qt: {Qt.QT_VERSION_STR}" except ImportError: versions += 'not installed' except AttributeError: versions += 'unknown version' versions += os.linesep - versions += 'PyQt5: ' + versions += 'PySide2: ' try: - from PyQt5 import Qt - versions += f"PyQt: {Qt.PYQT_VERSION_STR} - Qt: {Qt.QT_VERSION_STR}" + import PySide2 + from PySide2 import QtCore + versions += f"PySide: {PySide2.__version__} - Qt: {QtCore.__version__}" except ImportError: versions += 'not installed' except AttributeError: versions += 'unknown version' versions += os.linesep - versions += 'PySide2: ' + versions += 'PyQt6: ' try: - import PySide2 - from PySide2 import QtCore - versions += f"PySide: {PySide2.__version__} - Qt: {QtCore.__version__}" + from PyQt6 import QtCore + versions += \ + f"PyQt: {QtCore.PYQT_VERSION_STR} - Qt: {QtCore.QT_VERSION_STR}" except ImportError: versions += 'not installed' except AttributeError: diff --git a/qtpy/tests/test_cli.py b/qtpy/tests/test_cli.py index 42a10f33..6b0f0381 100644 --- a/qtpy/tests/test_cli.py +++ b/qtpy/tests/test_cli.py @@ -46,29 +46,29 @@ def test_cli_mypy_args(): if qtpy.PYQT5: expected = ' '.join([ '--always-true=PYQT5', - '--always-false=PYQT6', '--always-false=PYSIDE2', + '--always-false=PYQT6', '--always-false=PYSIDE6', ]) - elif qtpy.PYQT6: + elif qtpy.PYSIDE2: expected = ' '.join([ '--always-false=PYQT5', - '--always-true=PYQT6', - '--always-false=PYSIDE2', + '--always-true=PYSIDE2', + '--always-false=PYQT6', '--always-false=PYSIDE6', ]) - elif qtpy.PYSIDE2: + elif qtpy.PYQT6: expected = ' '.join([ '--always-false=PYQT5', - '--always-false=PYQT6', - '--always-true=PYSIDE2', + '--always-false=PYSIDE2', + '--always-true=PYQT6', '--always-false=PYSIDE6', ]) elif qtpy.PYSIDE6: expected = ' '.join([ '--always-false=PYQT5', - '--always-false=PYQT6', '--always-false=PYSIDE2', + '--always-false=PYQT6', '--always-true=PYSIDE6', ]) else: diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index 000ddf10..dd57fcca 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -69,27 +69,32 @@ def test_qt_api(): if QT_API == 'pyqt5': assert_pyqt5() - elif QT_API == 'pyqt6': - assert_pyqt6() elif QT_API == 'pyside2': assert_pyside2() + elif QT_API == 'pyqt6': + assert_pyqt6() elif QT_API == 'pyside6': assert_pyside6() else: # If the tests are run locally, USE_QT_API and QT_API may not be # defined, but we still want to make sure qtpy is behaving sensibly. # We should then be loading, in order of decreasing preference, PyQt5, - # PyQt6, and PySide2. + # PySide2, PyQt6 and PySide6. try: import PyQt5 except ImportError: try: - import PyQt6 - except ImportError: import PySide2 - assert_pyside2() + except ImportError: + try: + import PyQt6 + except ImportError: + import PySide6 + assert_pyside6() + else: + assert_pyqt6() else: - assert_pyqt6() + assert_pyside2() else: assert_pyqt5() From fbe81684911a34543edf6b64f4241b4a20a2be90 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 2 May 2022 11:51:58 -0500 Subject: [PATCH 411/703] Release 2.1.0 --- CHANGELOG.md | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ qtpy/__init__.py | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49465431..5b1f891e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,54 @@ # History of changes +## Version 2.1.0 (2022-05-02) + +### New features + +* New CLI to get mypy arguments and check QtPy version + +### Important fixes + +* Remove Python 3.6 support +* Fix `QT_API` environmental variable handling so new processes get the correct value when `Qt_API` is not initially set +* Change try order for bindings in case the `QT_API` environmental variable is not set i.e `PyQt5 - PySide2 - PyQt6 - PySide6` + +### Issues Closed + +* [Issue 342](https://github.com/spyder-ide/qtpy/issues/342) - Change bindings `try order` to follow QtPy `v1.x` convention ([PR 343](https://github.com/spyder-ide/qtpy/pull/343) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 338](https://github.com/spyder-ide/qtpy/issues/338) - Release QtPy 2.1.0 +* [Issue 336](https://github.com/spyder-ide/qtpy/issues/336) - Segfaulting tests on PyQt6 + Windows on both Py3.7 and 3.10 (no conda) probably due to PyQt 6.3.0 ([PR 335](https://github.com/spyder-ide/qtpy/pull/335) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 332](https://github.com/spyder-ide/qtpy/issues/332) - Remove `QHeaderView` patch ([PR 334](https://github.com/spyder-ide/qtpy/pull/334) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 327](https://github.com/spyder-ide/qtpy/issues/327) - Missing `QtCore.Qt.MidButton` alias for `QtCore.Qt.MouseButton.MiddleButton` with PyQt6 ([PR 328](https://github.com/spyder-ide/qtpy/pull/328) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 323](https://github.com/spyder-ide/qtpy/issues/323) - Fix test suite failing at collection on Python 3.10 in Pytest 7.x ([PR 324](https://github.com/spyder-ide/qtpy/pull/324) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 297](https://github.com/spyder-ide/qtpy/issues/297) - Drop Python 3.6 support ([PR 329](https://github.com/spyder-ide/qtpy/pull/329) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 265](https://github.com/spyder-ide/qtpy/issues/265) - Investigate and (hopefully) resolve segfaults and other issues in UIC tests ([PR 335](https://github.com/spyder-ide/qtpy/pull/335) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 216](https://github.com/spyder-ide/qtpy/issues/216) - Support type hints and mypy ([PR 337](https://github.com/spyder-ide/qtpy/pull/337) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) + +In this release 9 issues were closed. + +### Pull Requests Merged + +* [PR 343](https://github.com/spyder-ide/qtpy/pull/343) - PR: Change bindings `try order` to be `PyQt5, PySide2, PyQt6, PySide6`, by [@dalthviz](https://github.com/dalthviz) ([342](https://github.com/spyder-ide/qtpy/issues/342)) +* [PR 341](https://github.com/spyder-ide/qtpy/pull/341) - PR: Add workaround for `mode` argument in QTextCursor.movePosition (Pyside2), by [@rear1019](https://github.com/rear1019) +* [PR 340](https://github.com/spyder-ide/qtpy/pull/340) - PR: Fix bug with environ handling, by [@larsoner](https://github.com/larsoner) +* [PR 337](https://github.com/spyder-ide/qtpy/pull/337) - PR: Add command line support for Mypy, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([217](https://github.com/spyder-ide/qtpy/issues/217), [216](https://github.com/spyder-ide/qtpy/issues/216)) +* [PR 335](https://github.com/spyder-ide/qtpy/pull/335) - PR: Fix uic skipped tests and PyQt 6.3.0 segfaulting tests, by [@dalthviz](https://github.com/dalthviz) ([336](https://github.com/spyder-ide/qtpy/issues/336), [265](https://github.com/spyder-ide/qtpy/issues/265)) +* [PR 334](https://github.com/spyder-ide/qtpy/pull/334) - PR: Remove `QHeaderView` patch related files, by [@dalthviz](https://github.com/dalthviz) ([332](https://github.com/spyder-ide/qtpy/issues/332)) +* [PR 333](https://github.com/spyder-ide/qtpy/pull/333) - PR: Skip import of QOpenGLTime* on architectures where not available, by [@juliangilbey](https://github.com/juliangilbey) +* [PR 331](https://github.com/spyder-ide/qtpy/pull/331) - PR: Use QFileDialog.Option to be compatiable with Qt6, by [@frmdstryr](https://github.com/frmdstryr) +* [PR 330](https://github.com/spyder-ide/qtpy/pull/330) - PR: Fix typo in constant name, by [@eyllanesc](https://github.com/eyllanesc) +* [PR 329](https://github.com/spyder-ide/qtpy/pull/329) - PR: Drop support for Python 3.6, by [@dalthviz](https://github.com/dalthviz) ([297](https://github.com/spyder-ide/qtpy/issues/297)) +* [PR 328](https://github.com/spyder-ide/qtpy/pull/328) - PR: Add missing `Qt.MidButton` on PyQt6, by [@dalthviz](https://github.com/dalthviz) ([327](https://github.com/spyder-ide/qtpy/issues/327)) +* [PR 326](https://github.com/spyder-ide/qtpy/pull/326) - PR: Add missing `QWebEngineScript` support for PyQt5/6 and PySide2/6, by [@EasyIsrael](https://github.com/EasyIsrael) +* [PR 325](https://github.com/spyder-ide/qtpy/pull/325) - PR: Monkey patch `pyside2uic` `UIParser.readResources` for Python 3.9 compatibility, by [@n-elie](https://github.com/n-elie) +* [PR 324](https://github.com/spyder-ide/qtpy/pull/324) - PR: Restrict broken Pytest versions to those not affected by the Pytest 7.0.0 import-mode=importlib behavior regression, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([323](https://github.com/spyder-ide/qtpy/issues/323)) + +In this release 14 pull requests were closed. + + +---- + + ## Version 2.0.1 (2022-02-02) ### Issues Closed diff --git a/qtpy/__init__.py b/qtpy/__init__.py index a37505aa..8b2cba0d 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.1.0.dev0' +__version__ = '2.1.0' class PythonQtError(RuntimeError): From 3c6bd90c13c7c3789a7ff3926aeee9a54e1e2707 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 2 May 2022 12:00:11 -0500 Subject: [PATCH 412/703] Back to work --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 8b2cba0d..e9003060 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.1.0' +__version__ = '2.2.0.dev0' class PythonQtError(RuntimeError): From 4531ad0235ab264b52888ab6cd2a09c5dc0ab13d Mon Sep 17 00:00:00 2001 From: "A. Reit" Date: Fri, 20 May 2022 08:24:28 +0200 Subject: [PATCH 413/703] pyside6: Add workaround for `mode` argument in QTextCursor.movePosition Also see commit 4a00ec1ddaac. --- qtpy/QtGui.py | 34 +++++++++++++++++++--------------- qtpy/tests/test_qtgui.py | 4 ++-- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 8bce3a19..8b470612 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -33,24 +33,37 @@ if hasattr(QFontMetrics, 'horizontalAdvance'): # Needed to prevent raising a DeprecationWarning when using QFontMetrics.width QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) +elif PYSIDE6: + from PySide6.QtGui import * + from PySide6.QtOpenGL import * + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) + + # Map DeprecationWarning methods + QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QGuiApplication.exec_ = QGuiApplication.exec +else: + raise PythonQtError('No Qt bindings could be found') - # PySide2 does not accept the `mode` keyword argument in +if PYSIDE2 or PYSIDE6: + # PySide{2,6} do not accept the `mode` keyword argument in # QTextCursor.movePosition() even though it is a valid optional argument # as per C++ API. Fix this by monkeypatching. # # Notes: # - # * The `mode` argument is called `arg__2` in PySide2 as per + # * The `mode` argument is called `arg__2` in PySide{2,6} as per # QTextCursor.movePosition.__doc__ and __signature__. Using `arg__2` as # keyword argument works as intended, so does using a positional - # argument. Tested with PySide2 5.15.0, 5.15.2.1 and 5.15.3; older - # version, down to PySide 1, are probably affected as well [1]. + # argument. Tested with PySide2 5.15.0, 5.15.2.1 and 5.15.3 and PySide6 + # 6.3.0; older version, down to PySide 1, are probably affected as well [1]. # # * PySide2 5.15.0 and 5.15.2.1 silently ignore invalid keyword arguments, # i.e. passing the `mode` keyword argument has no effect and doesn’t # raise an exception. Older versions, down to PySide 1, are probably - # affected as well [1]. PySide2 5.15.3 raises an exception when `mode`or - # any other invalid keyword argument is passed. + # affected as well [1]. At least PySide2 5.15.3 and PySide6 6.3.0 raise an + # exception when `mode` or any other invalid keyword argument is passed. + # + # [1] https://bugreports.qt.io/browse/PYSIDE-185 movePosition = QTextCursor.movePosition def movePositionPatched( self, @@ -60,13 +73,4 @@ def movePositionPatched( ) -> bool: return movePosition(self, operation, mode, n) QTextCursor.movePosition = movePositionPatched -elif PYSIDE6: - from PySide6.QtGui import * - from PySide6.QtOpenGL import * - QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) - # Map DeprecationWarning methods - QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QGuiApplication.exec_ = QGuiApplication.exec -else: - raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 6b097120..6fafac2c 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -4,7 +4,7 @@ import pytest -from qtpy import PYQT5, PYQT_VERSION, PYSIDE2, QtGui +from qtpy import PYQT5, PYQT_VERSION, PYSIDE2, PYSIDE6, QtGui from qtpy.tests.utils import not_using_conda @@ -57,7 +57,7 @@ def test_enum_access(): assert QtGui.QIcon.Normal == QtGui.QIcon.Mode.Normal assert QtGui.QImage.Format_Invalid == QtGui.QImage.Format.Format_Invalid -@pytest.mark.skipif(not PYSIDE2, reason="PySide2 specific test") +@pytest.mark.skipif(not (PYSIDE2 or PYSIDE6), reason="PySide{2,6} specific test") def test_qtextcursor_moveposition(): """Test monkeypatched QTextCursor.movePosition""" doc = QtGui.QTextDocument("foo bar baz") From 3d00342f481de643679f65a78c9045cb5967e9af Mon Sep 17 00:00:00 2001 From: Daelon Suzuka Date: Sat, 30 Apr 2022 19:33:22 -0700 Subject: [PATCH 414/703] Add missing PySide6 import QtWebSockets was not being imported when using PySide6. --- qtpy/QtWebSockets.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qtpy/QtWebSockets.py b/qtpy/QtWebSockets.py index 7b2118b8..f1933898 100644 --- a/qtpy/QtWebSockets.py +++ b/qtpy/QtWebSockets.py @@ -7,7 +7,7 @@ """Provides QtWebSockets classes and functions.""" # Local imports -from . import PYSIDE2, PYQT5, PYQT6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtWebSockets import * @@ -15,5 +15,7 @@ from PyQt6.QtWebSockets import * elif PYSIDE2: from PySide2.QtWebSockets import * +elif PYSIDE6: + from PySide6.QtWebSockets import * else: raise PythonQtError('No Qt bindings could be found') From cecb9fce208a6d00cac15cbf38a1c59470e7c3cd Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sun, 15 May 2022 03:44:48 -0400 Subject: [PATCH 415/703] Add missing imports --- qtpy/Qt3DAnimation.py | 30 +++++++++++++++++++++---- qtpy/Qt3DCore.py | 30 ++++++++++++++++++++----- qtpy/Qt3DExtras.py | 30 ++++++++++++++++++++----- qtpy/Qt3DInput.py | 30 ++++++++++++++++++++----- qtpy/Qt3DLogic.py | 30 ++++++++++++++++++++----- qtpy/Qt3DRender.py | 30 ++++++++++++++++++++----- qtpy/QtAxContainer.py | 23 +++++++++++++++++++ qtpy/QtBluetooth.py | 23 +++++++++++++++++++ qtpy/QtCharts.py | 12 +++++----- qtpy/QtConcurrent.py | 23 +++++++++++++++++++ qtpy/QtDBus.py | 26 ++++++++++++++-------- qtpy/QtDataVisualization.py | 30 ++++++++++++++++++------- qtpy/QtDesigner.py | 8 ++++--- qtpy/QtHelp.py | 15 +++++++------ qtpy/QtLocation.py | 12 +++++++--- qtpy/QtMacExtras.py | 27 +++++++++++++++++++++++ qtpy/QtMultimedia.py | 19 +++++++++++----- qtpy/QtMultimediaWidgets.py | 8 ++++--- qtpy/QtNetwork.py | 8 +++---- qtpy/QtNetworkAuth.py | 28 ++++++++++++++++++----- qtpy/QtNfc.py | 23 +++++++++++++++++++ qtpy/QtOpenGLWidgets.py | 12 +++++++--- qtpy/QtPositioning.py | 12 +++++++--- qtpy/QtPurchasing.py | 23 +++++++++++++++++++ qtpy/QtQuick3D.py | 23 +++++++++++++++++++ qtpy/QtQuickControls2.py | 23 +++++++++++++++++++ qtpy/QtScxml.py | 23 +++++++++++++++++++ qtpy/QtStateMachine.py | 23 +++++++++++++++++++ qtpy/QtSvgWidgets.py | 23 +++++++++++++++++++ qtpy/QtTextToSpeech.py | 11 ++++++++-- qtpy/QtUiTools.py | 23 +++++++++++++++++++ qtpy/QtWebEngine.py | 19 ++++++++++++---- qtpy/QtWebEngineCore.py | 22 +++++++++++++++---- qtpy/QtWebEngineQuick.py | 20 +++++++++++++---- qtpy/QtWebEngineWidgets.py | 44 ++++++++++++++++++------------------- qtpy/QtWebSockets.py | 6 +++-- qtpy/QtWinExtras.py | 24 +++++++++++++++----- qtpy/QtX11Extras.py | 27 +++++++++++++++++++++++ qtpy/QtXmlPatterns.py | 12 +++++++--- 39 files changed, 694 insertions(+), 141 deletions(-) create mode 100644 qtpy/QtAxContainer.py create mode 100644 qtpy/QtBluetooth.py create mode 100644 qtpy/QtConcurrent.py create mode 100644 qtpy/QtMacExtras.py create mode 100644 qtpy/QtNfc.py create mode 100644 qtpy/QtPurchasing.py create mode 100644 qtpy/QtQuick3D.py create mode 100644 qtpy/QtQuickControls2.py create mode 100644 qtpy/QtScxml.py create mode 100644 qtpy/QtStateMachine.py create mode 100644 qtpy/QtSvgWidgets.py create mode 100644 qtpy/QtUiTools.py create mode 100644 qtpy/QtX11Extras.py diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index 9923d2eb..e923abe0 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -4,18 +4,40 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides Qt3DAnimation classes and functions.""" -# Local imports -from . import PYQT5, PYSIDE2, PythonQtError, PYSIDE_VERSION +""" +Provides Qt3DAnimation classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: - from PyQt5.Qt3DAnimation import * + try: + from PyQt5.Qt3DAnimation import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DAnimation module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error +elif PYQT6: + try: + from PyQt6.Qt3DAnimation import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DAnimation module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DAnimation as __temp import inspect for __name in inspect.getmembers(__temp.Qt3DAnimation): globals()[__name[0]] = __name[1] +elif PYSIDE6: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide6.Qt3DAnimation as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DAnimation): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 57f1ef1c..293e7e06 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -4,22 +4,40 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides Qt3DCore classes and functions.""" -# Local imports +""" +Provides Qt3DCore classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: - from PyQt5.Qt3DCore import * + try: + from PyQt5.Qt3DCore import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DCore module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error elif PYQT6: - from PyQt6.Qt3DCore import * -elif PYSIDE6: - from PySide6.Qt3DCore.Qt3DCore import * + try: + from PyQt6.Qt3DCore import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DCore module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DCore as __temp import inspect for __name in inspect.getmembers(__temp.Qt3DCore): globals()[__name[0]] = __name[1] +elif PYSIDE6: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide6.Qt3DCore as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DCore): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 4397caa1..0c901c0b 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -4,22 +4,40 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides Qt3DExtras classes and functions.""" -# Local imports +""" +Provides Qt3DExtras classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: - from PyQt5.Qt3DExtras import * + try: + from PyQt5.Qt3DExtras import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DExtras module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error elif PYQT6: - from PyQt6.Qt3DExtras import * -elif PYSIDE6: - from PySide6.Qt3DExtras.Qt3DExtras import * + try: + from PyQt6.Qt3DExtras import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DExtras module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DExtras as __temp import inspect for __name in inspect.getmembers(__temp.Qt3DExtras): globals()[__name[0]] = __name[1] +elif PYSIDE6: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide6.Qt3DExtras as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DExtras): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 45ebf2ac..67ad35d1 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -4,22 +4,40 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides Qt3DInput classes and functions.""" -# Local imports +""" +Provides Qt3DInput classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: - from PyQt5.Qt3DInput import * + try: + from PyQt5.Qt3DInput import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DInput module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error elif PYQT6: - from PyQt6.Qt3DInput import * -elif PYSIDE6: - from PySide6.Qt3DInput.Qt3DInput import * + try: + from PyQt6.Qt3DInput import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DInput module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DInput as __temp import inspect for __name in inspect.getmembers(__temp.Qt3DInput): globals()[__name[0]] = __name[1] +elif PYSIDE6: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide6.Qt3DInput as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DInput): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index 2a71d0af..433df0d5 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -4,22 +4,40 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides Qt3DLogic classes and functions.""" -# Local imports +""" +Provides Qt3DLogic classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: - from PyQt5.Qt3DLogic import * + try: + from PyQt5.Qt3DLogic import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DLogic module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error elif PYQT6: - from PyQt6.Qt3DLogic import * -elif PYSIDE6: - from PySide6.Qt3DLogic.Qt3DLogic import * + try: + from PyQt6.Qt3DLogic import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DLogic module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DLogic as __temp import inspect for __name in inspect.getmembers(__temp.Qt3DLogic): globals()[__name[0]] = __name[1] +elif PYSIDE6: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide6.Qt3DLogic as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DLogic): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index 4e9ada31..92709663 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -4,22 +4,40 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides Qt3DRender classes and functions.""" -# Local imports +""" +Provides Qt3DRender classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: - from PyQt5.Qt3DRender import * -elif PYSIDE6: - from PySide6.Qt3DRender.Qt3DRender import * + try: + from PyQt5.Qt3DRender import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DRender module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error elif PYQT6: - from PyQt6.Qt3DRender import * + try: + from PyQt6.Qt3DRender import * + except ImportError as error: + raise PythonQtError( + 'The Qt3DRender module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DRender as __temp import inspect for __name in inspect.getmembers(__temp.Qt3DRender): globals()[__name[0]] = __name[1] +elif PYSIDE6: + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 + import PySide6.Qt3DRender as __temp + import inspect + for __name in inspect.getmembers(__temp.Qt3DRender): + globals()[__name[0]] = __name[1] else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py new file mode 100644 index 00000000..f0742f90 --- /dev/null +++ b/qtpy/QtAxContainer.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtAxContainer classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + raise PythonQtError('QtAxContainer not implemented in PyQt5') +elif PYQT6: + raise PythonQtError('QtAxContainer not implemented in PyQt6') +elif PYSIDE2: + from PySide2.QtAxContainer import * +elif PYSIDE6: + from PySide6.QtAxContainer import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtBluetooth.py b/qtpy/QtBluetooth.py new file mode 100644 index 00000000..f85f55a2 --- /dev/null +++ b/qtpy/QtBluetooth.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtBluetooth classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + from PyQt5.QtBluetooth import * +elif PYQT6: + from PyQt6.QtBluetooth import * +elif PYSIDE2: + raise PythonQtError('QtBluetooth not implemented in PySide2') +elif PYSIDE6: + from PySide6.QtBluetooth import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 164be471..47990314 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -4,9 +4,11 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtChart classes and functions.""" -# Local imports +""" +Provides QtChart classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: @@ -27,9 +29,6 @@ 'The QtCharts module was not found. ' 'It needs to be installed separately for PyQt6.' ) from error -elif PYSIDE6: - from PySide6.QtCharts import * - from PySide6 import QtCharts elif PYSIDE2: from PySide2.QtCharts import * # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 @@ -37,5 +36,8 @@ import inspect for __name in inspect.getmembers(__temp.QtCharts): globals()[__name[0]] = __name[1] +elif PYSIDE6: + from PySide6.QtCharts import * + from PySide6 import QtCharts else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtConcurrent.py b/qtpy/QtConcurrent.py new file mode 100644 index 00000000..0e62f453 --- /dev/null +++ b/qtpy/QtConcurrent.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtConcurrent classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + raise PythonQtError('QtConcurrent not implemented in PyQt5') +elif PYQT6: + raise PythonQtError('QtConcurrent not implemented in PyQt6') +elif PYSIDE2: + from PySide2.QtConcurrent import * +elif PYSIDE6: + from PySide6.QtConcurrent import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index 9499a9f7..11476c02 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -4,16 +4,24 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtDBus classes and functions.""" -# Local imports +""" +Provides QtDBus classes and functions. +""" + +import platform from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if PYQT5: - from PyQt5.QtDBus import * -elif PYQT6: - from PyQt6.QtDBus import * -elif PYSIDE6: - from PySide6.QtDBus import * +if platform.system() == 'Linux': + if PYQT5: + from PyQt5.QtDBus import * + elif PYQT6: + from PyQt6.QtDBus import * + elif PYSIDE2: + raise PythonQtError('QtDBus not implemented in PySide2') + elif PYSIDE6: + from PySide6.QtDBus import * + else: + raise PythonQtError("No Qt bindings could be found") else: - raise PythonQtError('No Qt bindings could be found') + raise PythonQtError('QtDBus does not exist on this operating system') \ No newline at end of file diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index a0e1dfc2..fc77abdd 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -4,22 +4,36 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtDataVisualization classes and functions.""" -# Local imports +""" +Provides QtDataVisualization classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if PYQT6: - from PyQt6.QtDataVisualization import * -elif PYQT5: - from PyQt5.QtDataVisualization import * -elif PYSIDE6: - from PySide6.QtDataVisualization import * +if PYQT5: + try: + from PyQt5.QtDataVisualization import * + except ImportError as error: + raise PythonQtError( + 'The QtDataVisualization module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error +elif PYQT6: + try: + from PyQt6.QtDataVisualization import * + except ImportError as error: + raise PythonQtError( + 'The QtDataVisualization module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.QtDataVisualization as __temp import inspect for __name in inspect.getmembers(__temp.QtDataVisualization): globals()[__name[0]] = __name[1] +elif PYSIDE6: + from PySide6.QtDataVisualization import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index 2b086bbe..07fa573b 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -1,20 +1,22 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- """ Provides QtDesigner classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE6, PythonQtError - +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtDesigner import * elif PYQT6: from PyQt6.QtDesigner import * +elif PYSIDE2: + raise PythonQtError('QtDesigner not implemented in PySide2') elif PYSIDE6: from PySide6.QtDesigner import * else: diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index 2fcfdcc9..33b72c37 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -1,22 +1,23 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- -"""QtHelp Wrapper.""" - -import warnings +""" +QtHelp Wrapper. +""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtHelp import * elif PYQT6: from PyQt6.QtHelp import * -elif PYSIDE6: - from PySide6.QtHelp import * elif PYSIDE2: from PySide2.QtHelp import * +elif PYSIDE6: + from PySide6.QtHelp import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtLocation.py b/qtpy/QtLocation.py index 7241158d..2d370325 100644 --- a/qtpy/QtLocation.py +++ b/qtpy/QtLocation.py @@ -4,14 +4,20 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtLocation classes and functions.""" -# Local imports -from . import PYQT5, PYSIDE2, PythonQtError +""" +Provides QtLocation classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtLocation import * +elif PYQT6: + raise PythonQtError('QtLocation not implemented in PyQt6') elif PYSIDE2: from PySide2.QtLocation import * +elif PYSIDE6: + raise PythonQtError('QtLocation not implemented in PySide6') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtMacExtras.py b/qtpy/QtMacExtras.py new file mode 100644 index 00000000..015c9ac0 --- /dev/null +++ b/qtpy/QtMacExtras.py @@ -0,0 +1,27 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides classes and functions specific to macOS and iOS operating systems +""" + +import platform +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if platform.system() == 'Darwin': + if PYQT5: + from PyQt5.QtMacExtras import * + elif PYQT6: + raise PythonQtError('QtMacExtras does not exist in Qt6') + elif PYSIDE2: + from PySide2.QtMacExtras import * + elif PYSIDE6: + raise PythonQtError('QtMacExtras does not exist in Qt6') + else: + raise PythonQtError('No Qt bindings could be found') +else: + raise PythonQtError('QtMacExtras does not exist on this operating system') diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index 683c3bb1..46618350 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -1,16 +1,23 @@ -import warnings +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- -from . import PYQT5, PYQT6 -from . import PYSIDE2 -from . import PYSIDE6 +""" +Provides low-level multimedia functionality. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtMultimedia import * elif PYQT6: from PyQt6.QtMultimedia import * -elif PYSIDE6: - from PySide6.QtMultimedia import * elif PYSIDE2: from PySide2.QtMultimedia import * +elif PYSIDE6: + from PySide6.QtMultimedia import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtMultimediaWidgets.py b/qtpy/QtMultimediaWidgets.py index ee8db677..eab6da8d 100644 --- a/qtpy/QtMultimediaWidgets.py +++ b/qtpy/QtMultimediaWidgets.py @@ -4,10 +4,12 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtMultimediaWidgets classes and functions.""" -# Local imports -from . import PYSIDE2, PYSIDE6, PYQT5, PYQT6, PythonQtError +""" +Provides QtMultimediaWidgets classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtMultimediaWidgets import * diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index b053fc41..68629459 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -1,9 +1,10 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- """ Provides QtNetwork classes and functions. @@ -11,14 +12,13 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError - if PYQT5: from PyQt5.QtNetwork import * elif PYQT6: from PyQt6.QtNetwork import * -elif PYSIDE6: - from PySide6.QtNetwork import * elif PYSIDE2: from PySide2.QtNetwork import * +elif PYSIDE6: + from PySide6.QtNetwork import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py index 1a377778..75b7dbe5 100644 --- a/qtpy/QtNetworkAuth.py +++ b/qtpy/QtNetworkAuth.py @@ -4,15 +4,31 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtNetworkAuth classes and functions.""" -# Local imports +""" +Provides QtNetworkAuth classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if PYQT6: - from PyQt6.QtNetworkAuth import * -elif PYQT5: - from PyQt5.QtNetworkAuth import * +if PYQT5: + try: + from PyQt5.QtNetworkAuth import * + except ImportError as error: + raise PythonQtError( + 'The QtNetworkAuth module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error +elif PYQT6: + try: + from PyQt6.QtNetworkAuth import * + except ImportError as error: + raise PythonQtError( + 'The QtNetworkAuth module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error +elif PYSIDE2: + raise PythonQtError('QtNetworkAuth not implemented in PySide2') elif PYSIDE6: from PySide6.QtNetworkAuth import * else: diff --git a/qtpy/QtNfc.py b/qtpy/QtNfc.py new file mode 100644 index 00000000..98739594 --- /dev/null +++ b/qtpy/QtNfc.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtNfc classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + from PyQt5.QtNfc import * +elif PYQT6: + from PyQt6.QtNfc import * +elif PYSIDE2: + raise PythonQtError('QtNfc not implemented in PySide2') +elif PYSIDE6: + from PySide6.QtNfc import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtOpenGLWidgets.py b/qtpy/QtOpenGLWidgets.py index 4df07110..05113954 100644 --- a/qtpy/QtOpenGLWidgets.py +++ b/qtpy/QtOpenGLWidgets.py @@ -4,13 +4,19 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtOpenGLWidgets classes and functions.""" -# Local imports +""" +Provides QtOpenGLWidgets classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if PYQT6: +if PYQT5: + raise PythonQtError('QtTextToSpeech not implemented in PyQt5') +elif PYQT6: from PyQt6.QtOpenGLWidgets import * +elif PYSIDE2: + raise PythonQtError('QtTextToSpeech not implemented in PySide2') elif PYSIDE6: from PySide6.QtOpenGLWidgets import * else: diff --git a/qtpy/QtPositioning.py b/qtpy/QtPositioning.py index 5535ef3f..2f31ac57 100644 --- a/qtpy/QtPositioning.py +++ b/qtpy/QtPositioning.py @@ -4,14 +4,20 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtPositioning classes and functions.""" -# Local imports -from . import PYQT5, PYSIDE2, PythonQtError +""" +Provides QtPositioning classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtPositioning import * +elif PYQT6: + from PyQt6.QtPositioning import * elif PYSIDE2: from PySide2.QtPositioning import * +elif PYSIDE6: + from PySide6.QtPositioning import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtPurchasing.py b/qtpy/QtPurchasing.py new file mode 100644 index 00000000..36de0314 --- /dev/null +++ b/qtpy/QtPurchasing.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtPurchasing classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + from PyQt5.QtQml import * +elif PYQT6: + raise PythonQtError('QtPurchasing not implemented in PyQt6') +elif PYSIDE2: + raise PythonQtError('QtPurchasing not implemented in PySide2') +elif PYSIDE6: + raise PythonQtError('QtPurchasing not implemented in PySide6') +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtQuick3D.py b/qtpy/QtQuick3D.py new file mode 100644 index 00000000..a1e1e082 --- /dev/null +++ b/qtpy/QtQuick3D.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtQuick3D classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + from PyQt5.QtQuick3D import * +elif PYQT6: + from PyQt6.QtQuick3D import * +elif PYSIDE2: + raise PythonQtError('QtQuick3D not implemented in PySide2') +elif PYSIDE6: + from PySide6.QtQuick3D import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtQuickControls2.py b/qtpy/QtQuickControls2.py new file mode 100644 index 00000000..1a104ca3 --- /dev/null +++ b/qtpy/QtQuickControls2.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtQuickControls2 classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + raise PythonQtError('QtQuickControls2 not implemented in PyQt5') +elif PYQT6: + raise PythonQtError('QtQuickControls2 not implemented in PyQt6') +elif PYSIDE2: + from PySide2.QtQuickControls2 import * +elif PYSIDE6: + from PySide6.QtQuickControls2 import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtScxml.py b/qtpy/QtScxml.py new file mode 100644 index 00000000..1786ad30 --- /dev/null +++ b/qtpy/QtScxml.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtScxml classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + raise PythonQtError('QtScxml not implemented in PyQt5') +elif PYQT6: + raise PythonQtError('QtScxml not implemented in PyQt6') +elif PYSIDE2: + from PySide2.QtScxml import * +elif PYSIDE6: + from PySide6.QtScxml import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtStateMachine.py b/qtpy/QtStateMachine.py new file mode 100644 index 00000000..1399361c --- /dev/null +++ b/qtpy/QtStateMachine.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtStateMachine classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + raise PythonQtError('QtStateMachine not implemented in PyQt5') +elif PYQT6: + raise PythonQtError('QtStateMachine not implemented in PyQt6') +elif PYSIDE2: + raise PythonQtError('QtStateMachine not implemented in PySide2') +elif PYSIDE6: + from PySide6.QtStateMachine import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtSvgWidgets.py b/qtpy/QtSvgWidgets.py new file mode 100644 index 00000000..49b6e06f --- /dev/null +++ b/qtpy/QtSvgWidgets.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtSvgWidgets classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + raise PythonQtError('QtSvgWidgets not implemented in PyQt5') +elif PYQT6: + from PyQt6.QtSvgWidgets import * +elif PYSIDE2: + raise PythonQtError('QtSvgWidgets not implemented in PySide2') +elif PYSIDE6: + from PySide6.QtSvgWidgets import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtTextToSpeech.py b/qtpy/QtTextToSpeech.py index e02e037a..96d48f4a 100644 --- a/qtpy/QtTextToSpeech.py +++ b/qtpy/QtTextToSpeech.py @@ -4,13 +4,20 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtTextToSpeech classes and functions.""" -from . import PYQT5, PYSIDE2, PythonQtError +""" +Provides QtTextToSpeech classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtTextToSpeech import * +elif PYQT6: + raise PythonQtError('QtTextToSpeech not implemented in PyQt6') elif PYSIDE2: from PySide2.QtTextToSpeech import * +elif PYSIDE6: + raise PythonQtError('QtTextToSpeech not implemented in PySide6') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtUiTools.py b/qtpy/QtUiTools.py new file mode 100644 index 00000000..b31e2f09 --- /dev/null +++ b/qtpy/QtUiTools.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides QtUiTools classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if PYQT5: + raise PythonQtError('QtUiTools not implemented in PyQt5') +elif PYQT6: + raise PythonQtError('QtUiTools not implemented in PyQt6') +elif PYSIDE2: + from PySide2.QtUiTools import * +elif PYSIDE6: + from PySide6.QtUiTools import * +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index 1b913a1a..06b1bf75 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -1,19 +1,30 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- """ Provides QtWebEngine classes and functions. """ -from . import PYQT5, PYSIDE6 +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: - from PyQt5.QtWebEngine import * + try: + from PyQt5.QtWebEngine import * + except ImportError as error: + raise PythonQtError( + 'The QtWebEngine module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error +elif PYQT6: + raise PythonQtError('QtWebEngine does not exist in Qt6') +elif PYSIDE2: + from PySide2.QtWebEngine import * elif PYSIDE6: - from PySide6.QtWebEngine import * + raise PythonQtError('QtWebEngine does not exist in Qt6') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py index 591b14f2..dc6f4091 100644 --- a/qtpy/QtWebEngineCore.py +++ b/qtpy/QtWebEngineCore.py @@ -4,15 +4,29 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtWebEngineCore classes and functions.""" -# Local imports +""" +Provides QtWebEngineCore classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: - from PyQt5.QtWebEngineCore import * + try: + from PyQt5.QtWebEngineCore import * + except ImportError as error: + raise PythonQtError( + 'The QtWebEngineCore module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error elif PYQT6: - from PyQt6.QtWebEngineCore import * + try: + from PyQt6.QtWebEngineCore import * + except ImportError as error: + raise PythonQtError( + 'The QtWebEngineCore module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error elif PYSIDE2: from PySide2.QtWebEngineCore import * elif PYSIDE6: diff --git a/qtpy/QtWebEngineQuick.py b/qtpy/QtWebEngineQuick.py index e5d9afdc..dbe30460 100644 --- a/qtpy/QtWebEngineQuick.py +++ b/qtpy/QtWebEngineQuick.py @@ -4,13 +4,25 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtWebEngineQuick classes and functions.""" -# Local imports +""" +Provides QtWebEngineQuick classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if PYQT6: - from PyQt6.QtWebEngineQuick import * +if PYQT5: + raise PythonQtError('QtWebEngineQuick not implemented in PyQt5') +elif PYQT6: + try: + from PyQt6.QtWebEngineQuick import * + except ImportError as error: + raise PythonQtError( + 'The QtWebEngineQuick module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error +elif PYSIDE2: + raise PythonQtError('QtWebEngineQuick not implemented in PySide2') elif PYSIDE6: from PySide6.QtWebEngineQuick import * else: diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 5bcaebf0..400d9c68 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -11,11 +11,6 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError - -# To test if we are using WebEngine or WebKit -WEBENGINE = True - - if PYQT5: try: from PyQt5.QtWebEngineWidgets import QWebEnginePage @@ -24,24 +19,23 @@ from PyQt5.QtWebEngineWidgets import QWebEngineScript # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 from PyQt5.QtWebEngineWidgets import QWebEngineProfile - except ImportError: - from PyQt5.QtWebKitWidgets import QWebPage as QWebEnginePage - from PyQt5.QtWebKitWidgets import QWebView as QWebEngineView - from PyQt5.QtWebKit import QWebSettings as QWebEngineSettings - WEBENGINE = False + except ImportError as error: + raise PythonQtError( + 'The QtWebEngineWidgets module was not found. ' + 'It needs to be installed separately for PyQt5.' + ) from error elif PYQT6: - from PyQt6.QtWebEngineWidgets import * - from PyQt6.QtWebEngineCore import QWebEnginePage - from PyQt6.QtWebEngineCore import QWebEngineSettings - from PyQt6.QtWebEngineCore import QWebEngineProfile - from PyQt6.QtWebEngineCore import QWebEngineScript - -elif PYSIDE6: - from PySide6.QtWebEngineWidgets import * - from PySide6.QtWebEngineCore import QWebEnginePage - from PySide6.QtWebEngineCore import QWebEngineSettings - from PySide6.QtWebEngineCore import QWebEngineProfile - from PySide6.QtWebEngineCore import QWebEngineScript + try: + from PyQt6.QtWebEngineWidgets import * + from PyQt6.QtWebEngineCore import QWebEnginePage + from PyQt6.QtWebEngineCore import QWebEngineSettings + from PyQt6.QtWebEngineCore import QWebEngineProfile + from PyQt6.QtWebEngineCore import QWebEngineScript + except ImportError as error: + raise PythonQtError( + 'The QtWebEngineWidgets module was not found. ' + 'It needs to be installed separately for PyQt6.' + ) from error elif PYSIDE2: from PySide2.QtWebEngineWidgets import QWebEnginePage from PySide2.QtWebEngineWidgets import QWebEngineView @@ -49,5 +43,11 @@ from PySide2.QtWebEngineWidgets import QWebEngineScript # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 from PySide2.QtWebEngineWidgets import QWebEngineProfile +elif PYSIDE6: + from PySide6.QtWebEngineWidgets import * + from PySide6.QtWebEngineCore import QWebEnginePage + from PySide6.QtWebEngineCore import QWebEngineSettings + from PySide6.QtWebEngineCore import QWebEngineProfile + from PySide6.QtWebEngineCore import QWebEngineScript else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebSockets.py b/qtpy/QtWebSockets.py index f1933898..63d4b373 100644 --- a/qtpy/QtWebSockets.py +++ b/qtpy/QtWebSockets.py @@ -4,9 +4,11 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtWebSockets classes and functions.""" -# Local imports +""" +Provides QtWebSockets classes and functions. +""" + from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index 8b870bdf..4493f1cb 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -3,13 +3,25 @@ # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- -from . import PYQT5, PYSIDE2, PythonQtError +""" +Provides Windows-specific utilities +""" +import platform +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if PYQT5: - from PyQt5.QtWinExtras import * -elif PYSIDE2: - from PySide2.QtWinExtras import * +if platform.system() == 'Windows': + if PYQT5: + from PyQt5.QtWinExtras import * + elif PYQT6: + raise PythonQtError('QtWinExtras does not exist in Qt6') + elif PYSIDE2: + from PySide2.QtWinExtras import * + elif PYSIDE6: + raise PythonQtError('QtWinExtras does not exist in Qt6') + else: + raise PythonQtError('No Qt bindings could be found') else: - raise PythonQtError('No Qt bindings could be found') + raise PythonQtError('QtWinExtras does not exist on this operating system') diff --git a/qtpy/QtX11Extras.py b/qtpy/QtX11Extras.py new file mode 100644 index 00000000..b8b577cc --- /dev/null +++ b/qtpy/QtX11Extras.py @@ -0,0 +1,27 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +""" +Provides Linux-specific utilities +""" + +import platform +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError + +if platform.system() == 'Linux': + if PYQT5: + from PyQt5.QtX11Extras import * + elif PYQT6: + raise PythonQtError('QtX11Extras does not exist in Qt6') + elif PYSIDE2: + from PySide2.QtX11Extras import * + elif PYSIDE6: + raise PythonQtError('QtX11Extras does not exist in Qt6') + else: + raise PythonQtError('No Qt bindings could be found') +else: + raise PythonQtError('QtX11Extras does not exist on this operating system') diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py index f6b22a32..eaf37d14 100644 --- a/qtpy/QtXmlPatterns.py +++ b/qtpy/QtXmlPatterns.py @@ -4,14 +4,20 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -"""Provides QtXmlPatterns classes and functions.""" -# Local imports -from . import PYSIDE2, PYQT5, PythonQtError +""" +Provides QtXmlPatterns classes and functions. +""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtXmlPatterns import * +elif PYQT6: + raise PythonQtError('QtXmlPatterns does not exist in Qt6') elif PYSIDE2: from PySide2.QtXmlPatterns import * +elif PYSIDE6: + raise PythonQtError('QtXmlPatterns does not exist in Qt6') else: raise PythonQtError('No Qt bindings could be found') From b784f035bb9aad24f37614327d9f2c0d44166c19 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sun, 15 May 2022 03:45:39 -0400 Subject: [PATCH 416/703] Partial implementation of test improvements --- qtpy/tests/test_qt3dcore.py | 3 ++- qtpy/tests/test_qt3drender.py | 3 ++- qtpy/tests/test_qtdbus.py | 4 +++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_qt3dcore.py b/qtpy/tests/test_qt3dcore.py index b3fdada4..9596aa23 100644 --- a/qtpy/tests/test_qt3dcore.py +++ b/qtpy/tests/test_qt3dcore.py @@ -1,7 +1,8 @@ import pytest -from qtpy import PYQT6 +from qtpy import PYQT6, PYSIDE6 @pytest.mark.skipif(PYQT6, reason="Not complete in PyQt6") +@pytest.mark.skipif(PYSIDE6, reason="Not complete in PySide6") def test_qt3dcore(): """Test the qtpy.Qt3DCore namespace""" Qt3DCore = pytest.importorskip("qtpy.Qt3DCore") diff --git a/qtpy/tests/test_qt3drender.py b/qtpy/tests/test_qt3drender.py index e0c4e99c..648d56e7 100644 --- a/qtpy/tests/test_qt3drender.py +++ b/qtpy/tests/test_qt3drender.py @@ -1,7 +1,8 @@ import pytest -from qtpy import PYQT6 +from qtpy import PYQT6, PYSIDE6 @pytest.mark.skipif(PYQT6, reason="Not complete in PyQt6") +@pytest.mark.skipif(PYSIDE6, reason="Not complete in PySide6") def test_qt3drender(): """Test the qtpy.Qt3DRender namespace""" Qt3DRender = pytest.importorskip("qtpy.Qt3DRender") diff --git a/qtpy/tests/test_qtdbus.py b/qtpy/tests/test_qtdbus.py index 2eaf4f5b..c0ed4dd7 100644 --- a/qtpy/tests/test_qtdbus.py +++ b/qtpy/tests/test_qtdbus.py @@ -1,7 +1,9 @@ import pytest +import platform from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 -@pytest.mark.skipif(PYSIDE2 or PYSIDE6, reason="Not available in PySide2, not on CI for PySide6") +@pytest.mark.skipif(PYSIDE2, reason="Not available in PySide2") +@pytest.mark.skipif(platform.system() == 'Windows', reason="Not available on Windows") def test_qtdbus(): """Test the qtpy.QtDBus namespace""" from qtpy import QtDBus From 8b1337675c4326534ac04dfd846366bc223f1ca0 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 06:06:02 -0400 Subject: [PATCH 417/703] Remove WebKit checks WebKit was fully removed from PyQt5 before the oldest supported version --- qtpy/tests/test_main.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/qtpy/tests/test_main.py b/qtpy/tests/test_main.py index dd57fcca..f6c6efbf 100644 --- a/qtpy/tests/test_main.py +++ b/qtpy/tests/test_main.py @@ -43,10 +43,6 @@ def assert_pyqt5(): assert QtCore.QEvent is PyQt5.QtCore.QEvent assert QtGui.QPainter is PyQt5.QtGui.QPainter assert QtWidgets.QWidget is PyQt5.QtWidgets.QWidget - if QtWebEngineWidgets.WEBENGINE: - assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebEngineWidgets.QWebEnginePage - else: - assert QtWebEngineWidgets.QWebEnginePage is PyQt5.QtWebKitWidgets.QWebPage assert os.environ['QT_API'] == 'pyqt5' def assert_pyqt6(): From 33764c80ebacd3dfa8cbab7689284eabd4add93c Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 06:07:45 -0400 Subject: [PATCH 418/703] Add new error types suggested by @CAM-Gerlach Co-authored-by: CAM Gerlach --- qtpy/__init__.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index e9003060..097adad4 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -76,6 +76,34 @@ class PythonQtValueError(ValueError): """Error raised if an invalid QT_API is specified.""" +class QtModuleNotFoundError(ModuleNotFoundError, PythonQtError): + """Raised when a Python Qt binding submodule is not installed/supported.""" + _msg = 'The {name} module was not found for {binding}.' + _msg_extra = '' + + def __init__(self, *, name, binding=None, msg=None, **msg_kwargs): + self.binding = binding + msg = msg or f'{self._msg} {self._msg_extra}'.strip() + msg = msg.format(name=name, binding=binding, **msg_kwargs) + super().__init__(msg, name=name) + + +class QtBindingMissingModuleError(QtModuleNotFoundError): + """Raised when a module is not supported by a given binding.""" + _msg_extra = 'It is not currently implemented in {binding}.' + + +class QtModuleNotInstalledError(QtModuleNotFoundError): + """Raise when a module is supported by the binding, but not installed.""" + _msg_extra = 'It must be installed separately' + + def __init__(self, *, missing_package=None, **superclass_kwargs): + self.missing_package = missing_package + if missing_package is not None: + self._msg_extra += ' as {missing_package}.' + super().__init__(missing_package=missing_package, **superclass_kwargs) + + # Qt API environment variable name QT_API = 'QT_API' From f3886cb4182541f1d696faa9bc3af401b57a1eaf Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 06:17:34 -0400 Subject: [PATCH 419/703] Implement QtModuleNotInstalledError and update tests --- qtpy/Qt3DAnimation.py | 16 +++++----------- qtpy/Qt3DCore.py | 16 +++++----------- qtpy/Qt3DExtras.py | 16 +++++----------- qtpy/Qt3DInput.py | 16 +++++----------- qtpy/Qt3DLogic.py | 16 +++++----------- qtpy/Qt3DRender.py | 16 +++++----------- qtpy/QtCharts.py | 16 +++++----------- qtpy/QtDataVisualization.py | 16 +++++----------- qtpy/QtNetworkAuth.py | 16 +++++----------- qtpy/QtWebEngine.py | 9 +++------ qtpy/QtWebEngineCore.py | 16 +++++----------- qtpy/QtWebEngineWidgets.py | 16 +++++----------- qtpy/tests/test_qtcharts.py | 5 +++-- qtpy/tests/test_qtnetworkauth.py | 3 ++- qtpy/tests/test_qtwebenginecore.py | 2 +- 15 files changed, 64 insertions(+), 131 deletions(-) diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index e923abe0..5580ee7a 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -9,24 +9,18 @@ Provides Qt3DAnimation classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.Qt3DAnimation import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DAnimation module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DAnimation', binding=API_NAME, missing_package='PyQt3D') elif PYQT6: try: from PyQt6.Qt3DAnimation import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DAnimation module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DAnimation', binding=API_NAME, missing_package='PyQt6-3D') elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DAnimation as __temp diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 293e7e06..09526d26 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -9,24 +9,18 @@ Provides Qt3DCore classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.Qt3DCore import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DCore module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DCore', binding=API_NAME, missing_package='PyQt3D') elif PYQT6: try: from PyQt6.Qt3DCore import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DCore module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DCore', binding=API_NAME, missing_package='PyQt6-3D') elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DCore as __temp diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 0c901c0b..3f76f60d 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -9,24 +9,18 @@ Provides Qt3DExtras classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.Qt3DExtras import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DExtras module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DExtras', binding=API_NAME, missing_package='PyQt3D') elif PYQT6: try: from PyQt6.Qt3DExtras import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DExtras module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DExtras', binding=API_NAME, missing_package='PyQt6-3D') elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DExtras as __temp diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 67ad35d1..53130665 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -9,24 +9,18 @@ Provides Qt3DInput classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.Qt3DInput import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DInput module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DInput', binding=API_NAME, missing_package='PyQt3D') elif PYQT6: try: from PyQt6.Qt3DInput import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DInput module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DInput', binding=API_NAME, missing_package='PyQt6-3D') elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DInput as __temp diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index 433df0d5..c0e26442 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -9,24 +9,18 @@ Provides Qt3DLogic classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.Qt3DLogic import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DLogic module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DLogic', binding=API_NAME, missing_package='PyQt3D') elif PYQT6: try: from PyQt6.Qt3DLogic import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DLogic module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DLogic', binding=API_NAME, missing_package='PyQt6-3D') elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DLogic as __temp diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index 92709663..e66c1691 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -9,24 +9,18 @@ Provides Qt3DRender classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.Qt3DRender import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DRender module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DRender', binding=API_NAME, missing_package='PyQt3D') elif PYQT6: try: from PyQt6.Qt3DRender import * - except ImportError as error: - raise PythonQtError( - 'The Qt3DRender module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DRender', binding=API_NAME, missing_package='PyQt6-3D') elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DRender as __temp diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 47990314..6d3518da 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -9,26 +9,20 @@ Provides QtChart classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.QtChart import * from PyQt5 import QtChart as QtCharts - except ImportError as error: - raise PythonQtError( - 'The QtChart module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DCharts', binding=API_NAME, missing_package='PyQtChart') elif PYQT6: try: from PyQt6.QtCharts import * from PyQt6 import QtCharts - except ImportError as error: - raise PythonQtError( - 'The QtCharts module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='Qt3DCharts', binding=API_NAME, missing_package='PyQt6-Charts') elif PYSIDE2: from PySide2.QtCharts import * # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index fc77abdd..75fe58bc 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -9,24 +9,18 @@ Provides QtDataVisualization classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.QtDataVisualization import * - except ImportError as error: - raise PythonQtError( - 'The QtDataVisualization module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='QtDataVisualization', binding=API_NAME, missing_package='PyQtDataVisualization') elif PYQT6: try: from PyQt6.QtDataVisualization import * - except ImportError as error: - raise PythonQtError( - 'The QtDataVisualization module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='QtDataVisualization', binding=API_NAME, missing_package='PyQt6-DataVisualization') elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.QtDataVisualization as __temp diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py index 75b7dbe5..10ebee71 100644 --- a/qtpy/QtNetworkAuth.py +++ b/qtpy/QtNetworkAuth.py @@ -9,24 +9,18 @@ Provides QtNetworkAuth classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.QtNetworkAuth import * - except ImportError as error: - raise PythonQtError( - 'The QtNetworkAuth module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='QtNetworkAuth', binding=API_NAME, missing_package='PyQtNetworkAuth') elif PYQT6: try: from PyQt6.QtNetworkAuth import * - except ImportError as error: - raise PythonQtError( - 'The QtNetworkAuth module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='QtNetworkAuth', binding=API_NAME, missing_package='PyQt6-NetworkAuth') elif PYSIDE2: raise PythonQtError('QtNetworkAuth not implemented in PySide2') elif PYSIDE6: diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index 06b1bf75..212d8c2b 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -10,16 +10,13 @@ Provides QtWebEngine classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.QtWebEngine import * - except ImportError as error: - raise PythonQtError( - 'The QtWebEngine module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='QtWebEngine', binding=API_NAME, missing_package='PyQtWebEngine') elif PYQT6: raise PythonQtError('QtWebEngine does not exist in Qt6') elif PYSIDE2: diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py index dc6f4091..0d4e29c5 100644 --- a/qtpy/QtWebEngineCore.py +++ b/qtpy/QtWebEngineCore.py @@ -9,24 +9,18 @@ Provides QtWebEngineCore classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: from PyQt5.QtWebEngineCore import * - except ImportError as error: - raise PythonQtError( - 'The QtWebEngineCore module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='QtWebEngineCore', binding=API_NAME, missing_package='PyQtWebEngine') elif PYQT6: try: from PyQt6.QtWebEngineCore import * - except ImportError as error: - raise PythonQtError( - 'The QtWebEngineCore module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='QtWebEngineCore', binding=API_NAME, missing_package='PyQt6-WebEngine') elif PYSIDE2: from PySide2.QtWebEngineCore import * elif PYSIDE6: diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 400d9c68..b911a30b 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -9,7 +9,7 @@ Provides QtWebEngineWidgets classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME if PYQT5: try: @@ -19,11 +19,8 @@ from PyQt5.QtWebEngineWidgets import QWebEngineScript # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 from PyQt5.QtWebEngineWidgets import QWebEngineProfile - except ImportError as error: - raise PythonQtError( - 'The QtWebEngineWidgets module was not found. ' - 'It needs to be installed separately for PyQt5.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='QtWebEngineWidgets', binding=API_NAME, missing_package='PyQtWebEngine') elif PYQT6: try: from PyQt6.QtWebEngineWidgets import * @@ -31,11 +28,8 @@ from PyQt6.QtWebEngineCore import QWebEngineSettings from PyQt6.QtWebEngineCore import QWebEngineProfile from PyQt6.QtWebEngineCore import QWebEngineScript - except ImportError as error: - raise PythonQtError( - 'The QtWebEngineWidgets module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError(name='QtWebEngineWidgets', binding=API_NAME, missing_package='PyQt6-WebEngine') elif PYSIDE2: from PySide2.QtWebEngineWidgets import QWebEnginePage from PySide2.QtWebEngineWidgets import QWebEngineView diff --git a/qtpy/tests/test_qtcharts.py b/qtpy/tests/test_qtcharts.py index 0531b169..342ad40a 100644 --- a/qtpy/tests/test_qtcharts.py +++ b/qtpy/tests/test_qtcharts.py @@ -5,7 +5,8 @@ @pytest.mark.skipif(not (PYSIDE2 or PYSIDE6), reason="Only available by default in PySide") def test_qtcharts(): - """Test the qtpy.QtCharts namespace""" - from qtpy import QtCharts + """Test the qtpy.QtCharts namespace""" + QtCharts = pytest.importorskip("qtpy.QtCharts") + assert QtCharts.QChart is not None assert QtCharts.QtCharts.QChart is not None diff --git a/qtpy/tests/test_qtnetworkauth.py b/qtpy/tests/test_qtnetworkauth.py index e41a5db1..30fc0c11 100644 --- a/qtpy/tests/test_qtnetworkauth.py +++ b/qtpy/tests/test_qtnetworkauth.py @@ -6,7 +6,8 @@ reason="Not available by default in PyQt. Not available for PySide2") def test_qtnetworkauth(): """Test the qtpy.QtNetworkAuth namespace""" - from qtpy import QtNetworkAuth + QtNetworkAuth = pytest.importorskip("qtpy.QtNetworkAuth") + assert QtNetworkAuth.QAbstractOAuth is not None assert QtNetworkAuth.QAbstractOAuth2 is not None assert QtNetworkAuth.QAbstractOAuthReplyHandler is not None diff --git a/qtpy/tests/test_qtwebenginecore.py b/qtpy/tests/test_qtwebenginecore.py index 591b0763..d52c2786 100644 --- a/qtpy/tests/test_qtwebenginecore.py +++ b/qtpy/tests/test_qtwebenginecore.py @@ -3,7 +3,7 @@ def test_qtwebenginecore(): """Test the qtpy.QtWebEngineCore namespace""" - from qtpy import QtWebEngineCore + QtWebEngineCore = pytest.importorskip("qtpy.QtWebEngineCore") assert QtWebEngineCore.QWebEngineHttpRequest is not None From c7d739fcfbb74eb4b9115a3384e63e4f8caa6ac6 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 07:00:26 -0400 Subject: [PATCH 420/703] Implement first QtBindingMissingModuleError and add tests for QtAxContainer --- qtpy/QtAxContainer.py | 6 +++--- qtpy/tests/test_qtaxcontainer.py | 8 ++++++++ 2 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 qtpy/tests/test_qtaxcontainer.py diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py index f0742f90..bdae6031 100644 --- a/qtpy/QtAxContainer.py +++ b/qtpy/QtAxContainer.py @@ -9,12 +9,12 @@ Provides QtAxContainer classes and functions. """ -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError, API_NAME if PYQT5: - raise PythonQtError('QtAxContainer not implemented in PyQt5') + raise QtBindingMissingModuleError(name='QtAxContainer', binding=API_NAME, missing_package='PyQt3D') elif PYQT6: - raise PythonQtError('QtAxContainer not implemented in PyQt6') + raise QtBindingMissingModuleError(name='QtAxContainer', binding=API_NAME, missing_package='PyQt6-3D') elif PYSIDE2: from PySide2.QtAxContainer import * elif PYSIDE6: diff --git a/qtpy/tests/test_qtaxcontainer.py b/qtpy/tests/test_qtaxcontainer.py new file mode 100644 index 00000000..aa17f4d6 --- /dev/null +++ b/qtpy/tests/test_qtaxcontainer.py @@ -0,0 +1,8 @@ +import pytest + +def test_qtaxcontainer(): + """Test the qtpy.QtAxContainer namespace""" + QtAxContainer = pytest.importorskip("qtpy.QtAxContainer") + + assert QtAxContainer.QAxSelect is not None + assert QtAxContainer.QAxWidget is not None \ No newline at end of file From 3f0c4e22f30fe2e89444d7ffe38de30f84e42461 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 16:48:35 -0400 Subject: [PATCH 421/703] Fix failing MacOS test --- qtpy/tests/test_qtdbus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtdbus.py b/qtpy/tests/test_qtdbus.py index c0ed4dd7..a5b9fe7f 100644 --- a/qtpy/tests/test_qtdbus.py +++ b/qtpy/tests/test_qtdbus.py @@ -3,7 +3,7 @@ from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 @pytest.mark.skipif(PYSIDE2, reason="Not available in PySide2") -@pytest.mark.skipif(platform.system() == 'Windows', reason="Not available on Windows") +@pytest.mark.skipif(platform.system() != 'Linux', reason="Only available on Linux") def test_qtdbus(): """Test the qtpy.QtDBus namespace""" from qtpy import QtDBus From 805ed63cf09ab2bf908077d2e2fd0bd678f87ce0 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 17:25:22 -0400 Subject: [PATCH 422/703] Inherit context from original error in all re-raised exceptions --- qtpy/Qt3DAnimation.py | 8 ++++++-- qtpy/Qt3DCore.py | 8 ++++++-- qtpy/Qt3DExtras.py | 8 ++++++-- qtpy/Qt3DInput.py | 8 ++++++-- qtpy/Qt3DLogic.py | 8 ++++++-- qtpy/Qt3DRender.py | 8 ++++++-- qtpy/QtAxContainer.py | 8 ++++++-- qtpy/QtCharts.py | 8 ++++++-- qtpy/QtDataVisualization.py | 8 ++++++-- qtpy/QtNetworkAuth.py | 8 ++++++-- qtpy/QtWebEngine.py | 4 +++- qtpy/QtWebEngineCore.py | 8 ++++++-- qtpy/QtWebEngineWidgets.py | 8 ++++++-- 13 files changed, 75 insertions(+), 25 deletions(-) diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index 5580ee7a..bdb7229c 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -15,12 +15,16 @@ try: from PyQt5.Qt3DAnimation import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DAnimation', binding=API_NAME, missing_package='PyQt3D') + raise QtModuleNotInstalledError( + name='Qt3DAnimation', binding=API_NAME, missing_package='PyQt3D' + ) from error elif PYQT6: try: from PyQt6.Qt3DAnimation import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DAnimation', binding=API_NAME, missing_package='PyQt6-3D') + raise QtModuleNotInstalledError( + name='Qt3DAnimation', binding=API_NAME, missing_package='PyQt6-3D' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DAnimation as __temp diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 09526d26..97646619 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -15,12 +15,16 @@ try: from PyQt5.Qt3DCore import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DCore', binding=API_NAME, missing_package='PyQt3D') + raise QtModuleNotInstalledError( + name='Qt3DCore', binding=API_NAME, missing_package='PyQt3D' + ) from error elif PYQT6: try: from PyQt6.Qt3DCore import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DCore', binding=API_NAME, missing_package='PyQt6-3D') + raise QtModuleNotInstalledError( + name='Qt3DCore', binding=API_NAME, missing_package='PyQt6-3D' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DCore as __temp diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 3f76f60d..62c65edc 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -15,12 +15,16 @@ try: from PyQt5.Qt3DExtras import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DExtras', binding=API_NAME, missing_package='PyQt3D') + raise QtModuleNotInstalledError( + name='Qt3DExtras', binding=API_NAME, missing_package='PyQt3D' + ) from error elif PYQT6: try: from PyQt6.Qt3DExtras import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DExtras', binding=API_NAME, missing_package='PyQt6-3D') + raise QtModuleNotInstalledError( + name='Qt3DExtras', binding=API_NAME, missing_package='PyQt6-3D' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DExtras as __temp diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 53130665..1ab1eb10 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -15,12 +15,16 @@ try: from PyQt5.Qt3DInput import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DInput', binding=API_NAME, missing_package='PyQt3D') + raise QtModuleNotInstalledError( + name='Qt3DInput', binding=API_NAME, missing_package='PyQt3D' + ) from error elif PYQT6: try: from PyQt6.Qt3DInput import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DInput', binding=API_NAME, missing_package='PyQt6-3D') + raise QtModuleNotInstalledError( + name='Qt3DInput', binding=API_NAME, missing_package='PyQt6-3D' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DInput as __temp diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index c0e26442..3f53f2bb 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -15,12 +15,16 @@ try: from PyQt5.Qt3DLogic import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DLogic', binding=API_NAME, missing_package='PyQt3D') + raise QtModuleNotInstalledError( + name='Qt3DLogic', binding=API_NAME, missing_package='PyQt3D' + ) from error elif PYQT6: try: from PyQt6.Qt3DLogic import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DLogic', binding=API_NAME, missing_package='PyQt6-3D') + raise QtModuleNotInstalledError( + name='Qt3DLogic', binding=API_NAME, missing_package='PyQt6-3D' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DLogic as __temp diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index e66c1691..85147d28 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -15,12 +15,16 @@ try: from PyQt5.Qt3DRender import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DRender', binding=API_NAME, missing_package='PyQt3D') + raise QtModuleNotInstalledError( + name='Qt3DRender', binding=API_NAME, missing_package='PyQt3D' + ) from error elif PYQT6: try: from PyQt6.Qt3DRender import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DRender', binding=API_NAME, missing_package='PyQt6-3D') + raise QtModuleNotInstalledError( + name='Qt3DRender', binding=API_NAME, missing_package='PyQt6-3D' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DRender as __temp diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py index bdae6031..fd2bf150 100644 --- a/qtpy/QtAxContainer.py +++ b/qtpy/QtAxContainer.py @@ -12,9 +12,13 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError, API_NAME if PYQT5: - raise QtBindingMissingModuleError(name='QtAxContainer', binding=API_NAME, missing_package='PyQt3D') + raise QtBindingMissingModuleError( + name='QtAxContainer', binding=API_NAME, missing_package='PyQt3D' + ) elif PYQT6: - raise QtBindingMissingModuleError(name='QtAxContainer', binding=API_NAME, missing_package='PyQt6-3D') + raise QtBindingMissingModuleError( + name='QtAxContainer', binding=API_NAME, missing_package='PyQt6-3D' + ) elif PYSIDE2: from PySide2.QtAxContainer import * elif PYSIDE6: diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 6d3518da..ac46cc74 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -16,13 +16,17 @@ from PyQt5.QtChart import * from PyQt5 import QtChart as QtCharts except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DCharts', binding=API_NAME, missing_package='PyQtChart') + raise QtModuleNotInstalledError( + name='Qt3DCharts', binding=API_NAME, missing_package='PyQtChart' + ) from error elif PYQT6: try: from PyQt6.QtCharts import * from PyQt6 import QtCharts except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='Qt3DCharts', binding=API_NAME, missing_package='PyQt6-Charts') + raise QtModuleNotInstalledError( + name='Qt3DCharts', binding=API_NAME, missing_package='PyQt6-Charts' + ) from error elif PYSIDE2: from PySide2.QtCharts import * # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index 75fe58bc..b2c085fb 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -15,12 +15,16 @@ try: from PyQt5.QtDataVisualization import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='QtDataVisualization', binding=API_NAME, missing_package='PyQtDataVisualization') + raise QtModuleNotInstalledError( + name='QtDataVisualization', binding=API_NAME, missing_package='PyQtDataVisualization' + ) from error elif PYQT6: try: from PyQt6.QtDataVisualization import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='QtDataVisualization', binding=API_NAME, missing_package='PyQt6-DataVisualization') + raise QtModuleNotInstalledError( + name='QtDataVisualization', binding=API_NAME, missing_package='PyQt6-DataVisualization' + ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.QtDataVisualization as __temp diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py index 10ebee71..15ae83f0 100644 --- a/qtpy/QtNetworkAuth.py +++ b/qtpy/QtNetworkAuth.py @@ -15,12 +15,16 @@ try: from PyQt5.QtNetworkAuth import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='QtNetworkAuth', binding=API_NAME, missing_package='PyQtNetworkAuth') + raise QtModuleNotInstalledError( + name='QtNetworkAuth', binding=API_NAME, missing_package='PyQtNetworkAuth' + ) from error elif PYQT6: try: from PyQt6.QtNetworkAuth import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='QtNetworkAuth', binding=API_NAME, missing_package='PyQt6-NetworkAuth') + raise QtModuleNotInstalledError( + name='QtNetworkAuth', binding=API_NAME, missing_package='PyQt6-NetworkAuth' + ) from error elif PYSIDE2: raise PythonQtError('QtNetworkAuth not implemented in PySide2') elif PYSIDE6: diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index 212d8c2b..9bd57ecd 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -16,7 +16,9 @@ try: from PyQt5.QtWebEngine import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='QtWebEngine', binding=API_NAME, missing_package='PyQtWebEngine') + raise QtModuleNotInstalledError( + name='QtWebEngine', binding=API_NAME, missing_package='PyQtWebEngine' + ) from error elif PYQT6: raise PythonQtError('QtWebEngine does not exist in Qt6') elif PYSIDE2: diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py index 0d4e29c5..88b64c92 100644 --- a/qtpy/QtWebEngineCore.py +++ b/qtpy/QtWebEngineCore.py @@ -15,12 +15,16 @@ try: from PyQt5.QtWebEngineCore import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='QtWebEngineCore', binding=API_NAME, missing_package='PyQtWebEngine') + raise QtModuleNotInstalledError( + name='QtWebEngineCore', binding=API_NAME, missing_package='PyQtWebEngine' + ) from error elif PYQT6: try: from PyQt6.QtWebEngineCore import * except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='QtWebEngineCore', binding=API_NAME, missing_package='PyQt6-WebEngine') + raise QtModuleNotInstalledError( + name='QtWebEngineCore', binding=API_NAME, missing_package='PyQt6-WebEngine' + ) from error elif PYSIDE2: from PySide2.QtWebEngineCore import * elif PYSIDE6: diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index b911a30b..0ef4161d 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -20,7 +20,9 @@ # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 from PyQt5.QtWebEngineWidgets import QWebEngineProfile except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='QtWebEngineWidgets', binding=API_NAME, missing_package='PyQtWebEngine') + raise QtModuleNotInstalledError( + name='QtWebEngineWidgets', binding=API_NAME, missing_package='PyQtWebEngine' + ) from error elif PYQT6: try: from PyQt6.QtWebEngineWidgets import * @@ -29,7 +31,9 @@ from PyQt6.QtWebEngineCore import QWebEngineProfile from PyQt6.QtWebEngineCore import QWebEngineScript except ModuleNotFoundError as error: - raise QtModuleNotInstalledError(name='QtWebEngineWidgets', binding=API_NAME, missing_package='PyQt6-WebEngine') + raise QtModuleNotInstalledError( + name='QtWebEngineWidgets', binding=API_NAME, missing_package='PyQt6-WebEngine' + ) from error elif PYSIDE2: from PySide2.QtWebEngineWidgets import QWebEnginePage from PySide2.QtWebEngineWidgets import QWebEngineView From 0041ed7a0e4f990570bb8b2012b4d4b8e23dca0a Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 17:27:45 -0400 Subject: [PATCH 423/703] Removed uneccesary `missing_package` argument --- qtpy/QtAxContainer.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py index fd2bf150..de1d7a29 100644 --- a/qtpy/QtAxContainer.py +++ b/qtpy/QtAxContainer.py @@ -12,13 +12,9 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError, API_NAME if PYQT5: - raise QtBindingMissingModuleError( - name='QtAxContainer', binding=API_NAME, missing_package='PyQt3D' - ) + raise QtBindingMissingModuleError(name='QtAxContainer', binding=API_NAME) elif PYQT6: - raise QtBindingMissingModuleError( - name='QtAxContainer', binding=API_NAME, missing_package='PyQt6-3D' - ) + raise QtBindingMissingModuleError(name='QtAxContainer', binding=API_NAME) elif PYSIDE2: from PySide2.QtAxContainer import * elif PYSIDE6: From d01217875a1c720b3c6fabe05fd3b0c2b0d3b287 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 18:47:10 -0400 Subject: [PATCH 424/703] Replace generic PythonQtError with QtModuleNotInstalledError --- qtpy/QtWebEngineQuick.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/qtpy/QtWebEngineQuick.py b/qtpy/QtWebEngineQuick.py index dbe30460..4e3ab021 100644 --- a/qtpy/QtWebEngineQuick.py +++ b/qtpy/QtWebEngineQuick.py @@ -16,11 +16,10 @@ elif PYQT6: try: from PyQt6.QtWebEngineQuick import * - except ImportError as error: - raise PythonQtError( - 'The QtWebEngineQuick module was not found. ' - 'It needs to be installed separately for PyQt6.' - ) from error + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError( + name='QtWebEngineQuick', binding=API_NAME, missing_package='PyQt6-WebEngine' + ) from error elif PYSIDE2: raise PythonQtError('QtWebEngineQuick not implemented in PySide2') elif PYSIDE6: From bf142414193764250cc6f5afb1cab459095f2186 Mon Sep 17 00:00:00 2001 From: Daelon Suzuka Date: Mon, 16 May 2022 15:53:03 -0700 Subject: [PATCH 425/703] Fix QtModuleNotFoundError internal argument passing Co-authored-by: CAM Gerlach --- qtpy/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 097adad4..cd8b3740 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -78,11 +78,13 @@ class PythonQtValueError(ValueError): class QtModuleNotFoundError(ModuleNotFoundError, PythonQtError): """Raised when a Python Qt binding submodule is not installed/supported.""" - _msg = 'The {name} module was not found for {binding}.' + _msg = 'The {name} module was not found{binding}.' + _msg_binding = ' for {binding}` _msg_extra = '' def __init__(self, *, name, binding=None, msg=None, **msg_kwargs): self.binding = binding + binding = self._msg_binding.format(binding=binding) if binding else '' msg = msg or f'{self._msg} {self._msg_extra}'.strip() msg = msg.format(name=name, binding=binding, **msg_kwargs) super().__init__(msg, name=name) From eae72720e0b4cf54e08c905608186d998ec39806 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 19:22:00 -0400 Subject: [PATCH 426/703] Replace `platform.system()` with `sys.platform` --- qtpy/QtDBus.py | 4 ++-- qtpy/QtMacExtras.py | 4 ++-- qtpy/QtWinExtras.py | 4 ++-- qtpy/QtX11Extras.py | 4 ++-- qtpy/tests/test_qtdbus.py | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index 11476c02..03d541f9 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -9,10 +9,10 @@ Provides QtDBus classes and functions. """ -import platform +import sys from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if platform.system() == 'Linux': +if sys.platform == 'linux': if PYQT5: from PyQt5.QtDBus import * elif PYQT6: diff --git a/qtpy/QtMacExtras.py b/qtpy/QtMacExtras.py index 015c9ac0..6df4c040 100644 --- a/qtpy/QtMacExtras.py +++ b/qtpy/QtMacExtras.py @@ -9,10 +9,10 @@ Provides classes and functions specific to macOS and iOS operating systems """ -import platform +import sys from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if platform.system() == 'Darwin': +if sys.platform == 'darwin': if PYQT5: from PyQt5.QtMacExtras import * elif PYQT6: diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index 4493f1cb..75b87488 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -9,10 +9,10 @@ Provides Windows-specific utilities """ -import platform +import sys from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if platform.system() == 'Windows': +if sys.platform == 'win32': if PYQT5: from PyQt5.QtWinExtras import * elif PYQT6: diff --git a/qtpy/QtX11Extras.py b/qtpy/QtX11Extras.py index b8b577cc..376ef73a 100644 --- a/qtpy/QtX11Extras.py +++ b/qtpy/QtX11Extras.py @@ -9,10 +9,10 @@ Provides Linux-specific utilities """ -import platform +import sys from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if platform.system() == 'Linux': +if sys.platform == 'linux': if PYQT5: from PyQt5.QtX11Extras import * elif PYQT6: diff --git a/qtpy/tests/test_qtdbus.py b/qtpy/tests/test_qtdbus.py index a5b9fe7f..fd32d707 100644 --- a/qtpy/tests/test_qtdbus.py +++ b/qtpy/tests/test_qtdbus.py @@ -1,9 +1,9 @@ import pytest -import platform +import sys from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 @pytest.mark.skipif(PYSIDE2, reason="Not available in PySide2") -@pytest.mark.skipif(platform.system() != 'Linux', reason="Only available on Linux") +@pytest.mark.skipif(sys.platform != 'linux', reason="Only available on Linux") def test_qtdbus(): """Test the qtpy.QtDBus namespace""" from qtpy import QtDBus From d05da14ba23e2c91d42c12c3ef61f21e73b50151 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 16 May 2022 19:34:28 -0400 Subject: [PATCH 427/703] Fix GitHub's automated commit inserting a backtick instead of a single quote --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index cd8b3740..d645cb5b 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -79,7 +79,7 @@ class PythonQtValueError(ValueError): class QtModuleNotFoundError(ModuleNotFoundError, PythonQtError): """Raised when a Python Qt binding submodule is not installed/supported.""" _msg = 'The {name} module was not found{binding}.' - _msg_binding = ' for {binding}` + _msg_binding = ' for {binding}' _msg_extra = '' def __init__(self, *, name, binding=None, msg=None, **msg_kwargs): From d85fa829e54e0f8ad5e780c78c9117690fc674ed Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Thu, 19 May 2022 01:43:05 -0400 Subject: [PATCH 428/703] Fix docstrings and standardize document headers --- qtpy/Qt3DAnimation.py | 4 +--- qtpy/Qt3DCore.py | 4 +--- qtpy/Qt3DExtras.py | 4 +--- qtpy/Qt3DInput.py | 4 +--- qtpy/Qt3DLogic.py | 4 +--- qtpy/Qt3DRender.py | 4 +--- qtpy/QtAxContainer.py | 4 +--- qtpy/QtBluetooth.py | 4 +--- qtpy/QtCharts.py | 4 +--- qtpy/QtConcurrent.py | 4 +--- qtpy/QtCore.py | 8 ++++---- qtpy/QtDBus.py | 4 +--- qtpy/QtDataVisualization.py | 4 +--- qtpy/QtDesigner.py | 4 +--- qtpy/QtGui.py | 8 ++++---- qtpy/QtHelp.py | 4 +--- qtpy/QtLocation.py | 4 +--- qtpy/QtMacExtras.py | 4 +--- qtpy/QtMultimedia.py | 4 +--- qtpy/QtMultimediaWidgets.py | 4 +--- qtpy/QtNetwork.py | 4 +--- qtpy/QtNetworkAuth.py | 4 +--- qtpy/QtNfc.py | 4 +--- qtpy/QtOpenGL.py | 3 +-- qtpy/QtOpenGLWidgets.py | 4 +--- qtpy/QtPositioning.py | 4 +--- qtpy/QtPrintSupport.py | 8 +++----- qtpy/QtPurchasing.py | 4 +--- qtpy/QtQml.py | 2 +- qtpy/QtQuick.py | 2 +- qtpy/QtQuick3D.py | 4 +--- qtpy/QtQuickControls2.py | 4 +--- qtpy/QtQuickWidgets.py | 2 +- qtpy/QtRemoteObjects.py | 2 +- qtpy/QtScxml.py | 4 +--- qtpy/QtSensors.py | 2 +- qtpy/QtSerialPort.py | 2 +- qtpy/QtSql.py | 3 +-- qtpy/QtStateMachine.py | 4 +--- qtpy/QtSvg.py | 2 +- qtpy/QtSvgWidgets.py | 4 +--- qtpy/QtTest.py | 7 +++---- qtpy/QtTextToSpeech.py | 4 +--- qtpy/QtUiTools.py | 4 +--- qtpy/QtWebChannel.py | 1 + qtpy/QtWebEngine.py | 4 +--- qtpy/QtWebEngineCore.py | 4 +--- qtpy/QtWebEngineQuick.py | 4 +--- qtpy/QtWebEngineWidgets.py | 7 +++---- qtpy/QtWebSockets.py | 4 +--- qtpy/QtWidgets.py | 8 ++++---- qtpy/QtWinExtras.py | 4 +--- qtpy/QtX11Extras.py | 4 +--- qtpy/QtXml.py | 1 + qtpy/QtXmlPatterns.py | 4 +--- 55 files changed, 70 insertions(+), 150 deletions(-) diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index bdb7229c..e1ed07c4 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides Qt3DAnimation classes and functions. -""" +"""Provides Qt3DAnimation classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 97646619..39b6c546 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides Qt3DCore classes and functions. -""" +"""Provides Qt3DCore classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 62c65edc..ea835788 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides Qt3DExtras classes and functions. -""" +"""Provides Qt3DExtras classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 1ab1eb10..09bc3a0e 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides Qt3DInput classes and functions. -""" +"""Provides Qt3DInput classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index 3f53f2bb..6e14aec1 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides Qt3DLogic classes and functions. -""" +"""Provides Qt3DLogic classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index 85147d28..f4622b78 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides Qt3DRender classes and functions. -""" +"""Provides Qt3DRender classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py index de1d7a29..53a61de2 100644 --- a/qtpy/QtAxContainer.py +++ b/qtpy/QtAxContainer.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtAxContainer classes and functions. -""" +"""Provides QtAxContainer classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError, API_NAME diff --git a/qtpy/QtBluetooth.py b/qtpy/QtBluetooth.py index f85f55a2..926fd76f 100644 --- a/qtpy/QtBluetooth.py +++ b/qtpy/QtBluetooth.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtBluetooth classes and functions. -""" +"""Provides QtBluetooth classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index ac46cc74..f1bfa01b 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtChart classes and functions. -""" +"""Provides QtChart classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/QtConcurrent.py b/qtpy/QtConcurrent.py index 0e62f453..56ff1e80 100644 --- a/qtpy/QtConcurrent.py +++ b/qtpy/QtConcurrent.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtConcurrent classes and functions. -""" +"""Provides QtConcurrent classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 3b36d7a0..45087018 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -1,13 +1,13 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Provides QtCore classes and functions.""" -""" -Provides QtCore classes and functions. -""" from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index 03d541f9..140e4007 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtDBus classes and functions. -""" +"""Provides QtDBus classes and functions.""" import sys from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index b2c085fb..d04e3e0f 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtDataVisualization classes and functions. -""" +"""Provides QtDataVisualization classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index 07fa573b..e3980fb9 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtDesigner classes and functions. -""" +"""Provides QtDesigner classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 8b470612..73d96a5b 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -1,13 +1,13 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Provides QtGui classes and functions.""" -""" -Provides QtGui classes and functions. -""" from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index 33b72c37..e8fbec63 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -QtHelp Wrapper. -""" +"""QtHelp Wrapper.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtLocation.py b/qtpy/QtLocation.py index 2d370325..8363c4c0 100644 --- a/qtpy/QtLocation.py +++ b/qtpy/QtLocation.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtLocation classes and functions. -""" +"""Provides QtLocation classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtMacExtras.py b/qtpy/QtMacExtras.py index 6df4c040..6f673fb4 100644 --- a/qtpy/QtMacExtras.py +++ b/qtpy/QtMacExtras.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides classes and functions specific to macOS and iOS operating systems -""" +"""Provides classes and functions specific to macOS and iOS operating systems""" import sys from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index 46618350..c2f04f3a 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides low-level multimedia functionality. -""" +"""Provides low-level multimedia functionality.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtMultimediaWidgets.py b/qtpy/QtMultimediaWidgets.py index eab6da8d..337efffc 100644 --- a/qtpy/QtMultimediaWidgets.py +++ b/qtpy/QtMultimediaWidgets.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtMultimediaWidgets classes and functions. -""" +"""Provides QtMultimediaWidgets classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index 68629459..e76173d5 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -6,9 +6,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtNetwork classes and functions. -""" +"""Provides QtNetwork classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py index 15ae83f0..28e324e0 100644 --- a/qtpy/QtNetworkAuth.py +++ b/qtpy/QtNetworkAuth.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtNetworkAuth classes and functions. -""" +"""Provides QtNetworkAuth classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/QtNfc.py b/qtpy/QtNfc.py index 98739594..8eb32f7e 100644 --- a/qtpy/QtNfc.py +++ b/qtpy/QtNfc.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtNfc classes and functions. -""" +"""Provides QtNfc classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index b4cb5a93..0696cc57 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -4,9 +4,9 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtOpenGL classes and functions.""" -# Local imports from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: @@ -47,4 +47,3 @@ pass else: raise PythonQtError('No Qt bindings could be found') - diff --git a/qtpy/QtOpenGLWidgets.py b/qtpy/QtOpenGLWidgets.py index 05113954..2d5624b7 100644 --- a/qtpy/QtOpenGLWidgets.py +++ b/qtpy/QtOpenGLWidgets.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtOpenGLWidgets classes and functions. -""" +"""Provides QtOpenGLWidgets classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtPositioning.py b/qtpy/QtPositioning.py index 2f31ac57..1ab54686 100644 --- a/qtpy/QtPositioning.py +++ b/qtpy/QtPositioning.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtPositioning classes and functions. -""" +"""Provides QtPositioning classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 4fcec32b..e2602ff0 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -1,16 +1,14 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- -""" -Provides QtPrintSupport classes and functions. -""" +"""Provides QtPrintSupport classes and functions.""" from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError - if PYQT5: from PyQt5.QtPrintSupport import * elif PYQT6: diff --git a/qtpy/QtPurchasing.py b/qtpy/QtPurchasing.py index 36de0314..950cb4b3 100644 --- a/qtpy/QtPurchasing.py +++ b/qtpy/QtPurchasing.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtPurchasing classes and functions. -""" +"""Provides QtPurchasing classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtQml.py b/qtpy/QtQml.py index ec6934a7..dbd69852 100644 --- a/qtpy/QtQml.py +++ b/qtpy/QtQml.py @@ -4,9 +4,9 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtQml classes and functions.""" -# Local imports from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: diff --git a/qtpy/QtQuick.py b/qtpy/QtQuick.py index 10f6a41c..08e339ec 100644 --- a/qtpy/QtQuick.py +++ b/qtpy/QtQuick.py @@ -4,9 +4,9 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtQuick classes and functions.""" -# Local imports from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: diff --git a/qtpy/QtQuick3D.py b/qtpy/QtQuick3D.py index a1e1e082..25965ff2 100644 --- a/qtpy/QtQuick3D.py +++ b/qtpy/QtQuick3D.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtQuick3D classes and functions. -""" +"""Provides QtQuick3D classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtQuickControls2.py b/qtpy/QtQuickControls2.py index 1a104ca3..2e049c13 100644 --- a/qtpy/QtQuickControls2.py +++ b/qtpy/QtQuickControls2.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtQuickControls2 classes and functions. -""" +"""Provides QtQuickControls2 classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtQuickWidgets.py b/qtpy/QtQuickWidgets.py index dfadb2c0..ffecda11 100644 --- a/qtpy/QtQuickWidgets.py +++ b/qtpy/QtQuickWidgets.py @@ -4,9 +4,9 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtQuickWidgets classes and functions.""" -# Local imports from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: diff --git a/qtpy/QtRemoteObjects.py b/qtpy/QtRemoteObjects.py index d1515c57..c1ec2e6f 100644 --- a/qtpy/QtRemoteObjects.py +++ b/qtpy/QtRemoteObjects.py @@ -4,9 +4,9 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtRemoteObjects classes and functions.""" -# Local imports from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: diff --git a/qtpy/QtScxml.py b/qtpy/QtScxml.py index 1786ad30..883014d6 100644 --- a/qtpy/QtScxml.py +++ b/qtpy/QtScxml.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtScxml classes and functions. -""" +"""Provides QtScxml classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtSensors.py b/qtpy/QtSensors.py index 96ac1b12..a8c82e62 100644 --- a/qtpy/QtSensors.py +++ b/qtpy/QtSensors.py @@ -4,9 +4,9 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtSensors classes and functions.""" -# Local imports from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: diff --git a/qtpy/QtSerialPort.py b/qtpy/QtSerialPort.py index 426cb8f5..26b4710f 100644 --- a/qtpy/QtSerialPort.py +++ b/qtpy/QtSerialPort.py @@ -5,9 +5,9 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtSerialPort classes and functions.""" -# Local imports from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 038c34db..b4cf71db 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -4,9 +4,9 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtSql classes and functions.""" -# Local imports from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError if PYQT5: @@ -26,4 +26,3 @@ from PySide2.QtSql import * else: raise PythonQtError('No Qt bindings could be found') - diff --git a/qtpy/QtStateMachine.py b/qtpy/QtStateMachine.py index 1399361c..417f923d 100644 --- a/qtpy/QtStateMachine.py +++ b/qtpy/QtStateMachine.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtStateMachine classes and functions. -""" +"""Provides QtStateMachine classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index 4e7733b9..45326857 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -4,6 +4,7 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtSvg classes and functions.""" # Local imports @@ -19,4 +20,3 @@ from PySide2.QtSvg import * else: raise PythonQtError('No Qt bindings could be found') - diff --git a/qtpy/QtSvgWidgets.py b/qtpy/QtSvgWidgets.py index 49b6e06f..40026c0e 100644 --- a/qtpy/QtSvgWidgets.py +++ b/qtpy/QtSvgWidgets.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtSvgWidgets classes and functions. -""" +"""Provides QtSvgWidgets classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 557d1c74..40451199 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -1,13 +1,12 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Developmet Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- -""" -Provides QtTest and functions -""" +"""Provides QtTest and functions""" from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError diff --git a/qtpy/QtTextToSpeech.py b/qtpy/QtTextToSpeech.py index 96d48f4a..4aa79430 100644 --- a/qtpy/QtTextToSpeech.py +++ b/qtpy/QtTextToSpeech.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtTextToSpeech classes and functions. -""" +"""Provides QtTextToSpeech classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtUiTools.py b/qtpy/QtUiTools.py index b31e2f09..0347f2f4 100644 --- a/qtpy/QtUiTools.py +++ b/qtpy/QtUiTools.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtUiTools classes and functions. -""" +"""Provides QtUiTools classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtWebChannel.py b/qtpy/QtWebChannel.py index efd402c8..4f96b8c4 100644 --- a/qtpy/QtWebChannel.py +++ b/qtpy/QtWebChannel.py @@ -4,6 +4,7 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtWebChannel classes and functions.""" # Local imports diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index 9bd57ecd..9d026a65 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -6,9 +6,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtWebEngine classes and functions. -""" +"""Provides QtWebEngine classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py index 88b64c92..d3a8d7fd 100644 --- a/qtpy/QtWebEngineCore.py +++ b/qtpy/QtWebEngineCore.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtWebEngineCore classes and functions. -""" +"""Provides QtWebEngineCore classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/QtWebEngineQuick.py b/qtpy/QtWebEngineQuick.py index 4e3ab021..d154e7cd 100644 --- a/qtpy/QtWebEngineQuick.py +++ b/qtpy/QtWebEngineQuick.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtWebEngineQuick classes and functions. -""" +"""Provides QtWebEngineQuick classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 0ef4161d..8952c0bc 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -1,13 +1,12 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- -""" -Provides QtWebEngineWidgets classes and functions. -""" +"""Provides QtWebEngineWidgets classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME diff --git a/qtpy/QtWebSockets.py b/qtpy/QtWebSockets.py index 63d4b373..8a3b7147 100644 --- a/qtpy/QtWebSockets.py +++ b/qtpy/QtWebSockets.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtWebSockets classes and functions. -""" +"""Provides QtWebSockets classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 70996ae7..0cfd7c54 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -1,13 +1,13 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy # Copyright © 2009- The Spyder Developmet Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Provides widget classes and functions.""" -""" -Provides widget classes and functions. -""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index 75b87488..1d86821b 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides Windows-specific utilities -""" +"""Provides Windows-specific utilities""" import sys from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtX11Extras.py b/qtpy/QtX11Extras.py index 376ef73a..2f3cd6a2 100644 --- a/qtpy/QtX11Extras.py +++ b/qtpy/QtX11Extras.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides Linux-specific utilities -""" +"""Provides Linux-specific utilities""" import sys from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError diff --git a/qtpy/QtXml.py b/qtpy/QtXml.py index 3c160e60..d94963f7 100644 --- a/qtpy/QtXml.py +++ b/qtpy/QtXml.py @@ -4,6 +4,7 @@ # Licensed under the terms of the MIT License # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- + """Provides QtXml classes and functions.""" # Local imports diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py index eaf37d14..e71a567a 100644 --- a/qtpy/QtXmlPatterns.py +++ b/qtpy/QtXmlPatterns.py @@ -5,9 +5,7 @@ # (see LICENSE.txt for details) # ----------------------------------------------------------------------------- -""" -Provides QtXmlPatterns classes and functions. -""" +"""Provides QtXmlPatterns classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError From 34d280d887c4569d84492101acfbe85fa7992239 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Thu, 19 May 2022 01:44:11 -0400 Subject: [PATCH 429/703] Standardize import and API branch order --- qtpy/QtCore.py | 70 ++++++++++++++++++++++---------------------- qtpy/QtGui.py | 7 ++--- qtpy/QtSvg.py | 13 ++++---- qtpy/QtTest.py | 10 +++---- qtpy/QtWebChannel.py | 7 ++--- qtpy/QtWidgets.py | 11 ++++--- qtpy/QtXml.py | 7 ++--- 7 files changed, 60 insertions(+), 65 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 45087018..47989e9b 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -10,8 +10,25 @@ from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError +if PYQT5: + from PyQt5.QtCore import * + from PyQt5.QtCore import pyqtSignal as Signal + from PyQt5.QtCore import pyqtBoundSignal as SignalInstance + from PyQt5.QtCore import pyqtSlot as Slot + from PyQt5.QtCore import pyqtProperty as Property + from PyQt5.QtCore import QT_VERSION_STR as __version__ -if PYQT6: + # For issue #153 and updated for issue #305 + from PyQt5.QtCore import QDateTime + QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) + + # Map missing methods on PyQt5 5.12 + QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + + # Those are imported from `import *` + del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR + +elif PYQT6: from PyQt6 import QtCore from PyQt6.QtCore import * from PyQt6.QtCore import pyqtSignal as Signal @@ -55,23 +72,26 @@ # Alias for MiddleButton removed in PyQt6 but available in PyQt5, PySide2 and PySide6 Qt.MidButton = Qt.MiddleButton -elif PYQT5: - from PyQt5.QtCore import * - from PyQt5.QtCore import pyqtSignal as Signal - from PyQt5.QtCore import pyqtBoundSignal as SignalInstance - from PyQt5.QtCore import pyqtSlot as Slot - from PyQt5.QtCore import pyqtProperty as Property - from PyQt5.QtCore import QT_VERSION_STR as __version__ +elif PYSIDE2: + from PySide2.QtCore import * - # For issue #153 and updated for issue #305 - from PyQt5.QtCore import QDateTime - QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) + try: # may be limited to PySide-5.11a1 only + from PySide2.QtGui import QStringListModel + except Exception: + pass - # Map missing methods on PyQt5 5.12 - QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + import PySide2.QtCore + __version__ = PySide2.QtCore.__version__ - # Those are imported from `import *` - del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR + # Missing QtGui utility functions on Qt + if getattr(Qt, 'mightBeRichText', None) is None: + try: + from PySide2.QtGui import Qt as guiQt + Qt.mightBeRichText = guiQt.mightBeRichText + del guiQt + except ImportError: + # Fails with PySide2 5.12.0 + pass elif PYSIDE6: from PySide6.QtCore import * @@ -95,25 +115,5 @@ QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) -elif PYSIDE2: - from PySide2.QtCore import * - - try: # may be limited to PySide-5.11a1 only - from PySide2.QtGui import QStringListModel - except Exception: - pass - - import PySide2.QtCore - __version__ = PySide2.QtCore.__version__ - - # Missing QtGui utility functions on Qt - if getattr(Qt, 'mightBeRichText', None) is None: - try: - from PySide2.QtGui import Qt as guiQt - Qt.mightBeRichText = guiQt.mightBeRichText - del guiQt - except ImportError: - # Fails with PySide2 5.12.0 - pass else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 73d96a5b..98c2a33e 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -10,8 +10,9 @@ from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError - -if PYQT6: +if PYQT5: + from PyQt5.QtGui import * +elif PYQT6: from PyQt6 import QtGui from PyQt6.QtGui import * from PyQt6.QtOpenGL import * @@ -26,8 +27,6 @@ from .enums_compat import promote_enums promote_enums(QtGui) del QtGui -elif PYQT5: - from PyQt5.QtGui import * elif PYSIDE2: from PySide2.QtGui import * if hasattr(QFontMetrics, 'horizontalAdvance'): diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index 45326857..a259d67c 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -7,16 +7,15 @@ """Provides QtSvg classes and functions.""" -# Local imports -from . import PYSIDE6, PYSIDE2, PYQT5, PYQT6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError -if PYQT6: - from PyQt6.QtSvg import * -elif PYQT5: +if PYQT5: from PyQt5.QtSvg import * -elif PYSIDE6: - from PySide6.QtSvg import * +elif PYQT6: + from PyQt6.QtSvg import * elif PYSIDE2: from PySide2.QtSvg import * +elif PYSIDE6: + from PySide6.QtSvg import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 40451199..40f5b178 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -10,7 +10,9 @@ from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError -if PYQT6: +if PYQT5: + from PyQt5.QtTest import * +elif PYQT6: from PyQt6 import QtTest from PyQt6.QtTest import * @@ -18,11 +20,9 @@ from .enums_compat import promote_enums promote_enums(QtTest) del QtTest -elif PYQT5: - from PyQt5.QtTest import * -elif PYSIDE6: - from PySide6.QtTest import * elif PYSIDE2: from PySide2.QtTest import * +elif PYSIDE6: + from PySide6.QtTest import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWebChannel.py b/qtpy/QtWebChannel.py index 4f96b8c4..5f7f6734 100644 --- a/qtpy/QtWebChannel.py +++ b/qtpy/QtWebChannel.py @@ -7,16 +7,15 @@ """Provides QtWebChannel classes and functions.""" -# Local imports -from . import PYSIDE2, PYSIDE6, PYQT5, PYQT6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtWebChannel import * elif PYQT6: from PyQt6.QtWebChannel import * -elif PYSIDE6: - from PySide6.QtWebChannel import * elif PYSIDE2: from PySide2.QtWebChannel import * +elif PYSIDE6: + from PySide6.QtWebChannel import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 0cfd7c54..ce69dcad 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -10,8 +10,9 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError - -if PYQT6: +if PYQT5: + from PyQt5.QtWidgets import * +elif PYQT6: from PyQt6 import QtWidgets from PyQt6.QtWidgets import * from PyQt6.QtGui import QAction, QActionGroup, QShortcut, QFileSystemModel @@ -32,8 +33,8 @@ from .enums_compat import promote_enums promote_enums(QtWidgets) del QtWidgets -elif PYQT5: - from PyQt5.QtWidgets import * +elif PYSIDE2: + from PySide2.QtWidgets import * elif PYSIDE6: from PySide6.QtWidgets import * from PySide6.QtGui import QAction, QActionGroup, QShortcut @@ -49,7 +50,5 @@ QApplication.exec_ = QApplication.exec QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) -elif PYSIDE2: - from PySide2.QtWidgets import * else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtXml.py b/qtpy/QtXml.py index d94963f7..7f90491c 100644 --- a/qtpy/QtXml.py +++ b/qtpy/QtXml.py @@ -7,16 +7,15 @@ """Provides QtXml classes and functions.""" -# Local imports -from . import PYSIDE2, PYSIDE6, PYQT5, PYQT6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError if PYQT5: from PyQt5.QtXml import * elif PYQT6: from PyQt6.QtXml import * -elif PYSIDE6: - from PySide6.QtXml import * elif PYSIDE2: from PySide2.QtXml import * +elif PYSIDE6: + from PySide6.QtXml import * else: raise PythonQtError('No Qt bindings could be found') From afe2ae656429341255e7b68011675170c3fcedaa Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Thu, 19 May 2022 09:09:19 -0400 Subject: [PATCH 430/703] Make Qt errors automatically determine binding and implement remaining instances of QtBindingMissingModuleError --- qtpy/Qt3DAnimation.py | 6 +++--- qtpy/Qt3DCore.py | 6 +++--- qtpy/Qt3DExtras.py | 6 +++--- qtpy/Qt3DInput.py | 6 +++--- qtpy/Qt3DLogic.py | 6 +++--- qtpy/Qt3DRender.py | 6 +++--- qtpy/QtAxContainer.py | 6 +++--- qtpy/QtBluetooth.py | 4 ++-- qtpy/QtCharts.py | 6 +++--- qtpy/QtConcurrent.py | 6 +++--- qtpy/QtDBus.py | 4 ++-- qtpy/QtDataVisualization.py | 6 +++--- qtpy/QtDesigner.py | 4 ++-- qtpy/QtLocation.py | 6 +++--- qtpy/QtNetworkAuth.py | 8 ++++---- qtpy/QtNfc.py | 4 ++-- qtpy/QtOpenGLWidgets.py | 6 +++--- qtpy/QtPurchasing.py | 8 ++++---- qtpy/QtQuick3D.py | 4 ++-- qtpy/QtQuickControls2.py | 6 +++--- qtpy/QtScxml.py | 6 +++--- qtpy/QtStateMachine.py | 8 ++++---- qtpy/QtSvgWidgets.py | 6 +++--- qtpy/QtTextToSpeech.py | 6 +++--- qtpy/QtUiTools.py | 6 +++--- qtpy/QtWebEngine.py | 4 ++-- qtpy/QtWebEngineCore.py | 6 +++--- qtpy/QtWebEngineQuick.py | 8 ++++---- qtpy/QtWebEngineWidgets.py | 6 +++--- qtpy/QtXmlPatterns.py | 6 +++--- qtpy/__init__.py | 10 +++++----- 31 files changed, 93 insertions(+), 93 deletions(-) diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index e1ed07c4..fa591e27 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -7,21 +7,21 @@ """Provides Qt3DAnimation classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: from PyQt5.Qt3DAnimation import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DAnimation', binding=API_NAME, missing_package='PyQt3D' + name='Qt3DAnimation', missing_package='PyQt3D' ) from error elif PYQT6: try: from PyQt6.Qt3DAnimation import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DAnimation', binding=API_NAME, missing_package='PyQt6-3D' + name='Qt3DAnimation', missing_package='PyQt6-3D' ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 39b6c546..49d140e3 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -7,21 +7,21 @@ """Provides Qt3DCore classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: from PyQt5.Qt3DCore import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DCore', binding=API_NAME, missing_package='PyQt3D' + name='Qt3DCore', missing_package='PyQt3D' ) from error elif PYQT6: try: from PyQt6.Qt3DCore import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DCore', binding=API_NAME, missing_package='PyQt6-3D' + name='Qt3DCore', missing_package='PyQt6-3D' ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index ea835788..fe7fd95b 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -7,21 +7,21 @@ """Provides Qt3DExtras classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: from PyQt5.Qt3DExtras import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DExtras', binding=API_NAME, missing_package='PyQt3D' + name='Qt3DExtras', missing_package='PyQt3D' ) from error elif PYQT6: try: from PyQt6.Qt3DExtras import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DExtras', binding=API_NAME, missing_package='PyQt6-3D' + name='Qt3DExtras', missing_package='PyQt6-3D' ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 09bc3a0e..ba378860 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -7,21 +7,21 @@ """Provides Qt3DInput classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: from PyQt5.Qt3DInput import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DInput', binding=API_NAME, missing_package='PyQt3D' + name='Qt3DInput', missing_package='PyQt3D' ) from error elif PYQT6: try: from PyQt6.Qt3DInput import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DInput', binding=API_NAME, missing_package='PyQt6-3D' + name='Qt3DInput', missing_package='PyQt6-3D' ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index 6e14aec1..a3eaf38b 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -7,21 +7,21 @@ """Provides Qt3DLogic classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: from PyQt5.Qt3DLogic import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DLogic', binding=API_NAME, missing_package='PyQt3D' + name='Qt3DLogic', missing_package='PyQt3D' ) from error elif PYQT6: try: from PyQt6.Qt3DLogic import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DLogic', binding=API_NAME, missing_package='PyQt6-3D' + name='Qt3DLogic', missing_package='PyQt6-3D' ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index f4622b78..2b1744ac 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -7,21 +7,21 @@ """Provides Qt3DRender classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: from PyQt5.Qt3DRender import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DRender', binding=API_NAME, missing_package='PyQt3D' + name='Qt3DRender', missing_package='PyQt3D' ) from error elif PYQT6: try: from PyQt6.Qt3DRender import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DRender', binding=API_NAME, missing_package='PyQt6-3D' + name='Qt3DRender', missing_package='PyQt6-3D' ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py index 53a61de2..9f3f4a78 100644 --- a/qtpy/QtAxContainer.py +++ b/qtpy/QtAxContainer.py @@ -7,12 +7,12 @@ """Provides QtAxContainer classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: - raise QtBindingMissingModuleError(name='QtAxContainer', binding=API_NAME) + raise QtBindingMissingModuleError(name='QtAxContainer') elif PYQT6: - raise QtBindingMissingModuleError(name='QtAxContainer', binding=API_NAME) + raise QtBindingMissingModuleError(name='QtAxContainer') elif PYSIDE2: from PySide2.QtAxContainer import * elif PYSIDE6: diff --git a/qtpy/QtBluetooth.py b/qtpy/QtBluetooth.py index 926fd76f..f78ef0e9 100644 --- a/qtpy/QtBluetooth.py +++ b/qtpy/QtBluetooth.py @@ -7,14 +7,14 @@ """Provides QtBluetooth classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtBluetooth import * elif PYQT6: from PyQt6.QtBluetooth import * elif PYSIDE2: - raise PythonQtError('QtBluetooth not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtBluetooth') elif PYSIDE6: from PySide6.QtBluetooth import * else: diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index f1bfa01b..7815eaf0 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -7,7 +7,7 @@ """Provides QtChart classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: @@ -15,7 +15,7 @@ from PyQt5 import QtChart as QtCharts except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DCharts', binding=API_NAME, missing_package='PyQtChart' + name='Qt3DCharts', missing_package='PyQtChart' ) from error elif PYQT6: try: @@ -23,7 +23,7 @@ from PyQt6 import QtCharts except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DCharts', binding=API_NAME, missing_package='PyQt6-Charts' + name='Qt3DCharts', missing_package='PyQt6-Charts' ) from error elif PYSIDE2: from PySide2.QtCharts import * diff --git a/qtpy/QtConcurrent.py b/qtpy/QtConcurrent.py index 56ff1e80..ea326202 100644 --- a/qtpy/QtConcurrent.py +++ b/qtpy/QtConcurrent.py @@ -7,12 +7,12 @@ """Provides QtConcurrent classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: - raise PythonQtError('QtConcurrent not implemented in PyQt5') + raise QtBindingMissingModuleError(name='QtConcurrent') elif PYQT6: - raise PythonQtError('QtConcurrent not implemented in PyQt6') + raise QtBindingMissingModuleError(name='QtConcurrent') elif PYSIDE2: from PySide2.QtConcurrent import * elif PYSIDE6: diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index 140e4007..89cbb6ba 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -8,7 +8,7 @@ """Provides QtDBus classes and functions.""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if sys.platform == 'linux': if PYQT5: @@ -16,7 +16,7 @@ elif PYQT6: from PyQt6.QtDBus import * elif PYSIDE2: - raise PythonQtError('QtDBus not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtDBus') elif PYSIDE6: from PySide6.QtDBus import * else: diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index d04e3e0f..60828b9b 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -7,21 +7,21 @@ """Provides QtDataVisualization classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: from PyQt5.QtDataVisualization import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtDataVisualization', binding=API_NAME, missing_package='PyQtDataVisualization' + name='QtDataVisualization', missing_package='PyQtDataVisualization' ) from error elif PYQT6: try: from PyQt6.QtDataVisualization import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtDataVisualization', binding=API_NAME, missing_package='PyQt6-DataVisualization' + name='QtDataVisualization', missing_package='PyQt6-DataVisualization' ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index e3980fb9..04096659 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -7,14 +7,14 @@ """Provides QtDesigner classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtDesigner import * elif PYQT6: from PyQt6.QtDesigner import * elif PYSIDE2: - raise PythonQtError('QtDesigner not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtDesigner') elif PYSIDE6: from PySide6.QtDesigner import * else: diff --git a/qtpy/QtLocation.py b/qtpy/QtLocation.py index 8363c4c0..20b76c42 100644 --- a/qtpy/QtLocation.py +++ b/qtpy/QtLocation.py @@ -7,15 +7,15 @@ """Provides QtLocation classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtLocation import * elif PYQT6: - raise PythonQtError('QtLocation not implemented in PyQt6') + raise QtBindingMissingModuleError(name='QtLocation') elif PYSIDE2: from PySide2.QtLocation import * elif PYSIDE6: - raise PythonQtError('QtLocation not implemented in PySide6') + raise QtBindingMissingModuleError(name='QtLocation') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py index 28e324e0..cc8b1aec 100644 --- a/qtpy/QtNetworkAuth.py +++ b/qtpy/QtNetworkAuth.py @@ -7,24 +7,24 @@ """Provides QtNetworkAuth classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, QtBindingMissingModuleError if PYQT5: try: from PyQt5.QtNetworkAuth import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtNetworkAuth', binding=API_NAME, missing_package='PyQtNetworkAuth' + name='QtNetworkAuth', missing_package='PyQtNetworkAuth' ) from error elif PYQT6: try: from PyQt6.QtNetworkAuth import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtNetworkAuth', binding=API_NAME, missing_package='PyQt6-NetworkAuth' + name='QtNetworkAuth', missing_package='PyQt6-NetworkAuth' ) from error elif PYSIDE2: - raise PythonQtError('QtNetworkAuth not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtNetworkAuth') elif PYSIDE6: from PySide6.QtNetworkAuth import * else: diff --git a/qtpy/QtNfc.py b/qtpy/QtNfc.py index 8eb32f7e..54156de3 100644 --- a/qtpy/QtNfc.py +++ b/qtpy/QtNfc.py @@ -7,14 +7,14 @@ """Provides QtNfc classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtNfc import * elif PYQT6: from PyQt6.QtNfc import * elif PYSIDE2: - raise PythonQtError('QtNfc not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtNfc') elif PYSIDE6: from PySide6.QtNfc import * else: diff --git a/qtpy/QtOpenGLWidgets.py b/qtpy/QtOpenGLWidgets.py index 2d5624b7..cda3ac5d 100644 --- a/qtpy/QtOpenGLWidgets.py +++ b/qtpy/QtOpenGLWidgets.py @@ -7,14 +7,14 @@ """Provides QtOpenGLWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: - raise PythonQtError('QtTextToSpeech not implemented in PyQt5') + raise QtBindingMissingModuleError(name='QtTextToSpeech') elif PYQT6: from PyQt6.QtOpenGLWidgets import * elif PYSIDE2: - raise PythonQtError('QtTextToSpeech not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtTextToSpeech') elif PYSIDE6: from PySide6.QtOpenGLWidgets import * else: diff --git a/qtpy/QtPurchasing.py b/qtpy/QtPurchasing.py index 950cb4b3..cba7c8a2 100644 --- a/qtpy/QtPurchasing.py +++ b/qtpy/QtPurchasing.py @@ -7,15 +7,15 @@ """Provides QtPurchasing classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtQml import * elif PYQT6: - raise PythonQtError('QtPurchasing not implemented in PyQt6') + raise QtBindingMissingModuleError(name='QtPurchasing') elif PYSIDE2: - raise PythonQtError('QtPurchasing not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtPurchasing') elif PYSIDE6: - raise PythonQtError('QtPurchasing not implemented in PySide6') + raise QtBindingMissingModuleError(name='QtPurchasing') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtQuick3D.py b/qtpy/QtQuick3D.py index 25965ff2..fc7375c0 100644 --- a/qtpy/QtQuick3D.py +++ b/qtpy/QtQuick3D.py @@ -7,14 +7,14 @@ """Provides QtQuick3D classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtQuick3D import * elif PYQT6: from PyQt6.QtQuick3D import * elif PYSIDE2: - raise PythonQtError('QtQuick3D not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtQuick3D') elif PYSIDE6: from PySide6.QtQuick3D import * else: diff --git a/qtpy/QtQuickControls2.py b/qtpy/QtQuickControls2.py index 2e049c13..7e71e7d7 100644 --- a/qtpy/QtQuickControls2.py +++ b/qtpy/QtQuickControls2.py @@ -7,12 +7,12 @@ """Provides QtQuickControls2 classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: - raise PythonQtError('QtQuickControls2 not implemented in PyQt5') + raise QtBindingMissingModuleError(name='QtQuickControls2') elif PYQT6: - raise PythonQtError('QtQuickControls2 not implemented in PyQt6') + raise QtBindingMissingModuleError(name='QtQuickControls2') elif PYSIDE2: from PySide2.QtQuickControls2 import * elif PYSIDE6: diff --git a/qtpy/QtScxml.py b/qtpy/QtScxml.py index 883014d6..bb82be7c 100644 --- a/qtpy/QtScxml.py +++ b/qtpy/QtScxml.py @@ -7,12 +7,12 @@ """Provides QtScxml classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: - raise PythonQtError('QtScxml not implemented in PyQt5') + raise QtBindingMissingModuleError(name='QtScxml') elif PYQT6: - raise PythonQtError('QtScxml not implemented in PyQt6') + raise QtBindingMissingModuleError(name='QtScxml') elif PYSIDE2: from PySide2.QtScxml import * elif PYSIDE6: diff --git a/qtpy/QtStateMachine.py b/qtpy/QtStateMachine.py index 417f923d..51ea5866 100644 --- a/qtpy/QtStateMachine.py +++ b/qtpy/QtStateMachine.py @@ -7,14 +7,14 @@ """Provides QtStateMachine classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: - raise PythonQtError('QtStateMachine not implemented in PyQt5') + raise QtBindingMissingModuleError(name='QtStateMachine') elif PYQT6: - raise PythonQtError('QtStateMachine not implemented in PyQt6') + raise QtBindingMissingModuleError(name='QtStateMachine') elif PYSIDE2: - raise PythonQtError('QtStateMachine not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtStateMachine') elif PYSIDE6: from PySide6.QtStateMachine import * else: diff --git a/qtpy/QtSvgWidgets.py b/qtpy/QtSvgWidgets.py index 40026c0e..ef5bc269 100644 --- a/qtpy/QtSvgWidgets.py +++ b/qtpy/QtSvgWidgets.py @@ -7,14 +7,14 @@ """Provides QtSvgWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: - raise PythonQtError('QtSvgWidgets not implemented in PyQt5') + raise QtBindingMissingModuleError(name='QtSvgWidgets') elif PYQT6: from PyQt6.QtSvgWidgets import * elif PYSIDE2: - raise PythonQtError('QtSvgWidgets not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtSvgWidgets') elif PYSIDE6: from PySide6.QtSvgWidgets import * else: diff --git a/qtpy/QtTextToSpeech.py b/qtpy/QtTextToSpeech.py index 4aa79430..10ee85b5 100644 --- a/qtpy/QtTextToSpeech.py +++ b/qtpy/QtTextToSpeech.py @@ -7,15 +7,15 @@ """Provides QtTextToSpeech classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtTextToSpeech import * elif PYQT6: - raise PythonQtError('QtTextToSpeech not implemented in PyQt6') + raise QtBindingMissingModuleError(name='QtTextToSpeech') elif PYSIDE2: from PySide2.QtTextToSpeech import * elif PYSIDE6: - raise PythonQtError('QtTextToSpeech not implemented in PySide6') + raise QtBindingMissingModuleError(name='QtTextToSpeech') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/QtUiTools.py b/qtpy/QtUiTools.py index 0347f2f4..9f1a3d1d 100644 --- a/qtpy/QtUiTools.py +++ b/qtpy/QtUiTools.py @@ -7,12 +7,12 @@ """Provides QtUiTools classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: - raise PythonQtError('QtUiTools not implemented in PyQt5') + raise QtBindingMissingModuleError(name='QtUiTools') elif PYQT6: - raise PythonQtError('QtUiTools not implemented in PyQt6') + raise QtBindingMissingModuleError(name='QtUiTools') elif PYSIDE2: from PySide2.QtUiTools import * elif PYSIDE6: diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index 9d026a65..43e18edc 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -8,14 +8,14 @@ """Provides QtWebEngine classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: from PyQt5.QtWebEngine import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngine', binding=API_NAME, missing_package='PyQtWebEngine' + name='QtWebEngine', missing_package='PyQtWebEngine' ) from error elif PYQT6: raise PythonQtError('QtWebEngine does not exist in Qt6') diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py index d3a8d7fd..4f25defc 100644 --- a/qtpy/QtWebEngineCore.py +++ b/qtpy/QtWebEngineCore.py @@ -7,21 +7,21 @@ """Provides QtWebEngineCore classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: from PyQt5.QtWebEngineCore import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineCore', binding=API_NAME, missing_package='PyQtWebEngine' + name='QtWebEngineCore', missing_package='PyQtWebEngine' ) from error elif PYQT6: try: from PyQt6.QtWebEngineCore import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineCore', binding=API_NAME, missing_package='PyQt6-WebEngine' + name='QtWebEngineCore', missing_package='PyQt6-WebEngine' ) from error elif PYSIDE2: from PySide2.QtWebEngineCore import * diff --git a/qtpy/QtWebEngineQuick.py b/qtpy/QtWebEngineQuick.py index d154e7cd..19b4a14e 100644 --- a/qtpy/QtWebEngineQuick.py +++ b/qtpy/QtWebEngineQuick.py @@ -7,19 +7,19 @@ """Provides QtWebEngineQuick classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, QtBindingMissingModuleError if PYQT5: - raise PythonQtError('QtWebEngineQuick not implemented in PyQt5') + raise QtBindingMissingModuleError(name='QtWebEngineQuick') elif PYQT6: try: from PyQt6.QtWebEngineQuick import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineQuick', binding=API_NAME, missing_package='PyQt6-WebEngine' + name='QtWebEngineQuick', missing_package='PyQt6-WebEngine' ) from error elif PYSIDE2: - raise PythonQtError('QtWebEngineQuick not implemented in PySide2') + raise QtBindingMissingModuleError(name='QtWebEngineQuick') elif PYSIDE6: from PySide6.QtWebEngineQuick import * else: diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 8952c0bc..149875c0 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -8,7 +8,7 @@ """Provides QtWebEngineWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, API_NAME +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError if PYQT5: try: @@ -20,7 +20,7 @@ from PyQt5.QtWebEngineWidgets import QWebEngineProfile except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineWidgets', binding=API_NAME, missing_package='PyQtWebEngine' + name='QtWebEngineWidgets', missing_package='PyQtWebEngine' ) from error elif PYQT6: try: @@ -31,7 +31,7 @@ from PyQt6.QtWebEngineCore import QWebEngineScript except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtWebEngineWidgets', binding=API_NAME, missing_package='PyQt6-WebEngine' + name='QtWebEngineWidgets', missing_package='PyQt6-WebEngine' ) from error elif PYSIDE2: from PySide2.QtWebEngineWidgets import QWebEnginePage diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py index e71a567a..0f991637 100644 --- a/qtpy/QtXmlPatterns.py +++ b/qtpy/QtXmlPatterns.py @@ -7,15 +7,15 @@ """Provides QtXmlPatterns classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtXmlPatterns import * elif PYQT6: - raise PythonQtError('QtXmlPatterns does not exist in Qt6') + raise QtBindingMissingModuleError(name='QtXmlPatterns') elif PYSIDE2: from PySide2.QtXmlPatterns import * elif PYSIDE6: - raise PythonQtError('QtXmlPatterns does not exist in Qt6') + raise QtBindingMissingModuleError(name='QtXmlPatterns') else: raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/__init__.py b/qtpy/__init__.py index d645cb5b..917ca4fb 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -78,13 +78,13 @@ class PythonQtValueError(ValueError): class QtModuleNotFoundError(ModuleNotFoundError, PythonQtError): """Raised when a Python Qt binding submodule is not installed/supported.""" - _msg = 'The {name} module was not found{binding}.' - _msg_binding = ' for {binding}' + _msg = 'The {name} module was not found.' + _msg_binding = '{binding}' _msg_extra = '' - def __init__(self, *, name, binding=None, msg=None, **msg_kwargs): - self.binding = binding - binding = self._msg_binding.format(binding=binding) if binding else '' + def __init__(self, *, name, msg=None, **msg_kwargs): + global API_NAME + binding = self._msg_binding.format(binding=API_NAME) msg = msg or f'{self._msg} {self._msg_extra}'.strip() msg = msg.format(name=name, binding=binding, **msg_kwargs) super().__init__(msg, name=name) From 67874c718b24dac2b16845a93f7da658605dcb65 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Thu, 19 May 2022 09:11:19 -0400 Subject: [PATCH 431/703] Add QtBindingsNotFoundError to potentially replace most common PythonQtError use --- qtpy/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 917ca4fb..9b135a68 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -76,6 +76,14 @@ class PythonQtValueError(ValueError): """Error raised if an invalid QT_API is specified.""" +class QtBindingsNotFoundError(PythonQtError): + """Error raised if no bindings could be selected.""" + _msg = 'No Qt bindings could be found' + + def __init__(self): + super().__init__(_msg) + + class QtModuleNotFoundError(ModuleNotFoundError, PythonQtError): """Raised when a Python Qt binding submodule is not installed/supported.""" _msg = 'The {name} module was not found.' From b41c5b8b55edc32be716d29d6aa76cd83b7a9003 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Thu, 19 May 2022 09:29:34 -0400 Subject: [PATCH 432/703] Fix QtDBus import conditions, hopefully --- qtpy/QtDBus.py | 22 +++++++++++----------- qtpy/tests/test_qtdbus.py | 6 +++--- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index 89cbb6ba..7d8a31e5 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -8,18 +8,18 @@ """Provides QtDBus classes and functions.""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError, QtModuleNotFoundError -if sys.platform == 'linux': - if PYQT5: - from PyQt5.QtDBus import * - elif PYQT6: - from PyQt6.QtDBus import * - elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtDBus') - elif PYSIDE6: +if PYQT5: + from PyQt5.QtDBus import * +elif PYQT6: + from PyQt6.QtDBus import * +elif PYSIDE2: + raise QtBindingMissingModuleError(name='QtDBus') +elif PYSIDE6: + if sys.platform != 'win32': from PySide6.QtDBus import * else: - raise PythonQtError("No Qt bindings could be found") + raise QtModuleNotFoundError(name='QtDBus', msg='{name} does not exist on this operating system.') else: - raise PythonQtError('QtDBus does not exist on this operating system') \ No newline at end of file + raise QtBindingsNotFoundError() \ No newline at end of file diff --git a/qtpy/tests/test_qtdbus.py b/qtpy/tests/test_qtdbus.py index fd32d707..40fdef69 100644 --- a/qtpy/tests/test_qtdbus.py +++ b/qtpy/tests/test_qtdbus.py @@ -2,11 +2,11 @@ import sys from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 -@pytest.mark.skipif(PYSIDE2, reason="Not available in PySide2") -@pytest.mark.skipif(sys.platform != 'linux', reason="Only available on Linux") +# @pytest.mark.skipif(PYSIDE2, reason="Not available in PySide2") +# @pytest.mark.skipif(sys.platform != 'linux', reason="Only available on Linux") def test_qtdbus(): """Test the qtpy.QtDBus namespace""" - from qtpy import QtDBus + QtDBus = pytest.importorskip("qtpy.QtDBus") assert QtDBus.QDBusAbstractAdaptor is not None assert QtDBus.QDBusAbstractInterface is not None From 8421052491b1bf6378f62230991dc4d9713c9d9f Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sat, 28 May 2022 13:16:04 -0400 Subject: [PATCH 433/703] Fix QtPurchasing importing the wrong module --- qtpy/QtPurchasing.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/qtpy/QtPurchasing.py b/qtpy/QtPurchasing.py index cba7c8a2..4c397ba3 100644 --- a/qtpy/QtPurchasing.py +++ b/qtpy/QtPurchasing.py @@ -7,10 +7,15 @@ """Provides QtPurchasing classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError, QtModuleNotInstalledError if PYQT5: - from PyQt5.QtQml import * + try: + from PyQt5.QtPurchasing import * + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError( + name='QtPurchasing', missing_package='PyQtPurchasing' + ) from error elif PYQT6: raise QtBindingMissingModuleError(name='QtPurchasing') elif PYSIDE2: From 0d404da439efcc4f9fd052e31bc129f6909126f9 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sat, 28 May 2022 13:16:13 -0400 Subject: [PATCH 434/703] Implement remaining tests --- qtpy/tests/test_qtbluetooth.py | 14 ++++++++++++++ qtpy/tests/test_qtconcurrent.py | 11 +++++++++++ qtpy/tests/test_qtmacextras.py | 17 +++++++++++++++++ qtpy/tests/test_qtpurchasing.py | 9 +++++++++ qtpy/tests/test_qtquick3d.py | 9 +++++++++ qtpy/tests/test_qtquickcontrols2.py | 7 +++++++ qtpy/tests/test_qtscxml.py | 9 +++++++++ qtpy/tests/test_qtstatemachine.py | 15 +++++++++++++++ qtpy/tests/test_qtsvgwidgets.py | 8 ++++++++ qtpy/tests/test_qtuitools.py | 8 ++++++++ qtpy/tests/test_qtwebenginequick.py | 5 +++-- 11 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 qtpy/tests/test_qtbluetooth.py create mode 100644 qtpy/tests/test_qtconcurrent.py create mode 100644 qtpy/tests/test_qtmacextras.py create mode 100644 qtpy/tests/test_qtpurchasing.py create mode 100644 qtpy/tests/test_qtquick3d.py create mode 100644 qtpy/tests/test_qtquickcontrols2.py create mode 100644 qtpy/tests/test_qtscxml.py create mode 100644 qtpy/tests/test_qtstatemachine.py create mode 100644 qtpy/tests/test_qtsvgwidgets.py create mode 100644 qtpy/tests/test_qtuitools.py diff --git a/qtpy/tests/test_qtbluetooth.py b/qtpy/tests/test_qtbluetooth.py new file mode 100644 index 00000000..84c3b002 --- /dev/null +++ b/qtpy/tests/test_qtbluetooth.py @@ -0,0 +1,14 @@ +import pytest +from qtpy import PYSIDE2 + +def test_qtbluetooth(): + """Test the qtpy.QtBluetooth namespace""" + QtBluetooth = pytest.importorskip("qtpy.QtBluetooth") + + assert QtBluetooth.QBluetooth is not None + assert QtBluetooth.QBluetoothDeviceInfo is not None + assert QtBluetooth.QBluetoothServer is not None + assert QtBluetooth.QBluetoothSocket is not None + assert QtBluetooth.QBluetoothAddress is not None + assert QtBluetooth.QBluetoothUuid is not None + assert QtBluetooth.QBluetoothServiceDiscoveryAgent is not None diff --git a/qtpy/tests/test_qtconcurrent.py b/qtpy/tests/test_qtconcurrent.py new file mode 100644 index 00000000..3d2b5342 --- /dev/null +++ b/qtpy/tests/test_qtconcurrent.py @@ -0,0 +1,11 @@ +import pytest + +def test_qtconcurrent(): + """Test the qtpy.QtConcurrent namespace""" + QtConcurrent = pytest.importorskip("qtpy.QtConcurrent") + + assert QtConcurrent.QtConcurrent is not None + assert QtConcurrent.QFutureQString is not None + assert QtConcurrent.QFutureVoid is not None + assert QtConcurrent.QFutureWatcherQString is not None + assert QtConcurrent.QFutureWatcherVoid is not None \ No newline at end of file diff --git a/qtpy/tests/test_qtmacextras.py b/qtpy/tests/test_qtmacextras.py new file mode 100644 index 00000000..65f1cdca --- /dev/null +++ b/qtpy/tests/test_qtmacextras.py @@ -0,0 +1,17 @@ +import pytest +import sys + +from qtpy import PYQT6, PYSIDE2, PYSIDE6 +from qtpy.tests.utils import using_conda + +@pytest.mark.skipif(PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") +@pytest.mark.skipif( + sys.platform != "darwin" or using_conda(), + reason="Only available in Qt5 bindings > 5.9 with pip on mac in CIs") +def test_qtmacextras(): + """Test the qtpy.QtMacExtras namespace""" + QtMacExtras = pytest.importorskip("qtpy.QtMacExtras") + + assert QtMacExtras.QMacPasteboardMime is not None + assert QtMacExtras.QMacToolBar is not None + assert QtMacExtras.QMacToolBarItem is not None diff --git a/qtpy/tests/test_qtpurchasing.py b/qtpy/tests/test_qtpurchasing.py new file mode 100644 index 00000000..55c86667 --- /dev/null +++ b/qtpy/tests/test_qtpurchasing.py @@ -0,0 +1,9 @@ +import pytest + +def test_qtpurchasing(): + """Test the qtpy.QtPurchasing namespace""" + QtPurchasing = pytest.importorskip("qtpy.QtPurchasing") + + assert QtPurchasing.QInAppProduct is not None + assert QtPurchasing.QInAppStore is not None + assert QtPurchasing.QInAppTransaction is not None \ No newline at end of file diff --git a/qtpy/tests/test_qtquick3d.py b/qtpy/tests/test_qtquick3d.py new file mode 100644 index 00000000..fca6d515 --- /dev/null +++ b/qtpy/tests/test_qtquick3d.py @@ -0,0 +1,9 @@ +import pytest + +def test_qtquick3d(): + """Test the qtpy.QtQuick3D namespace""" + QtQuick3D = pytest.importorskip("qtpy.QtQuick3D") + + assert QtQuick3D.QQuick3D is not None + assert QtQuick3D.QQuick3DGeometry is not None + assert QtQuick3D.QQuick3DObject is not None diff --git a/qtpy/tests/test_qtquickcontrols2.py b/qtpy/tests/test_qtquickcontrols2.py new file mode 100644 index 00000000..d87f45b4 --- /dev/null +++ b/qtpy/tests/test_qtquickcontrols2.py @@ -0,0 +1,7 @@ +import pytest + +def test_qtquickcontrols2(): + """Test the qtpy.QtQuickControls2 namespace""" + QtQuickControls2 = pytest.importorskip("qtpy.QtQuickControls2") + + assert QtQuickControls2.QQuickStyle is not None diff --git a/qtpy/tests/test_qtscxml.py b/qtpy/tests/test_qtscxml.py new file mode 100644 index 00000000..eb77931d --- /dev/null +++ b/qtpy/tests/test_qtscxml.py @@ -0,0 +1,9 @@ +import pytest + +def test_qtscxml(): + """Test the qtpy.QtScxml namespace""" + QtScxml = pytest.importorskip("qtpy.QtScxml") + + assert QtScxml.QScxmlCompiler is not None + assert QtScxml.QScxmlDynamicScxmlServiceFactory is not None + assert QtScxml.QScxmlExecutableContent is not None diff --git a/qtpy/tests/test_qtstatemachine.py b/qtpy/tests/test_qtstatemachine.py new file mode 100644 index 00000000..fc9b43f5 --- /dev/null +++ b/qtpy/tests/test_qtstatemachine.py @@ -0,0 +1,15 @@ +import pytest + +def test_qtstatemachine(): + """Test the qtpy.QtStateMachine namespace""" + QtStateMachine = pytest.importorskip("qtpy.QtStateMachine") + + assert QtStateMachine.QAbstractState is not None + assert QtStateMachine.QAbstractTransition is not None + assert QtStateMachine.QEventTransition is not None + assert QtStateMachine.QFinalState is not None + assert QtStateMachine.QHistoryState is not None + assert QtStateMachine.QKeyEventTransition is not None + assert QtStateMachine.QMouseEventTransition is not None + assert QtStateMachine.QSignalTransition is not None + assert QtStateMachine.QState is not None diff --git a/qtpy/tests/test_qtsvgwidgets.py b/qtpy/tests/test_qtsvgwidgets.py new file mode 100644 index 00000000..aa0d5c38 --- /dev/null +++ b/qtpy/tests/test_qtsvgwidgets.py @@ -0,0 +1,8 @@ +import pytest + +def test_qtsvgwidgets(): + """Test the qtpy.QtSvgWidgets namespace""" + QtSvgWidgets = pytest.importorskip("qtpy.QtSvgWidgets") + + assert QtSvgWidgets.QGraphicsSvgItem is not None + assert QtSvgWidgets.QSvgWidget is not None diff --git a/qtpy/tests/test_qtuitools.py b/qtpy/tests/test_qtuitools.py new file mode 100644 index 00000000..0ce085ea --- /dev/null +++ b/qtpy/tests/test_qtuitools.py @@ -0,0 +1,8 @@ +import pytest + +def test_qtuitools(): + """Test the qtpy.QtUiTools namespace""" + QtUiTools = pytest.importorskip("qtpy.QtUiTools") + + assert QtUiTools.QUiLoader is not None + assert QtUiTools.loadUiType is not None diff --git a/qtpy/tests/test_qtwebenginequick.py b/qtpy/tests/test_qtwebenginequick.py index 26074760..a6155c2d 100644 --- a/qtpy/tests/test_qtwebenginequick.py +++ b/qtpy/tests/test_qtwebenginequick.py @@ -4,7 +4,8 @@ @pytest.mark.skipif(PYQT5 or PYSIDE2, reason="Only available in Qt6 bindings") def test_qtwebenginequick(): """Test the qtpy.QtWebEngineQuick namespace""" - from qtpy import QtWebEngineQuick + + QtWebEngineQuick = pytest.importorskip("qtpy.QtWebEngineQuick") + assert QtWebEngineQuick.QtWebEngineQuick is not None assert QtWebEngineQuick.QQuickWebEngineProfile is not None - From 8edb602532e60a81a244f4fad8dfd3135344fdfd Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sat, 28 May 2022 13:54:21 -0400 Subject: [PATCH 435/703] Fix failing tests --- qtpy/tests/test_qtconcurrent.py | 14 +++++++++----- qtpy/tests/test_qtuitools.py | 1 - 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/qtpy/tests/test_qtconcurrent.py b/qtpy/tests/test_qtconcurrent.py index 3d2b5342..0ab231dd 100644 --- a/qtpy/tests/test_qtconcurrent.py +++ b/qtpy/tests/test_qtconcurrent.py @@ -1,11 +1,15 @@ import pytest +from packaging.version import parse +from qtpy import PYSIDE2, PYSIDE_VERSION def test_qtconcurrent(): """Test the qtpy.QtConcurrent namespace""" QtConcurrent = pytest.importorskip("qtpy.QtConcurrent") - + assert QtConcurrent.QtConcurrent is not None - assert QtConcurrent.QFutureQString is not None - assert QtConcurrent.QFutureVoid is not None - assert QtConcurrent.QFutureWatcherQString is not None - assert QtConcurrent.QFutureWatcherVoid is not None \ No newline at end of file + + if PYSIDE2 and parse(PYSIDE_VERSION) >= parse('5.15.2'): + assert QtConcurrent.QFutureQString is not None + assert QtConcurrent.QFutureVoid is not None + assert QtConcurrent.QFutureWatcherQString is not None + assert QtConcurrent.QFutureWatcherVoid is not None \ No newline at end of file diff --git a/qtpy/tests/test_qtuitools.py b/qtpy/tests/test_qtuitools.py index 0ce085ea..6e14cc49 100644 --- a/qtpy/tests/test_qtuitools.py +++ b/qtpy/tests/test_qtuitools.py @@ -5,4 +5,3 @@ def test_qtuitools(): QtUiTools = pytest.importorskip("qtpy.QtUiTools") assert QtUiTools.QUiLoader is not None - assert QtUiTools.loadUiType is not None From e027cff3980104e50fc349dcbb7118c573617e81 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sat, 28 May 2022 13:55:49 -0400 Subject: [PATCH 436/703] Add EOL newline --- qtpy/tests/test_qtconcurrent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtconcurrent.py b/qtpy/tests/test_qtconcurrent.py index 0ab231dd..8499545d 100644 --- a/qtpy/tests/test_qtconcurrent.py +++ b/qtpy/tests/test_qtconcurrent.py @@ -12,4 +12,4 @@ def test_qtconcurrent(): assert QtConcurrent.QFutureQString is not None assert QtConcurrent.QFutureVoid is not None assert QtConcurrent.QFutureWatcherQString is not None - assert QtConcurrent.QFutureWatcherVoid is not None \ No newline at end of file + assert QtConcurrent.QFutureWatcherVoid is not None From 02df403d2e64ffd1f6d6249c2b5214a754c0df9d Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sat, 28 May 2022 14:28:20 -0400 Subject: [PATCH 437/703] Standardize sip module --- qtpy/sip.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/qtpy/sip.py b/qtpy/sip.py index 64e71e66..6da93696 100644 --- a/qtpy/sip.py +++ b/qtpy/sip.py @@ -1,15 +1,21 @@ -# +# ----------------------------------------------------------------------------- # Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- -from . import PYQT6, PYQT5, PythonQtError +"""Provides access to sip.""" -if PYQT6: - from PyQt6.sip import * -elif PYQT5: +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError + +if PYQT5: from PyQt5.sip import * +elif PYQT6: + from PyQt6.sip import * +elif PYSIDE2: + raise QtBindingMissingModuleError(name='sip') +elif PYSIDE6: + raise QtBindingMissingModuleError(name='sip') else: - raise PythonQtError( - 'Currently selected Qt binding does not support this module') + raise PythonQtError('No Qt bindings could be found') From 368e66f1d370ac04f16d031607d25fb32a8107dc Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sat, 28 May 2022 14:28:43 -0400 Subject: [PATCH 438/703] Add shiboken module and associated tests --- qtpy/shiboken.py | 23 +++++++++++++++++++++++ qtpy/tests/test_shiboken.py | 11 +++++++++++ 2 files changed, 34 insertions(+) create mode 100644 qtpy/shiboken.py create mode 100644 qtpy/tests/test_shiboken.py diff --git a/qtpy/shiboken.py b/qtpy/shiboken.py new file mode 100644 index 00000000..bd98081e --- /dev/null +++ b/qtpy/shiboken.py @@ -0,0 +1,23 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Provides access to shiboken.""" + +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError + +if PYQT6: + raise QtBindingMissingModuleError(name='shiboken') +elif PYQT5: + raise QtBindingMissingModuleError(name='shiboken') +elif PYSIDE2: + from shiboken2 import * + import shiboken2 as shiboken +elif PYSIDE6: + from shiboken6 import * + import shiboken6 as shiboken +else: + raise PythonQtError('No Qt bindings could be found') diff --git a/qtpy/tests/test_shiboken.py b/qtpy/tests/test_shiboken.py new file mode 100644 index 00000000..214f3219 --- /dev/null +++ b/qtpy/tests/test_shiboken.py @@ -0,0 +1,11 @@ +import pytest + +def test_shiboken(): + """Test the qtpy.shiboken namespace""" + shiboken = pytest.importorskip("qtpy.shiboken") + + assert shiboken.isValid is not None + assert shiboken.wrapInstance is not None + assert shiboken.getCppPointer is not None + assert shiboken.delete is not None + assert shiboken.dump is not None From 31292e0648cd8c22011de5051030927579b794c8 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Sat, 28 May 2022 14:35:37 -0400 Subject: [PATCH 439/703] Add tests for sip module --- qtpy/tests/test_sip.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 qtpy/tests/test_sip.py diff --git a/qtpy/tests/test_sip.py b/qtpy/tests/test_sip.py new file mode 100644 index 00000000..14b77eb6 --- /dev/null +++ b/qtpy/tests/test_sip.py @@ -0,0 +1,24 @@ +import pytest + +def test_sip(): + """Test the qtpy.sip namespace""" + sip = pytest.importorskip("qtpy.sip") + + assert sip.assign is not None + assert sip.cast is not None + assert sip.delete is not None + assert sip.dump is not None + assert sip.enableautoconversion is not None + assert sip.isdeleted is not None + assert sip.ispycreated is not None + assert sip.ispyowned is not None + assert sip.setdeleted is not None + assert sip.settracemask is not None + assert sip.simplewrapper is not None + assert sip.transferback is not None + assert sip.transferto is not None + assert sip.unwrapinstance is not None + assert sip.voidptr is not None + assert sip.wrapinstance is not None + assert sip.wrapper is not None + assert sip.wrappertype is not None From 0d274588c0efb8e64b559a040f34e57bb498db80 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 30 May 2022 14:24:45 -0400 Subject: [PATCH 440/703] Fix order of imports --- qtpy/shiboken.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/shiboken.py b/qtpy/shiboken.py index bd98081e..fb5afb86 100644 --- a/qtpy/shiboken.py +++ b/qtpy/shiboken.py @@ -9,9 +9,9 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError -if PYQT6: +if PYQT5: raise QtBindingMissingModuleError(name='shiboken') -elif PYQT5: +elif PYQT6: raise QtBindingMissingModuleError(name='shiboken') elif PYSIDE2: from shiboken2 import * From b79d758626968384246b8bf110ab24660907a3d2 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 30 May 2022 14:24:58 -0400 Subject: [PATCH 441/703] Remove unused skips, per code review --- qtpy/tests/test_qtdbus.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qtpy/tests/test_qtdbus.py b/qtpy/tests/test_qtdbus.py index 40fdef69..cad9c90d 100644 --- a/qtpy/tests/test_qtdbus.py +++ b/qtpy/tests/test_qtdbus.py @@ -2,8 +2,6 @@ import sys from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 -# @pytest.mark.skipif(PYSIDE2, reason="Not available in PySide2") -# @pytest.mark.skipif(sys.platform != 'linux', reason="Only available on Linux") def test_qtdbus(): """Test the qtpy.QtDBus namespace""" QtDBus = pytest.importorskip("qtpy.QtDBus") From d33f1317e4dde468e0d196b3824a9e47cd98e20e Mon Sep 17 00:00:00 2001 From: Daelon Suzuka Date: Mon, 30 May 2022 11:29:37 -0700 Subject: [PATCH 442/703] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Althviz Moré --- qtpy/QtCharts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 7815eaf0..b790e089 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -15,7 +15,7 @@ from PyQt5 import QtChart as QtCharts except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DCharts', missing_package='PyQtChart' + name='QtCharts', missing_package='PyQtChart' ) from error elif PYQT6: try: @@ -23,7 +23,7 @@ from PyQt6 import QtCharts except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='Qt3DCharts', missing_package='PyQt6-Charts' + name='QtCharts', missing_package='PyQt6-Charts' ) from error elif PYSIDE2: from PySide2.QtCharts import * From 0ea93d3326eaa048e3106049328a4af06e280a40 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 30 May 2022 15:07:48 -0400 Subject: [PATCH 443/703] Replace common use of PythonQtError with QtBindingsNotFoundError --- qtpy/Qt3DAnimation.py | 4 ++-- qtpy/Qt3DCore.py | 4 ++-- qtpy/Qt3DExtras.py | 4 ++-- qtpy/Qt3DInput.py | 4 ++-- qtpy/Qt3DLogic.py | 4 ++-- qtpy/Qt3DRender.py | 4 ++-- qtpy/QtAxContainer.py | 4 ++-- qtpy/QtBluetooth.py | 4 ++-- qtpy/QtCharts.py | 4 ++-- qtpy/QtConcurrent.py | 4 ++-- qtpy/QtCore.py | 4 ++-- qtpy/QtDataVisualization.py | 4 ++-- qtpy/QtDesigner.py | 4 ++-- qtpy/QtGui.py | 3 +-- qtpy/QtHelp.py | 4 ++-- qtpy/QtLocation.py | 4 ++-- qtpy/QtMacExtras.py | 4 ++-- qtpy/QtMultimedia.py | 4 ++-- qtpy/QtMultimediaWidgets.py | 4 ++-- qtpy/QtNetwork.py | 4 ++-- qtpy/QtNetworkAuth.py | 4 ++-- qtpy/QtNfc.py | 4 ++-- qtpy/QtOpenGL.py | 4 ++-- qtpy/QtOpenGLWidgets.py | 4 ++-- qtpy/QtPositioning.py | 4 ++-- qtpy/QtPrintSupport.py | 4 ++-- qtpy/QtPurchasing.py | 4 ++-- qtpy/QtQml.py | 4 ++-- qtpy/QtQuick.py | 4 ++-- qtpy/QtQuick3D.py | 4 ++-- qtpy/QtQuickControls2.py | 4 ++-- qtpy/QtQuickWidgets.py | 4 ++-- qtpy/QtRemoteObjects.py | 4 ++-- qtpy/QtScxml.py | 4 ++-- qtpy/QtSensors.py | 4 ++-- qtpy/QtSerialPort.py | 4 ++-- qtpy/QtSql.py | 4 ++-- qtpy/QtStateMachine.py | 4 ++-- qtpy/QtSvg.py | 4 ++-- qtpy/QtSvgWidgets.py | 4 ++-- qtpy/QtTest.py | 4 ++-- qtpy/QtTextToSpeech.py | 4 ++-- qtpy/QtUiTools.py | 4 ++-- qtpy/QtWebChannel.py | 4 ++-- qtpy/QtWebEngine.py | 4 ++-- qtpy/QtWebEngineCore.py | 4 ++-- qtpy/QtWebEngineQuick.py | 4 ++-- qtpy/QtWebEngineWidgets.py | 4 ++-- qtpy/QtWebSockets.py | 4 ++-- qtpy/QtWidgets.py | 4 ++-- qtpy/QtWinExtras.py | 4 ++-- qtpy/QtX11Extras.py | 4 ++-- qtpy/QtXml.py | 4 ++-- qtpy/QtXmlPatterns.py | 4 ++-- qtpy/shiboken.py | 4 ++-- qtpy/sip.py | 4 ++-- 56 files changed, 111 insertions(+), 112 deletions(-) diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index fa591e27..b5ce94e0 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -7,7 +7,7 @@ """Provides Qt3DAnimation classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -36,4 +36,4 @@ for __name in inspect.getmembers(__temp.Qt3DAnimation): globals()[__name[0]] = __name[1] else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index 49d140e3..fe71ce33 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -7,7 +7,7 @@ """Provides Qt3DCore classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -36,4 +36,4 @@ for __name in inspect.getmembers(__temp.Qt3DCore): globals()[__name[0]] = __name[1] else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index fe7fd95b..b59908b8 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -7,7 +7,7 @@ """Provides Qt3DExtras classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -36,4 +36,4 @@ for __name in inspect.getmembers(__temp.Qt3DExtras): globals()[__name[0]] = __name[1] else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index ba378860..fbc772b3 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -7,7 +7,7 @@ """Provides Qt3DInput classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -36,4 +36,4 @@ for __name in inspect.getmembers(__temp.Qt3DInput): globals()[__name[0]] = __name[1] else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index a3eaf38b..06699625 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -7,7 +7,7 @@ """Provides Qt3DLogic classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -36,4 +36,4 @@ for __name in inspect.getmembers(__temp.Qt3DLogic): globals()[__name[0]] = __name[1] else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index 2b1744ac..8b83273b 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -7,7 +7,7 @@ """Provides Qt3DRender classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -36,4 +36,4 @@ for __name in inspect.getmembers(__temp.Qt3DRender): globals()[__name[0]] = __name[1] else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py index 9f3f4a78..e40af891 100644 --- a/qtpy/QtAxContainer.py +++ b/qtpy/QtAxContainer.py @@ -7,7 +7,7 @@ """Provides QtAxContainer classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='QtAxContainer') @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtAxContainer import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtBluetooth.py b/qtpy/QtBluetooth.py index f78ef0e9..5824e9c3 100644 --- a/qtpy/QtBluetooth.py +++ b/qtpy/QtBluetooth.py @@ -7,7 +7,7 @@ """Provides QtBluetooth classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtBluetooth import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtBluetooth import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index b790e089..5fb7cb78 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -7,7 +7,7 @@ """Provides QtChart classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -36,4 +36,4 @@ from PySide6.QtCharts import * from PySide6 import QtCharts else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtConcurrent.py b/qtpy/QtConcurrent.py index ea326202..f77c2d40 100644 --- a/qtpy/QtConcurrent.py +++ b/qtpy/QtConcurrent.py @@ -7,7 +7,7 @@ """Provides QtConcurrent classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='QtConcurrent') @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtConcurrent import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 47989e9b..3ae098ae 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -8,7 +8,7 @@ """Provides QtCore classes and functions.""" -from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtCore import * @@ -116,4 +116,4 @@ QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index 60828b9b..e2f62d37 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -7,7 +7,7 @@ """Provides QtDataVisualization classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -32,4 +32,4 @@ elif PYSIDE6: from PySide6.QtDataVisualization import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index 04096659..e906b29a 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -7,7 +7,7 @@ """Provides QtDesigner classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtDesigner import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtDesigner import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 98c2a33e..c58ba205 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -8,7 +8,7 @@ """Provides QtGui classes and functions.""" -from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtGui import * @@ -72,4 +72,3 @@ def movePositionPatched( ) -> bool: return movePosition(self, operation, mode, n) QTextCursor.movePosition = movePositionPatched - diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index e8fbec63..337e6b0f 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -7,7 +7,7 @@ """QtHelp Wrapper.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtHelp import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtHelp import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtLocation.py b/qtpy/QtLocation.py index 20b76c42..0a2e2e01 100644 --- a/qtpy/QtLocation.py +++ b/qtpy/QtLocation.py @@ -7,7 +7,7 @@ """Provides QtLocation classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtLocation import * @@ -18,4 +18,4 @@ elif PYSIDE6: raise QtBindingMissingModuleError(name='QtLocation') else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtMacExtras.py b/qtpy/QtMacExtras.py index 6f673fb4..d9259cf5 100644 --- a/qtpy/QtMacExtras.py +++ b/qtpy/QtMacExtras.py @@ -8,7 +8,7 @@ """Provides classes and functions specific to macOS and iOS operating systems""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingsNotFoundError if sys.platform == 'darwin': if PYQT5: @@ -20,6 +20,6 @@ elif PYSIDE6: raise PythonQtError('QtMacExtras does not exist in Qt6') else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() else: raise PythonQtError('QtMacExtras does not exist on this operating system') diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index c2f04f3a..cf9ecdc6 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -7,7 +7,7 @@ """Provides low-level multimedia functionality.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtMultimedia import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtMultimedia import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtMultimediaWidgets.py b/qtpy/QtMultimediaWidgets.py index 337efffc..0e9dee51 100644 --- a/qtpy/QtMultimediaWidgets.py +++ b/qtpy/QtMultimediaWidgets.py @@ -7,7 +7,7 @@ """Provides QtMultimediaWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtMultimediaWidgets import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtMultimediaWidgets import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index e76173d5..b3cfdd3c 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -8,7 +8,7 @@ """Provides QtNetwork classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtNetwork import * @@ -19,4 +19,4 @@ elif PYSIDE6: from PySide6.QtNetwork import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py index cc8b1aec..9fa7766f 100644 --- a/qtpy/QtNetworkAuth.py +++ b/qtpy/QtNetworkAuth.py @@ -7,7 +7,7 @@ """Provides QtNetworkAuth classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError, QtBindingMissingModuleError if PYQT5: try: @@ -28,4 +28,4 @@ elif PYSIDE6: from PySide6.QtNetworkAuth import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtNfc.py b/qtpy/QtNfc.py index 54156de3..e1fc2b34 100644 --- a/qtpy/QtNfc.py +++ b/qtpy/QtNfc.py @@ -7,7 +7,7 @@ """Provides QtNfc classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtNfc import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtNfc import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 0696cc57..9b79a50d 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -7,7 +7,7 @@ """Provides QtOpenGL classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtOpenGL import * @@ -46,4 +46,4 @@ except ImportError: pass else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtOpenGLWidgets.py b/qtpy/QtOpenGLWidgets.py index cda3ac5d..26eba1d8 100644 --- a/qtpy/QtOpenGLWidgets.py +++ b/qtpy/QtOpenGLWidgets.py @@ -7,7 +7,7 @@ """Provides QtOpenGLWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='QtTextToSpeech') @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtOpenGLWidgets import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtPositioning.py b/qtpy/QtPositioning.py index 1ab54686..0583d3e6 100644 --- a/qtpy/QtPositioning.py +++ b/qtpy/QtPositioning.py @@ -7,7 +7,7 @@ """Provides QtPositioning classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtPositioning import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtPositioning import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index e2602ff0..2fa24a20 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -7,7 +7,7 @@ """Provides QtPrintSupport classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError if PYQT5: from PyQt5.QtPrintSupport import * @@ -24,4 +24,4 @@ elif PYSIDE2: from PySide2.QtPrintSupport import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtPurchasing.py b/qtpy/QtPurchasing.py index 4c397ba3..1cd85215 100644 --- a/qtpy/QtPurchasing.py +++ b/qtpy/QtPurchasing.py @@ -7,7 +7,7 @@ """Provides QtPurchasing classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError, QtModuleNotInstalledError if PYQT5: try: @@ -23,4 +23,4 @@ elif PYSIDE6: raise QtBindingMissingModuleError(name='QtPurchasing') else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtQml.py b/qtpy/QtQml.py index dbd69852..4bb35a2c 100644 --- a/qtpy/QtQml.py +++ b/qtpy/QtQml.py @@ -7,7 +7,7 @@ """Provides QtQml classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtQml import * @@ -18,4 +18,4 @@ elif PYSIDE2: from PySide2.QtQml import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtQuick.py b/qtpy/QtQuick.py index 08e339ec..577fcdb0 100644 --- a/qtpy/QtQuick.py +++ b/qtpy/QtQuick.py @@ -7,7 +7,7 @@ """Provides QtQuick classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError if PYQT5: from PyQt5.QtQuick import * @@ -18,4 +18,4 @@ elif PYSIDE2: from PySide2.QtQuick import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtQuick3D.py b/qtpy/QtQuick3D.py index fc7375c0..bb78ab3a 100644 --- a/qtpy/QtQuick3D.py +++ b/qtpy/QtQuick3D.py @@ -7,7 +7,7 @@ """Provides QtQuick3D classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtQuick3D import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtQuick3D import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtQuickControls2.py b/qtpy/QtQuickControls2.py index 7e71e7d7..91fbc60d 100644 --- a/qtpy/QtQuickControls2.py +++ b/qtpy/QtQuickControls2.py @@ -7,7 +7,7 @@ """Provides QtQuickControls2 classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='QtQuickControls2') @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtQuickControls2 import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtQuickWidgets.py b/qtpy/QtQuickWidgets.py index ffecda11..1bc5521a 100644 --- a/qtpy/QtQuickWidgets.py +++ b/qtpy/QtQuickWidgets.py @@ -7,7 +7,7 @@ """Provides QtQuickWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError if PYQT5: from PyQt5.QtQuickWidgets import * @@ -18,4 +18,4 @@ elif PYSIDE2: from PySide2.QtQuickWidgets import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtRemoteObjects.py b/qtpy/QtRemoteObjects.py index c1ec2e6f..59cce95d 100644 --- a/qtpy/QtRemoteObjects.py +++ b/qtpy/QtRemoteObjects.py @@ -7,7 +7,7 @@ """Provides QtRemoteObjects classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtRemoteObjects import * @@ -18,4 +18,4 @@ elif PYSIDE2: from PySide2.QtRemoteObjects import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtScxml.py b/qtpy/QtScxml.py index bb82be7c..7f711368 100644 --- a/qtpy/QtScxml.py +++ b/qtpy/QtScxml.py @@ -7,7 +7,7 @@ """Provides QtScxml classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='QtScxml') @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtScxml import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtSensors.py b/qtpy/QtSensors.py index a8c82e62..6e28cc22 100644 --- a/qtpy/QtSensors.py +++ b/qtpy/QtSensors.py @@ -7,7 +7,7 @@ """Provides QtSensors classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtSensors import * @@ -18,4 +18,4 @@ elif PYSIDE2: from PySide2.QtSensors import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtSerialPort.py b/qtpy/QtSerialPort.py index 26b4710f..ed4584a9 100644 --- a/qtpy/QtSerialPort.py +++ b/qtpy/QtSerialPort.py @@ -8,7 +8,7 @@ """Provides QtSerialPort classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtSerialPort import * @@ -19,4 +19,4 @@ elif PYSIDE2: from PySide2.QtSerialPort import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index b4cf71db..372fe1f9 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -7,7 +7,7 @@ """Provides QtSql classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError if PYQT5: from PyQt5.QtSql import * @@ -25,4 +25,4 @@ elif PYSIDE2: from PySide2.QtSql import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtStateMachine.py b/qtpy/QtStateMachine.py index 51ea5866..99882eaf 100644 --- a/qtpy/QtStateMachine.py +++ b/qtpy/QtStateMachine.py @@ -7,7 +7,7 @@ """Provides QtStateMachine classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='QtStateMachine') @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtStateMachine import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index a259d67c..4d902875 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -7,7 +7,7 @@ """Provides QtSvg classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtSvg import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtSvg import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtSvgWidgets.py b/qtpy/QtSvgWidgets.py index ef5bc269..cc7016b1 100644 --- a/qtpy/QtSvgWidgets.py +++ b/qtpy/QtSvgWidgets.py @@ -7,7 +7,7 @@ """Provides QtSvgWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='QtSvgWidgets') @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtSvgWidgets import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 40f5b178..5a1b3446 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -8,7 +8,7 @@ """Provides QtTest and functions""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, PythonQtError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError if PYQT5: from PyQt5.QtTest import * @@ -25,4 +25,4 @@ elif PYSIDE6: from PySide6.QtTest import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtTextToSpeech.py b/qtpy/QtTextToSpeech.py index 10ee85b5..038116bc 100644 --- a/qtpy/QtTextToSpeech.py +++ b/qtpy/QtTextToSpeech.py @@ -7,7 +7,7 @@ """Provides QtTextToSpeech classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtTextToSpeech import * @@ -18,4 +18,4 @@ elif PYSIDE6: raise QtBindingMissingModuleError(name='QtTextToSpeech') else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtUiTools.py b/qtpy/QtUiTools.py index 9f1a3d1d..eb12bd4e 100644 --- a/qtpy/QtUiTools.py +++ b/qtpy/QtUiTools.py @@ -7,7 +7,7 @@ """Provides QtUiTools classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='QtUiTools') @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtUiTools import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebChannel.py b/qtpy/QtWebChannel.py index 5f7f6734..6c7ae285 100644 --- a/qtpy/QtWebChannel.py +++ b/qtpy/QtWebChannel.py @@ -7,7 +7,7 @@ """Provides QtWebChannel classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtWebChannel import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtWebChannel import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index 43e18edc..431bde14 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -8,7 +8,7 @@ """Provides QtWebEngine classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -24,4 +24,4 @@ elif PYSIDE6: raise PythonQtError('QtWebEngine does not exist in Qt6') else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py index 4f25defc..a23d22bd 100644 --- a/qtpy/QtWebEngineCore.py +++ b/qtpy/QtWebEngineCore.py @@ -7,7 +7,7 @@ """Provides QtWebEngineCore classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -28,4 +28,4 @@ elif PYSIDE6: from PySide6.QtWebEngineCore import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebEngineQuick.py b/qtpy/QtWebEngineQuick.py index 19b4a14e..6aef6259 100644 --- a/qtpy/QtWebEngineQuick.py +++ b/qtpy/QtWebEngineQuick.py @@ -7,7 +7,7 @@ """Provides QtWebEngineQuick classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='QtWebEngineQuick') @@ -23,4 +23,4 @@ elif PYSIDE6: from PySide6.QtWebEngineQuick import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 149875c0..c38a4f5d 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -8,7 +8,7 @@ """Provides QtWebEngineWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError if PYQT5: try: @@ -47,4 +47,4 @@ from PySide6.QtWebEngineCore import QWebEngineProfile from PySide6.QtWebEngineCore import QWebEngineScript else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebSockets.py b/qtpy/QtWebSockets.py index 8a3b7147..9a595888 100644 --- a/qtpy/QtWebSockets.py +++ b/qtpy/QtWebSockets.py @@ -7,7 +7,7 @@ """Provides QtWebSockets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtWebSockets import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtWebSockets import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index ce69dcad..25c8f16a 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -8,7 +8,7 @@ """Provides widget classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtWidgets import * @@ -51,4 +51,4 @@ QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index 1d86821b..0249beab 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -8,7 +8,7 @@ """Provides Windows-specific utilities""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingsNotFoundError if sys.platform == 'win32': if PYQT5: @@ -20,6 +20,6 @@ elif PYSIDE6: raise PythonQtError('QtWinExtras does not exist in Qt6') else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() else: raise PythonQtError('QtWinExtras does not exist on this operating system') diff --git a/qtpy/QtX11Extras.py b/qtpy/QtX11Extras.py index 2f3cd6a2..bebab581 100644 --- a/qtpy/QtX11Extras.py +++ b/qtpy/QtX11Extras.py @@ -8,7 +8,7 @@ """Provides Linux-specific utilities""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingsNotFoundError if sys.platform == 'linux': if PYQT5: @@ -20,6 +20,6 @@ elif PYSIDE6: raise PythonQtError('QtX11Extras does not exist in Qt6') else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() else: raise PythonQtError('QtX11Extras does not exist on this operating system') diff --git a/qtpy/QtXml.py b/qtpy/QtXml.py index 7f90491c..d2cb1653 100644 --- a/qtpy/QtXml.py +++ b/qtpy/QtXml.py @@ -7,7 +7,7 @@ """Provides QtXml classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: from PyQt5.QtXml import * @@ -18,4 +18,4 @@ elif PYSIDE6: from PySide6.QtXml import * else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py index 0f991637..a6492f0a 100644 --- a/qtpy/QtXmlPatterns.py +++ b/qtpy/QtXmlPatterns.py @@ -7,7 +7,7 @@ """Provides QtXmlPatterns classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: from PyQt5.QtXmlPatterns import * @@ -18,4 +18,4 @@ elif PYSIDE6: raise QtBindingMissingModuleError(name='QtXmlPatterns') else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/shiboken.py b/qtpy/shiboken.py index fb5afb86..49d5e94a 100644 --- a/qtpy/shiboken.py +++ b/qtpy/shiboken.py @@ -7,7 +7,7 @@ """Provides access to shiboken.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: raise QtBindingMissingModuleError(name='shiboken') @@ -20,4 +20,4 @@ from shiboken6 import * import shiboken6 as shiboken else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() diff --git a/qtpy/sip.py b/qtpy/sip.py index 6da93696..f79e47be 100644 --- a/qtpy/sip.py +++ b/qtpy/sip.py @@ -7,7 +7,7 @@ """Provides access to sip.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingMissingModuleError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: from PyQt5.sip import * @@ -18,4 +18,4 @@ elif PYSIDE6: raise QtBindingMissingModuleError(name='sip') else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() From d16ad78fa939bf126ba3a5e9cd5ee55191fb87e4 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 31 May 2022 12:30:36 -0400 Subject: [PATCH 444/703] Add missing EOF newlines --- qtpy/QtDBus.py | 2 +- qtpy/tests/test_qtaxcontainer.py | 2 +- qtpy/tests/test_qtpurchasing.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index 7d8a31e5..a6fd4ee2 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -22,4 +22,4 @@ else: raise QtModuleNotFoundError(name='QtDBus', msg='{name} does not exist on this operating system.') else: - raise QtBindingsNotFoundError() \ No newline at end of file + raise QtBindingsNotFoundError() diff --git a/qtpy/tests/test_qtaxcontainer.py b/qtpy/tests/test_qtaxcontainer.py index aa17f4d6..382abd1d 100644 --- a/qtpy/tests/test_qtaxcontainer.py +++ b/qtpy/tests/test_qtaxcontainer.py @@ -5,4 +5,4 @@ def test_qtaxcontainer(): QtAxContainer = pytest.importorskip("qtpy.QtAxContainer") assert QtAxContainer.QAxSelect is not None - assert QtAxContainer.QAxWidget is not None \ No newline at end of file + assert QtAxContainer.QAxWidget is not None diff --git a/qtpy/tests/test_qtpurchasing.py b/qtpy/tests/test_qtpurchasing.py index 55c86667..7b0db0e4 100644 --- a/qtpy/tests/test_qtpurchasing.py +++ b/qtpy/tests/test_qtpurchasing.py @@ -6,4 +6,4 @@ def test_qtpurchasing(): assert QtPurchasing.QInAppProduct is not None assert QtPurchasing.QInAppStore is not None - assert QtPurchasing.QInAppTransaction is not None \ No newline at end of file + assert QtPurchasing.QInAppTransaction is not None From 781f54ea08a9d7793fceaf5b50d7db1313eb6b57 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 31 May 2022 12:53:19 -0400 Subject: [PATCH 445/703] Switch last PythonQtError to QtBindingsNotFoundError --- qtpy/QtGui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index c58ba205..7c585b6a 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -41,7 +41,7 @@ QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QGuiApplication.exec_ = QGuiApplication.exec else: - raise PythonQtError('No Qt bindings could be found') + raise QtBindingsNotFoundError() if PYSIDE2 or PYSIDE6: # PySide{2,6} do not accept the `mode` keyword argument in From d9dce6f97019d688750c8143777d2c9e2acd4170 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 31 May 2022 13:13:12 -0400 Subject: [PATCH 446/703] Fix wrong module name in error message --- qtpy/QtOpenGLWidgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/QtOpenGLWidgets.py b/qtpy/QtOpenGLWidgets.py index 26eba1d8..b643498b 100644 --- a/qtpy/QtOpenGLWidgets.py +++ b/qtpy/QtOpenGLWidgets.py @@ -10,11 +10,11 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError if PYQT5: - raise QtBindingMissingModuleError(name='QtTextToSpeech') + raise QtBindingMissingModuleError(name='QtOpenGLWidgets') elif PYQT6: from PyQt6.QtOpenGLWidgets import * elif PYSIDE2: - raise QtBindingMissingModuleError(name='QtTextToSpeech') + raise QtBindingMissingModuleError(name='QtOpenGLWidgets') elif PYSIDE6: from PySide6.QtOpenGLWidgets import * else: From 6d3ad62f964c80f419242faba8a37600b2f206e2 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 31 May 2022 20:20:41 -0400 Subject: [PATCH 447/703] Add QtModuleNotInOSError and QtModuleNotInQtVersionError --- qtpy/QtDBus.py | 4 ++-- qtpy/QtMacExtras.py | 8 ++++---- qtpy/QtWebEngine.py | 6 +++--- qtpy/QtWinExtras.py | 8 ++++---- qtpy/QtX11Extras.py | 8 ++++---- qtpy/__init__.py | 15 +++++++++++++++ 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index a6fd4ee2..bc340748 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -8,7 +8,7 @@ """Provides QtDBus classes and functions.""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError, QtModuleNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError, QtModuleNotInOSError if PYQT5: from PyQt5.QtDBus import * @@ -20,6 +20,6 @@ if sys.platform != 'win32': from PySide6.QtDBus import * else: - raise QtModuleNotFoundError(name='QtDBus', msg='{name} does not exist on this operating system.') + raise QtModuleNotInOSError(name='QtDBus') else: raise QtBindingsNotFoundError() diff --git a/qtpy/QtMacExtras.py b/qtpy/QtMacExtras.py index d9259cf5..d4b51d19 100644 --- a/qtpy/QtMacExtras.py +++ b/qtpy/QtMacExtras.py @@ -8,18 +8,18 @@ """Provides classes and functions specific to macOS and iOS operating systems""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInOSError if sys.platform == 'darwin': if PYQT5: from PyQt5.QtMacExtras import * elif PYQT6: - raise PythonQtError('QtMacExtras does not exist in Qt6') + raise QtModuleNotInQtVersionError(name='QtMacExtras') elif PYSIDE2: from PySide2.QtMacExtras import * elif PYSIDE6: - raise PythonQtError('QtMacExtras does not exist in Qt6') + raise QtModuleNotInQtVersionError(name='QtMacExtras') else: raise QtBindingsNotFoundError() else: - raise PythonQtError('QtMacExtras does not exist on this operating system') + raise QtModuleNotInOSError(name='QtMacExtras') diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index 431bde14..2d6fb3cd 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -8,7 +8,7 @@ """Provides QtWebEngine classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInstalledError if PYQT5: try: @@ -18,10 +18,10 @@ name='QtWebEngine', missing_package='PyQtWebEngine' ) from error elif PYQT6: - raise PythonQtError('QtWebEngine does not exist in Qt6') + raise QtModuleNotInQtVersionError(name='QtWebEngine') elif PYSIDE2: from PySide2.QtWebEngine import * elif PYSIDE6: - raise PythonQtError('QtWebEngine does not exist in Qt6') + raise QtModuleNotInQtVersionError(name='QtWebEngine') else: raise QtBindingsNotFoundError() diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index 0249beab..ae786cb7 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -8,18 +8,18 @@ """Provides Windows-specific utilities""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInOSError if sys.platform == 'win32': if PYQT5: from PyQt5.QtWinExtras import * elif PYQT6: - raise PythonQtError('QtWinExtras does not exist in Qt6') + raise QtModuleNotInQtVersionError(name='QtWinExtras') elif PYSIDE2: from PySide2.QtWinExtras import * elif PYSIDE6: - raise PythonQtError('QtWinExtras does not exist in Qt6') + raise QtModuleNotInQtVersionError(name='QtWinExtras') else: raise QtBindingsNotFoundError() else: - raise PythonQtError('QtWinExtras does not exist on this operating system') + raise QtModuleNotInOSError(name='QtWinExtras') diff --git a/qtpy/QtX11Extras.py b/qtpy/QtX11Extras.py index bebab581..e3cd828d 100644 --- a/qtpy/QtX11Extras.py +++ b/qtpy/QtX11Extras.py @@ -8,18 +8,18 @@ """Provides Linux-specific utilities""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PythonQtError, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInOSError if sys.platform == 'linux': if PYQT5: from PyQt5.QtX11Extras import * elif PYQT6: - raise PythonQtError('QtX11Extras does not exist in Qt6') + raise QtModuleNotInQtVersionError(name='QtX11Extras') elif PYSIDE2: from PySide2.QtX11Extras import * elif PYSIDE6: - raise PythonQtError('QtX11Extras does not exist in Qt6') + raise QtModuleNotInQtVersionError(name='QtX11Extras') else: raise QtBindingsNotFoundError() else: - raise PythonQtError('QtX11Extras does not exist on this operating system') + raise QtModuleNotInOSError(name='QtX11Extras') diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 9b135a68..5bb058c2 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -98,6 +98,21 @@ def __init__(self, *, name, msg=None, **msg_kwargs): super().__init__(msg, name=name) +class QtModuleNotInOSError(QtModuleNotFoundError): + """Raised when a module is not supported on the current operating system.""" + _msg = '{name} does not exist on this operating system.' + + +class QtModuleNotInQtVersionError(QtModuleNotFoundError): + """Raised when a module is not implemented in the current Qt version.""" + _msg = '{name} does not exist in {version}.' + + def __init__(self, *, name, msg=None, **msg_kwargs): + global QT5, QT6 + version = 'Qt5' if QT5 else 'Qt6' + super().__init__(name=name, version=version) + + class QtBindingMissingModuleError(QtModuleNotFoundError): """Raised when a module is not supported by a given binding.""" _msg_extra = 'It is not currently implemented in {binding}.' From 038362a9965977f027ea4be6ce5bfd5ac496e04c Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 31 May 2022 20:32:15 -0400 Subject: [PATCH 448/703] Add placeholder test file for QtX11Extras --- qtpy/tests/test_qtx11extras.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 qtpy/tests/test_qtx11extras.py diff --git a/qtpy/tests/test_qtx11extras.py b/qtpy/tests/test_qtx11extras.py new file mode 100644 index 00000000..5824b32c --- /dev/null +++ b/qtpy/tests/test_qtx11extras.py @@ -0,0 +1,17 @@ +import sys +import pytest + +from qtpy import PYQT6, PYSIDE2, PYSIDE6 +from qtpy.tests.utils import using_conda + +# @pytest.mark.skipif( +# PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") +# @pytest.mark.skipif( +# sys.platform != "win32" or using_conda(), +# reason="Only available in Qt5 bindings > 5.9 with pip on Windows in CIs") +def test_qtwinextras(): + QtX11Extras = pytest.importorskip("qtpy.QtX11Extras") + + # TODO: this is just a placeholder file + + # assert QtX11Extras.QSomething is not None From 8c37d9493fbe45012d361c03675fb0de4b726827 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Fri, 3 Jun 2022 12:42:51 -0400 Subject: [PATCH 449/703] Implement QtX11Extras test --- qtpy/tests/test_qtx11extras.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/qtpy/tests/test_qtx11extras.py b/qtpy/tests/test_qtx11extras.py index 5824b32c..218757b6 100644 --- a/qtpy/tests/test_qtx11extras.py +++ b/qtpy/tests/test_qtx11extras.py @@ -1,17 +1,8 @@ -import sys import pytest -from qtpy import PYQT6, PYSIDE2, PYSIDE6 -from qtpy.tests.utils import using_conda - -# @pytest.mark.skipif( -# PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") -# @pytest.mark.skipif( -# sys.platform != "win32" or using_conda(), -# reason="Only available in Qt5 bindings > 5.9 with pip on Windows in CIs") def test_qtwinextras(): QtX11Extras = pytest.importorskip("qtpy.QtX11Extras") - # TODO: this is just a placeholder file - - # assert QtX11Extras.QSomething is not None + assert QtX11Extras is not None + # This module doesn't seem to contain any classes + # See https://doc.qt.io/qt-5/qtx11extras-module.html From 2e92fc99c49570e001c0031135af1c06a9af9c41 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 6 Jun 2022 13:23:22 -0400 Subject: [PATCH 450/703] Apply code review suggestions to modules --- qtpy/Qt3DAnimation.py | 11 +++++++++- qtpy/Qt3DCore.py | 11 +++++++++- qtpy/Qt3DExtras.py | 11 +++++++++- qtpy/Qt3DInput.py | 11 +++++++++- qtpy/Qt3DLogic.py | 11 +++++++++- qtpy/Qt3DRender.py | 11 +++++++++- qtpy/QtAxContainer.py | 9 +++++++- qtpy/QtBluetooth.py | 9 +++++++- qtpy/QtCharts.py | 11 +++++++++- qtpy/QtConcurrent.py | 9 +++++++- qtpy/QtDBus.py | 10 ++++++++- qtpy/QtDataVisualization.py | 13 +++++++++-- qtpy/QtDesigner.py | 9 +++++++- qtpy/QtLocation.py | 9 +++++++- qtpy/QtMacExtras.py | 10 ++++++++- qtpy/QtNetworkAuth.py | 10 ++++++++- qtpy/QtNfc.py | 9 +++++++- qtpy/QtOpenGL.py | 44 +++++++++++++++++++++++++++---------- qtpy/QtOpenGLWidgets.py | 9 +++++++- qtpy/QtPurchasing.py | 10 ++++++++- qtpy/QtQuick3D.py | 9 +++++++- qtpy/QtQuickControls2.py | 9 +++++++- qtpy/QtScxml.py | 9 +++++++- qtpy/QtStateMachine.py | 9 +++++++- qtpy/QtSvgWidgets.py | 9 +++++++- qtpy/QtTest.py | 1 + qtpy/QtTextToSpeech.py | 9 +++++++- qtpy/QtUiTools.py | 9 +++++++- qtpy/QtWebEngine.py | 10 ++++++++- qtpy/QtWebEngineCore.py | 9 +++++++- qtpy/QtWebEngineQuick.py | 10 ++++++++- qtpy/QtWebEngineWidgets.py | 11 +++++++++- qtpy/QtWinExtras.py | 10 ++++++++- qtpy/QtX11Extras.py | 10 ++++++++- qtpy/QtXmlPatterns.py | 9 +++++++- qtpy/shiboken.py | 9 +++++++- qtpy/sip.py | 9 +++++++- 37 files changed, 340 insertions(+), 48 deletions(-) diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index b5ce94e0..b51acfc6 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -7,7 +7,14 @@ """Provides Qt3DAnimation classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: @@ -27,12 +34,14 @@ # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DAnimation as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DAnimation): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide6.Qt3DAnimation as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DAnimation): globals()[__name[0]] = __name[1] else: diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index fe71ce33..e0b3f83f 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -7,7 +7,14 @@ """Provides Qt3DCore classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: @@ -27,12 +34,14 @@ # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DCore as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DCore): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide6.Qt3DCore as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DCore): globals()[__name[0]] = __name[1] else: diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index b59908b8..5bf610b2 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -7,7 +7,14 @@ """Provides Qt3DExtras classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: @@ -27,12 +34,14 @@ # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DExtras as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DExtras): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide6.Qt3DExtras as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DExtras): globals()[__name[0]] = __name[1] else: diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index fbc772b3..73471ed2 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -7,7 +7,14 @@ """Provides Qt3DInput classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: @@ -27,12 +34,14 @@ # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DInput as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DInput): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide6.Qt3DInput as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DInput): globals()[__name[0]] = __name[1] else: diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index 06699625..12a8b7c1 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -7,7 +7,14 @@ """Provides Qt3DLogic classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: @@ -27,12 +34,14 @@ # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DLogic as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DLogic): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide6.Qt3DLogic as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DLogic): globals()[__name[0]] = __name[1] else: diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index 8b83273b..1a783eae 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -7,7 +7,14 @@ """Provides Qt3DRender classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: @@ -27,12 +34,14 @@ # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.Qt3DRender as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DRender): globals()[__name[0]] = __name[1] elif PYSIDE6: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide6.Qt3DRender as __temp import inspect + for __name in inspect.getmembers(__temp.Qt3DRender): globals()[__name[0]] = __name[1] else: diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py index e40af891..3ad07148 100644 --- a/qtpy/QtAxContainer.py +++ b/qtpy/QtAxContainer.py @@ -7,7 +7,14 @@ """Provides QtAxContainer classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='QtAxContainer') diff --git a/qtpy/QtBluetooth.py b/qtpy/QtBluetooth.py index 5824e9c3..3bbcc93a 100644 --- a/qtpy/QtBluetooth.py +++ b/qtpy/QtBluetooth.py @@ -7,7 +7,14 @@ """Provides QtBluetooth classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: from PyQt5.QtBluetooth import * diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 5fb7cb78..21f84de9 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -7,7 +7,14 @@ """Provides QtChart classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: @@ -27,9 +34,11 @@ ) from error elif PYSIDE2: from PySide2.QtCharts import * + # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.QtCharts as __temp import inspect + for __name in inspect.getmembers(__temp.QtCharts): globals()[__name[0]] = __name[1] elif PYSIDE6: diff --git a/qtpy/QtConcurrent.py b/qtpy/QtConcurrent.py index f77c2d40..d849d4c2 100644 --- a/qtpy/QtConcurrent.py +++ b/qtpy/QtConcurrent.py @@ -7,7 +7,14 @@ """Provides QtConcurrent classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='QtConcurrent') diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index bc340748..0a27ec2c 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -8,7 +8,15 @@ """Provides QtDBus classes and functions.""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError, QtModuleNotInOSError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, + QtModuleNotInOSError, +) if PYQT5: from PyQt5.QtDBus import * diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index e2f62d37..c5885414 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -7,7 +7,14 @@ """Provides QtDataVisualization classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: @@ -21,12 +28,14 @@ from PyQt6.QtDataVisualization import * except ModuleNotFoundError as error: raise QtModuleNotInstalledError( - name='QtDataVisualization', missing_package='PyQt6-DataVisualization' + name='QtDataVisualization', + missing_package='PyQt6-DataVisualization', ) from error elif PYSIDE2: # https://bugreports.qt.io/projects/PYSIDE/issues/PYSIDE-1026 import PySide2.QtDataVisualization as __temp import inspect + for __name in inspect.getmembers(__temp.QtDataVisualization): globals()[__name[0]] = __name[1] elif PYSIDE6: diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index e906b29a..ae2696b2 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -7,7 +7,14 @@ """Provides QtDesigner classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: from PyQt5.QtDesigner import * diff --git a/qtpy/QtLocation.py b/qtpy/QtLocation.py index 0a2e2e01..72a6ba12 100644 --- a/qtpy/QtLocation.py +++ b/qtpy/QtLocation.py @@ -7,7 +7,14 @@ """Provides QtLocation classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: from PyQt5.QtLocation import * diff --git a/qtpy/QtMacExtras.py b/qtpy/QtMacExtras.py index d4b51d19..d660ad5b 100644 --- a/qtpy/QtMacExtras.py +++ b/qtpy/QtMacExtras.py @@ -8,7 +8,15 @@ """Provides classes and functions specific to macOS and iOS operating systems""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInOSError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInQtVersionError, + QtModuleNotInOSError, +) if sys.platform == 'darwin': if PYQT5: diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py index 9fa7766f..94f8f5bc 100644 --- a/qtpy/QtNetworkAuth.py +++ b/qtpy/QtNetworkAuth.py @@ -7,7 +7,15 @@ """Provides QtNetworkAuth classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, + QtBindingMissingModuleError, +) if PYQT5: try: diff --git a/qtpy/QtNfc.py b/qtpy/QtNfc.py index e1fc2b34..adf73e25 100644 --- a/qtpy/QtNfc.py +++ b/qtpy/QtNfc.py @@ -7,7 +7,14 @@ """Provides QtNfc classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: from PyQt5.QtNfc import * diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index 9b79a50d..b1f749dd 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -12,13 +12,23 @@ if PYQT5: from PyQt5.QtOpenGL import * from PyQt5.QtGui import ( - QOpenGLBuffer, QOpenGLFramebufferObject, - QOpenGLFramebufferObjectFormat, QOpenGLShader, QOpenGLShaderProgram, - QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, - QOpenGLDebugMessage, QOpenGLPixelTransferOptions, QOpenGLTexture, + QOpenGLBuffer, + QOpenGLFramebufferObject, + QOpenGLFramebufferObjectFormat, + QOpenGLShader, + QOpenGLShaderProgram, + QOpenGLContext, + QOpenGLContextGroup, + QOpenGLDebugLogger, + QOpenGLDebugMessage, + QOpenGLPixelTransferOptions, + QOpenGLTexture, QOpenGLTextureBlitter, - QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow + QOpenGLVersionProfile, + QOpenGLVertexArrayObject, + QOpenGLWindow, ) + # These are not present on some architectures such as armhf try: from PyQt5.QtGui import QOpenGLTimeMonitor, QOpenGLTimerQuery @@ -26,20 +36,30 @@ pass elif PYQT6: from PyQt6.QtOpenGL import * - from PyQt6.QtGui import (QOpenGLContext, QOpenGLContextGroup) + from PyQt6.QtGui import QOpenGLContext, QOpenGLContextGroup elif PYSIDE6: from PySide6.QtOpenGL import * - from PySide6.QtGui import (QOpenGLContext, QOpenGLContextGroup) + from PySide6.QtGui import QOpenGLContext, QOpenGLContextGroup elif PYSIDE2: from PySide2.QtOpenGL import * from PySide2.QtGui import ( - QOpenGLBuffer, QOpenGLFramebufferObject, - QOpenGLFramebufferObjectFormat, QOpenGLShader, QOpenGLShaderProgram, - QOpenGLContext, QOpenGLContextGroup, QOpenGLDebugLogger, - QOpenGLDebugMessage, QOpenGLPixelTransferOptions, QOpenGLTexture, + QOpenGLBuffer, + QOpenGLFramebufferObject, + QOpenGLFramebufferObjectFormat, + QOpenGLShader, + QOpenGLShaderProgram, + QOpenGLContext, + QOpenGLContextGroup, + QOpenGLDebugLogger, + QOpenGLDebugMessage, + QOpenGLPixelTransferOptions, + QOpenGLTexture, QOpenGLTextureBlitter, - QOpenGLVersionProfile, QOpenGLVertexArrayObject, QOpenGLWindow + QOpenGLVersionProfile, + QOpenGLVertexArrayObject, + QOpenGLWindow, ) + # These are not present on some architectures such as armhf try: from PySide2.QtGui import QOpenGLTimeMonitor, QOpenGLTimerQuery diff --git a/qtpy/QtOpenGLWidgets.py b/qtpy/QtOpenGLWidgets.py index b643498b..cf1a98d0 100644 --- a/qtpy/QtOpenGLWidgets.py +++ b/qtpy/QtOpenGLWidgets.py @@ -7,7 +7,14 @@ """Provides QtOpenGLWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='QtOpenGLWidgets') diff --git a/qtpy/QtPurchasing.py b/qtpy/QtPurchasing.py index 1cd85215..ab0aba57 100644 --- a/qtpy/QtPurchasing.py +++ b/qtpy/QtPurchasing.py @@ -7,7 +7,15 @@ """Provides QtPurchasing classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, + QtModuleNotInstalledError, +) if PYQT5: try: diff --git a/qtpy/QtQuick3D.py b/qtpy/QtQuick3D.py index bb78ab3a..ad81c14b 100644 --- a/qtpy/QtQuick3D.py +++ b/qtpy/QtQuick3D.py @@ -7,7 +7,14 @@ """Provides QtQuick3D classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: from PyQt5.QtQuick3D import * diff --git a/qtpy/QtQuickControls2.py b/qtpy/QtQuickControls2.py index 91fbc60d..0693857f 100644 --- a/qtpy/QtQuickControls2.py +++ b/qtpy/QtQuickControls2.py @@ -7,7 +7,14 @@ """Provides QtQuickControls2 classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='QtQuickControls2') diff --git a/qtpy/QtScxml.py b/qtpy/QtScxml.py index 7f711368..cbf2a96b 100644 --- a/qtpy/QtScxml.py +++ b/qtpy/QtScxml.py @@ -7,7 +7,14 @@ """Provides QtScxml classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='QtScxml') diff --git a/qtpy/QtStateMachine.py b/qtpy/QtStateMachine.py index 99882eaf..039749a6 100644 --- a/qtpy/QtStateMachine.py +++ b/qtpy/QtStateMachine.py @@ -7,7 +7,14 @@ """Provides QtStateMachine classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='QtStateMachine') diff --git a/qtpy/QtSvgWidgets.py b/qtpy/QtSvgWidgets.py index cc7016b1..571ca18c 100644 --- a/qtpy/QtSvgWidgets.py +++ b/qtpy/QtSvgWidgets.py @@ -7,7 +7,14 @@ """Provides QtSvgWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='QtSvgWidgets') diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 5a1b3446..53079983 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -18,6 +18,7 @@ # Allow unscoped access for enums inside the QtTest module from .enums_compat import promote_enums + promote_enums(QtTest) del QtTest elif PYSIDE2: diff --git a/qtpy/QtTextToSpeech.py b/qtpy/QtTextToSpeech.py index 038116bc..81eae9eb 100644 --- a/qtpy/QtTextToSpeech.py +++ b/qtpy/QtTextToSpeech.py @@ -7,7 +7,14 @@ """Provides QtTextToSpeech classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: from PyQt5.QtTextToSpeech import * diff --git a/qtpy/QtUiTools.py b/qtpy/QtUiTools.py index eb12bd4e..1dc9e377 100644 --- a/qtpy/QtUiTools.py +++ b/qtpy/QtUiTools.py @@ -7,7 +7,14 @@ """Provides QtUiTools classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='QtUiTools') diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index 2d6fb3cd..e592f4d7 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -8,7 +8,15 @@ """Provides QtWebEngine classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInQtVersionError, + QtModuleNotInstalledError, +) if PYQT5: try: diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py index a23d22bd..d8c6ca3a 100644 --- a/qtpy/QtWebEngineCore.py +++ b/qtpy/QtWebEngineCore.py @@ -7,7 +7,14 @@ """Provides QtWebEngineCore classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: diff --git a/qtpy/QtWebEngineQuick.py b/qtpy/QtWebEngineQuick.py index 6aef6259..cd10d1d5 100644 --- a/qtpy/QtWebEngineQuick.py +++ b/qtpy/QtWebEngineQuick.py @@ -7,7 +7,15 @@ """Provides QtWebEngineQuick classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='QtWebEngineQuick') diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index c38a4f5d..1d0fbda1 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -8,7 +8,14 @@ """Provides QtWebEngineWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInstalledError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInstalledError, +) if PYQT5: try: @@ -16,6 +23,7 @@ from PyQt5.QtWebEngineWidgets import QWebEngineView from PyQt5.QtWebEngineWidgets import QWebEngineSettings from PyQt5.QtWebEngineWidgets import QWebEngineScript + # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 from PyQt5.QtWebEngineWidgets import QWebEngineProfile except ModuleNotFoundError as error: @@ -38,6 +46,7 @@ from PySide2.QtWebEngineWidgets import QWebEngineView from PySide2.QtWebEngineWidgets import QWebEngineSettings from PySide2.QtWebEngineWidgets import QWebEngineScript + # Based on the work at https://github.com/spyder-ide/qtpy/pull/203 from PySide2.QtWebEngineWidgets import QWebEngineProfile elif PYSIDE6: diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index ae786cb7..3ad71bb0 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -8,7 +8,15 @@ """Provides Windows-specific utilities""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInOSError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInQtVersionError, + QtModuleNotInOSError, +) if sys.platform == 'win32': if PYQT5: diff --git a/qtpy/QtX11Extras.py b/qtpy/QtX11Extras.py index e3cd828d..3e225a0c 100644 --- a/qtpy/QtX11Extras.py +++ b/qtpy/QtX11Extras.py @@ -8,7 +8,15 @@ """Provides Linux-specific utilities""" import sys -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInOSError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtModuleNotInQtVersionError, + QtModuleNotInOSError, +) if sys.platform == 'linux': if PYQT5: diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py index a6492f0a..71040e0c 100644 --- a/qtpy/QtXmlPatterns.py +++ b/qtpy/QtXmlPatterns.py @@ -7,7 +7,14 @@ """Provides QtXmlPatterns classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: from PyQt5.QtXmlPatterns import * diff --git a/qtpy/shiboken.py b/qtpy/shiboken.py index 49d5e94a..4133e85d 100644 --- a/qtpy/shiboken.py +++ b/qtpy/shiboken.py @@ -7,7 +7,14 @@ """Provides access to shiboken.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: raise QtBindingMissingModuleError(name='shiboken') diff --git a/qtpy/sip.py b/qtpy/sip.py index f79e47be..736af76f 100644 --- a/qtpy/sip.py +++ b/qtpy/sip.py @@ -7,7 +7,14 @@ """Provides access to sip.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) if PYQT5: from PyQt5.sip import * From ce6932f99de1703c7790809d1b5a5dd116d97e9a Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Mon, 6 Jun 2022 13:34:06 -0400 Subject: [PATCH 451/703] Apply formatting suggestions to tests --- qtpy/tests/test_qdesktopservice_split.py | 2 -- qtpy/tests/test_qt3danimation.py | 1 + qtpy/tests/test_qt3dcore.py | 1 + qtpy/tests/test_qt3dextras.py | 1 + qtpy/tests/test_qt3dinput.py | 2 +- qtpy/tests/test_qt3dlogic.py | 3 ++- qtpy/tests/test_qt3drender.py | 1 + qtpy/tests/test_qtaxcontainer.py | 3 ++- qtpy/tests/test_qtbluetooth.py | 1 + qtpy/tests/test_qtcharts.py | 6 ++++-- qtpy/tests/test_qtconcurrent.py | 3 ++- qtpy/tests/test_qtcore.py | 10 +++++++++- qtpy/tests/test_qtdbus.py | 1 + qtpy/tests/test_qtdesigner.py | 2 ++ qtpy/tests/test_qtlocation.py | 4 +++- qtpy/tests/test_qtmacextras.py | 4 +++- qtpy/tests/test_qtmultimedia.py | 6 ++++-- qtpy/tests/test_qtmultimediawidgets.py | 10 +++++----- qtpy/tests/test_qtnetworkauth.py | 9 ++++++--- qtpy/tests/test_qtopengl.py | 2 +- qtpy/tests/test_qtopenglwidgets.py | 1 + qtpy/tests/test_qtpositioning.py | 2 ++ qtpy/tests/test_qtprintsupport.py | 6 ++++-- qtpy/tests/test_qtpurchasing.py | 3 ++- qtpy/tests/test_qtqml.py | 2 ++ qtpy/tests/test_qtquick.py | 10 ++++++---- qtpy/tests/test_qtquick3d.py | 3 ++- qtpy/tests/test_qtquickcontrols2.py | 3 ++- qtpy/tests/test_qtquickwidgets.py | 2 ++ qtpy/tests/test_qtremoteobjects.py | 1 + qtpy/tests/test_qtscxml.py | 1 + qtpy/tests/test_qtsensors.py | 2 +- qtpy/tests/test_qtserialport.py | 1 + qtpy/tests/test_qtsql.py | 11 +++++++---- qtpy/tests/test_qtstatemachine.py | 1 + qtpy/tests/test_qtsvg.py | 1 + qtpy/tests/test_qtsvgwidgets.py | 3 ++- qtpy/tests/test_qttexttospeech.py | 6 ++++-- qtpy/tests/test_qtuitools.py | 3 ++- qtpy/tests/test_qtwebchannel.py | 2 +- qtpy/tests/test_qtwebenginecore.py | 2 +- qtpy/tests/test_qtwebenginequick.py | 3 ++- qtpy/tests/test_qtwebenginewidgets.py | 1 + qtpy/tests/test_qtwebsockets.py | 1 + qtpy/tests/test_qtwinextras.py | 9 +++++---- qtpy/tests/test_qtx11extras.py | 1 + qtpy/tests/test_qtxml.py | 1 + qtpy/tests/test_qtxmlpatterns.py | 2 ++ qtpy/tests/test_shiboken.py | 1 + qtpy/tests/test_sip.py | 1 + 50 files changed, 111 insertions(+), 47 deletions(-) diff --git a/qtpy/tests/test_qdesktopservice_split.py b/qtpy/tests/test_qdesktopservice_split.py index d412e9c8..27021762 100644 --- a/qtpy/tests/test_qdesktopservice_split.py +++ b/qtpy/tests/test_qdesktopservice_split.py @@ -21,5 +21,3 @@ def test_qdesktopservice(): from qtpy.QtGui import QDesktopServices assert QDesktopServices.setUrlHandler is not None - - diff --git a/qtpy/tests/test_qt3danimation.py b/qtpy/tests/test_qt3danimation.py index 3e355cb8..c546f0c1 100644 --- a/qtpy/tests/test_qt3danimation.py +++ b/qtpy/tests/test_qt3danimation.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYQT5, PYSIDE2 + @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qt3danimation(): """Test the qtpy.Qt3DAnimation namespace""" diff --git a/qtpy/tests/test_qt3dcore.py b/qtpy/tests/test_qt3dcore.py index 9596aa23..cd17542e 100644 --- a/qtpy/tests/test_qt3dcore.py +++ b/qtpy/tests/test_qt3dcore.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYQT6, PYSIDE6 + @pytest.mark.skipif(PYQT6, reason="Not complete in PyQt6") @pytest.mark.skipif(PYSIDE6, reason="Not complete in PySide6") def test_qt3dcore(): diff --git a/qtpy/tests/test_qt3dextras.py b/qtpy/tests/test_qt3dextras.py index 500ee2a6..ba3f0e14 100644 --- a/qtpy/tests/test_qt3dextras.py +++ b/qtpy/tests/test_qt3dextras.py @@ -1,5 +1,6 @@ import pytest + def test_qt3dextras(): """Test the qtpy.Qt3DExtras namespace""" Qt3DExtras = pytest.importorskip("qtpy.Qt3DExtras") diff --git a/qtpy/tests/test_qt3dinput.py b/qtpy/tests/test_qt3dinput.py index 73ec711a..562055ed 100644 --- a/qtpy/tests/test_qt3dinput.py +++ b/qtpy/tests/test_qt3dinput.py @@ -1,5 +1,6 @@ import pytest + def test_qt3dinput(): """Test the qtpy.Qt3DInput namespace""" Qt3DInput = pytest.importorskip("qtpy.Qt3DInput") @@ -26,4 +27,3 @@ def test_qt3dinput(): assert Qt3DInput.QAction is not None assert Qt3DInput.QAbstractPhysicalDevice is not None assert Qt3DInput.QAxisSetting is not None - diff --git a/qtpy/tests/test_qt3dlogic.py b/qtpy/tests/test_qt3dlogic.py index 4c2df821..c325bf76 100644 --- a/qtpy/tests/test_qt3dlogic.py +++ b/qtpy/tests/test_qt3dlogic.py @@ -1,8 +1,9 @@ import pytest + def test_qt3dlogic(): """Test the qtpy.Qt3DLogic namespace""" Qt3DLogic = pytest.importorskip("qtpy.Qt3DLogic") - + assert Qt3DLogic.QLogicAspect is not None assert Qt3DLogic.QFrameAction is not None diff --git a/qtpy/tests/test_qt3drender.py b/qtpy/tests/test_qt3drender.py index 648d56e7..c620721b 100644 --- a/qtpy/tests/test_qt3drender.py +++ b/qtpy/tests/test_qt3drender.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYQT6, PYSIDE6 + @pytest.mark.skipif(PYQT6, reason="Not complete in PyQt6") @pytest.mark.skipif(PYSIDE6, reason="Not complete in PySide6") def test_qt3drender(): diff --git a/qtpy/tests/test_qtaxcontainer.py b/qtpy/tests/test_qtaxcontainer.py index 382abd1d..6e31a153 100644 --- a/qtpy/tests/test_qtaxcontainer.py +++ b/qtpy/tests/test_qtaxcontainer.py @@ -1,8 +1,9 @@ import pytest + def test_qtaxcontainer(): """Test the qtpy.QtAxContainer namespace""" QtAxContainer = pytest.importorskip("qtpy.QtAxContainer") - + assert QtAxContainer.QAxSelect is not None assert QtAxContainer.QAxWidget is not None diff --git a/qtpy/tests/test_qtbluetooth.py b/qtpy/tests/test_qtbluetooth.py index 84c3b002..c1f1e547 100644 --- a/qtpy/tests/test_qtbluetooth.py +++ b/qtpy/tests/test_qtbluetooth.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYSIDE2 + def test_qtbluetooth(): """Test the qtpy.QtBluetooth namespace""" QtBluetooth = pytest.importorskip("qtpy.QtBluetooth") diff --git a/qtpy/tests/test_qtcharts.py b/qtpy/tests/test_qtcharts.py index 342ad40a..4873e01f 100644 --- a/qtpy/tests/test_qtcharts.py +++ b/qtpy/tests/test_qtcharts.py @@ -3,9 +3,11 @@ from qtpy import PYSIDE2, PYSIDE6 -@pytest.mark.skipif(not (PYSIDE2 or PYSIDE6), reason="Only available by default in PySide") +@pytest.mark.skipif( + not (PYSIDE2 or PYSIDE6), reason="Only available by default in PySide" +) def test_qtcharts(): - """Test the qtpy.QtCharts namespace""" + """Test the qtpy.QtCharts namespace""" QtCharts = pytest.importorskip("qtpy.QtCharts") assert QtCharts.QChart is not None diff --git a/qtpy/tests/test_qtconcurrent.py b/qtpy/tests/test_qtconcurrent.py index 8499545d..7bd0c4ca 100644 --- a/qtpy/tests/test_qtconcurrent.py +++ b/qtpy/tests/test_qtconcurrent.py @@ -2,13 +2,14 @@ from packaging.version import parse from qtpy import PYSIDE2, PYSIDE_VERSION + def test_qtconcurrent(): """Test the qtpy.QtConcurrent namespace""" QtConcurrent = pytest.importorskip("qtpy.QtConcurrent") assert QtConcurrent.QtConcurrent is not None - if PYSIDE2 and parse(PYSIDE_VERSION) >= parse('5.15.2'): + if PYSIDE2 and parse(PYSIDE_VERSION) >= parse("5.15.2"): assert QtConcurrent.QFutureQString is not None assert QtConcurrent.QFutureVoid is not None assert QtConcurrent.QFutureWatcherQString is not None diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 158d6863..7ff60a01 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -5,7 +5,15 @@ import pytest -from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6, PYQT_VERSION, PYSIDE_VERSION, QtCore +from qtpy import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + PYQT_VERSION, + PYSIDE_VERSION, + QtCore, +) from qtpy.tests.utils import not_using_conda diff --git a/qtpy/tests/test_qtdbus.py b/qtpy/tests/test_qtdbus.py index cad9c90d..6b9a9ae2 100644 --- a/qtpy/tests/test_qtdbus.py +++ b/qtpy/tests/test_qtdbus.py @@ -2,6 +2,7 @@ import sys from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 + def test_qtdbus(): """Test the qtpy.QtDBus namespace""" QtDBus = pytest.importorskip("qtpy.QtDBus") diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index 04b1374e..be957410 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -1,10 +1,12 @@ import pytest from qtpy import PYSIDE2 + @pytest.mark.skipif(PYSIDE2, reason="QtDesigner is not avalaible in PySide2") def test_qtdesigner(): """Test the qtpy.QtDesigner namespace.""" from qtpy import QtDesigner + assert QtDesigner.QAbstractExtensionFactory is not None assert QtDesigner.QAbstractExtensionManager is not None assert QtDesigner.QDesignerActionEditorInterface is not None diff --git a/qtpy/tests/test_qtlocation.py b/qtpy/tests/test_qtlocation.py index 012db7bc..db3cd384 100644 --- a/qtpy/tests/test_qtlocation.py +++ b/qtpy/tests/test_qtlocation.py @@ -1,10 +1,12 @@ import pytest from qtpy import PYQT5, PYSIDE2 + @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtlocation(): """Test the qtpy.QtLocation namespace""" from qtpy import QtLocation + assert QtLocation.QGeoCodeReply is not None assert QtLocation.QGeoCodingManager is not None assert QtLocation.QGeoCodingManagerEngine is not None @@ -16,7 +18,7 @@ def test_qtlocation(): assert QtLocation.QGeoRoutingManager is not None assert QtLocation.QGeoRoutingManagerEngine is not None assert QtLocation.QGeoServiceProvider is not None - #assert QtLocation.QGeoServiceProviderFactory is not None + # assert QtLocation.QGeoServiceProviderFactory is not None assert QtLocation.QPlace is not None assert QtLocation.QPlaceAttribute is not None assert QtLocation.QPlaceCategory is not None diff --git a/qtpy/tests/test_qtmacextras.py b/qtpy/tests/test_qtmacextras.py index 65f1cdca..3e391d53 100644 --- a/qtpy/tests/test_qtmacextras.py +++ b/qtpy/tests/test_qtmacextras.py @@ -4,10 +4,12 @@ from qtpy import PYQT6, PYSIDE2, PYSIDE6 from qtpy.tests.utils import using_conda + @pytest.mark.skipif(PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") @pytest.mark.skipif( sys.platform != "darwin" or using_conda(), - reason="Only available in Qt5 bindings > 5.9 with pip on mac in CIs") + reason="Only available in Qt5 bindings > 5.9 with pip on mac in CIs", +) def test_qtmacextras(): """Test the qtpy.QtMacExtras namespace""" QtMacExtras = pytest.importorskip("qtpy.QtMacExtras") diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 6558b2ba..5da7ea4b 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -4,9 +4,11 @@ from qtpy import PYSIDE6, PYQT6 + @pytest.mark.skipif( - sys.platform.startswith('linux') and (PYSIDE6 or PYQT6), - reason="Needs to setup GStreamer on Linux") + sys.platform.startswith("linux") and (PYSIDE6 or PYQT6), + reason="Needs to setup GStreamer on Linux", +) def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" from qtpy import QtMultimedia diff --git a/qtpy/tests/test_qtmultimediawidgets.py b/qtpy/tests/test_qtmultimediawidgets.py index 4a7fca9a..9d6b099a 100644 --- a/qtpy/tests/test_qtmultimediawidgets.py +++ b/qtpy/tests/test_qtmultimediawidgets.py @@ -3,13 +3,13 @@ import pytest from qtpy import PYQT5, PYSIDE2 -from qtpy.tests.utils import using_conda +from qtpy.tests.utils import using_conda + +@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") @pytest.mark.skipif( - not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") -@pytest.mark.skipif( - using_conda(), - reason="Conda packages don't seem to include QtMultimedia") + using_conda(), reason="Conda packages don't seem to include QtMultimedia" +) def test_qtmultimediawidgets(): """Test the qtpy.QtMultimediaWidgets namespace""" from qtpy import QtMultimediaWidgets diff --git a/qtpy/tests/test_qtnetworkauth.py b/qtpy/tests/test_qtnetworkauth.py index 30fc0c11..ba71d0ab 100644 --- a/qtpy/tests/test_qtnetworkauth.py +++ b/qtpy/tests/test_qtnetworkauth.py @@ -2,12 +2,15 @@ from qtpy import PYQT5, PYQT6, PYSIDE2 -@pytest.mark.skipif(PYQT5 or PYQT6 or PYSIDE2, - reason="Not available by default in PyQt. Not available for PySide2") + +@pytest.mark.skipif( + PYQT5 or PYQT6 or PYSIDE2, + reason="Not available by default in PyQt. Not available for PySide2", +) def test_qtnetworkauth(): """Test the qtpy.QtNetworkAuth namespace""" QtNetworkAuth = pytest.importorskip("qtpy.QtNetworkAuth") - + assert QtNetworkAuth.QAbstractOAuth is not None assert QtNetworkAuth.QAbstractOAuth2 is not None assert QtNetworkAuth.QAbstractOAuthReplyHandler is not None diff --git a/qtpy/tests/test_qtopengl.py b/qtpy/tests/test_qtopengl.py index b4b25120..567fed3b 100644 --- a/qtpy/tests/test_qtopengl.py +++ b/qtpy/tests/test_qtopengl.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 + def test_qtopengl(): """Test the qtpy.QtOpenGL namespace""" from qtpy import QtOpenGL @@ -22,4 +23,3 @@ def test_qtopengl(): assert QtOpenGL.QOpenGLWindow is not None # We do not test for QOpenGLTimeMonitor or QOpenGLTimerQuery as # they are not present on some architectures such as armhf - diff --git a/qtpy/tests/test_qtopenglwidgets.py b/qtpy/tests/test_qtopenglwidgets.py index 3085c078..8cde7296 100644 --- a/qtpy/tests/test_qtopenglwidgets.py +++ b/qtpy/tests/test_qtopenglwidgets.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 + @pytest.mark.skipif(PYSIDE2 or PYQT5, reason="Not available in PySide2/PyQt5") def test_qtopenglwidgets(): """Test the qtpy.QtOpenGLWidgets namespace""" diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py index 5c691864..bc72f4cc 100644 --- a/qtpy/tests/test_qtpositioning.py +++ b/qtpy/tests/test_qtpositioning.py @@ -1,10 +1,12 @@ import pytest from qtpy import PYQT5, PYSIDE2 + @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtpositioning(): """Test the qtpy.QtPositioning namespace""" from qtpy import QtPositioning + assert QtPositioning.QGeoAddress is not None assert QtPositioning.QGeoAreaMonitorInfo is not None assert QtPositioning.QGeoAreaMonitorSource is not None diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py index 45770042..952909fb 100644 --- a/qtpy/tests/test_qtprintsupport.py +++ b/qtpy/tests/test_qtprintsupport.py @@ -7,6 +7,7 @@ from qtpy import QtPrintSupport from qtpy.tests.utils import not_using_conda + def test_qtprintsupport(): """Test the qtpy.QtPrintSupport namespace""" assert QtPrintSupport.QAbstractPrintDialog is not None @@ -30,8 +31,9 @@ def test_qprintdialog_exec_(): @pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") + sys.platform.startswith("linux") and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda", +) def test_qprintpreviewwidget_print_(qtbot): """Test qtpy.QtPrintSupport.QPrintPreviewWidget print_""" assert QtPrintSupport.QPrintPreviewWidget.print_ is not None diff --git a/qtpy/tests/test_qtpurchasing.py b/qtpy/tests/test_qtpurchasing.py index 7b0db0e4..d4c5173b 100644 --- a/qtpy/tests/test_qtpurchasing.py +++ b/qtpy/tests/test_qtpurchasing.py @@ -1,9 +1,10 @@ import pytest + def test_qtpurchasing(): """Test the qtpy.QtPurchasing namespace""" QtPurchasing = pytest.importorskip("qtpy.QtPurchasing") - + assert QtPurchasing.QInAppProduct is not None assert QtPurchasing.QInAppStore is not None assert QtPurchasing.QInAppTransaction is not None diff --git a/qtpy/tests/test_qtqml.py b/qtpy/tests/test_qtqml.py index f2a229b9..8f8fe431 100644 --- a/qtpy/tests/test_qtqml.py +++ b/qtpy/tests/test_qtqml.py @@ -1,10 +1,12 @@ import pytest from qtpy import PYQT5, PYSIDE2 + @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtqml(): """Test the qtpy.QtQml namespace""" from qtpy import QtQml + assert QtQml.QJSEngine is not None assert QtQml.QJSValue is not None assert QtQml.QJSValueIterator is not None diff --git a/qtpy/tests/test_qtquick.py b/qtpy/tests/test_qtquick.py index d21e2af8..07a7dc46 100644 --- a/qtpy/tests/test_qtquick.py +++ b/qtpy/tests/test_qtquick.py @@ -1,10 +1,12 @@ import pytest from qtpy import PYQT5, PYSIDE2 + @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtquick(): """Test the qtpy.QtQuick namespace""" from qtpy import QtQuick + assert QtQuick.QQuickAsyncImageProvider is not None if not PYSIDE2: assert QtQuick.QQuickCloseEvent is not None @@ -28,7 +30,7 @@ def test_qtquick(): assert QtQuick.QSGFlatColorMaterial is not None assert QtQuick.QSGGeometry is not None assert QtQuick.QSGGeometryNode is not None - #assert QtQuick.QSGImageNode is not None + # assert QtQuick.QSGImageNode is not None if not PYSIDE2: assert QtQuick.QSGMaterial is not None assert QtQuick.QSGMaterialShader is not None @@ -37,9 +39,9 @@ def test_qtquick(): assert QtQuick.QSGOpacityNode is not None if not PYSIDE2: assert QtQuick.QSGOpaqueTextureMaterial is not None - #assert QtQuick.QSGRectangleNode is not None - #assert QtQuick.QSGRenderNode is not None - #assert QtQuick.QSGRendererInterface is not None + # assert QtQuick.QSGRectangleNode is not None + # assert QtQuick.QSGRenderNode is not None + # assert QtQuick.QSGRendererInterface is not None assert QtQuick.QSGSimpleRectNode is not None assert QtQuick.QSGSimpleTextureNode is not None assert QtQuick.QSGTexture is not None diff --git a/qtpy/tests/test_qtquick3d.py b/qtpy/tests/test_qtquick3d.py index fca6d515..ca614bd6 100644 --- a/qtpy/tests/test_qtquick3d.py +++ b/qtpy/tests/test_qtquick3d.py @@ -1,9 +1,10 @@ import pytest + def test_qtquick3d(): """Test the qtpy.QtQuick3D namespace""" QtQuick3D = pytest.importorskip("qtpy.QtQuick3D") - + assert QtQuick3D.QQuick3D is not None assert QtQuick3D.QQuick3DGeometry is not None assert QtQuick3D.QQuick3DObject is not None diff --git a/qtpy/tests/test_qtquickcontrols2.py b/qtpy/tests/test_qtquickcontrols2.py index d87f45b4..a77ef001 100644 --- a/qtpy/tests/test_qtquickcontrols2.py +++ b/qtpy/tests/test_qtquickcontrols2.py @@ -1,7 +1,8 @@ import pytest + def test_qtquickcontrols2(): """Test the qtpy.QtQuickControls2 namespace""" QtQuickControls2 = pytest.importorskip("qtpy.QtQuickControls2") - + assert QtQuickControls2.QQuickStyle is not None diff --git a/qtpy/tests/test_qtquickwidgets.py b/qtpy/tests/test_qtquickwidgets.py index f055a308..53c1d208 100644 --- a/qtpy/tests/test_qtquickwidgets.py +++ b/qtpy/tests/test_qtquickwidgets.py @@ -1,8 +1,10 @@ import pytest from qtpy import PYQT5, PYSIDE2 + @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtquickwidgets(): """Test the qtpy.QtQuickWidgets namespace""" from qtpy import QtQuickWidgets + assert QtQuickWidgets.QQuickWidget is not None diff --git a/qtpy/tests/test_qtremoteobjects.py b/qtpy/tests/test_qtremoteobjects.py index 88234b9d..4d91fb2f 100644 --- a/qtpy/tests/test_qtremoteobjects.py +++ b/qtpy/tests/test_qtremoteobjects.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYSIDE2, PYSIDE6, PYQT5, PYQT6 + def test_qtremoteobjects(): """Test the qtpy.QtRemoteObjects namespace""" QtRemoteObjects = pytest.importorskip("qtpy.QtRemoteObjects") diff --git a/qtpy/tests/test_qtscxml.py b/qtpy/tests/test_qtscxml.py index eb77931d..40033799 100644 --- a/qtpy/tests/test_qtscxml.py +++ b/qtpy/tests/test_qtscxml.py @@ -1,5 +1,6 @@ import pytest + def test_qtscxml(): """Test the qtpy.QtScxml namespace""" QtScxml = pytest.importorskip("qtpy.QtScxml") diff --git a/qtpy/tests/test_qtsensors.py b/qtpy/tests/test_qtsensors.py index b0d1f21f..3bc087bd 100644 --- a/qtpy/tests/test_qtsensors.py +++ b/qtpy/tests/test_qtsensors.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYSIDE6, PYQT6 + def test_qtsensors(): """Test the qtpy.QtSensors namespace""" from qtpy import QtSensors @@ -8,4 +9,3 @@ def test_qtsensors(): assert QtSensors.QAccelerometer is not None assert QtSensors.QAccelerometerFilter is not None assert QtSensors.QAccelerometerReading is not None - diff --git a/qtpy/tests/test_qtserialport.py b/qtpy/tests/test_qtserialport.py index cb913427..a6cf5031 100644 --- a/qtpy/tests/test_qtserialport.py +++ b/qtpy/tests/test_qtserialport.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYSIDE2 + @pytest.mark.skipif(PYSIDE2, reason="Not available in CI") def test_qtserialport(): """Test the qtpy.QtSerialPort namespace""" diff --git a/qtpy/tests/test_qtsql.py b/qtpy/tests/test_qtsql.py index f69e6c55..f9dcf96b 100644 --- a/qtpy/tests/test_qtsql.py +++ b/qtpy/tests/test_qtsql.py @@ -35,9 +35,11 @@ def test_qtsql(): # Following modules are not (yet) part of any wrapper: # QSqlDriverCreator, QSqlDriverPlugin + @pytest.mark.skipif( - sys.platform == 'win32' and PYSIDE2 and PYSIDE_VERSION.startswith('5.13'), - reason="SQLite driver unavailable on PySide 5.13.2 with Windows") + sys.platform == "win32" and PYSIDE2 and PYSIDE_VERSION.startswith("5.13"), + reason="SQLite driver unavailable on PySide 5.13.2 with Windows", +) def test_qtsql_members_aliases(database_connection): """ Test aliased methods over qtpy.QtSql members including: @@ -59,7 +61,7 @@ def test_qtsql_members_aliases(database_connection): id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL, name VARCHAR(40) NOT NULL ) - """ + """, ) # Created table 'test' and 'sqlite_sequence' @@ -78,7 +80,8 @@ def test_qtsql_members_aliases(database_connection): select_table_query.prepare( """ SELECT * FROM test - """) + """ + ) select_table_query.exec_() record = select_table_query.record() assert not record.isEmpty() diff --git a/qtpy/tests/test_qtstatemachine.py b/qtpy/tests/test_qtstatemachine.py index fc9b43f5..5fa986b0 100644 --- a/qtpy/tests/test_qtstatemachine.py +++ b/qtpy/tests/test_qtstatemachine.py @@ -1,5 +1,6 @@ import pytest + def test_qtstatemachine(): """Test the qtpy.QtStateMachine namespace""" QtStateMachine = pytest.importorskip("qtpy.QtStateMachine") diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py index 4b2a0bb9..298d9a93 100644 --- a/qtpy/tests/test_qtsvg.py +++ b/qtpy/tests/test_qtsvg.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYSIDE6, PYQT6 + def test_qtsvg(): """Test the qtpy.QtSvg namespace""" from qtpy import QtSvg diff --git a/qtpy/tests/test_qtsvgwidgets.py b/qtpy/tests/test_qtsvgwidgets.py index aa0d5c38..75339250 100644 --- a/qtpy/tests/test_qtsvgwidgets.py +++ b/qtpy/tests/test_qtsvgwidgets.py @@ -1,8 +1,9 @@ import pytest + def test_qtsvgwidgets(): """Test the qtpy.QtSvgWidgets namespace""" QtSvgWidgets = pytest.importorskip("qtpy.QtSvgWidgets") - + assert QtSvgWidgets.QGraphicsSvgItem is not None assert QtSvgWidgets.QSvgWidget is not None diff --git a/qtpy/tests/test_qttexttospeech.py b/qtpy/tests/test_qttexttospeech.py index 4cc0daa1..f62bc3e0 100644 --- a/qtpy/tests/test_qttexttospeech.py +++ b/qtpy/tests/test_qttexttospeech.py @@ -3,8 +3,10 @@ from qtpy import PYQT5, PYSIDE2, PYQT_VERSION -@pytest.mark.skipif(not ((PYQT5 and version.parse(PYQT_VERSION) >= version.parse('5.15.1')) or PYSIDE2), - reason="Only available in Qt5 bindings (PyQt5 >= 5.15.1 or PySide2)") +@pytest.mark.skipif( + not ((PYQT5 and version.parse(PYQT_VERSION) >= version.parse("5.15.1")) or PYSIDE2), + reason="Only available in Qt5 bindings (PyQt5 >= 5.15.1 or PySide2)", +) def test_qttexttospeech(): """Test the qtpy.QtTextToSpeech namespace.""" from qtpy import QtTextToSpeech diff --git a/qtpy/tests/test_qtuitools.py b/qtpy/tests/test_qtuitools.py index 6e14cc49..13ee4026 100644 --- a/qtpy/tests/test_qtuitools.py +++ b/qtpy/tests/test_qtuitools.py @@ -1,7 +1,8 @@ import pytest + def test_qtuitools(): """Test the qtpy.QtUiTools namespace""" QtUiTools = pytest.importorskip("qtpy.QtUiTools") - + assert QtUiTools.QUiLoader is not None diff --git a/qtpy/tests/test_qtwebchannel.py b/qtpy/tests/test_qtwebchannel.py index 4f33a03b..25e991f3 100644 --- a/qtpy/tests/test_qtwebchannel.py +++ b/qtpy/tests/test_qtwebchannel.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYQT5, PYSIDE2 + @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtwebchannel(): """Test the qtpy.QtWebChannel namespace""" @@ -8,4 +9,3 @@ def test_qtwebchannel(): assert QtWebChannel.QWebChannel is not None assert QtWebChannel.QWebChannelAbstractTransport is not None - diff --git a/qtpy/tests/test_qtwebenginecore.py b/qtpy/tests/test_qtwebenginecore.py index d52c2786..a00287ee 100644 --- a/qtpy/tests/test_qtwebenginecore.py +++ b/qtpy/tests/test_qtwebenginecore.py @@ -1,9 +1,9 @@ import pytest from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6 + def test_qtwebenginecore(): """Test the qtpy.QtWebEngineCore namespace""" QtWebEngineCore = pytest.importorskip("qtpy.QtWebEngineCore") assert QtWebEngineCore.QWebEngineHttpRequest is not None - diff --git a/qtpy/tests/test_qtwebenginequick.py b/qtpy/tests/test_qtwebenginequick.py index a6155c2d..50b4bc56 100644 --- a/qtpy/tests/test_qtwebenginequick.py +++ b/qtpy/tests/test_qtwebenginequick.py @@ -1,10 +1,11 @@ import pytest from qtpy import PYQT5, PYQT6, PYSIDE2, PYSIDE6 + @pytest.mark.skipif(PYQT5 or PYSIDE2, reason="Only available in Qt6 bindings") def test_qtwebenginequick(): """Test the qtpy.QtWebEngineQuick namespace""" - + QtWebEngineQuick = pytest.importorskip("qtpy.QtWebEngineQuick") assert QtWebEngineQuick.QtWebEngineQuick is not None diff --git a/qtpy/tests/test_qtwebenginewidgets.py b/qtpy/tests/test_qtwebenginewidgets.py index f9b3ac48..680de397 100644 --- a/qtpy/tests/test_qtwebenginewidgets.py +++ b/qtpy/tests/test_qtwebenginewidgets.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYSIDE6, PYQT6 + @pytest.mark.skipif(PYSIDE6 or PYQT6, reason="Only available in Qt<6,>=6.2 bindings") def test_qtwebenginewidgets(): """Test the qtpy.QtWebEngineWidget namespace""" diff --git a/qtpy/tests/test_qtwebsockets.py b/qtpy/tests/test_qtwebsockets.py index 420aa866..52de7692 100644 --- a/qtpy/tests/test_qtwebsockets.py +++ b/qtpy/tests/test_qtwebsockets.py @@ -1,6 +1,7 @@ import pytest from qtpy import PYQT5, PYSIDE2 + @pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtwebsockets(): """Test the qtpy.QtWebSockets namespace""" diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index ec220104..f68a4b4b 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -7,14 +7,16 @@ from qtpy import PYQT6, PYSIDE2, PYSIDE6 from qtpy.tests.utils import using_conda -@pytest.mark.skipif( - PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") + +@pytest.mark.skipif(PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") @pytest.mark.skipif( sys.platform != "win32" or using_conda(), - reason="Only available in Qt5 bindings > 5.9 with pip on Windows in CIs") + reason="Only available in Qt5 bindings > 5.9 with pip on Windows in CIs", +) def test_qtwinextras(): """Test the qtpy.QtWinExtras namespace""" from qtpy import QtWinExtras + assert QtWinExtras.QWinJumpList is not None assert QtWinExtras.QWinJumpListCategory is not None assert QtWinExtras.QWinJumpListItem is not None @@ -29,4 +31,3 @@ def test_qtwinextras(): assert QtWinExtras.QWinColorizationChangeEvent is not None assert QtWinExtras.QWinCompositionChangeEvent is not None assert QtWinExtras.QWinEvent is not None - diff --git a/qtpy/tests/test_qtx11extras.py b/qtpy/tests/test_qtx11extras.py index 218757b6..f1e683dc 100644 --- a/qtpy/tests/test_qtx11extras.py +++ b/qtpy/tests/test_qtx11extras.py @@ -1,5 +1,6 @@ import pytest + def test_qtwinextras(): QtX11Extras = pytest.importorskip("qtpy.QtX11Extras") diff --git a/qtpy/tests/test_qtxml.py b/qtpy/tests/test_qtxml.py index 27d4f235..432d975b 100644 --- a/qtpy/tests/test_qtxml.py +++ b/qtpy/tests/test_qtxml.py @@ -1,5 +1,6 @@ import pytest + def test_qtxml(): """Test the qtpy.QtXml namespace""" from qtpy import QtXml diff --git a/qtpy/tests/test_qtxmlpatterns.py b/qtpy/tests/test_qtxmlpatterns.py index a2984879..c75d91d2 100644 --- a/qtpy/tests/test_qtxmlpatterns.py +++ b/qtpy/tests/test_qtxmlpatterns.py @@ -1,10 +1,12 @@ import pytest from qtpy import PYSIDE2, PYSIDE6, PYQT6 + @pytest.mark.skipif((PYSIDE6 or PYQT6), reason="not available with qt 6.0") def test_qtxmlpatterns(): """Test the qtpy.QtXmlPatterns namespace""" from qtpy import QtXmlPatterns + assert QtXmlPatterns.QAbstractMessageHandler is not None assert QtXmlPatterns.QAbstractUriResolver is not None assert QtXmlPatterns.QAbstractXmlNodeModel is not None diff --git a/qtpy/tests/test_shiboken.py b/qtpy/tests/test_shiboken.py index 214f3219..4920fa55 100644 --- a/qtpy/tests/test_shiboken.py +++ b/qtpy/tests/test_shiboken.py @@ -1,5 +1,6 @@ import pytest + def test_shiboken(): """Test the qtpy.shiboken namespace""" shiboken = pytest.importorskip("qtpy.shiboken") diff --git a/qtpy/tests/test_sip.py b/qtpy/tests/test_sip.py index 14b77eb6..620d1fdf 100644 --- a/qtpy/tests/test_sip.py +++ b/qtpy/tests/test_sip.py @@ -1,5 +1,6 @@ import pytest + def test_sip(): """Test the qtpy.sip namespace""" sip = pytest.importorskip("qtpy.sip") From 08dded4c9cb86ff4bb74b45f349f5c1611dc7c98 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Thu, 9 Jun 2022 16:09:20 -0400 Subject: [PATCH 452/703] Rearrange asserts per suggestion from @dalthviz --- qtpy/tests/test_qtlocation.py | 4 +++- qtpy/tests/test_qtquick.py | 30 +++++++++++++----------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/qtpy/tests/test_qtlocation.py b/qtpy/tests/test_qtlocation.py index db3cd384..40e99c82 100644 --- a/qtpy/tests/test_qtlocation.py +++ b/qtpy/tests/test_qtlocation.py @@ -7,6 +7,9 @@ def test_qtlocation(): """Test the qtpy.QtLocation namespace""" from qtpy import QtLocation + if PYQT5: + assert QtLocation.QGeoServiceProviderFactory is not None + assert QtLocation.QGeoCodeReply is not None assert QtLocation.QGeoCodingManager is not None assert QtLocation.QGeoCodingManagerEngine is not None @@ -18,7 +21,6 @@ def test_qtlocation(): assert QtLocation.QGeoRoutingManager is not None assert QtLocation.QGeoRoutingManagerEngine is not None assert QtLocation.QGeoServiceProvider is not None - # assert QtLocation.QGeoServiceProviderFactory is not None assert QtLocation.QPlace is not None assert QtLocation.QPlaceAttribute is not None assert QtLocation.QPlaceCategory is not None diff --git a/qtpy/tests/test_qtquick.py b/qtpy/tests/test_qtquick.py index 07a7dc46..e1e68f34 100644 --- a/qtpy/tests/test_qtquick.py +++ b/qtpy/tests/test_qtquick.py @@ -7,9 +7,20 @@ def test_qtquick(): """Test the qtpy.QtQuick namespace""" from qtpy import QtQuick - assert QtQuick.QQuickAsyncImageProvider is not None - if not PYSIDE2: + if PYQT5: assert QtQuick.QQuickCloseEvent is not None + assert QtQuick.QSGFlatColorMaterial is not None + assert QtQuick.QSGImageNode is not None + assert QtQuick.QSGMaterial is not None + assert QtQuick.QSGMaterialShader is not None + assert QtQuick.QSGOpaqueTextureMaterial is not None + assert QtQuick.QSGRectangleNode is not None + assert QtQuick.QSGRenderNode is not None + assert QtQuick.QSGRendererInterface is not None + assert QtQuick.QSGTextureMaterial is not None + assert QtQuick.QSGVertexColorMaterial is not None + + assert QtQuick.QQuickAsyncImageProvider is not None assert QtQuick.QQuickFramebufferObject is not None assert QtQuick.QQuickImageProvider is not None assert QtQuick.QQuickImageResponse is not None @@ -26,28 +37,13 @@ def test_qtquick(): assert QtQuick.QSGClipNode is not None assert QtQuick.QSGDynamicTexture is not None assert QtQuick.QSGEngine is not None - if not PYSIDE2: - assert QtQuick.QSGFlatColorMaterial is not None assert QtQuick.QSGGeometry is not None assert QtQuick.QSGGeometryNode is not None - # assert QtQuick.QSGImageNode is not None - if not PYSIDE2: - assert QtQuick.QSGMaterial is not None - assert QtQuick.QSGMaterialShader is not None assert QtQuick.QSGMaterialType is not None assert QtQuick.QSGNode is not None assert QtQuick.QSGOpacityNode is not None - if not PYSIDE2: - assert QtQuick.QSGOpaqueTextureMaterial is not None - # assert QtQuick.QSGRectangleNode is not None - # assert QtQuick.QSGRenderNode is not None - # assert QtQuick.QSGRendererInterface is not None assert QtQuick.QSGSimpleRectNode is not None assert QtQuick.QSGSimpleTextureNode is not None assert QtQuick.QSGTexture is not None - if not PYSIDE2: - assert QtQuick.QSGTextureMaterial is not None assert QtQuick.QSGTextureProvider is not None assert QtQuick.QSGTransformNode is not None - if not PYSIDE2: - assert QtQuick.QSGVertexColorMaterial is not None From 9c01187bf04e05bb8189cf6789f8f2edeb436d69 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Thu, 9 Jun 2022 16:12:36 -0400 Subject: [PATCH 453/703] Add missing line after standard library imports --- qtpy/QtDBus.py | 1 + qtpy/QtMacExtras.py | 1 + qtpy/QtWinExtras.py | 1 + qtpy/QtX11Extras.py | 1 + 4 files changed, 4 insertions(+) diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index 0a27ec2c..2f863c3f 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -8,6 +8,7 @@ """Provides QtDBus classes and functions.""" import sys + from . import ( PYQT5, PYQT6, diff --git a/qtpy/QtMacExtras.py b/qtpy/QtMacExtras.py index d660ad5b..9d391cb6 100644 --- a/qtpy/QtMacExtras.py +++ b/qtpy/QtMacExtras.py @@ -8,6 +8,7 @@ """Provides classes and functions specific to macOS and iOS operating systems""" import sys + from . import ( PYQT5, PYQT6, diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index 3ad71bb0..475ad5e5 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -8,6 +8,7 @@ """Provides Windows-specific utilities""" import sys + from . import ( PYQT5, PYQT6, diff --git a/qtpy/QtX11Extras.py b/qtpy/QtX11Extras.py index 3e225a0c..be02348e 100644 --- a/qtpy/QtX11Extras.py +++ b/qtpy/QtX11Extras.py @@ -8,6 +8,7 @@ """Provides Linux-specific utilities""" import sys + from . import ( PYQT5, PYQT6, From f5786ab2835d1e6713d119524b334079c4542591 Mon Sep 17 00:00:00 2001 From: Daelon Suzuka Date: Thu, 9 Jun 2022 14:11:03 -0700 Subject: [PATCH 454/703] Fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Althviz Moré --- qtpy/tests/test_qtlocation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtlocation.py b/qtpy/tests/test_qtlocation.py index 40e99c82..bb599153 100644 --- a/qtpy/tests/test_qtlocation.py +++ b/qtpy/tests/test_qtlocation.py @@ -7,7 +7,7 @@ def test_qtlocation(): """Test the qtpy.QtLocation namespace""" from qtpy import QtLocation - if PYQT5: + if PYSIDE2: assert QtLocation.QGeoServiceProviderFactory is not None assert QtLocation.QGeoCodeReply is not None From 01985c1ea0cd56a39e3274e360b1980b4ef943e3 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Mon, 13 Jun 2022 10:04:55 -0500 Subject: [PATCH 455/703] Restore WEBENGINE constant in QtWebEngineWidgets --- qtpy/QtWebEngineWidgets.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index 1d0fbda1..eaeac6fa 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -17,6 +17,13 @@ QtModuleNotInstalledError, ) + +# To test if we are using WebEngine or WebKit +# NOTE: This constant is imported by other projects (e.g. Spyder), so please +# don't remove it. +WEBENGINE = True + + if PYQT5: try: from PyQt5.QtWebEngineWidgets import QWebEnginePage From d55f7093841ab58d7222de2d8ea635023502582d Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Thu, 30 Jun 2022 18:30:56 -0500 Subject: [PATCH 456/703] Add note to readme about use with Pyright --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 78b4473e..6b75616a 100644 --- a/README.md +++ b/README.md @@ -71,7 +71,7 @@ conda install qtpy ``` -### Mypy integration +### Type checker integration A Command Line Interface (CLI) is offered to help with usage of QtPy. Presently, its only feature is to generate command line arguments for Mypy @@ -101,6 +101,8 @@ the Mypy command line invocation as follows: mypy --package mypackage $(qtpy mypy-args) ``` +For Pyright support and other usage notes, see [this comment](https://github.com/spyder-ide/qtpy/issues/352#issuecomment-1170684412). + ## Contributing From 60af2d85eb25590e2ae7c1382011fe7fac818279 Mon Sep 17 00:00:00 2001 From: Zach Pearson Date: Sat, 16 Jul 2022 10:49:55 -0700 Subject: [PATCH 457/703] compat.py: Add wrapper around sip/shiboken isdeleted/isvalid --- qtpy/compat.py | 21 +++++++++++++++++++++ qtpy/tests/test_compat.py | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 qtpy/tests/test_compat.py diff --git a/qtpy/compat.py b/qtpy/compat.py index 45e11338..738a3d7f 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -7,6 +7,14 @@ """ import sys +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, +) + from .QtWidgets import QFileDialog @@ -129,3 +137,16 @@ def getsavefilename(parent=None, caption='', basedir='', filters='', caption=caption, basedir=basedir, filters=filters, selectedfilter=selectedfilter, options=options) + +# ============================================================================= +def isalive(object): + """Wrapper around sip.isdeleted and shiboken.isValid which tests whether + an object is currently alive.""" + if PYQT5 or PYQT6: + from . import sip + return not sip.isdeleted(object) + elif PYSIDE2 or PYSIDE6: + from . import shiboken + return shiboken.isValid(object) + else: + raise QtBindingsNotFoundError() diff --git a/qtpy/tests/test_compat.py b/qtpy/tests/test_compat.py new file mode 100644 index 00000000..856f20bc --- /dev/null +++ b/qtpy/tests/test_compat.py @@ -0,0 +1,21 @@ +"""Test the compat module.""" +import pytest +import sys + +from qtpy import compat, QtWidgets +from qtpy.tests.utils import not_using_conda + +@pytest.mark.skipif( + ((sys.version_info.major == 3 and sys.version_info.minor == 7) + and sys.platform.startswith('win') and not not_using_conda()) + or + (sys.platform.startswith('linux') and not_using_conda()), + reason="sip not included in Python3.7 on Windows, or in non-conda test suite on Linux" +) +def test_isalive(qtbot): + """Test compat.isalive""" + test_widget = QtWidgets.QWidget() + assert compat.isalive(test_widget) == True + with qtbot.waitSignal(test_widget.destroyed): + test_widget.deleteLater() + assert compat.isalive(test_widget) == False From b0942e1883ed89f69135f292903d046207773794 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 21 Jul 2022 14:28:37 -0500 Subject: [PATCH 458/703] Add initial 'Methods, helpers and QtPy namespace specifics' section to the README --- README.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/README.md b/README.md index 6b75616a..8d75e946 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,44 @@ default unless you set the `QT_API` environment variable. * `pyside6` (to use PySide6). +### Methods, helpers and QtPy namespace specifics + +As part of providing compatibility for the different Python Qt bindings available, QtPy does some modules mapping and exposes a `compat` module. Bellow you can check some elements to take into account when using QtPy. + +#### Module aliases and constants + +* `QtCore.pyqtSignal`, `QtCore.pyqtSlot` and `QtCore.pyqtProperty` (available on PyQt5/6) are replaced by `QtCore.Signal`, `QtCore.Slot` and `QtCore.Property` following the Qt5 module layout. + +* The Qt version being used can be checked with `QtCore.__version__` (instead of `QtCore.QT_VERSION_STR`) as well as from `qtpy.QT_VERSION`. + +* 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 `QOpenGL*` classes. + +* To check the current PySide2/6 binding version you can use `qtpy.PYSIDE_VERSION`. If PySide is not being used as the binding the value returned will be `None`. + +* To check the current PyQt5/6 binding version you can use `qtpy.PYQT_VERSION`. If PyQt is not being used as the binding the value returned will be `None`. + +* To check the current selected binding you can use `qtpy.API_NAME` + +* There are boolean values to check if Qt5/6, PyQt5/6 or PySide2/6 are being used: `qtpy.Qt5`, `qtpy.Qt6`, `qtpy.PYQT5`, `qtpy.PYQT6`, `qtpy.PYSIDE2` and `qtpy.PYSIDE6`. `True` if currently being used, `False` otherwise. + +#### Compat module + +In the `qtpy.compat` module you can find wrappers for `QFileDialog` static methods and sip/shiboken functions as: + +* `QFileDialog.getExistingDirectory` wrapped with `qtpy.compat.getexistingdirectory` + +* `QFileDialog.getOpenFileName` wrapped with `qtpy.compat.getopenfilename` + +* `QFileDialog.getOpenFileNames` wrapped with `qtpy.compat.getopenfilenames` + +* `QFileDialog.getSaveFileName` wrapped with `qtpy.compat.getsavefilename` + +* `sip.isdeleted` and `shiboken.isValid` wrapped with `qtpy.compat.isalive` + + ### Installation ```bash From a1aeb777ccfd6210ab2e4adaafa83da3b6162901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Fri, 22 Jul 2022 11:28:15 -0500 Subject: [PATCH 459/703] Apply suggestions from code review Co-authored-by: CAM Gerlach --- README.md | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 8d75e946..c482c6b6 100644 --- a/README.md +++ b/README.md @@ -58,32 +58,25 @@ default unless you set the `QT_API` environment variable. * `pyside6` (to use PySide6). -### Methods, helpers and QtPy namespace specifics - -As part of providing compatibility for the different Python Qt bindings available, QtPy does some modules mapping and exposes a `compat` module. Bellow you can check some elements to take into account when using QtPy. - -#### Module aliases and constants +### Module aliases and constants * `QtCore.pyqtSignal`, `QtCore.pyqtSlot` and `QtCore.pyqtProperty` (available on PyQt5/6) are replaced by `QtCore.Signal`, `QtCore.Slot` and `QtCore.Property` following the Qt5 module layout. * The Qt version being used can be checked with `QtCore.__version__` (instead of `QtCore.QT_VERSION_STR`) as well as from `qtpy.QT_VERSION`. -* 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 `QOpenGL*` classes. +* For PyQt6 enums, unscoped enum access was added by promoting the enums of the `QtCore`, `QtGui`, `QtTest` and `QtWidgets` modules. -* To check the current PySide2/6 binding version you can use `qtpy.PYSIDE_VERSION`. If PySide is not being used as the binding the value returned will be `None`. +* Compatibility is added between the `QtGui` and `QtOpenGL` modules for the `QOpenGL*` classes. -* To check the current PyQt5/6 binding version you can use `qtpy.PYQT_VERSION`. If PyQt is not being used as the binding the value returned will be `None`. +* To check the current binding version, you can use `qtpy.PYSIDE_VERSION` for PySide2/6 and `qtpy.PYQT_VERSION` for PyQt5/6. If the respective binding is not being used, the value of its attribute will be `None`. -* To check the current selected binding you can use `qtpy.API_NAME` +* To check the current selected binding, you can use `qtpy.API_NAME` * There are boolean values to check if Qt5/6, PyQt5/6 or PySide2/6 are being used: `qtpy.Qt5`, `qtpy.Qt6`, `qtpy.PYQT5`, `qtpy.PYQT6`, `qtpy.PYSIDE2` and `qtpy.PYSIDE6`. `True` if currently being used, `False` otherwise. #### Compat module -In the `qtpy.compat` module you can find wrappers for `QFileDialog` static methods and sip/shiboken functions as: +In the `qtpy.compat` module, you can find wrappers for `QFileDialog` static methods and SIP/Shiboken functions, such as: * `QFileDialog.getExistingDirectory` wrapped with `qtpy.compat.getexistingdirectory` From 050a38348d6bc3ec39ba0937be95618d10a2b68d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Fri, 22 Jul 2022 11:39:42 -0500 Subject: [PATCH 460/703] Apply suggestions from code review Co-authored-by: CAM Gerlach --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c482c6b6..9cb09597 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ default unless you set the `QT_API` environment variable. ### Module aliases and constants -* `QtCore.pyqtSignal`, `QtCore.pyqtSlot` and `QtCore.pyqtProperty` (available on PyQt5/6) are replaced by `QtCore.Signal`, `QtCore.Slot` and `QtCore.Property` following the Qt5 module layout. +* `QtCore.pyqtSignal`, `QtCore.pyqtSlot` and `QtCore.pyqtProperty` (available on PyQt5/6) are instead exposed as `QtCore.Signal`, `QtCore.Slot` and `QtCore.Property`, respectively, following the Qt5 module layout. * The Qt version being used can be checked with `QtCore.__version__` (instead of `QtCore.QT_VERSION_STR`) as well as from `qtpy.QT_VERSION`. From a9777c01842ea2e2bf9c882736a4be0fa6ed8926 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sat, 23 Jul 2022 19:27:49 -0400 Subject: [PATCH 461/703] fix pyqt6 typing import for Qt --- qtpy/QtCore.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 3ae098ae..4f12d7dd 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -7,7 +7,7 @@ # ----------------------------------------------------------------------------- """Provides QtCore classes and functions.""" - +from typing import TYPE_CHECKING from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: @@ -45,10 +45,11 @@ # Seems like there is an error with sip. Without first # trying to import `PyQt6.QtGui.Qt`, some functions like # `PyQt6.QtCore.Qt.mightBeRichText` are missing. - try: - from PyQt6.QtGui import Qt - except ImportError: - pass + if not TYPE_CHECKING: + try: + from PyQt6.QtGui import Qt + except ImportError: + pass # Map missing methods QCoreApplication.exec_ = QCoreApplication.exec From 4ae7625f55b17f7eda6290c5c05c7fad12854432 Mon Sep 17 00:00:00 2001 From: Talley Lambert Date: Sun, 24 Jul 2022 14:00:49 -0400 Subject: [PATCH 462/703] Update qtpy/QtCore.py Co-authored-by: CAM Gerlach --- qtpy/QtCore.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 4f12d7dd..d892c07e 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -8,6 +8,7 @@ """Provides QtCore classes and functions.""" from typing import TYPE_CHECKING + from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtBindingsNotFoundError if PYQT5: From a36708ef6334b053de105fa406458fefc862d899 Mon Sep 17 00:00:00 2001 From: Bob Schumaker Date: Fri, 29 Jul 2022 07:14:23 -0700 Subject: [PATCH 463/703] Add toPython conversion to QDate and QTime. --- qtpy/QtCore.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index d892c07e..fa68d464 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -21,7 +21,9 @@ # For issue #153 and updated for issue #305 from PyQt5.QtCore import QDateTime + QDate.toPython = lambda self, *args, **kwargs: self.toPyDate(*args, **kwargs) QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) + QTime.toPython = lambda self, *args, **kwargs: self.toPyTime(*args, **kwargs) # Map missing methods on PyQt5 5.12 QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) @@ -39,8 +41,10 @@ from PyQt6.QtCore import QT_VERSION_STR as __version__ # For issue #153 and updated for issue #305 - from PyQt6.QtCore import QDateTime + from PyQt6.QtCore import QDate, QDateTime, QTime + QDate.toPython = lambda self, *args, **kwargs: self.toPyDate(*args, **kwargs) QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) + QTime.toPython = lambda self, *args, **kwargs: self.toPyTime(*args, **kwargs) # For issue #311 # Seems like there is an error with sip. Without first From 7841f4d398c9dbdc3b479a1df9f997dc14101088 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 10 Aug 2022 12:17:10 -0500 Subject: [PATCH 464/703] Release 2.2.0 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ qtpy/__init__.py | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b1f891e..3d06d5a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # History of changes +## Version 2.2.0 (2022-08-10) + +### Issues Closed + +* [Issue 359](https://github.com/spyder-ide/qtpy/issues/359) - Release QtPy 2.2.0 +* [Issue 352](https://github.com/spyder-ide/qtpy/issues/352) - Deprecation Warning for Enum Access ([PR 353](https://github.com/spyder-ide/qtpy/pull/353) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 351](https://github.com/spyder-ide/qtpy/issues/351) - `PySide6.QtSvgWidgets` not exposed +* [Issue 302](https://github.com/spyder-ide/qtpy/issues/302) - Compat shiboken and sip like Qt.py ([PR 354](https://github.com/spyder-ide/qtpy/pull/354) by [@zjp](https://github.com/zjp)) +* [Issue 61](https://github.com/spyder-ide/qtpy/issues/61) - Add documentation for methods or helpers that are specific to qtpy ([PR 357](https://github.com/spyder-ide/qtpy/pull/357) by [@dalthviz](https://github.com/dalthviz)) + +In this release 5 issues were closed. + +### Pull Requests Merged + +* [PR 358](https://github.com/spyder-ide/qtpy/pull/358) - PR: Fix PyQt6 typing import for Qt, by [@tlambert03](https://github.com/tlambert03) +* [PR 357](https://github.com/spyder-ide/qtpy/pull/357) - PR: Add initial `Methods, helpers and QtPy namespace specifics` section to the README, by [@dalthviz](https://github.com/dalthviz) ([61](https://github.com/spyder-ide/qtpy/issues/61)) +* [PR 354](https://github.com/spyder-ide/qtpy/pull/354) - PR: Add wrapper around sip/shiboken isdeleted/isvalid (compat.py), by [@zjp](https://github.com/zjp) ([302](https://github.com/spyder-ide/qtpy/issues/302)) +* [PR 353](https://github.com/spyder-ide/qtpy/pull/353) - PR: Add note to readme about use with Pyright, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([352](https://github.com/spyder-ide/qtpy/issues/352)) +* [PR 350](https://github.com/spyder-ide/qtpy/pull/350) - PR: Restore `WEBENGINE` constant in `QtWebEngineWidgets`, by [@ccordoba12](https://github.com/ccordoba12) +* [PR 346](https://github.com/spyder-ide/qtpy/pull/346) - PR: Add workaround for `mode` argument in QTextCursor.movePosition (PySide6), by [@rear1019](https://github.com/rear1019) +* [PR 344](https://github.com/spyder-ide/qtpy/pull/344) - PR: Add missing imports and modules, by [@DaelonSuzuka](https://github.com/DaelonSuzuka) + +In this release 7 pull requests were closed. + + +---- + + ## Version 2.1.0 (2022-05-02) ### New features diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 5bb058c2..f63eb6c7 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.2.0.dev0' +__version__ = '2.2.0' class PythonQtError(RuntimeError): From 7a19de25aa595b2a952ba36a9bd782bde72bf3e9 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 10 Aug 2022 12:25:37 -0500 Subject: [PATCH 465/703] Back to work --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index f63eb6c7..a5c2066c 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.2.0' +__version__ = '2.3.0.dev0' class PythonQtError(RuntimeError): From 8d08245b69be00f12a0c849f37134c6a00cc4f5c Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 11 Aug 2022 11:08:37 -0500 Subject: [PATCH 466/703] CI: Ensure conda-forge channel usage when testing --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e19231c4..b033861d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,6 +82,7 @@ jobs: auto-activate-base: true auto-update-conda: true channels: conda-forge + channel-priority: strict - name: Print Conda info shell: bash -l {0} run: | From 503a07dbd85769c4703f51dab76a44f8407fadf9 Mon Sep 17 00:00:00 2001 From: Bob Schumaker <48453409+bob-schumaker@users.noreply.github.com> Date: Thu, 11 Aug 2022 18:15:59 -0700 Subject: [PATCH 467/703] Update qtpy/QtCore.py Missed a step that didn't get caught in testing for some reason. Co-authored-by: CAM Gerlach --- qtpy/QtCore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index fa68d464..15ddc042 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -20,7 +20,7 @@ from PyQt5.QtCore import QT_VERSION_STR as __version__ # For issue #153 and updated for issue #305 - from PyQt5.QtCore import QDateTime + from PyQt5.QtCore import QDate, QDateTime, QTime QDate.toPython = lambda self, *args, **kwargs: self.toPyDate(*args, **kwargs) QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) QTime.toPython = lambda self, *args, **kwargs: self.toPyTime(*args, **kwargs) From f6164b1c9dcf09664c9f64a9a62c4cc97fdd5901 Mon Sep 17 00:00:00 2001 From: Bob Schumaker Date: Fri, 12 Aug 2022 11:08:47 -0700 Subject: [PATCH 468/703] Add tests for new functionality. --- qtpy/tests/test_qtcore.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 7ff60a01..6327ec92 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,6 +1,6 @@ """Test QtCore.""" -from datetime import datetime +from datetime import date, datetime, time import sys import pytest @@ -30,6 +30,22 @@ def test_qdatetime_toPython(): assert isinstance(py_date, datetime) +def test_qdate_toPython(): + """Test QDate.toPython""" + q_date = QtCore.QDate.currentDate() + assert QtCore.QDate.toPython is not None + py_date = q_date.toPython() + assert isinstance(py_date, date) + + +def test_qtime_toPython(): + """Test QTime.toPython""" + q_time = QtCore.QTime.currentTime() + assert QtCore.QTime.toPython is not None + py_time = q_time.toPython() + assert isinstance(py_time, time) + + @pytest.mark.skipif( sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") From cf8782e7442cd3b4a8b4567f995d56244e11aa0b Mon Sep 17 00:00:00 2001 From: luz paz Date: Fri, 19 Aug 2022 17:40:43 -0400 Subject: [PATCH 469/703] Fix typos Found via `codespell -q 3` --- qtpy/QtTest.py | 2 +- qtpy/QtWidgets.py | 2 +- qtpy/tests/test_qtdesigner.py | 2 +- qtpy/tests/test_qtmacextras.py | 2 +- qtpy/tests/test_qtwinextras.py | 2 +- qtpy/tests/test_uic.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index 53079983..e08f5b74 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -1,6 +1,6 @@ # ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy -# Copyright © 2009- The Spyder Developmet Team +# Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 25c8f16a..5c1fd4be 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -1,6 +1,6 @@ # ----------------------------------------------------------------------------- # Copyright © 2014-2015 Colin Duquesnoy -# Copyright © 2009- The Spyder Developmet Team +# Copyright © 2009- The Spyder Development Team # # Licensed under the terms of the MIT License # (see LICENSE.txt for details) diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index be957410..36306c27 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -2,7 +2,7 @@ from qtpy import PYSIDE2 -@pytest.mark.skipif(PYSIDE2, reason="QtDesigner is not avalaible in PySide2") +@pytest.mark.skipif(PYSIDE2, reason="QtDesigner is not available in PySide2") def test_qtdesigner(): """Test the qtpy.QtDesigner namespace.""" from qtpy import QtDesigner diff --git a/qtpy/tests/test_qtmacextras.py b/qtpy/tests/test_qtmacextras.py index 3e391d53..60e8788c 100644 --- a/qtpy/tests/test_qtmacextras.py +++ b/qtpy/tests/test_qtmacextras.py @@ -5,7 +5,7 @@ from qtpy.tests.utils import using_conda -@pytest.mark.skipif(PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") +@pytest.mark.skipif(PYQT6 or PYSIDE6, reason="Not available on Qt6-based bindings") @pytest.mark.skipif( sys.platform != "darwin" or using_conda(), reason="Only available in Qt5 bindings > 5.9 with pip on mac in CIs", diff --git a/qtpy/tests/test_qtwinextras.py b/qtpy/tests/test_qtwinextras.py index f68a4b4b..d6c7f05d 100644 --- a/qtpy/tests/test_qtwinextras.py +++ b/qtpy/tests/test_qtwinextras.py @@ -8,7 +8,7 @@ from qtpy.tests.utils import using_conda -@pytest.mark.skipif(PYQT6 or PYSIDE6, reason="Not availible on Qt6-based bindings") +@pytest.mark.skipif(PYQT6 or PYSIDE6, reason="Not available on Qt6-based bindings") @pytest.mark.skipif( sys.platform != "win32" or using_conda(), reason="Only available in Qt5 bindings > 5.9 with pip on Windows in CIs", diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index a715dcbe..1d9a791f 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -61,7 +61,7 @@ def test_load_ui(qtbot): @pytest.mark.skipif( PYSIDE2 or PYSIDE6, - reason="PySide2uic not consistantly installed across platforms/versions") + reason="PySide2uic not consistently installed across platforms/versions") @pytest.mark.skipif( sys.platform.startswith('linux') and not_using_conda(), reason="Segfaults on Linux when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") From c65ae3388ad04dcbae1f6a10a064510322a0cd4f Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Fri, 26 Aug 2022 19:23:18 +0000 Subject: [PATCH 470/703] Fix typo in Qt flags in Readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cb09597..6781dbfb 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ default unless you set the `QT_API` environment variable. * To check the current selected binding, you can use `qtpy.API_NAME` -* There are boolean values to check if Qt5/6, PyQt5/6 or PySide2/6 are being used: `qtpy.Qt5`, `qtpy.Qt6`, `qtpy.PYQT5`, `qtpy.PYQT6`, `qtpy.PYSIDE2` and `qtpy.PYSIDE6`. `True` if currently being used, `False` otherwise. +* There are boolean values to check if Qt5/6, PyQt5/6 or PySide2/6 are being used: `qtpy.QT5`, `qtpy.QT6`, `qtpy.PYQT5`, `qtpy.PYQT6`, `qtpy.PYSIDE2` and `qtpy.PYSIDE6`. `True` if currently being used, `False` otherwise. #### Compat module From e1a0c22b6c8d4d8d2b94c2b1b480d4b730251622 Mon Sep 17 00:00:00 2001 From: Random Developer <32579853+random-developer@users.noreply.github.com> Date: Fri, 2 Sep 2022 09:02:23 -0700 Subject: [PATCH 471/703] Aliased QUndoCommand, provided QLineEdit.getTextMargins() and QFontMetricsF.width() --- qtpy/QtGui.py | 2 ++ qtpy/QtWidgets.py | 6 ++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 7c585b6a..df9f6632 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -17,6 +17,7 @@ from PyQt6.QtGui import * from PyQt6.QtOpenGL import * QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) + QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) # Map missing/renamed methods QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) @@ -36,6 +37,7 @@ from PySide6.QtGui import * from PySide6.QtOpenGL import * QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) + QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) # Map DeprecationWarning methods QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 5c1fd4be..d8fb7b69 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -15,7 +15,7 @@ elif PYQT6: from PyQt6 import QtWidgets from PyQt6.QtWidgets import * - from PyQt6.QtGui import QAction, QActionGroup, QShortcut, QFileSystemModel + from PyQt6.QtGui import QAction, QActionGroup, QShortcut, QFileSystemModel, QUndoCommand from PyQt6.QtOpenGLWidgets import QOpenGLWidget # Map missing/renamed methods @@ -28,6 +28,7 @@ QApplication.exec_ = QApplication.exec QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom()) # Allow unscoped access for enums inside the QtWidgets module from .enums_compat import promote_enums @@ -37,7 +38,7 @@ from PySide2.QtWidgets import * elif PYSIDE6: from PySide6.QtWidgets import * - from PySide6.QtGui import QAction, QActionGroup, QShortcut + from PySide6.QtGui import QAction, QActionGroup, QShortcut, QUndoCommand from PySide6.QtOpenGLWidgets import QOpenGLWidget # Map missing/renamed methods @@ -45,6 +46,7 @@ QTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) QPlainTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) QPlainTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) + QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom()) # Map DeprecationWarning methods QApplication.exec_ = QApplication.exec From 69ab5fa5b7214f799c327f00f0ff9f32126aa248 Mon Sep 17 00:00:00 2001 From: Random Developer <32579853+random-developer@users.noreply.github.com> Date: Fri, 2 Sep 2022 11:15:54 -0700 Subject: [PATCH 472/703] Added test cases. --- qtpy/tests/test_qtgui.py | 6 +++++- qtpy/tests/test_qtwidgets.py | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 6fafac2c..9c01e591 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -12,12 +12,16 @@ sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qfontmetrics_width(qtbot): - """Test QFontMetrics width""" + """Test QFontMetrics and QFontMetricsF width""" assert QtGui.QFontMetrics.width is not None + assert QtGui.QFontMetricsF.width is not None font = QtGui.QFont("times", 24) font_metrics = QtGui.QFontMetrics(font) + font_metricsF = QtGui.QFontMetricsF(font) width = font_metrics.width("Test") + widthF = font_metricsF.width("Test") assert width in range(40, 62) + assert widthF == width @pytest.mark.skipif( diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 0b08158e..88614b5f 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -24,6 +24,16 @@ def test_qtextedit_functions(qtbot, pdf_writer): assert output_path.exists() +def test_qlineedit_functions(qtbot, pdf_writer): + """Test functions mapping for QtWidgets.QLineEdit""" + assert QtWidgets.QLineEdit.getTextMargins + + +def test_qundocommand_object(qtbot, pdf_writer): + """Test object aliasing for QUndoCommand""" + assert QtWidgets.QUndoCommand + + @pytest.mark.skipif( sys.platform.startswith('linux') and not_using_conda(), reason="Fatal Python error: Aborted on Linux CI when not using conda") From 458e3ded9504a9dfb4e9701dc91e1ab5a677fa99 Mon Sep 17 00:00:00 2001 From: Random Developer <32579853+random-developer@users.noreply.github.com> Date: Sat, 3 Sep 2022 08:50:04 -0700 Subject: [PATCH 473/703] Removed unneeded test paramter (which seems to affect how the test runs). --- qtpy/tests/test_qtgui.py | 2 +- qtpy/tests/test_qtwidgets.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 9c01e591..802129c9 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -21,7 +21,7 @@ def test_qfontmetrics_width(qtbot): width = font_metrics.width("Test") widthF = font_metricsF.width("Test") assert width in range(40, 62) - assert widthF == width + assert 39 <= widthF <= 63 @pytest.mark.skipif( diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 88614b5f..a04e662d 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -24,12 +24,12 @@ def test_qtextedit_functions(qtbot, pdf_writer): assert output_path.exists() -def test_qlineedit_functions(qtbot, pdf_writer): +def test_qlineedit_functions(): """Test functions mapping for QtWidgets.QLineEdit""" assert QtWidgets.QLineEdit.getTextMargins -def test_qundocommand_object(qtbot, pdf_writer): +def test_qundocommand_object(): """Test object aliasing for QUndoCommand""" assert QtWidgets.QUndoCommand From edf64793abd70b8143f6ae65b93c4fd31eb53473 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 22 Sep 2022 14:47:57 -0500 Subject: [PATCH 474/703] Add missing self for 'QtBindingsNotFoundError' --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index a5c2066c..2533636b 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -81,7 +81,7 @@ class QtBindingsNotFoundError(PythonQtError): _msg = 'No Qt bindings could be found' def __init__(self): - super().__init__(_msg) + super().__init__(self._msg) class QtModuleNotFoundError(ModuleNotFoundError, PythonQtError): From 14462cec23e0007a334a46370de15caea2614de0 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 3 Oct 2022 14:29:24 -0500 Subject: [PATCH 475/703] Release 2.2.1 --- CHANGELOG.md | 24 ++++++++++++++++++++++++ qtpy/__init__.py | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d06d5a7..64967682 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,29 @@ # History of changes +## Version 2.2.1 (2022-10-03) + +### Issues Closed + +* [Issue 369](https://github.com/spyder-ide/qtpy/issues/369) - Release QtPy 2.2.1 +* [Issue 365](https://github.com/spyder-ide/qtpy/issues/365) - Additions needed for PySide6/PyQt6 support (`QFontMetricsF.width`, `QLineEdit.getTextMargins` and `QtWidgets.QUndoCommand`) ([PR 366](https://github.com/spyder-ide/qtpy/pull/366) by [@random-developer](https://github.com/random-developer)) + +In this release 2 issues were closed. + +### Pull Requests Merged + +* [PR 368](https://github.com/spyder-ide/qtpy/pull/368) - PR: Add missing `self` for `QtBindingsNotFoundError` definition, by [@dalthviz](https://github.com/dalthviz) +* [PR 366](https://github.com/spyder-ide/qtpy/pull/366) - PR: Aliased `QUndoCommand`, provided `QLineEdit.getTextMargins()` and `QFontMetricsF.width()`, by [@random-developer](https://github.com/random-developer) ([365](https://github.com/spyder-ide/qtpy/issues/365)) +* [PR 363](https://github.com/spyder-ide/qtpy/pull/363) - PR: Fix typo in Qt flags in Readme, by [@Czaki](https://github.com/Czaki) +* [PR 362](https://github.com/spyder-ide/qtpy/pull/362) - PR: Fix various minor typos found with Codespell, by [@luzpaz](https://github.com/luzpaz) +* [PR 361](https://github.com/spyder-ide/qtpy/pull/361) - PR: toPython helpers for QDate and QTime, by [@bob-schumaker](https://github.com/bob-schumaker) +* [PR 360](https://github.com/spyder-ide/qtpy/pull/360) - CI: Ensure conda-forge channel usage when testing with conda, by [@dalthviz](https://github.com/dalthviz) + +In this release 6 pull requests were closed. + + +---- + + ## Version 2.2.0 (2022-08-10) ### Issues Closed diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 2533636b..3a9da15a 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.3.0.dev0' +__version__ = '2.2.1' class PythonQtError(RuntimeError): From 65a8ed007f13a3e19cb2c6217f82fd23b9a0e08a Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 3 Oct 2022 14:31:55 -0500 Subject: [PATCH 476/703] Back to work --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 3a9da15a..2533636b 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.2.1' +__version__ = '2.3.0.dev0' class PythonQtError(RuntimeError): From bc602acfc7363d8ec557653138d4c2b68ea8db46 Mon Sep 17 00:00:00 2001 From: Dennis Goeries Date: Sun, 9 Oct 2022 23:51:44 +0200 Subject: [PATCH 477/703] Add Qsci to the imports --- qtpy/Qsci.py | 28 +++++++++++++++++ qtpy/tests/test_qsci.py | 66 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 qtpy/Qsci.py create mode 100644 qtpy/tests/test_qsci.py diff --git a/qtpy/Qsci.py b/qtpy/Qsci.py new file mode 100644 index 00000000..599e79e8 --- /dev/null +++ b/qtpy/Qsci.py @@ -0,0 +1,28 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Provides Qsci classes and functions.""" + +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) + +if PYQT5: + from PyQt5.Qsci import * +elif PYQT6: + from PyQt6.Qsci import * +elif PYSIDE2: + raise QtBindingMissingModuleError(name='Qsci') +elif PYSIDE6: + raise QtBindingMissingModuleError(name='Qsci') +else: + raise QtBindingsNotFoundError() diff --git a/qtpy/tests/test_qsci.py b/qtpy/tests/test_qsci.py new file mode 100644 index 00000000..7dc79e2c --- /dev/null +++ b/qtpy/tests/test_qsci.py @@ -0,0 +1,66 @@ +"""Test Qsci.""" + +import pytest + +from qtpy import PYSIDE, PYSIDE2, Qsci + + +@pytest.mark.skipif( + PYSIDE or PYSIDE2, + reason="Qsci bindings not available under PySide and PySide 2", +) +def test_qsci(): + """Test the qtpy.Qsci namespace""" + assert Qsci.QSCINTILLA_VERSION is not None + assert Qsci.QSCINTILLA_VERSION_STR is not None + assert Qsci.QsciAPIs is not None + assert Qsci.QsciAbstractAPIs is not None + assert Qsci.QsciCommand is not None + assert Qsci.QsciCommandSet is not None + assert Qsci.QsciDocument is not None + assert Qsci.QsciLexer is not None + assert Qsci.QsciLexerAVS is not None + assert Qsci.QsciLexerBash is not None + assert Qsci.QsciLexerBatch is not None + assert Qsci.QsciLexerCMake is not None + assert Qsci.QsciLexerCPP is not None + assert Qsci.QsciLexerCSS is not None + assert Qsci.QsciLexerCSharp is not None + assert Qsci.QsciLexerCoffeeScript is not None + assert Qsci.QsciLexerCustom is not None + assert Qsci.QsciLexerD is not None + assert Qsci.QsciLexerDiff is not None + assert Qsci.QsciLexerFortran is not None + assert Qsci.QsciLexerFortran77 is not None + assert Qsci.QsciLexerHTML is not None + assert Qsci.QsciLexerIDL is not None + assert Qsci.QsciLexerJSON is not None + assert Qsci.QsciLexerJava is not None + assert Qsci.QsciLexerJavaScript is not None + assert Qsci.QsciLexerLua is not None + assert Qsci.QsciLexerMakefile is not None + assert Qsci.QsciLexerMarkdown is not None + assert Qsci.QsciLexerMatlab is not None + assert Qsci.QsciLexerOctave is not None + assert Qsci.QsciLexerPO is not None + assert Qsci.QsciLexerPOV is not None + assert Qsci.QsciLexerPascal is not None + assert Qsci.QsciLexerPerl is not None + assert Qsci.QsciLexerPostScript is not None + assert Qsci.QsciLexerProperties is not None + assert Qsci.QsciLexerPython is not None + assert Qsci.QsciLexerRuby is not None + assert Qsci.QsciLexerSQL is not None + assert Qsci.QsciLexerSpice is not None + assert Qsci.QsciLexerTCL is not None + assert Qsci.QsciLexerTeX is not None + assert Qsci.QsciLexerVHDL is not None + assert Qsci.QsciLexerVerilog is not None + assert Qsci.QsciLexerXML is not None + assert Qsci.QsciLexerYAML is not None + assert Qsci.QsciMacro is not None + assert Qsci.QsciPrinter is not None + assert Qsci.QsciScintilla is not None + assert Qsci.QsciScintillaBase is not None + assert Qsci.QsciStyle is not None + assert Qsci.QsciStyledText is not None From fbbbbac4cc27a383061fd184eaf16ece54e73f5e Mon Sep 17 00:00:00 2001 From: Dennis Goeries Date: Mon, 10 Oct 2022 00:18:53 +0200 Subject: [PATCH 478/703] Add scintilla to workflow --- .github/workflows/ci.yml | 1 + .github/workflows/test.sh | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b033861d..17c0fd37 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,7 @@ jobs: PYQT6_QT_VERSION: ${{ matrix.pyqt6-qt-version }} PYSIDE2_QT_VERSION: ${{ matrix.pyside2-qt-version }} PYSIDE6_QT_VERSION: ${{ matrix.pyside6-qt-version }} + QSCINTILLA_VERSION: '2.13.*' strategy: fail-fast: false matrix: diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index adcd1002..bb4d74c0 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -17,7 +17,7 @@ conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then if [ "${1}" = "pyqt5" ]; then - conda install -q qt=${PYQT5_QT_VERSION:-"5.12"} pyqt=${PYQT5_VERSION:-"5"} + conda install -q qt=${PYQT5_QT_VERSION:-"5.12"} pyqt=${PYQT5_VERSION:-"5"} qscintilla2=$QSCINTILLA_VERSION elif [ "${1}" = "pyside2" ]; then conda install -q qt=${PYSIDE2_QT_VERSION:-"5.12"} pyside2=${PYSIDE2_VERSION:-"5"} else @@ -27,9 +27,9 @@ if [ "$USE_CONDA" = "Yes" ]; then else if [ "${1}" = "pyqt5" ]; then - pip install pyqt5==${PYQT5_VERSION:-"5.15"}.* PyQtWebEngine==${PYQT5_VERSION:-"5.15"}.* + pip install pyqt5==${PYQT5_VERSION:-"5.15"}.* PyQtWebEngine==${PYQT5_VERSION:-"5.15"}.* QScintilla==$QSCINTILLA_VERSION elif [ "${1}" = "pyqt6" ]; then - pip install pyqt6==${PYQT6_VERSION:-"6.3"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.3"}.* PyQt6-Qt6==${PYQT6_QT_VERSION:-"6.3"}.* + pip install pyqt6==${PYQT6_VERSION:-"6.3"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.3"}.* PyQt6-Qt6==${PYQT6_QT_VERSION:-"6.3"}.* PyQt6-QScintilla==$QSCINTILLA_VERSION elif [ "${1}" = "pyside2" ]; then pip install pyside2==${PYSIDE2_VERSION:-"5.12"}.* elif [ "${1}" = "pyside6" ]; then From 6320f663fed23089b67a7d1133896f2aab28460a Mon Sep 17 00:00:00 2001 From: Dennis Goeries Date: Mon, 10 Oct 2022 00:24:36 +0200 Subject: [PATCH 479/703] Remove QScintilla from conda test --- .github/workflows/test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index bb4d74c0..0b2d5cac 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -17,7 +17,7 @@ conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then if [ "${1}" = "pyqt5" ]; then - conda install -q qt=${PYQT5_QT_VERSION:-"5.12"} pyqt=${PYQT5_VERSION:-"5"} qscintilla2=$QSCINTILLA_VERSION + conda install -q qt=${PYQT5_QT_VERSION:-"5.12"} pyqt=${PYQT5_VERSION:-"5"} elif [ "${1}" = "pyside2" ]; then conda install -q qt=${PYSIDE2_QT_VERSION:-"5.12"} pyside2=${PYSIDE2_VERSION:-"5"} else From e972232753096014624926b763e0bf4e9eb00506 Mon Sep 17 00:00:00 2001 From: Dennis Goeries Date: Mon, 10 Oct 2022 00:28:01 +0200 Subject: [PATCH 480/703] pytest import or skip for QScintilla --- qtpy/tests/test_qsci.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_qsci.py b/qtpy/tests/test_qsci.py index 7dc79e2c..2f96bd80 100644 --- a/qtpy/tests/test_qsci.py +++ b/qtpy/tests/test_qsci.py @@ -2,7 +2,7 @@ import pytest -from qtpy import PYSIDE, PYSIDE2, Qsci +from qtpy import PYSIDE, PYSIDE2 @pytest.mark.skipif( @@ -11,6 +11,7 @@ ) def test_qsci(): """Test the qtpy.Qsci namespace""" + Qsci = pytest.importorskip("qtpy.Qsci") assert Qsci.QSCINTILLA_VERSION is not None assert Qsci.QSCINTILLA_VERSION_STR is not None assert Qsci.QsciAPIs is not None From b3b45e7cb7a6d4a3279a478bf80d7171b0c380eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20G=C3=B6ries?= <43136580+dgoeries@users.noreply.github.com> Date: Tue, 11 Oct 2022 00:57:21 +0200 Subject: [PATCH 481/703] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle that QScintilla bindings might not be installed. Use conda flag in tests. Co-authored-by: Daniel Althviz Moré --- qtpy/Qsci.py | 15 +++++++++++++-- qtpy/tests/test_qsci.py | 7 ++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/qtpy/Qsci.py b/qtpy/Qsci.py index 599e79e8..7639e6cc 100644 --- a/qtpy/Qsci.py +++ b/qtpy/Qsci.py @@ -14,12 +14,23 @@ PYSIDE6, QtBindingsNotFoundError, QtBindingMissingModuleError, + QtModuleNotInstalledError ) if PYQT5: - from PyQt5.Qsci import * + try: + from PyQt5.Qsci import * + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError( + name='Qsci', missing_package='QScintilla' + ) from error elif PYQT6: - from PyQt6.Qsci import * + try: + from PyQt6.Qsci import * + except ModuleNotFoundError as error: + raise QtModuleNotInstalledError( + name='Qsci', missing_package='PyQt6-QScintilla' + ) from error elif PYSIDE2: raise QtBindingMissingModuleError(name='Qsci') elif PYSIDE6: diff --git a/qtpy/tests/test_qsci.py b/qtpy/tests/test_qsci.py index 2f96bd80..8f001588 100644 --- a/qtpy/tests/test_qsci.py +++ b/qtpy/tests/test_qsci.py @@ -2,12 +2,13 @@ import pytest -from qtpy import PYSIDE, PYSIDE2 +from qtpy import PYSIDE2, PYSIDE6 +from qtpy.tests.utils import using_conda @pytest.mark.skipif( - PYSIDE or PYSIDE2, - reason="Qsci bindings not available under PySide and PySide 2", + PYSIDE2 or PYSIDE6 or using_conda(), + reason="Qsci bindings not available under PySide 2/6 and conda installations", ) def test_qsci(): """Test the qtpy.Qsci namespace""" From 51d0077d422315ce1914f7a6cc68147585b95642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dennis=20G=C3=B6ries?= <43136580+dgoeries@users.noreply.github.com> Date: Tue, 11 Oct 2022 17:50:29 +0200 Subject: [PATCH 482/703] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Work on QScintilla default version in the CI workflow Co-authored-by: Daniel Althviz Moré --- .github/workflows/ci.yml | 2 +- .github/workflows/test.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 17c0fd37..ba07c3f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: PYQT6_QT_VERSION: ${{ matrix.pyqt6-qt-version }} PYSIDE2_QT_VERSION: ${{ matrix.pyside2-qt-version }} PYSIDE6_QT_VERSION: ${{ matrix.pyside6-qt-version }} - QSCINTILLA_VERSION: '2.13.*' + QSCINTILLA_VERSION: '2.13' strategy: fail-fast: false matrix: diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 0b2d5cac..23d857e8 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -27,9 +27,9 @@ if [ "$USE_CONDA" = "Yes" ]; then else if [ "${1}" = "pyqt5" ]; then - pip install pyqt5==${PYQT5_VERSION:-"5.15"}.* PyQtWebEngine==${PYQT5_VERSION:-"5.15"}.* QScintilla==$QSCINTILLA_VERSION + pip install pyqt5==${PYQT5_VERSION:-"5.15"}.* PyQtWebEngine==${PYQT5_VERSION:-"5.15"}.* QScintilla==${QSCINTILLA_VERSION:-"2.13"}.* elif [ "${1}" = "pyqt6" ]; then - pip install pyqt6==${PYQT6_VERSION:-"6.3"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.3"}.* PyQt6-Qt6==${PYQT6_QT_VERSION:-"6.3"}.* PyQt6-QScintilla==$QSCINTILLA_VERSION + pip install pyqt6==${PYQT6_VERSION:-"6.3"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.3"}.* PyQt6-Qt6==${PYQT6_QT_VERSION:-"6.3"}.* PyQt6-QScintilla==${QSCINTILLA_VERSION:-"2.13"}.* elif [ "${1}" = "pyside2" ]; then pip install pyside2==${PYSIDE2_VERSION:-"5.12"}.* elif [ "${1}" = "pyside6" ]; then From ad9d386cea1edebac6d5f614075415f2d9a551ea Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Sun, 16 Oct 2022 15:06:08 +0100 Subject: [PATCH 483/703] Don't re-assign Qt.MouseButton.MiddleButton on PySide6 --- qtpy/QtCore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 15ddc042..43265c1d 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -113,7 +113,7 @@ # Alias deprecated ItemDataRole enum values removed in Qt6 Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole - Qt.MidButton = Qt.MouseButton.MiddleButton = Qt.MiddleButton + Qt.MidButton = Qt.MiddleButton # Map DeprecationWarning methods QCoreApplication.exec_ = QCoreApplication.exec From 28a211c957b7662e71c4800f7425689124e0f9c9 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Thu, 20 Oct 2022 17:08:36 +0200 Subject: [PATCH 484/703] CI: Try PySide6 on conda --- .github/workflows/ci.yml | 3 +-- .github/workflows/test.sh | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba07c3f3..f45a5b0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,9 +46,8 @@ jobs: special-invocation: 'xvfb-run --auto-servernum ' # Needed for GUI tests to work - python-version: '3.10' skip-pyside2: true # Skip Pyside2 on all Python 3.10 builds until it supports it - - use-conda: 'Yes' # No conda packages yet for Qt6 + - use-conda: 'Yes' # No PyQt6 conda packages yet skip-pyqt6: true - skip-pyside6: true - os: windows-latest python-version: '3.10' use-conda: 'No' diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 23d857e8..bee52d49 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -20,6 +20,8 @@ if [ "$USE_CONDA" = "Yes" ]; then conda install -q qt=${PYQT5_QT_VERSION:-"5.12"} pyqt=${PYQT5_VERSION:-"5"} elif [ "${1}" = "pyside2" ]; then conda install -q qt=${PYSIDE2_QT_VERSION:-"5.12"} pyside2=${PYSIDE2_VERSION:-"5"} + elif [ "${1}" = "pyside6" ]; then + conda install -q pyside6=${PYSIDE6_VERSION:-"6.4"} else exit 1 fi From 9ad6400e337a52f8304de1e712e71156c6f36fc1 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Thu, 20 Oct 2022 17:09:36 +0200 Subject: [PATCH 485/703] Allow split packages --- qtpy/tests/test_qtdesigner.py | 2 +- qtpy/tests/test_qtsensors.py | 2 +- qtpy/tests/test_qtserialport.py | 2 +- qtpy/tests/test_qtsvg.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qtpy/tests/test_qtdesigner.py b/qtpy/tests/test_qtdesigner.py index 36306c27..6978b6be 100644 --- a/qtpy/tests/test_qtdesigner.py +++ b/qtpy/tests/test_qtdesigner.py @@ -5,7 +5,7 @@ @pytest.mark.skipif(PYSIDE2, reason="QtDesigner is not available in PySide2") def test_qtdesigner(): """Test the qtpy.QtDesigner namespace.""" - from qtpy import QtDesigner + QtDesigner = pytest.importorskip("qtpy.QtDesigner") assert QtDesigner.QAbstractExtensionFactory is not None assert QtDesigner.QAbstractExtensionManager is not None diff --git a/qtpy/tests/test_qtsensors.py b/qtpy/tests/test_qtsensors.py index 3bc087bd..c78cd55e 100644 --- a/qtpy/tests/test_qtsensors.py +++ b/qtpy/tests/test_qtsensors.py @@ -4,7 +4,7 @@ def test_qtsensors(): """Test the qtpy.QtSensors namespace""" - from qtpy import QtSensors + QtSensors = pytest.importorskip("qtpy.QtSensors") assert QtSensors.QAccelerometer is not None assert QtSensors.QAccelerometerFilter is not None diff --git a/qtpy/tests/test_qtserialport.py b/qtpy/tests/test_qtserialport.py index a6cf5031..b506a459 100644 --- a/qtpy/tests/test_qtserialport.py +++ b/qtpy/tests/test_qtserialport.py @@ -5,7 +5,7 @@ @pytest.mark.skipif(PYSIDE2, reason="Not available in CI") def test_qtserialport(): """Test the qtpy.QtSerialPort namespace""" - from qtpy import QtSerialPort + QtSerialPort = pytest.importorskip("qtpy.QtSerialPort") assert QtSerialPort.QSerialPort is not None assert QtSerialPort.QSerialPortInfo is not None diff --git a/qtpy/tests/test_qtsvg.py b/qtpy/tests/test_qtsvg.py index 298d9a93..d1a5cce7 100644 --- a/qtpy/tests/test_qtsvg.py +++ b/qtpy/tests/test_qtsvg.py @@ -4,7 +4,7 @@ def test_qtsvg(): """Test the qtpy.QtSvg namespace""" - from qtpy import QtSvg + QtSvg = pytest.importorskip("qtpy.QtSvg") if not (PYSIDE6 or PYQT6): assert QtSvg.QGraphicsSvgItem is not None From 529beaea4861ac39495bcbf1ce719019a8619280 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Fri, 21 Oct 2022 08:13:13 +0200 Subject: [PATCH 486/703] PySide6: Handle QLibraryInfo.location --- qtpy/QtCore.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 43265c1d..18cc4d9b 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -121,5 +121,6 @@ QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QLibraryInfo.location = QLibraryInfo.path else: raise QtBindingsNotFoundError() From 615cd460f6e2be149764d1b02336f6bc20ef4669 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Fri, 21 Oct 2022 08:45:37 +0200 Subject: [PATCH 487/703] Skip windows --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f45a5b0a..f7273a97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,6 +58,7 @@ jobs: use-conda: 'Yes' pyqt5-qt-version: '5.9' pyqt6-qt-version: '6.2' + skip-pyside6: true # test hangs - os: macos-latest python-version: '3.7' use-conda: 'No' From cac53d5256d80a1554d9d2e4af4a9b98c2eaa6f0 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Fri, 21 Oct 2022 09:18:03 +0200 Subject: [PATCH 488/703] Skip windows --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7273a97..2c2f049e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,6 +59,10 @@ jobs: pyqt5-qt-version: '5.9' pyqt6-qt-version: '6.2' skip-pyside6: true # test hangs + - os: windows-latest + python-version: '3.10' + use-conda: 'Yes' + skip-pyside6: true # test hangs - os: macos-latest python-version: '3.7' use-conda: 'No' From 218a2e851e23e37acad2a568f427db31af42707d Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Fri, 21 Oct 2022 20:40:38 +0200 Subject: [PATCH 489/703] Enable more tests --- .github/workflows/ci.yml | 2 +- qtpy/tests/test_qt3danimation.py | 1 - qtpy/tests/test_qtmultimediawidgets.py | 9 +++------ qtpy/tests/test_qtpositioning.py | 1 - qtpy/tests/test_qtqml.py | 5 ++--- qtpy/tests/test_qtquick.py | 6 +++--- qtpy/tests/test_qtquickwidgets.py | 1 - qtpy/tests/test_qtwebchannel.py | 1 - qtpy/tests/test_qtwebsockets.py | 1 - 9 files changed, 9 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2c2f049e..d239da03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -79,7 +79,7 @@ jobs: shell: bash run: | sudo apt update - sudo apt install libpulse-dev libegl1-mesa libopengl0 + sudo apt install libpulse-dev libegl1-mesa libopengl0 gstreamer1.0-gl - name: Install Conda uses: conda-incubator/setup-miniconda@v2 with: diff --git a/qtpy/tests/test_qt3danimation.py b/qtpy/tests/test_qt3danimation.py index c546f0c1..6eac3c33 100644 --- a/qtpy/tests/test_qt3danimation.py +++ b/qtpy/tests/test_qt3danimation.py @@ -2,7 +2,6 @@ from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qt3danimation(): """Test the qtpy.Qt3DAnimation namespace""" Qt3DAnimation = pytest.importorskip("qtpy.Qt3DAnimation") diff --git a/qtpy/tests/test_qtmultimediawidgets.py b/qtpy/tests/test_qtmultimediawidgets.py index 9d6b099a..3226dc37 100644 --- a/qtpy/tests/test_qtmultimediawidgets.py +++ b/qtpy/tests/test_qtmultimediawidgets.py @@ -6,15 +6,12 @@ from qtpy.tests.utils import using_conda -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") -@pytest.mark.skipif( - using_conda(), reason="Conda packages don't seem to include QtMultimedia" -) def test_qtmultimediawidgets(): """Test the qtpy.QtMultimediaWidgets namespace""" from qtpy import QtMultimediaWidgets - assert QtMultimediaWidgets.QCameraViewfinder is not None + if PYQT5 or PYSIDE2: + assert QtMultimediaWidgets.QCameraViewfinder is not None + # assert QtMultimediaWidgets.QVideoWidgetControl is not None assert QtMultimediaWidgets.QGraphicsVideoItem is not None assert QtMultimediaWidgets.QVideoWidget is not None - # assert QtMultimediaWidgets.QVideoWidgetControl is not None diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py index bc72f4cc..750abbf5 100644 --- a/qtpy/tests/test_qtpositioning.py +++ b/qtpy/tests/test_qtpositioning.py @@ -2,7 +2,6 @@ from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtpositioning(): """Test the qtpy.QtPositioning namespace""" from qtpy import QtPositioning diff --git a/qtpy/tests/test_qtqml.py b/qtpy/tests/test_qtqml.py index 8f8fe431..13e8db5c 100644 --- a/qtpy/tests/test_qtqml.py +++ b/qtpy/tests/test_qtqml.py @@ -1,8 +1,7 @@ import pytest -from qtpy import PYQT5, PYSIDE2 +from qtpy import PYQT5, PYSIDE2, PYSIDE6 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtqml(): """Test the qtpy.QtQml namespace""" from qtpy import QtQml @@ -22,7 +21,7 @@ def test_qtqml(): assert QtQml.QQmlFileSelector is not None assert QtQml.QQmlIncubationController is not None assert QtQml.QQmlIncubator is not None - if not PYSIDE2: + if not (PYSIDE2 or PYSIDE6): # https://wiki.qt.io/Qt_for_Python_Missing_Bindings#QtQml assert QtQml.QQmlListProperty is not None assert QtQml.QQmlListReference is not None diff --git a/qtpy/tests/test_qtquick.py b/qtpy/tests/test_qtquick.py index e1e68f34..add3db52 100644 --- a/qtpy/tests/test_qtquick.py +++ b/qtpy/tests/test_qtquick.py @@ -2,7 +2,6 @@ from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtquick(): """Test the qtpy.QtQuick namespace""" from qtpy import QtQuick @@ -32,11 +31,12 @@ def test_qtquick(): assert QtQuick.QQuickTextureFactory is not None assert QtQuick.QQuickView is not None assert QtQuick.QQuickWindow is not None - assert QtQuick.QSGAbstractRenderer is not None + if PYQT5 or PYSIDE2: + assert QtQuick.QSGAbstractRenderer is not None + assert QtQuick.QSGEngine is not None assert QtQuick.QSGBasicGeometryNode is not None assert QtQuick.QSGClipNode is not None assert QtQuick.QSGDynamicTexture is not None - assert QtQuick.QSGEngine is not None assert QtQuick.QSGGeometry is not None assert QtQuick.QSGGeometryNode is not None assert QtQuick.QSGMaterialType is not None diff --git a/qtpy/tests/test_qtquickwidgets.py b/qtpy/tests/test_qtquickwidgets.py index 53c1d208..e4df1b9d 100644 --- a/qtpy/tests/test_qtquickwidgets.py +++ b/qtpy/tests/test_qtquickwidgets.py @@ -2,7 +2,6 @@ from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtquickwidgets(): """Test the qtpy.QtQuickWidgets namespace""" from qtpy import QtQuickWidgets diff --git a/qtpy/tests/test_qtwebchannel.py b/qtpy/tests/test_qtwebchannel.py index 25e991f3..4337241f 100644 --- a/qtpy/tests/test_qtwebchannel.py +++ b/qtpy/tests/test_qtwebchannel.py @@ -2,7 +2,6 @@ from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtwebchannel(): """Test the qtpy.QtWebChannel namespace""" from qtpy import QtWebChannel diff --git a/qtpy/tests/test_qtwebsockets.py b/qtpy/tests/test_qtwebsockets.py index 52de7692..e03440bf 100644 --- a/qtpy/tests/test_qtwebsockets.py +++ b/qtpy/tests/test_qtwebsockets.py @@ -2,7 +2,6 @@ from qtpy import PYQT5, PYSIDE2 -@pytest.mark.skipif(not (PYQT5 or PYSIDE2), reason="Only available in Qt5 bindings") def test_qtwebsockets(): """Test the qtpy.QtWebSockets namespace""" from qtpy import QtWebSockets From c5e8f7b37eb5f513920af21588ca6049bea6b073 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Mon, 17 Oct 2022 17:44:34 -0500 Subject: [PATCH 490/703] Test 6.4 versions of Qt and the binding on one matrix job --- .codespellrc | 7 + .github/workflows/ci.yml | 5 +- .github/workflows/test.sh | 6 +- .pre-commit-config.yaml | 297 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 311 insertions(+), 4 deletions(-) create mode 100644 .codespellrc create mode 100644 .pre-commit-config.yaml diff --git a/.codespellrc b/.codespellrc new file mode 100644 index 00000000..290c5442 --- /dev/null +++ b/.codespellrc @@ -0,0 +1,7 @@ +[codespell] +builtin = clear,rare,informal,code,names +check-filenames = +check-hidden = +enable-colors = +ignore-words-list = sur +skip = .codespellrc,.gitattributes diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d239da03..a910d913 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,8 +51,11 @@ jobs: - os: windows-latest python-version: '3.10' use-conda: 'No' + pyqt6-version: '6.4' # Test upper bound + pyqt6-qt-version: '6.4' # Test upper bound pyside2-version: '5.15' # No 5.12 wheel on Windows and Python 3.10 - pyside6-version: '6.3' + pyside6-version: '6.4' # Test upper bound + pyside6-qt-version: '6.4' # Test upper bound - os: windows-latest python-version: '3.7' use-conda: 'Yes' diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index bee52d49..648669b7 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -35,10 +35,10 @@ else elif [ "${1}" = "pyside2" ]; then pip install pyside2==${PYSIDE2_VERSION:-"5.12"}.* elif [ "${1}" = "pyside6" ]; then - if [ "${PYSIDE6_VERSION:-"6.3":0:3}" = "6.3" ]; then - pip install pyside6==${PYSIDE6_VERSION:-"6.3"}.* pyside6-addons==${PYSIDE6_VERSION:-"6.3"}.* pyside6-essentials==${PYSIDE6_VERSION:-"6.3"}.* - else + if [ "${PYSIDE6_VERSION:-"6.2":0:3}" = "6.2" ]; then pip install pyside6==${PYSIDE6_VERSION:-"6.2"}.* + else + pip install pyside6==${PYSIDE6_VERSION:-"6.3"}.* pyside6-addons==${PYSIDE6_VERSION:-"6.3"}.* pyside6-essentials==${PYSIDE6_VERSION:-"6.3"}.* fi else exit 1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..c245b946 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,297 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks + +minimum_pre_commit_version: '2.10.0' + +default_language_version: + python: python3 + +default_stages: [commit] + + +repos: +# Meta checks +- repo: meta + hooks: + - id: check-useless-excludes + +# General fixers +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: trailing-whitespace + name: Trim trailing whitespace + exclude_types: [svg] + - id: mixed-line-ending + name: Normalize mixed line endings + args: [--fix=lf] + exclude_types: [batch] + - id: end-of-file-fixer + name: Fix end of files + exclude_types: [svg] + - id: fix-byte-order-marker + name: Remove Unicode BOM + + +# More general fixers +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.3.0 + hooks: + - id: remove-tabs + name: Replace tabs with spaces + types: [text] + exclude_types: [svg, batch] + args: [--whitespaces-count, '4'] + + +# Check and fix spelling +- repo: https://github.com/codespell-project/codespell + rev: v2.1.0 + hooks: + # Define separate hooks for checking and correcting spelling errors + # since codespell doesn't print verbose output when writing changes + - id: codespell + name: Check spelling + exclude: '\.codespellrc|\.gitattributes' + - id: codespell + name: Fix spelling + exclude: '\.codespellrc|\.gitattributes' + args: [--write-changes] + +# Lint and fix CSS with stylelint +- repo: https://github.com/awebdeveloper/pre-commit-stylelint + rev: '0.0.2' + hooks: + - id: stylelint + name: Lint and fix CSS (stylelint) + args: [--fix, --color, --maxWarnings, '0'] + additional_dependencies: + - 'stylelint@14.9.1' + - 'stylelint-config-standard@26.0.0' + - 'stylelint-csstree-validator@2.0.0' + - 'stylelint-declaration-block-no-ignored-properties@2.5.0' + - 'stylelint-no-unsupported-browser-features@5.0.3' + - 'stylelint-order@5.0.0' + +# Lint CSS with CSSlint +- repo: https://github.com/pre-commit/mirrors-csslint + rev: v1.0.5 + hooks: + - id: csslint + name: Check CSS (CSSlint) + +# Pretty-format INI +- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.4.0 + hooks: + - id: pretty-format-ini + name: Format INI + types: [ini] + args: [--autofix] + +# Check JSON +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-json + name: Check JSON + +# Add doctoc to Markdown +- repo: https://github.com/thlorenz/doctoc + rev: v2.2.0 + hooks: + - id: doctoc + name: Generate Markdown ToCs + args: [--maxlevel, '3', --update-only] + +# Lint Markdown +- repo: https://github.com/igorshubovych/markdownlint-cli + rev: v0.32.1 + hooks: + - id: markdownlint + name: Lint Markdown + args: [--fix] + +# Format packaging +- repo: https://github.com/asottile/setup-cfg-fmt + rev: v1.17.0 + hooks: + - id: setup-cfg-fmt + name: Format packaging with setup-cfg-fmt + +# Check packaging +- repo: https://github.com/regebro/pyroma + rev: '3.2' + hooks: + - id: pyroma + +# Fix Python +- repo: https://github.com/asottile/pyupgrade + rev: v2.24.0 + hooks: + - id: pyupgrade + name: Fix Python with Pyupgrade + args: ['--py37-plus', '--keep-runtime-typing'] + +# Check Python +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-ast + name: Check Python + +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.9.0 + hooks: + - id: python-check-mock-methods + name: Check for bad mock methods + - id: python-no-log-warn + name: Check for logger.warn + +# Format Python +- repo: https://github.com/psf/black + rev: '21.7b0' + hooks: + - id: black + name: Format Python with Black + +- repo: https://github.com/asottile/add-trailing-comma + rev: v2.1.0 + hooks: + - id: add-trailing-comma + args: [--py36-plus] + +- repo: https://github.com/pycqa/isort + rev: '5.9.3' + hooks: + - id: isort + name: Format Python imports with isort + +# Lint Python +- repo: https://github.com/pycqa/flake8 + rev: '4.0.1' + hooks: + - id: flake8 + name: Lint Python with Flake8 + exclude: '\bworkshops\b' + +- repo: https://github.com/pycqa/pylint + rev: v2.14.5 + hooks: + - id: pylint + name: Lint Python with Pylint + exclude: '\bworkshops\b' + +# Check Python dependencies +- repo: https://github.com/Lucas-C/pre-commit-hooks-safety + rev: v1.3.0 + hooks: + - id: python-safety-dependencies-check + name: Run Safety check on dependencies + files: 'requirements*.txt' + +# Check RST +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.9.0 + hooks: + - id: rst-backticks + name: Check RST backticks + - id: rst-directive-colons + name: Check RST directive colons + - id: rst-inline-touching-normal + name: Check RST inline touching normal + +# Lint RST +- repo: https://github.com/sphinx-contrib/sphinx-lint + rev: 'v0.6.1' + hooks: + - id: sphinx-lint + name: Lint RST + args: [--enable, all, --disable, line-too-long] + +# Check TOML and XML +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-toml + name: Check TOML + - id: check-xml + name: Check XML + +# Pretty-format YAML +- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks + rev: v2.4.0 + hooks: + - id: pretty-format-yaml + name: Format YAML + exclude: '\bworkshops\b' + args: [--autofix, --indent, '2', --preserve-quotes] + +# Check YAML +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-yaml + name: Check YAML + +# Lint YAML +- repo: https://github.com/adrienverge/yamllint.git + rev: v1.27.1 + hooks: + - id: yamllint + name: Lint YAML + types: [yaml] + args: [-s, -f, colored] + +# Replace CRLF with LF post-prettify +- repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.3.0 + hooks: + - id: remove-crlf + name: Replace CRLFs with LF + types: [text] + exclude_types: [batch, svg] + + +# General checkers + +- repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.9.0 + hooks: + - id: text-unicode-replacement-char + name: Check for Unicode replacement chars + +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.3.0 + hooks: + - id: check-added-large-files + name: Check for added large files + args: [--maxkb=1025] + - id: check-case-conflict + name: Check for case conflicts + - id: check-executables-have-shebangs + name: Check that executables have shebangs + exclude: 'pre\-commit\-config\.yaml' + - id: check-shebang-scripts-are-executable + name: Check that shebangs are executable + exclude: 'pre\-commit\-config\.yaml' + - id: check-merge-conflict + name: Check for merge conflicts + + +# Commit message hooks # + +# Check that commit message follows basic rules +- repo: https://github.com/jorisroovers/gitlint + rev: v0.17.0 + hooks: + - id: gitlint + name: Check commit message + +# Check commit message spelling +- repo: https://github.com/codespell-project/codespell + rev: v2.1.0 + hooks: + - id: codespell + name: Check commit message spelling + stages: [commit-msg] From cb12676ffe8404f254c7054956b4301fe7974c4a Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 27 Oct 2022 13:44:05 -0500 Subject: [PATCH 491/703] CI: Add pytest <7.2.0 constraint when using Qt 5.9 and pytest-qt 3.3.0 --- .github/workflows/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 648669b7..d77fa0fe 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -10,8 +10,8 @@ conda remove -q -n test-env --all || true BINDING=$(echo "$1" | tr '[:lower:]' '[:upper:]') QT_VERSION_VAR=${BINDING}_QT_VERSION # pytest-qt >=4 doesn't support Qt >=5.9 -if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; fi -conda create -q -n test-env python=${PYTHON_VERSION} "pytest>=6,!=7.0.0,!=7.0.1" "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} pip${PIP_VERSION:-} +if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; PYTEST_VERSION=">=6,!=7.0.0,!=7.0.1,<7.2.0"; fi +conda create -q -n test-env python=${PYTHON_VERSION} pytest${PYTEST_VERSION:-">=6,!=7.0.0,!=7.0.1"} "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} pip${PIP_VERSION:-} conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then From 245b4fe4cb8dbf0bf76ad96814d213e2fa7ab613 Mon Sep 17 00:00:00 2001 From: Julien Schueller Date: Wed, 26 Oct 2022 10:06:30 +0200 Subject: [PATCH 492/703] Add QtPdf Closes #381 --- qtpy/QtPdf.py | 28 ++++++++++++++++++++++++++++ qtpy/QtPdfWidgets.py | 28 ++++++++++++++++++++++++++++ qtpy/tests/test_qtpdf.py | 10 ++++++++++ qtpy/tests/test_qtpdfwidgets.py | 8 ++++++++ 4 files changed, 74 insertions(+) create mode 100644 qtpy/QtPdf.py create mode 100644 qtpy/QtPdfWidgets.py create mode 100644 qtpy/tests/test_qtpdf.py create mode 100644 qtpy/tests/test_qtpdfwidgets.py diff --git a/qtpy/QtPdf.py b/qtpy/QtPdf.py new file mode 100644 index 00000000..1bb4f213 --- /dev/null +++ b/qtpy/QtPdf.py @@ -0,0 +1,28 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Provides QtPdf classes and functions.""" + +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) + +if PYQT5: + raise QtBindingMissingModuleError(name='QtPdf') +elif PYQT6: + from PyQt6.QtPdf import * +elif PYSIDE2: + raise QtBindingMissingModuleError(name='QtPdf') +elif PYSIDE6: + from PySide6.QtPdf import * +else: + raise QtBindingsNotFoundError() diff --git a/qtpy/QtPdfWidgets.py b/qtpy/QtPdfWidgets.py new file mode 100644 index 00000000..cb6d92ec --- /dev/null +++ b/qtpy/QtPdfWidgets.py @@ -0,0 +1,28 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2009- The Spyder Development Team +# +# Licensed under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Provides QtPdfWidgets classes and functions.""" + +from . import ( + PYQT5, + PYQT6, + PYSIDE2, + PYSIDE6, + QtBindingsNotFoundError, + QtBindingMissingModuleError, +) + +if PYQT5: + raise QtBindingMissingModuleError(name='QtPdfWidgets') +elif PYQT6: + from PyQt6.QtPdfWidgets import * +elif PYSIDE2: + raise QtBindingMissingModuleError(name='QtPdfWidgets') +elif PYSIDE6: + from PySide6.QtPdfWidgets import * +else: + raise QtBindingsNotFoundError() diff --git a/qtpy/tests/test_qtpdf.py b/qtpy/tests/test_qtpdf.py new file mode 100644 index 00000000..f9611b1f --- /dev/null +++ b/qtpy/tests/test_qtpdf.py @@ -0,0 +1,10 @@ +import pytest + + +def test_qtpdf(): + """Test the qtpy.QtPdf namespace""" + QtPdf = pytest.importorskip("qtpy.QtPdf") + + assert QtPdf.QPdfDocument is not None + assert QtPdf.QPdfLink is not None + assert QtPdf.QPdfSelection is not None diff --git a/qtpy/tests/test_qtpdfwidgets.py b/qtpy/tests/test_qtpdfwidgets.py new file mode 100644 index 00000000..55f508cf --- /dev/null +++ b/qtpy/tests/test_qtpdfwidgets.py @@ -0,0 +1,8 @@ +import pytest + + +def test_qtpdfwidgets(): + """Test the qtpy.QtPdfWidgets namespace""" + QtPdfWidgets = pytest.importorskip("qtpy.QtPdfWidgets") + + assert QtPdfWidgets.QPdfView is not None From 589a964b64e5ee08d8788e841ce1cb325a1e69ee Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 20 Oct 2022 13:19:46 -0500 Subject: [PATCH 493/703] Raise error when no bindings are found at init Co-authored-by: tgwoodcock --- qtpy/Qsci.py | 3 --- qtpy/Qt3DAnimation.py | 3 --- qtpy/Qt3DCore.py | 3 --- qtpy/Qt3DExtras.py | 3 --- qtpy/Qt3DInput.py | 3 --- qtpy/Qt3DLogic.py | 3 --- qtpy/Qt3DRender.py | 3 --- qtpy/QtAxContainer.py | 3 --- qtpy/QtBluetooth.py | 3 --- qtpy/QtCharts.py | 3 --- qtpy/QtConcurrent.py | 3 --- qtpy/QtCore.py | 5 +---- qtpy/QtDBus.py | 3 --- qtpy/QtDataVisualization.py | 3 --- qtpy/QtDesigner.py | 3 --- qtpy/QtGui.py | 4 +--- qtpy/QtHelp.py | 4 +--- qtpy/QtLocation.py | 3 --- qtpy/QtMacExtras.py | 3 --- qtpy/QtMultimedia.py | 4 +--- qtpy/QtMultimediaWidgets.py | 4 +--- qtpy/QtNetwork.py | 4 +--- qtpy/QtNetworkAuth.py | 3 --- qtpy/QtNfc.py | 3 --- qtpy/QtOpenGL.py | 4 +--- qtpy/QtOpenGLWidgets.py | 3 --- qtpy/QtPositioning.py | 4 +--- qtpy/QtPrintSupport.py | 4 +--- qtpy/QtPurchasing.py | 3 --- qtpy/QtQml.py | 4 +--- qtpy/QtQuick.py | 4 +--- qtpy/QtQuick3D.py | 3 --- qtpy/QtQuickControls2.py | 3 --- qtpy/QtQuickWidgets.py | 4 +--- qtpy/QtRemoteObjects.py | 4 +--- qtpy/QtScxml.py | 3 --- qtpy/QtSensors.py | 4 +--- qtpy/QtSerialPort.py | 4 +--- qtpy/QtSql.py | 4 +--- qtpy/QtStateMachine.py | 3 --- qtpy/QtSvg.py | 4 +--- qtpy/QtSvgWidgets.py | 3 --- qtpy/QtTest.py | 4 +--- qtpy/QtTextToSpeech.py | 3 --- qtpy/QtUiTools.py | 3 --- qtpy/QtWebChannel.py | 4 +--- qtpy/QtWebEngine.py | 3 --- qtpy/QtWebEngineCore.py | 3 --- qtpy/QtWebEngineQuick.py | 3 --- qtpy/QtWebEngineWidgets.py | 3 --- qtpy/QtWebSockets.py | 4 +--- qtpy/QtWidgets.py | 4 +--- qtpy/QtWinExtras.py | 3 --- qtpy/QtX11Extras.py | 3 --- qtpy/QtXml.py | 4 +--- qtpy/QtXmlPatterns.py | 3 --- qtpy/__init__.py | 2 +- qtpy/compat.py | 3 --- qtpy/shiboken.py | 3 --- qtpy/sip.py | 3 --- 60 files changed, 23 insertions(+), 179 deletions(-) diff --git a/qtpy/Qsci.py b/qtpy/Qsci.py index 7639e6cc..18d1ef06 100644 --- a/qtpy/Qsci.py +++ b/qtpy/Qsci.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, QtModuleNotInstalledError ) @@ -35,5 +34,3 @@ raise QtBindingMissingModuleError(name='Qsci') elif PYSIDE6: raise QtBindingMissingModuleError(name='Qsci') -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DAnimation.py b/qtpy/Qt3DAnimation.py index b51acfc6..4befdd92 100644 --- a/qtpy/Qt3DAnimation.py +++ b/qtpy/Qt3DAnimation.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -44,5 +43,3 @@ for __name in inspect.getmembers(__temp.Qt3DAnimation): globals()[__name[0]] = __name[1] -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DCore.py b/qtpy/Qt3DCore.py index e0b3f83f..007ef876 100644 --- a/qtpy/Qt3DCore.py +++ b/qtpy/Qt3DCore.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -44,5 +43,3 @@ for __name in inspect.getmembers(__temp.Qt3DCore): globals()[__name[0]] = __name[1] -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DExtras.py b/qtpy/Qt3DExtras.py index 5bf610b2..81b82c16 100644 --- a/qtpy/Qt3DExtras.py +++ b/qtpy/Qt3DExtras.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -44,5 +43,3 @@ for __name in inspect.getmembers(__temp.Qt3DExtras): globals()[__name[0]] = __name[1] -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DInput.py b/qtpy/Qt3DInput.py index 73471ed2..5a706be5 100644 --- a/qtpy/Qt3DInput.py +++ b/qtpy/Qt3DInput.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -44,5 +43,3 @@ for __name in inspect.getmembers(__temp.Qt3DInput): globals()[__name[0]] = __name[1] -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DLogic.py b/qtpy/Qt3DLogic.py index 12a8b7c1..59077c06 100644 --- a/qtpy/Qt3DLogic.py +++ b/qtpy/Qt3DLogic.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -44,5 +43,3 @@ for __name in inspect.getmembers(__temp.Qt3DLogic): globals()[__name[0]] = __name[1] -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/Qt3DRender.py b/qtpy/Qt3DRender.py index 1a783eae..d7bf9f00 100644 --- a/qtpy/Qt3DRender.py +++ b/qtpy/Qt3DRender.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -44,5 +43,3 @@ for __name in inspect.getmembers(__temp.Qt3DRender): globals()[__name[0]] = __name[1] -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtAxContainer.py b/qtpy/QtAxContainer.py index 3ad07148..801491d6 100644 --- a/qtpy/QtAxContainer.py +++ b/qtpy/QtAxContainer.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ from PySide2.QtAxContainer import * elif PYSIDE6: from PySide6.QtAxContainer import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtBluetooth.py b/qtpy/QtBluetooth.py index 3bbcc93a..2f649743 100644 --- a/qtpy/QtBluetooth.py +++ b/qtpy/QtBluetooth.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ raise QtBindingMissingModuleError(name='QtBluetooth') elif PYSIDE6: from PySide6.QtBluetooth import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtCharts.py b/qtpy/QtCharts.py index 21f84de9..568d9c99 100644 --- a/qtpy/QtCharts.py +++ b/qtpy/QtCharts.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -44,5 +43,3 @@ elif PYSIDE6: from PySide6.QtCharts import * from PySide6 import QtCharts -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtConcurrent.py b/qtpy/QtConcurrent.py index d849d4c2..ca14d42e 100644 --- a/qtpy/QtConcurrent.py +++ b/qtpy/QtConcurrent.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ from PySide2.QtConcurrent import * elif PYSIDE6: from PySide6.QtConcurrent import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 18cc4d9b..d87c4278 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -9,7 +9,7 @@ """Provides QtCore classes and functions.""" from typing import TYPE_CHECKING -from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtCore import * @@ -120,7 +120,4 @@ QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QLibraryInfo.location = QLibraryInfo.path -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtDBus.py b/qtpy/QtDBus.py index 2f863c3f..6d479fa5 100644 --- a/qtpy/QtDBus.py +++ b/qtpy/QtDBus.py @@ -14,7 +14,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, QtModuleNotInOSError, ) @@ -30,5 +29,3 @@ from PySide6.QtDBus import * else: raise QtModuleNotInOSError(name='QtDBus') -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtDataVisualization.py b/qtpy/QtDataVisualization.py index c5885414..692430f1 100644 --- a/qtpy/QtDataVisualization.py +++ b/qtpy/QtDataVisualization.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -40,5 +39,3 @@ globals()[__name[0]] = __name[1] elif PYSIDE6: from PySide6.QtDataVisualization import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtDesigner.py b/qtpy/QtDesigner.py index ae2696b2..969d8480 100644 --- a/qtpy/QtDesigner.py +++ b/qtpy/QtDesigner.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ raise QtBindingMissingModuleError(name='QtDesigner') elif PYSIDE6: from PySide6.QtDesigner import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index df9f6632..662b84cd 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -8,7 +8,7 @@ """Provides QtGui classes and functions.""" -from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtGui import * @@ -42,8 +42,6 @@ # Map DeprecationWarning methods QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QGuiApplication.exec_ = QGuiApplication.exec -else: - raise QtBindingsNotFoundError() if PYSIDE2 or PYSIDE6: # PySide{2,6} do not accept the `mode` keyword argument in diff --git a/qtpy/QtHelp.py b/qtpy/QtHelp.py index 337e6b0f..d0e2babb 100644 --- a/qtpy/QtHelp.py +++ b/qtpy/QtHelp.py @@ -7,7 +7,7 @@ """QtHelp Wrapper.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtHelp import * @@ -17,5 +17,3 @@ from PySide2.QtHelp import * elif PYSIDE6: from PySide6.QtHelp import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtLocation.py b/qtpy/QtLocation.py index 72a6ba12..1f7132dc 100644 --- a/qtpy/QtLocation.py +++ b/qtpy/QtLocation.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ from PySide2.QtLocation import * elif PYSIDE6: raise QtBindingMissingModuleError(name='QtLocation') -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtMacExtras.py b/qtpy/QtMacExtras.py index 9d391cb6..d495c1eb 100644 --- a/qtpy/QtMacExtras.py +++ b/qtpy/QtMacExtras.py @@ -14,7 +14,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInOSError, ) @@ -28,7 +27,5 @@ from PySide2.QtMacExtras import * elif PYSIDE6: raise QtModuleNotInQtVersionError(name='QtMacExtras') - else: - raise QtBindingsNotFoundError() else: raise QtModuleNotInOSError(name='QtMacExtras') diff --git a/qtpy/QtMultimedia.py b/qtpy/QtMultimedia.py index cf9ecdc6..7403e64d 100644 --- a/qtpy/QtMultimedia.py +++ b/qtpy/QtMultimedia.py @@ -7,7 +7,7 @@ """Provides low-level multimedia functionality.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtMultimedia import * @@ -17,5 +17,3 @@ from PySide2.QtMultimedia import * elif PYSIDE6: from PySide6.QtMultimedia import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtMultimediaWidgets.py b/qtpy/QtMultimediaWidgets.py index 0e9dee51..69af111a 100644 --- a/qtpy/QtMultimediaWidgets.py +++ b/qtpy/QtMultimediaWidgets.py @@ -7,7 +7,7 @@ """Provides QtMultimediaWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtMultimediaWidgets import * @@ -17,5 +17,3 @@ from PySide2.QtMultimediaWidgets import * elif PYSIDE6: from PySide6.QtMultimediaWidgets import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtNetwork.py b/qtpy/QtNetwork.py index b3cfdd3c..2c4e5476 100644 --- a/qtpy/QtNetwork.py +++ b/qtpy/QtNetwork.py @@ -8,7 +8,7 @@ """Provides QtNetwork classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtNetwork import * @@ -18,5 +18,3 @@ from PySide2.QtNetwork import * elif PYSIDE6: from PySide6.QtNetwork import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtNetworkAuth.py b/qtpy/QtNetworkAuth.py index 94f8f5bc..abf7526e 100644 --- a/qtpy/QtNetworkAuth.py +++ b/qtpy/QtNetworkAuth.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, QtBindingMissingModuleError, ) @@ -35,5 +34,3 @@ raise QtBindingMissingModuleError(name='QtNetworkAuth') elif PYSIDE6: from PySide6.QtNetworkAuth import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtNfc.py b/qtpy/QtNfc.py index adf73e25..38007248 100644 --- a/qtpy/QtNfc.py +++ b/qtpy/QtNfc.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ raise QtBindingMissingModuleError(name='QtNfc') elif PYSIDE6: from PySide6.QtNfc import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtOpenGL.py b/qtpy/QtOpenGL.py index b1f749dd..345853a1 100644 --- a/qtpy/QtOpenGL.py +++ b/qtpy/QtOpenGL.py @@ -7,7 +7,7 @@ """Provides QtOpenGL classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtOpenGL import * @@ -65,5 +65,3 @@ from PySide2.QtGui import QOpenGLTimeMonitor, QOpenGLTimerQuery except ImportError: pass -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtOpenGLWidgets.py b/qtpy/QtOpenGLWidgets.py index cf1a98d0..fb9fa62a 100644 --- a/qtpy/QtOpenGLWidgets.py +++ b/qtpy/QtOpenGLWidgets.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ raise QtBindingMissingModuleError(name='QtOpenGLWidgets') elif PYSIDE6: from PySide6.QtOpenGLWidgets import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtPositioning.py b/qtpy/QtPositioning.py index 0583d3e6..5083cee7 100644 --- a/qtpy/QtPositioning.py +++ b/qtpy/QtPositioning.py @@ -7,7 +7,7 @@ """Provides QtPositioning classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtPositioning import * @@ -17,5 +17,3 @@ from PySide2.QtPositioning import * elif PYSIDE6: from PySide6.QtPositioning import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtPrintSupport.py b/qtpy/QtPrintSupport.py index 2fa24a20..263747d2 100644 --- a/qtpy/QtPrintSupport.py +++ b/qtpy/QtPrintSupport.py @@ -7,7 +7,7 @@ """Provides QtPrintSupport classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 if PYQT5: from PyQt5.QtPrintSupport import * @@ -23,5 +23,3 @@ QPrintDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) elif PYSIDE2: from PySide2.QtPrintSupport import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtPurchasing.py b/qtpy/QtPurchasing.py index ab0aba57..00141a25 100644 --- a/qtpy/QtPurchasing.py +++ b/qtpy/QtPurchasing.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, QtModuleNotInstalledError, ) @@ -30,5 +29,3 @@ raise QtBindingMissingModuleError(name='QtPurchasing') elif PYSIDE6: raise QtBindingMissingModuleError(name='QtPurchasing') -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtQml.py b/qtpy/QtQml.py index 4bb35a2c..9d07f0e8 100644 --- a/qtpy/QtQml.py +++ b/qtpy/QtQml.py @@ -7,7 +7,7 @@ """Provides QtQml classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtQml import * @@ -17,5 +17,3 @@ from PySide6.QtQml import * elif PYSIDE2: from PySide2.QtQml import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtQuick.py b/qtpy/QtQuick.py index 577fcdb0..7f140cb1 100644 --- a/qtpy/QtQuick.py +++ b/qtpy/QtQuick.py @@ -7,7 +7,7 @@ """Provides QtQuick classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 if PYQT5: from PyQt5.QtQuick import * @@ -17,5 +17,3 @@ from PySide6.QtQuick import * elif PYSIDE2: from PySide2.QtQuick import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtQuick3D.py b/qtpy/QtQuick3D.py index ad81c14b..eeb0a60c 100644 --- a/qtpy/QtQuick3D.py +++ b/qtpy/QtQuick3D.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ raise QtBindingMissingModuleError(name='QtQuick3D') elif PYSIDE6: from PySide6.QtQuick3D import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtQuickControls2.py b/qtpy/QtQuickControls2.py index 0693857f..743aa5d4 100644 --- a/qtpy/QtQuickControls2.py +++ b/qtpy/QtQuickControls2.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ from PySide2.QtQuickControls2 import * elif PYSIDE6: from PySide6.QtQuickControls2 import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtQuickWidgets.py b/qtpy/QtQuickWidgets.py index 1bc5521a..4ed4a21c 100644 --- a/qtpy/QtQuickWidgets.py +++ b/qtpy/QtQuickWidgets.py @@ -7,7 +7,7 @@ """Provides QtQuickWidgets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 if PYQT5: from PyQt5.QtQuickWidgets import * @@ -17,5 +17,3 @@ from PySide6.QtQuickWidgets import * elif PYSIDE2: from PySide2.QtQuickWidgets import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtRemoteObjects.py b/qtpy/QtRemoteObjects.py index 59cce95d..1035586b 100644 --- a/qtpy/QtRemoteObjects.py +++ b/qtpy/QtRemoteObjects.py @@ -7,7 +7,7 @@ """Provides QtRemoteObjects classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtRemoteObjects import * @@ -17,5 +17,3 @@ from PySide6.QtRemoteObjects import * elif PYSIDE2: from PySide2.QtRemoteObjects import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtScxml.py b/qtpy/QtScxml.py index cbf2a96b..81a9ac13 100644 --- a/qtpy/QtScxml.py +++ b/qtpy/QtScxml.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ from PySide2.QtScxml import * elif PYSIDE6: from PySide6.QtScxml import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtSensors.py b/qtpy/QtSensors.py index 6e28cc22..25b29190 100644 --- a/qtpy/QtSensors.py +++ b/qtpy/QtSensors.py @@ -7,7 +7,7 @@ """Provides QtSensors classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtSensors import * @@ -17,5 +17,3 @@ from PySide6.QtSensors import * elif PYSIDE2: from PySide2.QtSensors import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtSerialPort.py b/qtpy/QtSerialPort.py index ed4584a9..878c35b1 100644 --- a/qtpy/QtSerialPort.py +++ b/qtpy/QtSerialPort.py @@ -8,7 +8,7 @@ """Provides QtSerialPort classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtSerialPort import * @@ -18,5 +18,3 @@ from PySide6.QtSerialPort import * elif PYSIDE2: from PySide2.QtSerialPort import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtSql.py b/qtpy/QtSql.py index 372fe1f9..5f7395b8 100644 --- a/qtpy/QtSql.py +++ b/qtpy/QtSql.py @@ -7,7 +7,7 @@ """Provides QtSql classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 if PYQT5: from PyQt5.QtSql import * @@ -24,5 +24,3 @@ QSqlResult.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) elif PYSIDE2: from PySide2.QtSql import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtStateMachine.py b/qtpy/QtStateMachine.py index 039749a6..4afbcf49 100644 --- a/qtpy/QtStateMachine.py +++ b/qtpy/QtStateMachine.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ raise QtBindingMissingModuleError(name='QtStateMachine') elif PYSIDE6: from PySide6.QtStateMachine import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtSvg.py b/qtpy/QtSvg.py index 4d902875..0ee4f9e1 100644 --- a/qtpy/QtSvg.py +++ b/qtpy/QtSvg.py @@ -7,7 +7,7 @@ """Provides QtSvg classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtSvg import * @@ -17,5 +17,3 @@ from PySide2.QtSvg import * elif PYSIDE6: from PySide6.QtSvg import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtSvgWidgets.py b/qtpy/QtSvgWidgets.py index 571ca18c..07eedb07 100644 --- a/qtpy/QtSvgWidgets.py +++ b/qtpy/QtSvgWidgets.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ raise QtBindingMissingModuleError(name='QtSvgWidgets') elif PYSIDE6: from PySide6.QtSvgWidgets import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtTest.py b/qtpy/QtTest.py index e08f5b74..3be7f95a 100644 --- a/qtpy/QtTest.py +++ b/qtpy/QtTest.py @@ -8,7 +8,7 @@ """Provides QtTest and functions""" -from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE6, PYSIDE2 if PYQT5: from PyQt5.QtTest import * @@ -25,5 +25,3 @@ from PySide2.QtTest import * elif PYSIDE6: from PySide6.QtTest import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtTextToSpeech.py b/qtpy/QtTextToSpeech.py index 81eae9eb..cd978328 100644 --- a/qtpy/QtTextToSpeech.py +++ b/qtpy/QtTextToSpeech.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ from PySide2.QtTextToSpeech import * elif PYSIDE6: raise QtBindingMissingModuleError(name='QtTextToSpeech') -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtUiTools.py b/qtpy/QtUiTools.py index 1dc9e377..5104a645 100644 --- a/qtpy/QtUiTools.py +++ b/qtpy/QtUiTools.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ from PySide2.QtUiTools import * elif PYSIDE6: from PySide6.QtUiTools import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebChannel.py b/qtpy/QtWebChannel.py index 6c7ae285..b2c35fff 100644 --- a/qtpy/QtWebChannel.py +++ b/qtpy/QtWebChannel.py @@ -7,7 +7,7 @@ """Provides QtWebChannel classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtWebChannel import * @@ -17,5 +17,3 @@ from PySide2.QtWebChannel import * elif PYSIDE6: from PySide6.QtWebChannel import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebEngine.py b/qtpy/QtWebEngine.py index e592f4d7..c5c80d52 100644 --- a/qtpy/QtWebEngine.py +++ b/qtpy/QtWebEngine.py @@ -13,7 +13,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInstalledError, ) @@ -31,5 +30,3 @@ from PySide2.QtWebEngine import * elif PYSIDE6: raise QtModuleNotInQtVersionError(name='QtWebEngine') -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebEngineCore.py b/qtpy/QtWebEngineCore.py index d8c6ca3a..76efbbfb 100644 --- a/qtpy/QtWebEngineCore.py +++ b/qtpy/QtWebEngineCore.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -34,5 +33,3 @@ from PySide2.QtWebEngineCore import * elif PYSIDE6: from PySide6.QtWebEngineCore import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebEngineQuick.py b/qtpy/QtWebEngineQuick.py index cd10d1d5..659834a3 100644 --- a/qtpy/QtWebEngineQuick.py +++ b/qtpy/QtWebEngineQuick.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, QtBindingMissingModuleError, ) @@ -30,5 +29,3 @@ raise QtBindingMissingModuleError(name='QtWebEngineQuick') elif PYSIDE6: from PySide6.QtWebEngineQuick import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index eaeac6fa..05a18378 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -13,7 +13,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInstalledError, ) @@ -62,5 +61,3 @@ from PySide6.QtWebEngineCore import QWebEngineSettings from PySide6.QtWebEngineCore import QWebEngineProfile from PySide6.QtWebEngineCore import QWebEngineScript -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtWebSockets.py b/qtpy/QtWebSockets.py index 9a595888..a9bd33d9 100644 --- a/qtpy/QtWebSockets.py +++ b/qtpy/QtWebSockets.py @@ -7,7 +7,7 @@ """Provides QtWebSockets classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtWebSockets import * @@ -17,5 +17,3 @@ from PySide2.QtWebSockets import * elif PYSIDE6: from PySide6.QtWebSockets import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index d8fb7b69..1e4a0340 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -8,7 +8,7 @@ """Provides widget classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtWidgets import * @@ -52,5 +52,3 @@ QApplication.exec_ = QApplication.exec QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtWinExtras.py b/qtpy/QtWinExtras.py index 475ad5e5..0396f953 100644 --- a/qtpy/QtWinExtras.py +++ b/qtpy/QtWinExtras.py @@ -14,7 +14,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInOSError, ) @@ -28,7 +27,5 @@ from PySide2.QtWinExtras import * elif PYSIDE6: raise QtModuleNotInQtVersionError(name='QtWinExtras') - else: - raise QtBindingsNotFoundError() else: raise QtModuleNotInOSError(name='QtWinExtras') diff --git a/qtpy/QtX11Extras.py b/qtpy/QtX11Extras.py index be02348e..51247c13 100644 --- a/qtpy/QtX11Extras.py +++ b/qtpy/QtX11Extras.py @@ -14,7 +14,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtModuleNotInQtVersionError, QtModuleNotInOSError, ) @@ -28,7 +27,5 @@ from PySide2.QtX11Extras import * elif PYSIDE6: raise QtModuleNotInQtVersionError(name='QtX11Extras') - else: - raise QtBindingsNotFoundError() else: raise QtModuleNotInOSError(name='QtX11Extras') diff --git a/qtpy/QtXml.py b/qtpy/QtXml.py index d2cb1653..5f1e3b82 100644 --- a/qtpy/QtXml.py +++ b/qtpy/QtXml.py @@ -7,7 +7,7 @@ """Provides QtXml classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtBindingsNotFoundError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 if PYQT5: from PyQt5.QtXml import * @@ -17,5 +17,3 @@ from PySide2.QtXml import * elif PYSIDE6: from PySide6.QtXml import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtXmlPatterns.py b/qtpy/QtXmlPatterns.py index 71040e0c..5e63f448 100644 --- a/qtpy/QtXmlPatterns.py +++ b/qtpy/QtXmlPatterns.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ from PySide2.QtXmlPatterns import * elif PYSIDE6: raise QtBindingMissingModuleError(name='QtXmlPatterns') -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 2533636b..53b0e5a6 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -256,7 +256,7 @@ def __init__(self, *, missing_package=None, **superclass_kwargs): QT6 = PYSIDE6 = True except ImportError: - API = 'pyqt5' + raise QtBindingsNotFoundError() else: os.environ[QT_API] = API diff --git a/qtpy/compat.py b/qtpy/compat.py index 738a3d7f..325c0ddb 100644 --- a/qtpy/compat.py +++ b/qtpy/compat.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, ) from .QtWidgets import QFileDialog @@ -148,5 +147,3 @@ def isalive(object): elif PYSIDE2 or PYSIDE6: from . import shiboken return shiboken.isValid(object) - else: - raise QtBindingsNotFoundError() diff --git a/qtpy/shiboken.py b/qtpy/shiboken.py index 4133e85d..e9d79320 100644 --- a/qtpy/shiboken.py +++ b/qtpy/shiboken.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -26,5 +25,3 @@ elif PYSIDE6: from shiboken6 import * import shiboken6 as shiboken -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/sip.py b/qtpy/sip.py index 736af76f..89a31ff3 100644 --- a/qtpy/sip.py +++ b/qtpy/sip.py @@ -12,7 +12,6 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) @@ -24,5 +23,3 @@ raise QtBindingMissingModuleError(name='sip') elif PYSIDE6: raise QtBindingMissingModuleError(name='sip') -else: - raise QtBindingsNotFoundError() From 83e31a003f68cd2bd4729c71ad8aabe29729e919 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Sat, 5 Nov 2022 20:09:12 -0500 Subject: [PATCH 494/703] Remove QtBindingsNotFoundError for QtPdf and QtPdfWidgets modules --- qtpy/QtPdf.py | 5 ++--- qtpy/QtPdfWidgets.py | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/qtpy/QtPdf.py b/qtpy/QtPdf.py index 1bb4f213..3a6796ca 100644 --- a/qtpy/QtPdf.py +++ b/qtpy/QtPdf.py @@ -12,17 +12,16 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) if PYQT5: raise QtBindingMissingModuleError(name='QtPdf') elif PYQT6: + # Available with version >=6.4.0 from PyQt6.QtPdf import * elif PYSIDE2: raise QtBindingMissingModuleError(name='QtPdf') elif PYSIDE6: + # Available with version >=6.4.0 from PySide6.QtPdf import * -else: - raise QtBindingsNotFoundError() diff --git a/qtpy/QtPdfWidgets.py b/qtpy/QtPdfWidgets.py index cb6d92ec..1b0a2e11 100644 --- a/qtpy/QtPdfWidgets.py +++ b/qtpy/QtPdfWidgets.py @@ -12,17 +12,16 @@ PYQT6, PYSIDE2, PYSIDE6, - QtBindingsNotFoundError, QtBindingMissingModuleError, ) if PYQT5: raise QtBindingMissingModuleError(name='QtPdfWidgets') elif PYQT6: + # Available with version >=6.4.0 from PyQt6.QtPdfWidgets import * elif PYSIDE2: raise QtBindingMissingModuleError(name='QtPdfWidgets') elif PYSIDE6: + # Available with version >=6.4.0 from PySide6.QtPdfWidgets import * -else: - raise QtBindingsNotFoundError() From 558544a4e29d7c6cc3513c61805401e5235407fa Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 7 Nov 2022 16:44:27 -0500 Subject: [PATCH 495/703] Release 2.3.0 --- CHANGELOG.md | 29 +++++++++++++++++++++++++++++ qtpy/__init__.py | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64967682..1d6a1ae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # History of changes +## Version 2.3.0 (2022-11-07) + +### Issues Closed + +* [Issue 384](https://github.com/spyder-ide/qtpy/issues/384) - Release QtPy 2.3.0 +* [Issue 381](https://github.com/spyder-ide/qtpy/issues/381) - Add QtPdf, QtPdfWidgets ([PR 382](https://github.com/spyder-ide/qtpy/pull/382) by [@jschueller](https://github.com/jschueller)) +* [Issue 375](https://github.com/spyder-ide/qtpy/issues/375) - Test PySide6 6.4.x ([PR 376](https://github.com/spyder-ide/qtpy/pull/376) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 373](https://github.com/spyder-ide/qtpy/issues/373) - PySide6 6.4.0 support - `AttributeError: Cannot reassign members` when aliasing enum values ([PR 374](https://github.com/spyder-ide/qtpy/pull/374) by [@astrofrog](https://github.com/astrofrog)) +* [Issue 367](https://github.com/spyder-ide/qtpy/issues/367) - Import behaviour if no Qt bindings are installed ([PR 379](https://github.com/spyder-ide/qtpy/pull/379) by [@dalthviz](https://github.com/dalthviz)) +* [Issue 134](https://github.com/spyder-ide/qtpy/issues/134) - QScintilla Support ([PR 372](https://github.com/spyder-ide/qtpy/pull/372) by [@dgoeries](https://github.com/dgoeries)) + +In this release 6 issues were closed. + +### Pull Requests Merged + +* [PR 382](https://github.com/spyder-ide/qtpy/pull/382) - PR: Add `QtPdf` and `QtPdfWidgets`, by [@jschueller](https://github.com/jschueller) ([381](https://github.com/spyder-ide/qtpy/issues/381)) +* [PR 380](https://github.com/spyder-ide/qtpy/pull/380) - PR: Enable more qt6 tests, by [@jschueller](https://github.com/jschueller) +* [PR 379](https://github.com/spyder-ide/qtpy/pull/379) - PR: Raise error when no bindings are found at `__init__`, by [@dalthviz](https://github.com/dalthviz) ([367](https://github.com/spyder-ide/qtpy/issues/367)) +* [PR 378](https://github.com/spyder-ide/qtpy/pull/378) - PR: Try PySide6 on conda, by [@jschueller](https://github.com/jschueller) +* [PR 376](https://github.com/spyder-ide/qtpy/pull/376) - PR: Expand the CI test matrix with Qt/bindings 6.4 and fix tests with PyQt 5.9, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([375](https://github.com/spyder-ide/qtpy/issues/375)) +* [PR 374](https://github.com/spyder-ide/qtpy/pull/374) - Don't re-assign Qt.MouseButton.MiddleButton on PySide6, by [@astrofrog](https://github.com/astrofrog) ([373](https://github.com/spyder-ide/qtpy/issues/373)) +* [PR 372](https://github.com/spyder-ide/qtpy/pull/372) - PR: Add `Qsci` to the imports, by [@dgoeries](https://github.com/dgoeries) ([134](https://github.com/spyder-ide/qtpy/issues/134)) + +In this release 7 pull requests were closed. + + +---- + + ## Version 2.2.1 (2022-10-03) ### Issues Closed diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 53b0e5a6..309b7b7e 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.3.0.dev0' +__version__ = '2.3.0' class PythonQtError(RuntimeError): From 15caa9e0da28a9d55d9bc42e9f7dac449f7d3a0e Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 7 Nov 2022 16:51:05 -0500 Subject: [PATCH 496/703] Back to work --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 309b7b7e..be8ecbd6 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.3.0' +__version__ = '2.4.0.dev0' class PythonQtError(RuntimeError): From 8d2436bed19a06b74facc6c82d2983922212602c Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Fri, 9 Dec 2022 21:24:31 -0500 Subject: [PATCH 497/703] Remove OpenGL imports to improve modularity --- qtpy/QtGui.py | 2 -- qtpy/QtWidgets.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 662b84cd..5ef9da97 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -15,7 +15,6 @@ elif PYQT6: from PyQt6 import QtGui from PyQt6.QtGui import * - from PyQt6.QtOpenGL import * QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) @@ -35,7 +34,6 @@ QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) elif PYSIDE6: from PySide6.QtGui import * - from PySide6.QtOpenGL import * QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 1e4a0340..e23d4fd5 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -16,7 +16,6 @@ from PyQt6 import QtWidgets from PyQt6.QtWidgets import * from PyQt6.QtGui import QAction, QActionGroup, QShortcut, QFileSystemModel, QUndoCommand - from PyQt6.QtOpenGLWidgets import QOpenGLWidget # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) @@ -39,7 +38,6 @@ elif PYSIDE6: from PySide6.QtWidgets import * from PySide6.QtGui import QAction, QActionGroup, QShortcut, QUndoCommand - from PySide6.QtOpenGLWidgets import QOpenGLWidget # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) From 3380cb41ed4b04be2caf5f31ed89678f219f410f Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 16 Dec 2022 18:45:33 -0600 Subject: [PATCH 498/703] Use raise from None when raising QtBindingsNotFoundError in __init__ --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index be8ecbd6..51cd5a0a 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -256,7 +256,7 @@ def __init__(self, *, missing_package=None, **superclass_kwargs): QT6 = PYSIDE6 = True except ImportError: - raise QtBindingsNotFoundError() + raise QtBindingsNotFoundError from None else: os.environ[QT_API] = API From e2c2d77749e3de7148cef0b1597ab6711be2e8bd Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 16 Dec 2022 20:11:19 -0600 Subject: [PATCH 499/703] Fix/upgrade a few minor ancillary bits of infra/config --- .github/workflows/ci.yml | 4 ++-- .github/workflows/test.sh | 2 +- pyproject.toml | 1 - setup.cfg | 1 - 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a910d913..ba241fa2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,9 +72,9 @@ jobs: skip-pyside6: true # pytest-qt crashes on pytest startup under this environment steps: - name: Checkout branch - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Setup Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: '3.x' - name: Install Linux system packages diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index d77fa0fe..15f0fc63 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -21,7 +21,7 @@ if [ "$USE_CONDA" = "Yes" ]; then elif [ "${1}" = "pyside2" ]; then conda install -q qt=${PYSIDE2_QT_VERSION:-"5.12"} pyside2=${PYSIDE2_VERSION:-"5"} elif [ "${1}" = "pyside6" ]; then - conda install -q pyside6=${PYSIDE6_VERSION:-"6.4"} + conda install -q qt6-main=${PYSIDE6_QT_VERSION:-"6.4"} pyside6=${PYSIDE6_VERSION:-"6.4"} else exit 1 fi diff --git a/pyproject.toml b/pyproject.toml index f6c16894..15caa0ee 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,5 @@ [build-system] requires = [ "setuptools>=42", - "wheel", ] build-backend = "setuptools.build_meta" diff --git a/setup.cfg b/setup.cfg index 63b88881..7d3d1808 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,7 +10,6 @@ author_email = spyder.python@gmail.com maintainer = Spyder Development Team and QtPy Contributors maintainer_email = spyder.python@gmail.com license = MIT -license_file = LICENSE.txt license_files = AUTHORS.md LICENSE.txt From 5a620af73341591ae087e6da5305214aa7d13638 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 16 Dec 2022 22:30:06 -0600 Subject: [PATCH 500/703] Single-source dependency version selection in CI matrix configuration --- .github/workflows/ci.yml | 55 ++++++++++++++++++++++----------------- .github/workflows/test.sh | 22 ++++++++-------- 2 files changed, 42 insertions(+), 35 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba241fa2..81b4681c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,47 +25,54 @@ jobs: CI: 'True' PYTHON_VERSION: ${{ matrix.python-version }} USE_CONDA: ${{ matrix.use-conda }} + PYQT5_VERSION: ${{ matrix.pyqt5-version || matrix.qt5-version-default }} + PYQT5_QT_VERSION: ${{ matrix.pyqt5-qt-version || matrix.pyqt5-version || matrix.qt5-version-default }} + PYQT6_VERSION: ${{ matrix.pyqt6-version || matrix.qt6-version-default }} + PYQT6_QT_VERSION: ${{ matrix.pyqt6-qt-version || matrix.pyqt6-version || matrix.qt6-version-default }} + PYSIDE2_VERSION: ${{ matrix.pyside2-version || matrix.qt5-version-default }} + PYSIDE2_QT_VERSION: ${{ matrix.pyside2-qt-version || matrix.pyside2-version || matrix.qt5-version-default }} + PYSIDE6_VERSION: ${{ matrix.pyside6-version || matrix.qt6-version-default }} + PYSIDE6_QT_VERSION: ${{ matrix.pyside6-qt-version || matrix.pyside6-version || matrix.qt6-version-default }} + QSCINTILLA_VERSION: ${{ matrix.qscintilla-version || matrix.qscintilla-version-default }} SKIP_PIP_CHECK: ${{ matrix.skip-pip-check }} - PYQT5_VERSION: ${{ matrix.pyqt5-version }} - PYQT6_VERSION: ${{ matrix.pyqt6-version }} - PYSIDE2_VERSION: ${{ matrix.pyside2-version }} - PYSIDE6_VERSION: ${{ matrix.pyside6-version }} - PYQT5_QT_VERSION: ${{ matrix.pyqt5-qt-version }} - PYQT6_QT_VERSION: ${{ matrix.pyqt6-qt-version }} - PYSIDE2_QT_VERSION: ${{ matrix.pyside2-qt-version }} - PYSIDE6_QT_VERSION: ${{ matrix.pyside6-qt-version }} - QSCINTILLA_VERSION: '2.13' strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] python-version: ['3.7', '3.10'] use-conda: ['Yes', 'No'] + qt5-version-default: ['5.12'] + qt6-version-default: ['6.3'] + qscintilla-version-default: ['2.13'] include: - os: ubuntu-latest special-invocation: 'xvfb-run --auto-servernum ' # Needed for GUI tests to work - python-version: '3.10' - skip-pyside2: true # Skip Pyside2 on all Python 3.10 builds until it supports it - - use-conda: 'Yes' # No PyQt6 conda packages yet - skip-pyqt6: true - - os: windows-latest - python-version: '3.10' - use-conda: 'No' - pyqt6-version: '6.4' # Test upper bound - pyqt6-qt-version: '6.4' # Test upper bound - pyside2-version: '5.15' # No 5.12 wheel on Windows and Python 3.10 - pyside6-version: '6.4' # Test upper bound - pyside6-qt-version: '6.4' # Test upper bound + skip-pyside2: true # Skip Pyside2 on all Python 3.10 builds until/unless it supports it + - use-conda: 'Yes' + skip-pyqt6: true # No PyQt6 conda packages yet + pyside2-version: '5.13' # Conda needs 5.13+ to work reliably + pyside2-qt-version: '5.12' # Conda only has 5.12 and 5.15, not 5.13 + pyside6-version: '6.4' # Conda only has 6.4+ + - use-conda: 'No' + pyqt5-version: '5.15' # Test with latest optional packages - os: windows-latest python-version: '3.7' use-conda: 'Yes' - pyqt5-qt-version: '5.9' - pyqt6-qt-version: '6.2' - skip-pyside6: true # test hangs + pyqt5-version: '5.9' # Test lower bound + pyqt6-version: '6.2' # Test lower bound + skip-pyside6: true # Test hangs - os: windows-latest python-version: '3.10' use-conda: 'Yes' - skip-pyside6: true # test hangs + skip-pyside6: true # Test hangs + - os: windows-latest + python-version: '3.10' + use-conda: 'No' + pyqt5-version: '5.15' # Test upper bound + pyqt6-version: '6.4' # Test upper bound + pyside2-version: '5.15' # No 5.12 wheel on Windows and Python 3.10 + pyside6-version: '6.4' # Test upper bound - os: macos-latest python-version: '3.7' use-conda: 'No' diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 15f0fc63..cce935a8 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -9,19 +9,19 @@ conda remove -q -n test-env --all || true # Create and activate conda environment for this test BINDING=$(echo "$1" | tr '[:lower:]' '[:upper:]') QT_VERSION_VAR=${BINDING}_QT_VERSION -# pytest-qt >=4 doesn't support Qt >=5.9 +# pytest-qt >=4 doesn't support Qt <=5.9 if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; PYTEST_VERSION=">=6,!=7.0.0,!=7.0.1,<7.2.0"; fi -conda create -q -n test-env python=${PYTHON_VERSION} pytest${PYTEST_VERSION:-">=6,!=7.0.0,!=7.0.1"} "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} pip${PIP_VERSION:-} +conda create -q -n test-env python=${PYTHON_VERSION} pytest${PYTEST_VERSION:-">=6,!=7.0.0,!=7.0.1"} "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then if [ "${1}" = "pyqt5" ]; then - conda install -q qt=${PYQT5_QT_VERSION:-"5.12"} pyqt=${PYQT5_VERSION:-"5"} + conda install -q qt=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} elif [ "${1}" = "pyside2" ]; then - conda install -q qt=${PYSIDE2_QT_VERSION:-"5.12"} pyside2=${PYSIDE2_VERSION:-"5"} + conda install -q qt=${PYSIDE2_QT_VERSION} pyside2=${PYSIDE2_VERSION} elif [ "${1}" = "pyside6" ]; then - conda install -q qt6-main=${PYSIDE6_QT_VERSION:-"6.4"} pyside6=${PYSIDE6_VERSION:-"6.4"} + conda install -q qt6-main=${PYSIDE6_QT_VERSION} pyside6=${PYSIDE6_VERSION} else exit 1 fi @@ -29,16 +29,16 @@ if [ "$USE_CONDA" = "Yes" ]; then else if [ "${1}" = "pyqt5" ]; then - pip install pyqt5==${PYQT5_VERSION:-"5.15"}.* PyQtWebEngine==${PYQT5_VERSION:-"5.15"}.* QScintilla==${QSCINTILLA_VERSION:-"2.13"}.* + pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.* elif [ "${1}" = "pyqt6" ]; then - pip install pyqt6==${PYQT6_VERSION:-"6.3"}.* PyQt6-WebEngine==${PYQT6_VERSION:-"6.3"}.* PyQt6-Qt6==${PYQT6_QT_VERSION:-"6.3"}.* PyQt6-QScintilla==${QSCINTILLA_VERSION:-"2.13"}.* + pip install pyqt6==${PYQT6_VERSION}.* PyQt6-WebEngine==${PYQT6_VERSION}.* PyQt6-Qt6==${PYQT6_QT_VERSION}.* PyQt6-QScintilla==${QSCINTILLA_VERSION}.* elif [ "${1}" = "pyside2" ]; then - pip install pyside2==${PYSIDE2_VERSION:-"5.12"}.* + pip install pyside2==${PYSIDE2_VERSION}.* elif [ "${1}" = "pyside6" ]; then - if [ "${PYSIDE6_VERSION:-"6.2":0:3}" = "6.2" ]; then - pip install pyside6==${PYSIDE6_VERSION:-"6.2"}.* + if [ "${PYSIDE6_VERSION:0:3}" = "6.2" ]; then + pip install pyside6==${PYSIDE6_VERSION}.* else - pip install pyside6==${PYSIDE6_VERSION:-"6.3"}.* pyside6-addons==${PYSIDE6_VERSION:-"6.3"}.* pyside6-essentials==${PYSIDE6_VERSION:-"6.3"}.* + pip install pyside6==${PYSIDE6_VERSION}.* pyside6-addons==${PYSIDE6_VERSION}.* pyside6-essentials==${PYSIDE6_VERSION}.* fi else exit 1 From 60936f906148db736b711bbd858b7f5a7f1c825b Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 16 Dec 2022 23:54:42 -0600 Subject: [PATCH 501/703] Add Python 3.11 to CI matrix & Trove tags --- .github/workflows/ci.yml | 23 ++++++++++------------- setup.cfg | 1 + 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81b4681c..08ce3139 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ['3.7', '3.10'] + python-version: ['3.7', '3.11'] use-conda: ['Yes', 'No'] qt5-version-default: ['5.12'] qt6-version-default: ['6.3'] @@ -47,15 +47,19 @@ jobs: include: - os: ubuntu-latest special-invocation: 'xvfb-run --auto-servernum ' # Needed for GUI tests to work - - python-version: '3.10' - skip-pyside2: true # Skip Pyside2 on all Python 3.10 builds until/unless it supports it + - python-version: '3.11' + pyqt5-version: '5.15' # Python 3.11 needs 5.15+ + skip-pyside2: true # Pyside2 doesn't support Python 3.11+ (and probably never will) + pyside6-version: '6.4' # Python 3.11 needs 5.4+ - use-conda: 'Yes' skip-pyqt6: true # No PyQt6 conda packages yet - pyside2-version: '5.13' # Conda needs 5.13+ to work reliably - pyside2-qt-version: '5.12' # Conda only has 5.12 and 5.15, not 5.13 pyside6-version: '6.4' # Conda only has 6.4+ - use-conda: 'No' pyqt5-version: '5.15' # Test with latest optional packages + - python-version: '3.7' + use-conda: 'Yes' + pyside2-version: '5.13' # Conda needs 5.13+ to work reliably + pyside2-qt-version: '5.12' # Conda only has 5.12 and 5.15, not 5.13 - os: windows-latest python-version: '3.7' use-conda: 'Yes' @@ -63,16 +67,9 @@ jobs: pyqt6-version: '6.2' # Test lower bound skip-pyside6: true # Test hangs - os: windows-latest - python-version: '3.10' + python-version: '3.11' use-conda: 'Yes' skip-pyside6: true # Test hangs - - os: windows-latest - python-version: '3.10' - use-conda: 'No' - pyqt5-version: '5.15' # Test upper bound - pyqt6-version: '6.4' # Test upper bound - pyside2-version: '5.15' # No 5.12 wheel on Windows and Python 3.10 - pyside6-version: '6.4' # Test upper bound - os: macos-latest python-version: '3.7' use-conda: 'No' diff --git a/setup.cfg b/setup.cfg index 7d3d1808..30000855 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,6 +27,7 @@ classifiers = Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 Topic :: Software Development :: Libraries Topic :: Software Development :: User Interfaces Topic :: Software Development :: Widget Sets From 3cb24d0de241613615b9aa90dcdb25a8d7b19e3a Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sat, 17 Dec 2022 02:27:14 -0600 Subject: [PATCH 502/703] Avoid conda qt 5.15 compat issue w/pyqt 5.15 & Python 3.11 on CI --- .github/workflows/test.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index cce935a8..7b9cf762 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -17,7 +17,12 @@ conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then if [ "${1}" = "pyqt5" ]; then - conda install -q qt=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} + if [ "${PYQT5_VERSION:0:4}" = "5.15" ]; then + # Must specify versions individually with PyQt 5.15 to avoid compat issues with Python 3.11 + conda install -q qt-main=${PYQT5_QT_VERSION} qt-webengine=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} + else + conda install -q qt=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} + fi elif [ "${1}" = "pyside2" ]; then conda install -q qt=${PYSIDE2_QT_VERSION} pyside2=${PYSIDE2_VERSION} elif [ "${1}" = "pyside6" ]; then From 38da4d05865e71742ae725a6542ba34944699a66 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sat, 17 Dec 2022 02:54:35 -0600 Subject: [PATCH 503/703] Unskip macos-37-pip CI job & add upper/lower API version bounds --- .github/workflows/ci.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 08ce3139..7c7731c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,8 +64,12 @@ jobs: python-version: '3.7' use-conda: 'Yes' pyqt5-version: '5.9' # Test lower bound - pyqt6-version: '6.2' # Test lower bound skip-pyside6: true # Test hangs + - os: windows-latest + python-version: '3.7' + use-conda: 'No' + pyqt6-version: 6.2 # Test lower bound + pyside6-version: 6.2 # Test lower bound - os: windows-latest python-version: '3.11' use-conda: 'Yes' @@ -73,7 +77,8 @@ jobs: - os: macos-latest python-version: '3.7' use-conda: 'No' - skip-pyside6: true # pytest-qt crashes on pytest startup under this environment + pyqt6-version: 6.4 # Test upper bound + pyside2-version: 5.15 # Test upper bound steps: - name: Checkout branch uses: actions/checkout@v3 From 6b9ca6bc2b5d85dbf3648de179604e18f3cce569 Mon Sep 17 00:00:00 2001 From: Anton Yablokov Date: Sat, 17 Dec 2022 17:01:31 +0300 Subject: [PATCH 504/703] =?UTF-8?q?Fixed=20LibraryLocation=20=E2=86=92?= =?UTF-8?q?=C2=A0LibraryPath=20renaming?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qtpy/QtCore.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index d87c4278..0f735b6b 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -63,6 +63,7 @@ QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QLibraryInfo.location = QLibraryInfo.path + QLibraryInfo.LibraryLocation = QLibraryInfo.LibraryPath # Those are imported from `import *` del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR @@ -121,3 +122,4 @@ QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QLibraryInfo.location = QLibraryInfo.path + QLibraryInfo.LibraryLocation = QLibraryInfo.LibraryPath From 1b44776bdd663ad0224360766188978c0bf1496f Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sun, 18 Dec 2022 17:25:44 -0600 Subject: [PATCH 505/703] Fix a few minor issues with overhauled CI config Co-authored-by: Carlos Cordoba --- .github/workflows/ci.yml | 2 +- .github/workflows/test.sh | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c7731c1..9f552ff8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: - python-version: '3.11' pyqt5-version: '5.15' # Python 3.11 needs 5.15+ skip-pyside2: true # Pyside2 doesn't support Python 3.11+ (and probably never will) - pyside6-version: '6.4' # Python 3.11 needs 5.4+ + pyside6-version: '6.4' # Python 3.11 needs 6.4+ - use-conda: 'Yes' skip-pyqt6: true # No PyQt6 conda packages yet pyside6-version: '6.4' # Conda only has 6.4+ diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 7b9cf762..a3c98f55 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -9,8 +9,13 @@ conda remove -q -n test-env --all || true # Create and activate conda environment for this test BINDING=$(echo "$1" | tr '[:lower:]' '[:upper:]') QT_VERSION_VAR=${BINDING}_QT_VERSION + # pytest-qt >=4 doesn't support Qt <=5.9 -if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTESTQT_VERSION="=3.3.0"; PYTEST_VERSION=">=6,!=7.0.0,!=7.0.1,<7.2.0"; fi +if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then + PYTESTQT_VERSION="=3.3.0" + PYTEST_VERSION=">=6,!=7.0.0,!=7.0.1,<7.2.0" +fi + conda create -q -n test-env python=${PYTHON_VERSION} pytest${PYTEST_VERSION:-">=6,!=7.0.0,!=7.0.1"} "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} conda activate test-env From ef6dfc48b59685d50ec170264c86a5c3860e075d Mon Sep 17 00:00:00 2001 From: Anton Yablokov Date: Mon, 19 Dec 2022 10:34:43 +0300 Subject: [PATCH 506/703] Added a test for `QLibraryInfo.LibraryLocation` --- qtpy/tests/test_qtcore.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 6327ec92..bdd08182 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -68,6 +68,11 @@ def test_qlibraryinfo_location(): assert QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PrefixPath) is not None +def test_qlibraryinfo_library_location(): + """Test QLibraryInfo.LibraryLocation""" + assert QtCore.QLibraryInfo.LibraryLocation is not None + + def test_qtextstreammanipulator_exec_(): """Test QTextStreamManipulator.exec_""" QtCore.QTextStreamManipulator.exec_ is not None From 0c8108b09af3e45d2e939f00c612e56eed6ba6e5 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Thu, 12 Jan 2023 14:55:58 -0600 Subject: [PATCH 507/703] Simplify CI script following Qt 5.15.6 feedstock update --- .github/workflows/test.sh | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index a3c98f55..977e7644 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -22,12 +22,7 @@ conda activate test-env if [ "$USE_CONDA" = "Yes" ]; then if [ "${1}" = "pyqt5" ]; then - if [ "${PYQT5_VERSION:0:4}" = "5.15" ]; then - # Must specify versions individually with PyQt 5.15 to avoid compat issues with Python 3.11 - conda install -q qt-main=${PYQT5_QT_VERSION} qt-webengine=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} - else - conda install -q qt=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} - fi + conda install -q qt=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} elif [ "${1}" = "pyside2" ]; then conda install -q qt=${PYSIDE2_QT_VERSION} pyside2=${PYSIDE2_VERSION} elif [ "${1}" = "pyside6" ]; then From 88c0bc66823df9125d52cdf01742e48c2bc9e792 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 13 Jan 2023 21:36:19 -0600 Subject: [PATCH 508/703] Make warning usage consistant and refine messages --- qtpy/__init__.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 51cd5a0a..3bc2956f 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -65,11 +65,11 @@ class PythonQtError(RuntimeError): - """Error raised if no bindings could be selected.""" + """Generic error superclass for QtPy.""" -class PythonQtWarning(Warning): - """Warning if some features are not implemented in a binding.""" +class PythonQtWarning(RuntimeWarning): + """Warning class for QtPy.""" class PythonQtValueError(ValueError): @@ -79,7 +79,7 @@ class PythonQtValueError(ValueError): class QtBindingsNotFoundError(PythonQtError): """Error raised if no bindings could be selected.""" _msg = 'No Qt bindings could be found' - + def __init__(self): super().__init__(self._msg) @@ -106,7 +106,7 @@ class QtModuleNotInOSError(QtModuleNotFoundError): class QtModuleNotInQtVersionError(QtModuleNotFoundError): """Raised when a module is not implemented in the current Qt version.""" _msg = '{name} does not exist in {version}.' - + def __init__(self, *, name, msg=None, **msg_kwargs): global QT5, QT6 version = 'Qt5' if QT5 else 'Qt6' @@ -264,8 +264,11 @@ def __init__(self, *, missing_package=None, **superclass_kwargs): # If a correct API name is passed to QT_API and it could not be found, # switches to another and informs through the warning if API != initial_api and binding_specified: - warnings.warn('Selected binding "{}" could not be found, ' - 'using "{}"'.format(initial_api, API), RuntimeWarning) + warnings.warn( + f'Selected binding {initial_api!r} could not be found; ' + f'falling back to {API!r}', + PythonQtWarning, + ) # Set display name of the Qt API @@ -282,10 +285,10 @@ def __init__(self, *, missing_package=None, **superclass_kwargs): def _warn_old_minor_version(name, old_version, min_version): """Warn if using a Qt or binding version no longer supported by QtPy.""" warning_message = ( - "{name} version {old_version} is not supported by QtPy. " - "To ensure your application works correctly with QtPy, " - "please upgrade to {name} {min_version} or later.".format( - name=name, old_version=old_version, min_version=min_version)) + f'{name} version {old_version} is not supported by QtPy. ' + 'To ensure your application works correctly with QtPy, ' + f'please upgrade to {name} {min_version} or later.' + ) warnings.warn(warning_message, PythonQtWarning) From a9e87e4517dcf097a5e2d961cda3851c00c52b01 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 1 Feb 2023 19:59:49 -0600 Subject: [PATCH 509/703] Unskip PySide2 5.15 on Python 3.11 with Conda on CIs --- .github/workflows/ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9f552ff8..a9da129c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: special-invocation: 'xvfb-run --auto-servernum ' # Needed for GUI tests to work - python-version: '3.11' pyqt5-version: '5.15' # Python 3.11 needs 5.15+ - skip-pyside2: true # Pyside2 doesn't support Python 3.11+ (and probably never will) + pyside2-version: '5.15' # Python 3.11 needs 5.15+ pyside6-version: '6.4' # Python 3.11 needs 6.4+ - use-conda: 'Yes' skip-pyqt6: true # No PyQt6 conda packages yet @@ -60,6 +60,9 @@ jobs: use-conda: 'Yes' pyside2-version: '5.13' # Conda needs 5.13+ to work reliably pyside2-qt-version: '5.12' # Conda only has 5.12 and 5.15, not 5.13 + - python-version: '3.11' + use-conda: 'No' + skip-pyside2: true # Pyside2 wheels don't support Python 3.11+ - os: windows-latest python-version: '3.7' use-conda: 'Yes' From 228ff228940f01d8269e38ffc8ee3fba8d367917 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Tue, 7 Feb 2023 12:13:11 +0300 Subject: [PATCH 510/703] Don't create `QTextStreamManipulator.exec_` on PyQt5/6 See https://github.com/spyder-ide/qtpy/issues/402 --- qtpy/QtCore.py | 4 ---- qtpy/tests/test_qtcore.py | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 0f735b6b..1f358e39 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -25,9 +25,6 @@ QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) QTime.toPython = lambda self, *args, **kwargs: self.toPyTime(*args, **kwargs) - # Map missing methods on PyQt5 5.12 - QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - # Those are imported from `import *` del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR @@ -60,7 +57,6 @@ QCoreApplication.exec_ = QCoreApplication.exec QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QLibraryInfo.location = QLibraryInfo.path QLibraryInfo.LibraryLocation = QLibraryInfo.LibraryPath diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index bdd08182..9f52a2d7 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -73,6 +73,8 @@ def test_qlibraryinfo_library_location(): assert QtCore.QLibraryInfo.LibraryLocation is not None +@pytest.mark.skipif(PYQT5 or PYQT6, + reason="Doesn't seem to be present on PyQt5 and PyQt6") def test_qtextstreammanipulator_exec_(): """Test QTextStreamManipulator.exec_""" QtCore.QTextStreamManipulator.exec_ is not None From dfeb5a8eec1668d344d402ca88386a15049afeda Mon Sep 17 00:00:00 2001 From: Anton Yablokov Date: Sat, 11 Feb 2023 14:47:30 +0300 Subject: [PATCH 511/703] =?UTF-8?q?Remove=20patch=20that=20=E2=80=9Cmay=20?= =?UTF-8?q?be=20limited=20to=20PySide-5.11a1=20only=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/spyder-ide/qtpy/issues/405 --- qtpy/QtCore.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 1f358e39..275b5171 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -78,11 +78,6 @@ elif PYSIDE2: from PySide2.QtCore import * - try: # may be limited to PySide-5.11a1 only - from PySide2.QtGui import QStringListModel - except Exception: - pass - import PySide2.QtCore __version__ = PySide2.QtCore.__version__ From a112bcf896b413c6d01d488d1e3fa3bafbe1f6f9 Mon Sep 17 00:00:00 2001 From: Anton Yablokov Date: Thu, 23 Feb 2023 08:21:08 +0300 Subject: [PATCH 512/703] PR: Add mappings for QMouseEvent methods (#408) * Might close https://github.com/spyder-ide/qtpy/issues/394 * Rephrase a comment, as @CAM-Gerlach suggested * Test the fix for https://github.com/spyder-ide/qtpy/issues/394 * Format the docstring as suggested by @CAM-Gerlach * Ensure the created window is of sufficient size to point at * Don't wait before moving and clicking the mouse. `QMainWindow.show()` finishes when the window appears. So, no extra waiting needed. * Don't close the window at the end Co-authored-by: C.A.M. Gerlach --- qtpy/QtGui.py | 13 +++++++++++++ qtpy/tests/test_qtgui.py | 37 ++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 662b84cd..86502fd2 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -72,3 +72,16 @@ def movePositionPatched( ) -> bool: return movePosition(self, operation, mode, n) QTextCursor.movePosition = movePositionPatched + +# Fix https://github.com/spyder-ide/qtpy/issues/394 +if PYQT5 or PYSIDE2: + from qtpy.QtCore import QPointF as __QPointF + QMouseEvent.position = lambda self: __QPointF(float(self.x()), float(self.y())) + QMouseEvent.globalPosition = lambda self: __QPointF(float(self.globalX()), float(self.globalY())) +if PYQT6 or PYSIDE6: + QMouseEvent.pos = lambda self: self.position().toPoint() + QMouseEvent.x = lambda self: self.position().toPoint().x() + QMouseEvent.y = lambda self: self.position().toPoint().y() + QMouseEvent.globalPos = lambda self: self.globalPosition().toPoint() + QMouseEvent.globalX = lambda self: self.globalPosition().toPoint().x() + QMouseEvent.globalY = lambda self: self.globalPosition().toPoint().y() diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 802129c9..d16f08a2 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -4,7 +4,7 @@ import pytest -from qtpy import PYQT5, PYQT_VERSION, PYSIDE2, PYSIDE6, QtGui +from qtpy import PYQT5, PYQT_VERSION, PYSIDE2, PYSIDE6, QtCore, QtGui, QtWidgets from qtpy.tests.utils import not_using_conda @@ -61,6 +61,41 @@ def test_enum_access(): assert QtGui.QIcon.Normal == QtGui.QIcon.Mode.Normal assert QtGui.QImage.Format_Invalid == QtGui.QImage.Format.Format_Invalid + +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") +@pytest.mark.skipif( + sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7") +def test_QMouseEvent_pos_functions(qtbot): + """ + Test `QMouseEvent.pos` and related functions removed in Qt 6, + and `QMouseEvent.position`, etc., missing from Qt 5. + """ + + class Window(QtWidgets.QMainWindow): + def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None: + assert event.globalPos() - event.pos() == self.mapToParent(QtCore.QPoint(0, 0)) + assert event.pos().x() == event.x() + assert event.pos().y() == event.y() + assert event.globalPos().x() == event.globalX() + assert event.globalPos().y() == event.globalY() + assert event.position().x() == event.pos().x() + assert event.position().y() == event.pos().y() + assert event.globalPosition().x() == event.globalPos().x() + assert event.globalPosition().y() == event.globalPos().y() + + event.accept() + + window = Window() + window.setMinimumSize(320, 240) # ensure the window is of sufficient size + window.show() + + qtbot.mouseMove(window, QtCore.QPoint(42, 6 * 9)) + qtbot.mouseDClick(window, QtCore.Qt.LeftButton) + + @pytest.mark.skipif(not (PYSIDE2 or PYSIDE6), reason="PySide{2,6} specific test") def test_qtextcursor_moveposition(): """Test monkeypatched QTextCursor.movePosition""" From a2da9cf5d34be255e17d26f7cc89526fd22a63b8 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Fri, 17 Mar 2023 21:18:37 -0500 Subject: [PATCH 513/703] Skip QtPositioning tests on Conda Qt >=6.4.3 where its not included --- qtpy/tests/test_qtpositioning.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtpositioning.py b/qtpy/tests/test_qtpositioning.py index 750abbf5..15a59620 100644 --- a/qtpy/tests/test_qtpositioning.py +++ b/qtpy/tests/test_qtpositioning.py @@ -1,7 +1,12 @@ import pytest -from qtpy import PYQT5, PYSIDE2 +from qtpy import QT6 +from qtpy.tests.utils import using_conda +@pytest.mark.skipif( + QT6 and using_conda(), + reason="QPositioning bindings not included in Conda qt-main >= 6.4.3.", +) def test_qtpositioning(): """Test the qtpy.QtPositioning namespace""" from qtpy import QtPositioning From 2f04efabdbbe115ec6b020e74f08b2776dea3b93 Mon Sep 17 00:00:00 2001 From: Clemens Brunner Date: Fri, 17 Mar 2023 09:54:49 +0100 Subject: [PATCH 514/703] QtBindingsNotFoundError inherits ImportError --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 3bc2956f..ddc95bcb 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -76,7 +76,7 @@ class PythonQtValueError(ValueError): """Error raised if an invalid QT_API is specified.""" -class QtBindingsNotFoundError(PythonQtError): +class QtBindingsNotFoundError(PythonQtError, ImportError): """Error raised if no bindings could be selected.""" _msg = 'No Qt bindings could be found' From 4265297f2e44cfaab131966366821a34759a6364 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 3 Mar 2023 18:37:33 +0300 Subject: [PATCH 515/703] Provide what has been moved to between QtWidgets and QtGui in Qt6 --- qtpy/QtGui.py | 7 +++++++ qtpy/tests/test_qtgui.py | 9 +++++++++ qtpy/tests/test_qtwidgets.py | 10 +++++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 86502fd2..4c762c63 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -12,6 +12,9 @@ if PYQT5: from PyQt5.QtGui import * + # Backport items moved to QtGui in Qt6 + from PyQt5.QtWidgets import QAction, QActionGroup, QFileSystemModel, QShortcut, QUndoCommand + elif PYQT6: from PyQt6 import QtGui from PyQt6.QtGui import * @@ -30,12 +33,16 @@ del QtGui elif PYSIDE2: from PySide2.QtGui import * + # Backport items moved to QtGui in Qt6 + from PySide2.QtWidgets import QAction, QActionGroup, QFileSystemModel, QShortcut, QUndoCommand if hasattr(QFontMetrics, 'horizontalAdvance'): # Needed to prevent raising a DeprecationWarning when using QFontMetrics.width QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) elif PYSIDE6: from PySide6.QtGui import * from PySide6.QtOpenGL import * + # Backport `QFileSystemModel` moved to QtGui in Qt6 + from PySide6.QtWidgets import QFileSystemModel QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index d16f08a2..787d7dba 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -39,6 +39,15 @@ def test_qguiapplication_functions(): assert QtGui.QGuiApplication.exec_ is not None +def test_what_moved_to_qtgui_in_qt6(): + """Test what has been moved to QtGui in Qt6""" + assert QtGui.QAction is not None + assert QtGui.QActionGroup is not None + assert QtGui.QFileSystemModel is not None + assert QtGui.QShortcut is not None + assert QtGui.QUndoCommand is not None + + @pytest.mark.skipif( sys.platform.startswith('linux') and not_using_conda(), reason="Segmentation fault/Aborted on Linux CI when not using conda") diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index a04e662d..8670fdf6 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -29,9 +29,13 @@ def test_qlineedit_functions(): assert QtWidgets.QLineEdit.getTextMargins -def test_qundocommand_object(): - """Test object aliasing for QUndoCommand""" - assert QtWidgets.QUndoCommand +def test_what_moved_to_qtgui_in_qt6(): + """Test that we move back what has been moved to QtGui in Qt6""" + assert QtWidgets.QAction is not None + assert QtWidgets.QActionGroup is not None + assert QtWidgets.QFileSystemModel is not None + assert QtWidgets.QShortcut is not None + assert QtWidgets.QUndoCommand is not None @pytest.mark.skipif( From 5c6dd9c0b2659dc9381a27ac002166f80626d243 Mon Sep 17 00:00:00 2001 From: Anton Yablokov Date: Sat, 18 Mar 2023 19:31:49 +0300 Subject: [PATCH 516/703] Add blank lines for better readability, as suggested by @ccordoba12 Co-authored-by: Carlos Cordoba --- qtpy/QtGui.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 4c762c63..35a83eab 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -12,9 +12,9 @@ if PYQT5: from PyQt5.QtGui import * + # Backport items moved to QtGui in Qt6 from PyQt5.QtWidgets import QAction, QActionGroup, QFileSystemModel, QShortcut, QUndoCommand - elif PYQT6: from PyQt6 import QtGui from PyQt6.QtGui import * @@ -33,16 +33,20 @@ del QtGui elif PYSIDE2: from PySide2.QtGui import * + # Backport items moved to QtGui in Qt6 from PySide2.QtWidgets import QAction, QActionGroup, QFileSystemModel, QShortcut, QUndoCommand + if hasattr(QFontMetrics, 'horizontalAdvance'): # Needed to prevent raising a DeprecationWarning when using QFontMetrics.width QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) elif PYSIDE6: from PySide6.QtGui import * from PySide6.QtOpenGL import * + # Backport `QFileSystemModel` moved to QtGui in Qt6 from PySide6.QtWidgets import QFileSystemModel + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) From 3c107df24137e2dff19d6035251d208eb1ffa0c8 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Wed, 22 Mar 2023 00:04:44 -0400 Subject: [PATCH 517/703] Implement delayed import error mechanism designed by @CAM-Gerlach Co-authored-by: CAM Gerlach --- qtpy/QtGui.py | 34 +++++++++++++++++++++++++++++++++- qtpy/QtWidgets.py | 32 +++++++++++++++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 5ef9da97..fb3b1d75 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -8,13 +8,27 @@ """Provides QtGui classes and functions.""" -from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6 +from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError + + +MISSING_OPENGL_NAMES = {} + if PYQT5: from PyQt5.QtGui import * elif PYQT6: from PyQt6 import QtGui from PyQt6.QtGui import * + + # Attempt to import QOpenGLWidget, but if that fails, don't throw an exception until the + # symbol is explicitly asked for + # this allows removing the sizeable OpenGL binaries from pyinstaller bundles, but + # maintains compatibility with apps that expect this symbol to be automatically imported + try: + from PyQt6.QtOpenGLWidgets import QOpenGLWidget + except (ModuleNotFoundError, ImportError): + MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PyQt6.QtOpenGLWidgets', 'pyopengl') + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) @@ -34,6 +48,16 @@ QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) elif PYSIDE6: from PySide6.QtGui import * + + # Attempt to import QOpenGLWidget, but if that fails, don't throw an exception until the + # symbol is explicitly asked for + # this allows removing the sizeable OpenGL binaries from pyinstaller bundles, but + # maintains compatibility with apps that expect this symbol to be automatically imported + try: + from PySide6.QtOpenGLWidgets import QOpenGLWidget + except (ModuleNotFoundError, ImportError): + MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PySide6.QtOpenGLWidgets', 'pyopengl') + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) @@ -70,3 +94,11 @@ def movePositionPatched( ) -> bool: return movePosition(self, operation, mode, n) QTextCursor.movePosition = movePositionPatched + + +def __getattr__(name): + if name in MISSING_OPENGL_NAMES: + module, package = MISSING_OPENGL_NAMES[name] + raise QtModuleNotInstalledError(name=module, missing_package=package) + else: + raise AttributeError(f'module {__name__!r} has no attribute {name!r}') diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index e23d4fd5..e262b5a6 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -8,7 +8,11 @@ """Provides widget classes and functions.""" -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError + + +MISSING_OPENGL_NAMES = {} + if PYQT5: from PyQt5.QtWidgets import * @@ -17,6 +21,15 @@ from PyQt6.QtWidgets import * from PyQt6.QtGui import QAction, QActionGroup, QShortcut, QFileSystemModel, QUndoCommand + # Attempt to import QOpenGLWidget, but if that fails, don't throw an exception until the + # symbol is explicitly asked for + # this allows removing the sizeable OpenGL binaries from pyinstaller bundles, but + # maintains compatibility with apps that expect this symbol to be automatically imported + try: + from PyQt6.QtOpenGLWidgets import QOpenGLWidget + except (ModuleNotFoundError, ImportError): + MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PyQt6.QtOpenGLWidgets', 'pyopengl') + # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) QTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) @@ -39,6 +52,15 @@ from PySide6.QtWidgets import * from PySide6.QtGui import QAction, QActionGroup, QShortcut, QUndoCommand + # Attempt to import QOpenGLWidget, but if that fails, don't throw an exception until the + # symbol is explicitly asked for + # this allows removing the sizeable OpenGL binaries from pyinstaller bundles, but + # maintains compatibility with apps that expect this symbol to be automatically imported + try: + from PySide6.QtOpenGLWidgets import QOpenGLWidget + except (ModuleNotFoundError, ImportError): + MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PySide6.QtOpenGLWidgets', 'pyopengl') + # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) QTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) @@ -50,3 +72,11 @@ QApplication.exec_ = QApplication.exec QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + + +def __getattr__(name): + if name in MISSING_OPENGL_NAMES: + module, package = MISSING_OPENGL_NAMES[name] + raise QtModuleNotInstalledError(name=module, missing_package=package) + else: + raise AttributeError(f'module {__name__!r} has no attribute {name!r}') From d60bffe7d846083de5eb06610ab23960ebff7866 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Wed, 22 Mar 2023 00:29:19 -0400 Subject: [PATCH 518/703] Refactor one file for comparison --- qtpy/QtWidgets.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index e262b5a6..f3c86e8a 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -11,7 +11,7 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -MISSING_OPENGL_NAMES = {} +MISSING_SYMBOL_ERRORS = {} if PYQT5: @@ -28,7 +28,7 @@ try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget except (ModuleNotFoundError, ImportError): - MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PyQt6.QtOpenGLWidgets', 'pyopengl') + MISSING_SYMBOL_ERRORS['QOpenGLWidget'] = QtModuleNotInstalledError(name='PyQt6.QtOpenGLWidgets', missing_package='pyopengl') # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) @@ -59,7 +59,7 @@ try: from PySide6.QtOpenGLWidgets import QOpenGLWidget except (ModuleNotFoundError, ImportError): - MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PySide6.QtOpenGLWidgets', 'pyopengl') + MISSING_SYMBOL_ERRORS['QOpenGLWidget'] = QtModuleNotInstalledError(name='PySide6.QtOpenGLWidgets', missing_package='pyopengl') # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) @@ -75,8 +75,7 @@ def __getattr__(name): - if name in MISSING_OPENGL_NAMES: - module, package = MISSING_OPENGL_NAMES[name] - raise QtModuleNotInstalledError(name=module, missing_package=package) + if name in MISSING_SYMBOL_ERRORS: + raise MISSING_SYMBOL_ERRORS[name] else: raise AttributeError(f'module {__name__!r} has no attribute {name!r}') From f93a1231e8847390158b2c5ea2679b754cb96b61 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 22 Mar 2023 01:58:53 -0500 Subject: [PATCH 519/703] Just except ImportError, not redundant ModuleNotFound importing OpenGL --- qtpy/QtGui.py | 4 ++-- qtpy/QtWidgets.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index fb3b1d75..802eebdd 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -26,7 +26,7 @@ # maintains compatibility with apps that expect this symbol to be automatically imported try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget - except (ModuleNotFoundError, ImportError): + except ImportError: MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PyQt6.QtOpenGLWidgets', 'pyopengl') QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) @@ -55,7 +55,7 @@ # maintains compatibility with apps that expect this symbol to be automatically imported try: from PySide6.QtOpenGLWidgets import QOpenGLWidget - except (ModuleNotFoundError, ImportError): + except ImportError: MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PySide6.QtOpenGLWidgets', 'pyopengl') QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index f3c86e8a..ea0d2d02 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -27,7 +27,7 @@ # maintains compatibility with apps that expect this symbol to be automatically imported try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget - except (ModuleNotFoundError, ImportError): + except ImportError: MISSING_SYMBOL_ERRORS['QOpenGLWidget'] = QtModuleNotInstalledError(name='PyQt6.QtOpenGLWidgets', missing_package='pyopengl') # Map missing/renamed methods @@ -58,7 +58,7 @@ # maintains compatibility with apps that expect this symbol to be automatically imported try: from PySide6.QtOpenGLWidgets import QOpenGLWidget - except (ModuleNotFoundError, ImportError): + except ImportError: MISSING_SYMBOL_ERRORS['QOpenGLWidget'] = QtModuleNotInstalledError(name='PySide6.QtOpenGLWidgets', missing_package='pyopengl') # Map missing/renamed methods From cb273b931d4ca97efdc933e751b6da49e62523e1 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 22 Mar 2023 02:03:14 -0500 Subject: [PATCH 520/703] Use more concise language & refer to PR in QOpenGL comments --- qtpy/QtGui.py | 14 ++++++-------- qtpy/QtWidgets.py | 14 ++++++-------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 802eebdd..45c54272 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -20,10 +20,9 @@ from PyQt6 import QtGui from PyQt6.QtGui import * - # Attempt to import QOpenGLWidget, but if that fails, don't throw an exception until the - # symbol is explicitly asked for - # this allows removing the sizeable OpenGL binaries from pyinstaller bundles, but - # maintains compatibility with apps that expect this symbol to be automatically imported + # Attempt to import QOpenGLWidget, but if that fails, + # don't raise an exception until the name is explicitly accessed. + # See https://github.com/spyder-ide/qtpy/pull/387/ try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget except ImportError: @@ -49,10 +48,9 @@ elif PYSIDE6: from PySide6.QtGui import * - # Attempt to import QOpenGLWidget, but if that fails, don't throw an exception until the - # symbol is explicitly asked for - # this allows removing the sizeable OpenGL binaries from pyinstaller bundles, but - # maintains compatibility with apps that expect this symbol to be automatically imported + # Attempt to import QOpenGLWidget, but if that fails, + # don't raise an exception until the name is explicitly accessed. + # See https://github.com/spyder-ide/qtpy/pull/387/ try: from PySide6.QtOpenGLWidgets import QOpenGLWidget except ImportError: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index ea0d2d02..fad9e717 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -21,10 +21,9 @@ from PyQt6.QtWidgets import * from PyQt6.QtGui import QAction, QActionGroup, QShortcut, QFileSystemModel, QUndoCommand - # Attempt to import QOpenGLWidget, but if that fails, don't throw an exception until the - # symbol is explicitly asked for - # this allows removing the sizeable OpenGL binaries from pyinstaller bundles, but - # maintains compatibility with apps that expect this symbol to be automatically imported + # Attempt to import QOpenGLWidget, but if that fails, + # don't raise an exception until the name is explicitly accessed. + # See https://github.com/spyder-ide/qtpy/pull/387/ try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget except ImportError: @@ -52,10 +51,9 @@ from PySide6.QtWidgets import * from PySide6.QtGui import QAction, QActionGroup, QShortcut, QUndoCommand - # Attempt to import QOpenGLWidget, but if that fails, don't throw an exception until the - # symbol is explicitly asked for - # this allows removing the sizeable OpenGL binaries from pyinstaller bundles, but - # maintains compatibility with apps that expect this symbol to be automatically imported + # Attempt to import QOpenGLWidget, but if that fails, + # don't raise an exception until the name is explicitly accessed. + # See https://github.com/spyder-ide/qtpy/pull/387/ try: from PySide6.QtOpenGLWidgets import QOpenGLWidget except ImportError: From a94216565d5da991ba120d68c3e40edd63c114a6 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 22 Mar 2023 02:46:39 -0500 Subject: [PATCH 521/703] Rename MISSING_OPENGL_NAMES module attribute to _missing_optional_names --- qtpy/QtGui.py | 10 +++++----- qtpy/QtWidgets.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 45c54272..8963d9b3 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -11,7 +11,7 @@ from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -MISSING_OPENGL_NAMES = {} +_missing_optional_names = {} if PYQT5: @@ -26,7 +26,7 @@ try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget except ImportError: - MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PyQt6.QtOpenGLWidgets', 'pyopengl') + _missing_optional_names['QOpenGLWidget'] = ('PyQt6.QtOpenGLWidgets', 'pyopengl') QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) @@ -54,7 +54,7 @@ try: from PySide6.QtOpenGLWidgets import QOpenGLWidget except ImportError: - MISSING_OPENGL_NAMES['QOpenGLWidget'] = ('PySide6.QtOpenGLWidgets', 'pyopengl') + _missing_optional_names['QOpenGLWidget'] = ('PySide6.QtOpenGLWidgets', 'pyopengl') QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) @@ -95,8 +95,8 @@ def movePositionPatched( def __getattr__(name): - if name in MISSING_OPENGL_NAMES: - module, package = MISSING_OPENGL_NAMES[name] + if name in _missing_optional_names: + module, package = _missing_optional_names[name] raise QtModuleNotInstalledError(name=module, missing_package=package) else: raise AttributeError(f'module {__name__!r} has no attribute {name!r}') diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index fad9e717..d6ee1112 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -11,7 +11,7 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -MISSING_SYMBOL_ERRORS = {} +_missing_optional_names = {} if PYQT5: @@ -27,7 +27,7 @@ try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget except ImportError: - MISSING_SYMBOL_ERRORS['QOpenGLWidget'] = QtModuleNotInstalledError(name='PyQt6.QtOpenGLWidgets', missing_package='pyopengl') + _missing_optional_names['QOpenGLWidget'] = QtModuleNotInstalledError(name='PyQt6.QtOpenGLWidgets', missing_package='pyopengl') # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) @@ -57,7 +57,7 @@ try: from PySide6.QtOpenGLWidgets import QOpenGLWidget except ImportError: - MISSING_SYMBOL_ERRORS['QOpenGLWidget'] = QtModuleNotInstalledError(name='PySide6.QtOpenGLWidgets', missing_package='pyopengl') + _missing_optional_names['QOpenGLWidget'] = QtModuleNotInstalledError(name='PySide6.QtOpenGLWidgets', missing_package='pyopengl') # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) @@ -73,7 +73,7 @@ def __getattr__(name): - if name in MISSING_SYMBOL_ERRORS: - raise MISSING_SYMBOL_ERRORS[name] + if name in _missing_optional_names: + raise _missing_optional_names[name] else: raise AttributeError(f'module {__name__!r} has no attribute {name!r}') From b121b0789a61a83b0ebec446a3ae0b6bcdf126da Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Wed, 22 Mar 2023 03:25:47 -0500 Subject: [PATCH 522/703] Add __cause__ to optional import errors & refactor logic into helper --- qtpy/QtGui.py | 22 ++++++++++++++-------- qtpy/QtWidgets.py | 24 ++++++++++++++++-------- qtpy/utils.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 16 deletions(-) create mode 100644 qtpy/utils.py diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 8963d9b3..da1f4bf8 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -9,6 +9,7 @@ """Provides QtGui classes and functions.""" from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError +from .utils import _getattr_missing_optional_dep _missing_optional_names = {} @@ -26,7 +27,11 @@ try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget except ImportError: - _missing_optional_names['QOpenGLWidget'] = ('PyQt6.QtOpenGLWidgets', 'pyopengl') + _missing_optional_names['QOpenGLWidget'] = { + 'name': 'PyQt6.QtOpenGLWidgets', + 'missing_package': 'pyopengl', + 'import_error': error, + } QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) @@ -54,8 +59,11 @@ try: from PySide6.QtOpenGLWidgets import QOpenGLWidget except ImportError: - _missing_optional_names['QOpenGLWidget'] = ('PySide6.QtOpenGLWidgets', 'pyopengl') - + _missing_optional_names['QOpenGLWidget'] = { + 'name': 'PySide6.QtOpenGLWidgets', + 'missing_package': 'pyopengl', + 'import_error': error, + } QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) @@ -95,8 +103,6 @@ def movePositionPatched( def __getattr__(name): - if name in _missing_optional_names: - module, package = _missing_optional_names[name] - raise QtModuleNotInstalledError(name=module, missing_package=package) - else: - raise AttributeError(f'module {__name__!r} has no attribute {name!r}') + """Custom getattr to chain and wrap errors due to missing optional deps.""" + raise _getattr_missing_optional_dep( + name, module_name=__name__, optional_names=_missing_optional_names) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index d6ee1112..09ce7e76 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -9,6 +9,7 @@ """Provides widget classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError +from .utils import _getattr_missing_optional_dep _missing_optional_names = {} @@ -26,8 +27,12 @@ # See https://github.com/spyder-ide/qtpy/pull/387/ try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget - except ImportError: - _missing_optional_names['QOpenGLWidget'] = QtModuleNotInstalledError(name='PyQt6.QtOpenGLWidgets', missing_package='pyopengl') + except ImportError as error: + _missing_optional_names['QOpenGLWidget'] = { + 'name': 'PyQt6.QtOpenGLWidgets', + 'missing_package': 'pyopengl', + 'import_error': error, + } # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) @@ -56,8 +61,12 @@ # See https://github.com/spyder-ide/qtpy/pull/387/ try: from PySide6.QtOpenGLWidgets import QOpenGLWidget - except ImportError: - _missing_optional_names['QOpenGLWidget'] = QtModuleNotInstalledError(name='PySide6.QtOpenGLWidgets', missing_package='pyopengl') + except ImportError as error: + _missing_optional_names['QOpenGLWidget'] = { + 'name': 'PySide6.QtOpenGLWidgets', + 'missing_package': 'pyopengl', + 'import_error': error, + } # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) @@ -73,7 +82,6 @@ def __getattr__(name): - if name in _missing_optional_names: - raise _missing_optional_names[name] - else: - raise AttributeError(f'module {__name__!r} has no attribute {name!r}') + """Custom getattr to chain and wrap errors due to missing optional deps.""" + raise _getattr_missing_optional_dep( + name, module_name=__name__, optional_names=_missing_optional_names) diff --git a/qtpy/utils.py b/qtpy/utils.py new file mode 100644 index 00000000..24dbf747 --- /dev/null +++ b/qtpy/utils.py @@ -0,0 +1,32 @@ +# ----------------------------------------------------------------------------- +# Copyright © 2023- The Spyder Development Team +# +# Released under the terms of the MIT License +# (see LICENSE.txt for details) +# ----------------------------------------------------------------------------- + +"""Provides utility functions for use by QtPy itself.""" + +import qtpy + + +def _wrap_missing_optional_dep_error( + attr_error, + *, + import_error, + wrapper=qtpy.QtModuleNotInstalledError, + **wrapper_kwargs, + ): + """Create a __cause__-chained wrapper error for a missing optional dep.""" + qtpy_error = wrapper(**wrapper_kwargs) + import_error.__cause__ = attr_error + qtpy_error.__cause__ = import_error + return qtpy_error + + +def _getattr_missing_optional_dep(name, module_name, optional_names): + """Wrap AttributeError in a special error if it matches.""" + attr_error = AttributeError(f'module {module_name!r} has no attribute {name!r}') + if name in optional_names: + return _wrap_missing_optional_dep_error(attr_error, **optional_names[name]) + return attr_error From 678c3484631ab57a0377061a1eeab12eec83f6bb Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Wed, 22 Mar 2023 05:32:42 -0400 Subject: [PATCH 523/703] Add missing "as error" --- qtpy/QtGui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index da1f4bf8..7fd915e8 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -26,7 +26,7 @@ # See https://github.com/spyder-ide/qtpy/pull/387/ try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget - except ImportError: + except ImportError as error: _missing_optional_names['QOpenGLWidget'] = { 'name': 'PyQt6.QtOpenGLWidgets', 'missing_package': 'pyopengl', @@ -58,7 +58,7 @@ # See https://github.com/spyder-ide/qtpy/pull/387/ try: from PySide6.QtOpenGLWidgets import QOpenGLWidget - except ImportError: + except ImportError as error: _missing_optional_names['QOpenGLWidget'] = { 'name': 'PySide6.QtOpenGLWidgets', 'missing_package': 'pyopengl', From 0f28ce6e1f8b3e5bae4e34f2f9af7cc27d3b8785 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Wed, 22 Mar 2023 06:08:16 -0400 Subject: [PATCH 524/703] Fix QtGui.py to import the correct OpenGL module --- qtpy/QtGui.py | 61 +++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 7fd915e8..a18b7892 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -25,13 +25,31 @@ # don't raise an exception until the name is explicitly accessed. # See https://github.com/spyder-ide/qtpy/pull/387/ try: - from PyQt6.QtOpenGLWidgets import QOpenGLWidget + from PyQt6.QtOpenGL import * except ImportError as error: - _missing_optional_names['QOpenGLWidget'] = { - 'name': 'PyQt6.QtOpenGLWidgets', - 'missing_package': 'pyopengl', - 'import_error': error, - } + names = [ + 'QOpenGLBuffer', + 'QOpenGLFramebufferObject', + 'QOpenGLFramebufferObjectFormat', + 'QOpenGLShader', + 'QOpenGLShaderProgram', + 'QOpenGLContext', + 'QOpenGLContextGroup', + 'QOpenGLDebugLogger', + 'QOpenGLDebugMessage', + 'QOpenGLPixelTransferOptions', + 'QOpenGLTexture', + 'QOpenGLTextureBlitter', + 'QOpenGLVersionProfile', + 'QOpenGLVertexArrayObject', + 'QOpenGLWindow', + ] + for name in names: + _missing_optional_names[name] = { + 'name': 'PyQt6.QtOpenGL', + 'missing_package': 'pyopengl', + 'import_error': error, + } QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) @@ -57,13 +75,32 @@ # don't raise an exception until the name is explicitly accessed. # See https://github.com/spyder-ide/qtpy/pull/387/ try: - from PySide6.QtOpenGLWidgets import QOpenGLWidget + from PySide6.QtOpenGL import * except ImportError as error: - _missing_optional_names['QOpenGLWidget'] = { - 'name': 'PySide6.QtOpenGLWidgets', - 'missing_package': 'pyopengl', - 'import_error': error, - } + names = [ + 'QOpenGLBuffer', + 'QOpenGLFramebufferObject', + 'QOpenGLFramebufferObjectFormat', + 'QOpenGLShader', + 'QOpenGLShaderProgram', + 'QOpenGLContext', + 'QOpenGLContextGroup', + 'QOpenGLDebugLogger', + 'QOpenGLDebugMessage', + 'QOpenGLPixelTransferOptions', + 'QOpenGLTexture', + 'QOpenGLTextureBlitter', + 'QOpenGLVersionProfile', + 'QOpenGLVertexArrayObject', + 'QOpenGLWindow', + ] + for name in names: + _missing_optional_names[name] = { + 'name': 'PySide6.QtOpenGL', + 'missing_package': 'pyopengl', + 'import_error': error, + } + QFontMetrics.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) QFontMetricsF.width = lambda self, *args, **kwargs: self.horizontalAdvance(*args, **kwargs) From 68415a59e0f16076f878b171a3529d985954575d Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Wed, 22 Mar 2023 06:57:25 -0400 Subject: [PATCH 525/703] Move OpenGL import names to a module-level list --- qtpy/QtGui.py | 56 ++++++++++++++++++--------------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index a18b7892..93353f08 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -14,6 +14,24 @@ _missing_optional_names = {} +_QTOPENGL_NAMES = [ + 'QOpenGLBuffer', + 'QOpenGLFramebufferObject', + 'QOpenGLFramebufferObjectFormat', + 'QOpenGLShader', + 'QOpenGLShaderProgram', + 'QOpenGLContext', + 'QOpenGLContextGroup', + 'QOpenGLDebugLogger', + 'QOpenGLDebugMessage', + 'QOpenGLPixelTransferOptions', + 'QOpenGLTexture', + 'QOpenGLTextureBlitter', + 'QOpenGLVersionProfile', + 'QOpenGLVertexArrayObject', + 'QOpenGLWindow', +] + if PYQT5: from PyQt5.QtGui import * @@ -27,24 +45,7 @@ try: from PyQt6.QtOpenGL import * except ImportError as error: - names = [ - 'QOpenGLBuffer', - 'QOpenGLFramebufferObject', - 'QOpenGLFramebufferObjectFormat', - 'QOpenGLShader', - 'QOpenGLShaderProgram', - 'QOpenGLContext', - 'QOpenGLContextGroup', - 'QOpenGLDebugLogger', - 'QOpenGLDebugMessage', - 'QOpenGLPixelTransferOptions', - 'QOpenGLTexture', - 'QOpenGLTextureBlitter', - 'QOpenGLVersionProfile', - 'QOpenGLVertexArrayObject', - 'QOpenGLWindow', - ] - for name in names: + for name in _QTOPENGL_NAMES: _missing_optional_names[name] = { 'name': 'PyQt6.QtOpenGL', 'missing_package': 'pyopengl', @@ -77,24 +78,7 @@ try: from PySide6.QtOpenGL import * except ImportError as error: - names = [ - 'QOpenGLBuffer', - 'QOpenGLFramebufferObject', - 'QOpenGLFramebufferObjectFormat', - 'QOpenGLShader', - 'QOpenGLShaderProgram', - 'QOpenGLContext', - 'QOpenGLContextGroup', - 'QOpenGLDebugLogger', - 'QOpenGLDebugMessage', - 'QOpenGLPixelTransferOptions', - 'QOpenGLTexture', - 'QOpenGLTextureBlitter', - 'QOpenGLVersionProfile', - 'QOpenGLVertexArrayObject', - 'QOpenGLWindow', - ] - for name in names: + for name in _QTOPENGL_NAMES: _missing_optional_names[name] = { 'name': 'PySide6.QtOpenGL', 'missing_package': 'pyopengl', From c752f223c3b6872c59864b719b01775d90ed2cac Mon Sep 17 00:00:00 2001 From: Daelon Suzuka Date: Wed, 22 Mar 2023 04:00:32 -0700 Subject: [PATCH 526/703] Change _QTOPENGL_NAMES to a set Co-authored-by: C.A.M. Gerlach --- qtpy/QtGui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 93353f08..75ccd4b9 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -14,7 +14,7 @@ _missing_optional_names = {} -_QTOPENGL_NAMES = [ +_QTOPENGL_NAMES = { 'QOpenGLBuffer', 'QOpenGLFramebufferObject', 'QOpenGLFramebufferObjectFormat', @@ -30,7 +30,7 @@ 'QOpenGLVersionProfile', 'QOpenGLVertexArrayObject', 'QOpenGLWindow', -] +} if PYQT5: From 0972ce70e7ca401733e24d5a35b5a8b5be9ed77f Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Wed, 22 Mar 2023 23:20:25 -0400 Subject: [PATCH 527/703] Fix lines that had 7 spaces for some reason --- qtpy/QtWidgets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 09ce7e76..56a18068 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -28,11 +28,11 @@ try: from PyQt6.QtOpenGLWidgets import QOpenGLWidget except ImportError as error: - _missing_optional_names['QOpenGLWidget'] = { + _missing_optional_names['QOpenGLWidget'] = { 'name': 'PyQt6.QtOpenGLWidgets', 'missing_package': 'pyopengl', 'import_error': error, - } + } # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) @@ -62,11 +62,11 @@ try: from PySide6.QtOpenGLWidgets import QOpenGLWidget except ImportError as error: - _missing_optional_names['QOpenGLWidget'] = { + _missing_optional_names['QOpenGLWidget'] = { 'name': 'PySide6.QtOpenGLWidgets', 'missing_package': 'pyopengl', 'import_error': error, - } + } # Map missing/renamed methods QTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) From e1515f82fbda31afcdfc24513e4fb18b019fea54 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 24 Mar 2023 17:04:53 +0300 Subject: [PATCH 528/703] Fix #394 for all children of `QSinglePointEvent` --- qtpy/QtGui.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 35a83eab..7ee57f32 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -90,9 +90,9 @@ def movePositionPatched( QMouseEvent.position = lambda self: __QPointF(float(self.x()), float(self.y())) QMouseEvent.globalPosition = lambda self: __QPointF(float(self.globalX()), float(self.globalY())) if PYQT6 or PYSIDE6: - QMouseEvent.pos = lambda self: self.position().toPoint() - QMouseEvent.x = lambda self: self.position().toPoint().x() - QMouseEvent.y = lambda self: self.position().toPoint().y() - QMouseEvent.globalPos = lambda self: self.globalPosition().toPoint() - QMouseEvent.globalX = lambda self: self.globalPosition().toPoint().x() - QMouseEvent.globalY = lambda self: self.globalPosition().toPoint().y() + QSinglePointEvent.pos = lambda self: self.position().toPoint() + QSinglePointEvent.x = lambda self: self.position().toPoint().x() + QSinglePointEvent.y = lambda self: self.position().toPoint().y() + QSinglePointEvent.globalPos = lambda self: self.globalPosition().toPoint() + QSinglePointEvent.globalX = lambda self: self.globalPosition().toPoint().x() + QSinglePointEvent.globalY = lambda self: self.globalPosition().toPoint().y() From e13b3a5b7c5352850c1cc897d8597d1df2721ae4 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 24 Mar 2023 17:04:53 +0300 Subject: [PATCH 529/703] Fix #394 for all children of `QSinglePointEvent` --- qtpy/QtGui.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 7ee57f32..4591f414 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -90,6 +90,10 @@ def movePositionPatched( QMouseEvent.position = lambda self: __QPointF(float(self.x()), float(self.y())) QMouseEvent.globalPosition = lambda self: __QPointF(float(self.globalX()), float(self.globalY())) if PYQT6 or PYSIDE6: + for _class in (QNativeGestureEvent, QEnterEvent, QTabletEvent, QHoverEvent, QWheelEvent, QMouseEvent): + for _obsolete_function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY'): + if hasattr(_class, _obsolete_function): + delattr(_class, _obsolete_function) QSinglePointEvent.pos = lambda self: self.position().toPoint() QSinglePointEvent.x = lambda self: self.position().toPoint().x() QSinglePointEvent.y = lambda self: self.position().toPoint().y() From af83b5655742507a0b138c0fdebe22b9a48451a1 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 24 Mar 2023 22:28:29 +0300 Subject: [PATCH 530/703] Add position functions of `QNativeGestureEvent`, `QEnterEvent`, `QTabletEvent`, and `QHoverEvent` --- qtpy/QtGui.py | 21 +++++++++++++++++-- qtpy/tests/test_qtgui.py | 45 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 4591f414..68e86431 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -87,14 +87,31 @@ def movePositionPatched( # Fix https://github.com/spyder-ide/qtpy/issues/394 if PYQT5 or PYSIDE2: from qtpy.QtCore import QPointF as __QPointF - QMouseEvent.position = lambda self: __QPointF(float(self.x()), float(self.y())) + QNativeGestureEvent.x = lambda self: self.localPos().toPoint().x() + QNativeGestureEvent.y = lambda self: self.localPos().toPoint().y() + QNativeGestureEvent.position = lambda self: self.localPos() + QNativeGestureEvent.globalX = lambda self: self.globalPos().x() + QNativeGestureEvent.globalY = lambda self: self.globalPos().y() + QNativeGestureEvent.globalPosition = lambda self: __QPointF(float(self.globalPos().x()), + float(self.globalPos().y())) + QEnterEvent.position = lambda self: self.localPos() + QEnterEvent.globalPosition = lambda self: __QPointF(float(self.globalX()), float(self.globalY())) + QTabletEvent.position = lambda self: self.posF() + QTabletEvent.globalPosition = lambda self: self.globalPosF() + QHoverEvent.x = lambda self: self.pos().x() + QHoverEvent.y = lambda self: self.pos().y() + QHoverEvent.position = lambda self: self.posF() + # no `QHoverEvent.globalPosition`, `QHoverEvent.globalX`, nor `QHoverEvent.globalY` in the Qt5 docs + QMouseEvent.position = lambda self: self.localPos() QMouseEvent.globalPosition = lambda self: __QPointF(float(self.globalX()), float(self.globalY())) if PYQT6 or PYSIDE6: - for _class in (QNativeGestureEvent, QEnterEvent, QTabletEvent, QHoverEvent, QWheelEvent, QMouseEvent): + for _class in (QNativeGestureEvent, QEnterEvent, QTabletEvent, QHoverEvent, QMouseEvent): for _obsolete_function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY'): if hasattr(_class, _obsolete_function): delattr(_class, _obsolete_function) QSinglePointEvent.pos = lambda self: self.position().toPoint() + QSinglePointEvent.posF = lambda self: self.position() + QSinglePointEvent.localPos = lambda self: self.position() QSinglePointEvent.x = lambda self: self.position().toPoint().x() QSinglePointEvent.y = lambda self: self.position().toPoint().y() QSinglePointEvent.globalPos = lambda self: self.globalPosition().toPoint() diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 787d7dba..d4e1c378 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -77,7 +77,7 @@ def test_enum_access(): @pytest.mark.skipif( sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), reason="Stalls on macOS CI with Python 3.7") -def test_QMouseEvent_pos_functions(qtbot): +def test_QSomethingEvent_pos_functions(qtbot): """ Test `QMouseEvent.pos` and related functions removed in Qt 6, and `QMouseEvent.position`, etc., missing from Qt 5. @@ -101,8 +101,47 @@ def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None: window.setMinimumSize(320, 240) # ensure the window is of sufficient size window.show() - qtbot.mouseMove(window, QtCore.QPoint(42, 6 * 9)) - qtbot.mouseDClick(window, QtCore.Qt.LeftButton) + with qtbot.waitExposed(window): + qtbot.mouseMove(window, QtCore.QPoint(42, 6 * 9)) + qtbot.mouseDClick(window, QtCore.Qt.LeftButton) + + # the rest of the functions are not actually tested + assert QtGui.QNativeGestureEvent.pos is not None + assert QtGui.QNativeGestureEvent.x is not None + assert QtGui.QNativeGestureEvent.y is not None + assert QtGui.QNativeGestureEvent.globalPos is not None + assert QtGui.QNativeGestureEvent.globalX is not None + assert QtGui.QNativeGestureEvent.globalY is not None + assert QtGui.QNativeGestureEvent.position is not None + assert QtGui.QNativeGestureEvent.globalPosition is not None + assert QtGui.QEnterEvent.pos is not None + assert QtGui.QEnterEvent.x is not None + assert QtGui.QEnterEvent.y is not None + assert QtGui.QEnterEvent.globalPos is not None + assert QtGui.QEnterEvent.globalX is not None + assert QtGui.QEnterEvent.globalY is not None + assert QtGui.QEnterEvent.position is not None + assert QtGui.QEnterEvent.globalPosition is not None + assert QtGui.QTabletEvent.pos is not None + assert QtGui.QTabletEvent.x is not None + assert QtGui.QTabletEvent.y is not None + assert QtGui.QTabletEvent.globalPos is not None + assert QtGui.QTabletEvent.globalX is not None + assert QtGui.QTabletEvent.globalY is not None + assert QtGui.QTabletEvent.position is not None + assert QtGui.QTabletEvent.globalPosition is not None + assert QtGui.QHoverEvent.pos is not None + assert QtGui.QHoverEvent.x is not None + assert QtGui.QHoverEvent.y is not None + assert QtGui.QHoverEvent.position is not None + assert QtGui.QTabletEvent.pos is not None + assert QtGui.QTabletEvent.x is not None + assert QtGui.QTabletEvent.y is not None + assert QtGui.QTabletEvent.globalPos is not None + assert QtGui.QTabletEvent.globalX is not None + assert QtGui.QTabletEvent.globalY is not None + assert QtGui.QTabletEvent.position is not None + assert QtGui.QTabletEvent.globalPosition is not None @pytest.mark.skipif(not (PYSIDE2 or PYSIDE6), reason="PySide{2,6} specific test") From fac2382aa924e788c27352fe71dc472b1551dae5 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Sat, 25 Mar 2023 00:08:41 +0300 Subject: [PATCH 531/703] Simplify the tests for the position functions of `Q...Event`, as @CAM-Gerlach suggested --- qtpy/tests/test_qtgui.py | 41 +++++----------------------------------- 1 file changed, 5 insertions(+), 36 deletions(-) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index d4e1c378..5bef40dc 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -106,42 +106,11 @@ def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None: qtbot.mouseDClick(window, QtCore.Qt.LeftButton) # the rest of the functions are not actually tested - assert QtGui.QNativeGestureEvent.pos is not None - assert QtGui.QNativeGestureEvent.x is not None - assert QtGui.QNativeGestureEvent.y is not None - assert QtGui.QNativeGestureEvent.globalPos is not None - assert QtGui.QNativeGestureEvent.globalX is not None - assert QtGui.QNativeGestureEvent.globalY is not None - assert QtGui.QNativeGestureEvent.position is not None - assert QtGui.QNativeGestureEvent.globalPosition is not None - assert QtGui.QEnterEvent.pos is not None - assert QtGui.QEnterEvent.x is not None - assert QtGui.QEnterEvent.y is not None - assert QtGui.QEnterEvent.globalPos is not None - assert QtGui.QEnterEvent.globalX is not None - assert QtGui.QEnterEvent.globalY is not None - assert QtGui.QEnterEvent.position is not None - assert QtGui.QEnterEvent.globalPosition is not None - assert QtGui.QTabletEvent.pos is not None - assert QtGui.QTabletEvent.x is not None - assert QtGui.QTabletEvent.y is not None - assert QtGui.QTabletEvent.globalPos is not None - assert QtGui.QTabletEvent.globalX is not None - assert QtGui.QTabletEvent.globalY is not None - assert QtGui.QTabletEvent.position is not None - assert QtGui.QTabletEvent.globalPosition is not None - assert QtGui.QHoverEvent.pos is not None - assert QtGui.QHoverEvent.x is not None - assert QtGui.QHoverEvent.y is not None - assert QtGui.QHoverEvent.position is not None - assert QtGui.QTabletEvent.pos is not None - assert QtGui.QTabletEvent.x is not None - assert QtGui.QTabletEvent.y is not None - assert QtGui.QTabletEvent.globalPos is not None - assert QtGui.QTabletEvent.globalX is not None - assert QtGui.QTabletEvent.globalY is not None - assert QtGui.QTabletEvent.position is not None - assert QtGui.QTabletEvent.globalPosition is not None + for _class in ('QNativeGestureEvent', 'QEnterEvent', 'QTabletEvent'): + for _function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY', 'position', 'globalPosition'): + assert hasattr(getattr(QtGui, _class), _function) + for _function in ('pos', 'x', 'y', 'position'): + assert hasattr(QtGui.QHoverEvent, _function) @pytest.mark.skipif(not (PYSIDE2 or PYSIDE6), reason="PySide{2,6} specific test") From bd1b013e6d3d9163d64bad17349895cba969df92 Mon Sep 17 00:00:00 2001 From: Anton Yablokov Date: Tue, 28 Mar 2023 07:55:55 +0300 Subject: [PATCH 532/703] Apply code style suggestions by @ccordoba12 Co-authored-by: Carlos Cordoba --- qtpy/QtGui.py | 16 ++++++++++------ qtpy/tests/test_qtgui.py | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 68e86431..d51540df 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -92,20 +92,24 @@ def movePositionPatched( QNativeGestureEvent.position = lambda self: self.localPos() QNativeGestureEvent.globalX = lambda self: self.globalPos().x() QNativeGestureEvent.globalY = lambda self: self.globalPos().y() - QNativeGestureEvent.globalPosition = lambda self: __QPointF(float(self.globalPos().x()), - float(self.globalPos().y())) + QNativeGestureEvent.globalPosition = lambda self: __QPointF( + float(self.globalPos().x()), float(self.globalPos().y())) QEnterEvent.position = lambda self: self.localPos() - QEnterEvent.globalPosition = lambda self: __QPointF(float(self.globalX()), float(self.globalY())) + QEnterEvent.globalPosition = lambda self: __QPointF( + float(self.globalX()), float(self.globalY())) QTabletEvent.position = lambda self: self.posF() QTabletEvent.globalPosition = lambda self: self.globalPosF() QHoverEvent.x = lambda self: self.pos().x() QHoverEvent.y = lambda self: self.pos().y() QHoverEvent.position = lambda self: self.posF() - # no `QHoverEvent.globalPosition`, `QHoverEvent.globalX`, nor `QHoverEvent.globalY` in the Qt5 docs + # No `QHoverEvent.globalPosition`, `QHoverEvent.globalX`, + # nor `QHoverEvent.globalY` in the Qt5 docs. QMouseEvent.position = lambda self: self.localPos() - QMouseEvent.globalPosition = lambda self: __QPointF(float(self.globalX()), float(self.globalY())) + QMouseEvent.globalPosition = lambda self: __QPointF( + float(self.globalX()), float(self.globalY())) if PYQT6 or PYSIDE6: - for _class in (QNativeGestureEvent, QEnterEvent, QTabletEvent, QHoverEvent, QMouseEvent): + for _class in (QNativeGestureEvent, QEnterEvent, QTabletEvent, QHoverEvent, + QMouseEvent): for _obsolete_function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY'): if hasattr(_class, _obsolete_function): delattr(_class, _obsolete_function) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 5bef40dc..36249415 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -107,7 +107,8 @@ def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None: # the rest of the functions are not actually tested for _class in ('QNativeGestureEvent', 'QEnterEvent', 'QTabletEvent'): - for _function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY', 'position', 'globalPosition'): + for _function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY', + 'position', 'globalPosition'): assert hasattr(getattr(QtGui, _class), _function) for _function in ('pos', 'x', 'y', 'position'): assert hasattr(QtGui.QHoverEvent, _function) From f809bbe44321968bb4319605ed2aac7a27af2947 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 28 Mar 2023 18:01:13 -0500 Subject: [PATCH 533/703] Release 2.3.1 --- CHANGELOG.md | 36 ++++++++++++++++++++++++++++++++++++ qtpy/__init__.py | 2 +- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d6a1ae4..42baac2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,41 @@ # History of changes +## Version 2.3.1 (2023-03-28) + +### Issues Closed + +* [Issue 416](https://github.com/spyder-ide/qtpy/issues/416) - Release QtPy 2.3.1 +* [Issue 412](https://github.com/spyder-ide/qtpy/issues/412) - How to catch QtBindingsNotFoundError ([PR 413](https://github.com/spyder-ide/qtpy/pull/413) by [@cbrnr](https://github.com/cbrnr)) +* [Issue 405](https://github.com/spyder-ide/qtpy/issues/405) - Remove patch that "may be limited to `PySide-5.11a1` only" +* [Issue 402](https://github.com/spyder-ide/qtpy/issues/402) - `QTextStreamManipulator` has no `exec` method on PyQt5/6 +* [Issue 394](https://github.com/spyder-ide/qtpy/issues/394) - Differences in QEvent subclass APIs in PyQt6 cause attribute and/or type errors +* [Issue 390](https://github.com/spyder-ide/qtpy/issues/390) - QtBindingsNotFoundError is not raised correctly (from None) in __init__ ([PR 391](https://github.com/spyder-ide/qtpy/pull/391) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 386](https://github.com/spyder-ide/qtpy/issues/386) - Add official support for Python 3.11 ([PR 392](https://github.com/spyder-ide/qtpy/pull/392) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) + +In this release 7 issues were closed. + +### Pull Requests Merged + +* [PR 417](https://github.com/spyder-ide/qtpy/pull/417) - PR: Add compatibility mappings between bindings for all children of `QSinglePointEvent`, by [@StSav012](https://github.com/StSav012) +* [PR 414](https://github.com/spyder-ide/qtpy/pull/414) - PR: Skip QtPositioning tests on Conda Qt >=6.4.3 where its not included, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) +* [PR 413](https://github.com/spyder-ide/qtpy/pull/413) - PR: Make `QtBindingsNotFoundError` also inherit from `ImportError`, by [@cbrnr](https://github.com/cbrnr) ([412](https://github.com/spyder-ide/qtpy/issues/412)) +* [PR 410](https://github.com/spyder-ide/qtpy/pull/410) - PR: Improve compatibility for `QtWidgets` and `QtGui` modules between Qt5 and Qt6 bindings, by [@StSav012](https://github.com/StSav012) +* [PR 408](https://github.com/spyder-ide/qtpy/pull/408) - PR: Add mappings for QMouseEvent methods, by [@StSav012](https://github.com/StSav012) +* [PR 407](https://github.com/spyder-ide/qtpy/pull/407) - PR: Remove patch that may be limited to PySide-5.11a1 only, by [@StSav012](https://github.com/StSav012) +* [PR 404](https://github.com/spyder-ide/qtpy/pull/404) - PR: Don't make `QTextStreamManipulator.exec_` on PyQt5/6, by [@StSav012](https://github.com/StSav012) +* [PR 401](https://github.com/spyder-ide/qtpy/pull/401) - PR: Unskip PySide2 5.15 on Python 3.11 with Conda on CIs, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) +* [PR 398](https://github.com/spyder-ide/qtpy/pull/398) - PR: Make warning usage consistant and refine messages, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) +* [PR 397](https://github.com/spyder-ide/qtpy/pull/397) - Simplify CI script following Qt 5.15.6 feedstock update, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) +* [PR 393](https://github.com/spyder-ide/qtpy/pull/393) - PR: Fix `LibraryLocation` -> `LibraryPath` renaming due to deprecation with Qt6, by [@StSav012](https://github.com/StSav012) +* [PR 392](https://github.com/spyder-ide/qtpy/pull/392) - PR: Add Python 3.11 to CIs/tags & overhaul CI config, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([386](https://github.com/spyder-ide/qtpy/issues/386)) +* [PR 391](https://github.com/spyder-ide/qtpy/pull/391) - PR: Use raise from None when raising QtBindingsNotFoundError in __init__, by [@CAM-Gerlach](https://github.com/CAM-Gerlach) ([390](https://github.com/spyder-ide/qtpy/issues/390)) + +In this release 13 pull requests were closed. + + +---- + + ## Version 2.3.0 (2022-11-07) ### Issues Closed diff --git a/qtpy/__init__.py b/qtpy/__init__.py index ddc95bcb..334a884a 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.4.0.dev0' +__version__ = '2.3.1' class PythonQtError(RuntimeError): From 96497b23da50f4a47956839cc1c2ece3fd3097e9 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Tue, 28 Mar 2023 18:04:54 -0500 Subject: [PATCH 534/703] Back to work --- qtpy/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/__init__.py b/qtpy/__init__.py index 334a884a..ddc95bcb 100644 --- a/qtpy/__init__.py +++ b/qtpy/__init__.py @@ -61,7 +61,7 @@ import warnings # Version of QtPy -__version__ = '2.3.1' +__version__ = '2.4.0.dev0' class PythonQtError(RuntimeError): From cce9787adc48c9765797a9f21e582cc694ce2e35 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Thu, 30 Mar 2023 19:05:05 -0500 Subject: [PATCH 535/703] Add missing issue closed for v2.3.1 changelog entry --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42baac2f..4d46cb3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,9 +10,10 @@ * [Issue 402](https://github.com/spyder-ide/qtpy/issues/402) - `QTextStreamManipulator` has no `exec` method on PyQt5/6 * [Issue 394](https://github.com/spyder-ide/qtpy/issues/394) - Differences in QEvent subclass APIs in PyQt6 cause attribute and/or type errors * [Issue 390](https://github.com/spyder-ide/qtpy/issues/390) - QtBindingsNotFoundError is not raised correctly (from None) in __init__ ([PR 391](https://github.com/spyder-ide/qtpy/pull/391) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) +* [Issue 389](https://github.com/spyder-ide/qtpy/issues/389) - Make `QtWidgets` and `QtGui` modules compatible with `PySide6`/`PyQt6` import locations for `PySide2`/`PyQt5` ([PR 410](https://github.com/spyder-ide/qtpy/pull/410) by [@StSav012](https://github.com/StSav012)) * [Issue 386](https://github.com/spyder-ide/qtpy/issues/386) - Add official support for Python 3.11 ([PR 392](https://github.com/spyder-ide/qtpy/pull/392) by [@CAM-Gerlach](https://github.com/CAM-Gerlach)) -In this release 7 issues were closed. +In this release 8 issues were closed. ### Pull Requests Merged From 34cf21d9808d7787626e8c81f01ceafcb5466dfb Mon Sep 17 00:00:00 2001 From: stsav012 Date: Mon, 3 Apr 2023 19:00:28 +0300 Subject: [PATCH 536/703] Symmetrize `path` and `location` of `QLibraryInfo` --- qtpy/QtCore.py | 9 ++++++--- qtpy/tests/test_qtcore.py | 11 +++++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 275b5171..46bca19c 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -58,9 +58,6 @@ QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QLibraryInfo.location = QLibraryInfo.path - QLibraryInfo.LibraryLocation = QLibraryInfo.LibraryPath - # Those are imported from `import *` del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR @@ -112,5 +109,11 @@ QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + +# Mirror https://github.com/spyder-ide/qtpy/pull/393 +if PYQT5 or PYSIDE2: + QLibraryInfo.path = QLibraryInfo.location + QLibraryInfo.LibraryPath = QLibraryInfo.LibraryLocation +if PYQT6 or PYSIDE6: QLibraryInfo.location = QLibraryInfo.path QLibraryInfo.LibraryLocation = QLibraryInfo.LibraryPath diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 9f52a2d7..f945a7a0 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -62,15 +62,18 @@ def test_qthread_exec_(): assert QtCore.QThread.exec_ is not None -def test_qlibraryinfo_location(): - """Test QLibraryInfo.location""" +def test_QLibraryInfo_location_and_path(): + """Test `QLibraryInfo.location` and `QLibraryInfo.path`""" assert QtCore.QLibraryInfo.location is not None assert QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PrefixPath) is not None + assert QtCore.QLibraryInfo.path is not None + assert QtCore.QLibraryInfo.path(QtCore.QLibraryInfo.LibraryPath.PrefixPath) is not None -def test_qlibraryinfo_library_location(): - """Test QLibraryInfo.LibraryLocation""" +def test_QLibraryInfo_LibraryLocation_and_LibraryPath(): + """Test `QLibraryInfo.LibraryLocation` and `QLibraryInfo.LibraryPath`""" assert QtCore.QLibraryInfo.LibraryLocation is not None + assert QtCore.QLibraryInfo.LibraryPath is not None @pytest.mark.skipif(PYQT5 or PYQT6, From 737478718463cac0d2928c00c62b70dff8e97362 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Mon, 3 Apr 2023 19:28:45 +0300 Subject: [PATCH 537/703] Fix AttributeError: type object 'LibraryLocation' has no attribute 'PrefixPath' on PyQt5 --- qtpy/tests/test_qtcore.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index f945a7a0..80acfd9f 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -67,7 +67,7 @@ def test_QLibraryInfo_location_and_path(): assert QtCore.QLibraryInfo.location is not None assert QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.PrefixPath) is not None assert QtCore.QLibraryInfo.path is not None - assert QtCore.QLibraryInfo.path(QtCore.QLibraryInfo.LibraryPath.PrefixPath) is not None + assert QtCore.QLibraryInfo.path(QtCore.QLibraryInfo.PrefixPath) is not None def test_QLibraryInfo_LibraryLocation_and_LibraryPath(): From 1ecf81b4c5e2d50f584a753a0dd056713d345844 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 4 Apr 2023 13:03:27 -0400 Subject: [PATCH 538/703] Fix typo in comments --- qtpy/QtGui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index d6b64df2..a195985d 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -42,7 +42,7 @@ from PyQt6 import QtGui from PyQt6.QtGui import * - # Attempt to import QOpenGLWidget, but if that fails, + # Attempt to import QOpenGL* classes, but if that fails, # don't raise an exception until the name is explicitly accessed. # See https://github.com/spyder-ide/qtpy/pull/387/ try: @@ -79,7 +79,7 @@ elif PYSIDE6: from PySide6.QtGui import * - # Attempt to import QOpenGLWidget, but if that fails, + # Attempt to import QOpenGL* classes, but if that fails, # don't raise an exception until the name is explicitly accessed. # See https://github.com/spyder-ide/qtpy/pull/387/ try: From e9c2245b13de6733fc828ad88b381a24aa6b1962 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 4 Apr 2023 13:03:52 -0400 Subject: [PATCH 539/703] Sort list of opengl names --- qtpy/QtGui.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index a195985d..f046fac4 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -16,15 +16,15 @@ _QTOPENGL_NAMES = { 'QOpenGLBuffer', - 'QOpenGLFramebufferObject', - 'QOpenGLFramebufferObjectFormat', - 'QOpenGLShader', - 'QOpenGLShaderProgram', 'QOpenGLContext', 'QOpenGLContextGroup', 'QOpenGLDebugLogger', 'QOpenGLDebugMessage', + 'QOpenGLFramebufferObject', + 'QOpenGLFramebufferObjectFormat', 'QOpenGLPixelTransferOptions', + 'QOpenGLShader', + 'QOpenGLShaderProgram', 'QOpenGLTexture', 'QOpenGLTextureBlitter', 'QOpenGLVersionProfile', From 94f1cf6783dcac33bbcabff1023f3395adc83530 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 4 Apr 2023 13:04:31 -0400 Subject: [PATCH 540/703] Add opengl import tests for QtWidgets and QtGui --- qtpy/tests/test_qtgui.py | 23 +++++++++++++++++++++++ qtpy/tests/test_qtwidgets.py | 10 +++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 787d7dba..a4d1b8e5 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -123,3 +123,26 @@ def test_qtextcursor_moveposition(): assert cursor.position() == cursor.anchor() assert cursor.movePosition(QtGui.QTextCursor.NextWord, QtGui.QTextCursor.KeepAnchor, 3) assert cursor.selectedText() == "foo bar baz" + + +def test_opengl_imports(): + """Test for presence of QOpenGL* classes + These classes were members of QtGui in Qt5, but moved to QtOpenGL in Qt6. + QtPy makes them available in QtGui to maintain compatibility + """ + + assert QtGui.QOpenGLBuffer is not None + assert QtGui.QOpenGLContext is not None + assert QtGui.QOpenGLContextGroup is not None + assert QtGui.QOpenGLDebugLogger is not None + assert QtGui.QOpenGLDebugMessage is not None + assert QtGui.QOpenGLFramebufferObject is not None + assert QtGui.QOpenGLFramebufferObjectFormat is not None + assert QtGui.QOpenGLPixelTransferOptions is not None + assert QtGui.QOpenGLShader is not None + assert QtGui.QOpenGLShaderProgram is not None + assert QtGui.QOpenGLTexture is not None + assert QtGui.QOpenGLTextureBlitter is not None + assert QtGui.QOpenGLVersionProfile is not None + assert QtGui.QOpenGLVertexArrayObject is not None + assert QtGui.QOpenGLWindow is not None \ No newline at end of file diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 8670fdf6..a7635edd 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -4,7 +4,7 @@ import pytest -from qtpy import PYQT5, PYQT_VERSION, QtCore, QtGui, QtWidgets +from qtpy import PYQT5, PYSIDE2, PYQT_VERSION, QtCore, QtGui, QtWidgets from qtpy.tests.utils import using_conda, not_using_conda @@ -116,3 +116,11 @@ def test_enum_access(): assert QtWidgets.QStyle.State_None == QtWidgets.QStyle.StateFlag.State_None assert QtWidgets.QSlider.TicksLeft == QtWidgets.QSlider.TickPosition.TicksAbove assert QtWidgets.QStyle.SC_SliderGroove == QtWidgets.QStyle.SubControl.SC_SliderGroove + + +def test_opengl_imports(): + """Test for presence of QOpenGLWidget + QOpenGLWidget was a member of QtWidgets in Qt5, but moved to QtOpenGLWidgets in Qt6. + QtPy makes QOpenGLWidget available in QtWidgets to maintain compatibility + """ + assert QtWidgets.QOpenGLWidget is not None \ No newline at end of file From 4b921da758e57c94c2e6a37113658d0cdbb2fda5 Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 4 Apr 2023 13:28:13 -0400 Subject: [PATCH 541/703] Add a test for the deferred import error mechanism --- qtpy/tests/optional_deps/__init__.py | 27 ++++++++++++++++++++++++ qtpy/tests/optional_deps/optional_dep.py | 4 ++++ qtpy/tests/test_missing_optional_deps.py | 20 ++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 qtpy/tests/optional_deps/__init__.py create mode 100644 qtpy/tests/optional_deps/optional_dep.py create mode 100644 qtpy/tests/test_missing_optional_deps.py diff --git a/qtpy/tests/optional_deps/__init__.py b/qtpy/tests/optional_deps/__init__.py new file mode 100644 index 00000000..35c3fb7c --- /dev/null +++ b/qtpy/tests/optional_deps/__init__.py @@ -0,0 +1,27 @@ +"""Module used for testing the deferred import error mechanism""" + + +# See https://github.com/spyder-ide/qtpy/pull/387/ + + +from qtpy.utils import _getattr_missing_optional_dep +from .optional_dep import ExampleClass + + +_missing_optional_names = {} + + +try: + from .optional_dep import MissingClass +except ImportError as error: + _missing_optional_names['MissingClass'] = { + 'name': 'optional_dep.MissingClass', + 'missing_package': 'test_package_please_ignore', + 'import_error': error, + } + + +def __getattr__(name): + """Custom getattr to chain and wrap errors due to missing optional deps.""" + raise _getattr_missing_optional_dep( + name, module_name=__name__, optional_names=_missing_optional_names) diff --git a/qtpy/tests/optional_deps/optional_dep.py b/qtpy/tests/optional_deps/optional_dep.py new file mode 100644 index 00000000..45284629 --- /dev/null +++ b/qtpy/tests/optional_deps/optional_dep.py @@ -0,0 +1,4 @@ + + +class ExampleClass: + pass diff --git a/qtpy/tests/test_missing_optional_deps.py b/qtpy/tests/test_missing_optional_deps.py new file mode 100644 index 00000000..129ec7c0 --- /dev/null +++ b/qtpy/tests/test_missing_optional_deps.py @@ -0,0 +1,20 @@ +"""Test the deferred import error mechanism""" + + +# See https://github.com/spyder-ide/qtpy/pull/387/ + + +import pytest + + +def test_missing_optional_deps(): + """Test importing a module that uses the deferred import error mechanism""" + from . import optional_deps + + assert optional_deps.ExampleClass is not None + + with pytest.raises(ImportError) as excinfo: + from .optional_deps import MissingClass + + msg = 'The optional_dep.MissingClass module was not found. It must be installed separately as test_package_please_ignore.' + assert msg == str(excinfo.value) \ No newline at end of file From 46a7108939e336db1b67f8b3626ef6114f06935b Mon Sep 17 00:00:00 2001 From: David Kincaid Date: Tue, 4 Apr 2023 13:34:17 -0400 Subject: [PATCH 542/703] Add trailing newlines --- qtpy/tests/test_missing_optional_deps.py | 2 +- qtpy/tests/test_qtgui.py | 2 +- qtpy/tests/test_qtwidgets.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_missing_optional_deps.py b/qtpy/tests/test_missing_optional_deps.py index 129ec7c0..07633e5a 100644 --- a/qtpy/tests/test_missing_optional_deps.py +++ b/qtpy/tests/test_missing_optional_deps.py @@ -17,4 +17,4 @@ def test_missing_optional_deps(): from .optional_deps import MissingClass msg = 'The optional_dep.MissingClass module was not found. It must be installed separately as test_package_please_ignore.' - assert msg == str(excinfo.value) \ No newline at end of file + assert msg == str(excinfo.value) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index a4d1b8e5..d05fe97f 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -145,4 +145,4 @@ def test_opengl_imports(): assert QtGui.QOpenGLTextureBlitter is not None assert QtGui.QOpenGLVersionProfile is not None assert QtGui.QOpenGLVertexArrayObject is not None - assert QtGui.QOpenGLWindow is not None \ No newline at end of file + assert QtGui.QOpenGLWindow is not None diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index a7635edd..f61ef19b 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -123,4 +123,4 @@ def test_opengl_imports(): QOpenGLWidget was a member of QtWidgets in Qt5, but moved to QtOpenGLWidgets in Qt6. QtPy makes QOpenGLWidget available in QtWidgets to maintain compatibility """ - assert QtWidgets.QOpenGLWidget is not None \ No newline at end of file + assert QtWidgets.QOpenGLWidget is not None From 2052f442fbbbcd1316bd6e22e2170b69429131a0 Mon Sep 17 00:00:00 2001 From: Daelon Suzuka Date: Tue, 4 Apr 2023 15:48:43 -0700 Subject: [PATCH 543/703] Apply suggestions from code review Co-authored-by: C.A.M. Gerlach --- qtpy/tests/optional_deps/__init__.py | 2 +- qtpy/tests/optional_deps/optional_dep.py | 2 +- qtpy/tests/test_missing_optional_deps.py | 4 +++- qtpy/tests/test_qtgui.py | 6 ++++-- qtpy/tests/test_qtwidgets.py | 8 +++++--- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/qtpy/tests/optional_deps/__init__.py b/qtpy/tests/optional_deps/__init__.py index 35c3fb7c..372c9a84 100644 --- a/qtpy/tests/optional_deps/__init__.py +++ b/qtpy/tests/optional_deps/__init__.py @@ -1,4 +1,4 @@ -"""Module used for testing the deferred import error mechanism""" +"""Package used for testing the deferred import error mechanism.""" # See https://github.com/spyder-ide/qtpy/pull/387/ diff --git a/qtpy/tests/optional_deps/optional_dep.py b/qtpy/tests/optional_deps/optional_dep.py index 45284629..530af118 100644 --- a/qtpy/tests/optional_deps/optional_dep.py +++ b/qtpy/tests/optional_deps/optional_dep.py @@ -1,4 +1,4 @@ - +"""Test module with an optional dependency that may be missing.""" class ExampleClass: pass diff --git a/qtpy/tests/test_missing_optional_deps.py b/qtpy/tests/test_missing_optional_deps.py index 07633e5a..8b8460c3 100644 --- a/qtpy/tests/test_missing_optional_deps.py +++ b/qtpy/tests/test_missing_optional_deps.py @@ -6,6 +6,8 @@ import pytest +from qtpy import QtModuleNotInstalledError + def test_missing_optional_deps(): """Test importing a module that uses the deferred import error mechanism""" @@ -13,7 +15,7 @@ def test_missing_optional_deps(): assert optional_deps.ExampleClass is not None - with pytest.raises(ImportError) as excinfo: + with pytest.raises(QtModuleNotInstalledError) as excinfo: from .optional_deps import MissingClass msg = 'The optional_dep.MissingClass module was not found. It must be installed separately as test_package_please_ignore.' diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index d05fe97f..67f3f878 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -126,9 +126,11 @@ def test_qtextcursor_moveposition(): def test_opengl_imports(): - """Test for presence of QOpenGL* classes + """ + Test for presence of QOpenGL* classes. + These classes were members of QtGui in Qt5, but moved to QtOpenGL in Qt6. - QtPy makes them available in QtGui to maintain compatibility + QtPy makes them available in QtGui to maintain compatibility. """ assert QtGui.QOpenGLBuffer is not None diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index f61ef19b..431f998a 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -4,7 +4,7 @@ import pytest -from qtpy import PYQT5, PYSIDE2, PYQT_VERSION, QtCore, QtGui, QtWidgets +from qtpy import PYQT5, PYQT_VERSION, QtCore, QtGui, QtWidgets from qtpy.tests.utils import using_conda, not_using_conda @@ -119,8 +119,10 @@ def test_enum_access(): def test_opengl_imports(): - """Test for presence of QOpenGLWidget + """ + Test for presence of QOpenGLWidget. + QOpenGLWidget was a member of QtWidgets in Qt5, but moved to QtOpenGLWidgets in Qt6. - QtPy makes QOpenGLWidget available in QtWidgets to maintain compatibility + QtPy makes QOpenGLWidget available in QtWidgets to maintain compatibility. """ assert QtWidgets.QOpenGLWidget is not None From c6142104c1569136f66e24caa44d81492a5b0fd7 Mon Sep 17 00:00:00 2001 From: Daelon Suzuka Date: Tue, 4 Apr 2023 18:00:15 -0700 Subject: [PATCH 544/703] Update qtpy/QtGui.py Co-authored-by: C.A.M. Gerlach --- qtpy/QtGui.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 87b6a428..0f9928dd 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -156,12 +156,6 @@ def movePositionPatched( QMouseEvent.globalPosition = lambda self: __QPointF( float(self.globalX()), float(self.globalY())) if PYQT6 or PYSIDE6: - QMouseEvent.pos = lambda self: self.position().toPoint() - QMouseEvent.x = lambda self: self.position().toPoint().x() - QMouseEvent.y = lambda self: self.position().toPoint().y() - QMouseEvent.globalPos = lambda self: self.globalPosition().toPoint() - QMouseEvent.globalX = lambda self: self.globalPosition().toPoint().x() - QMouseEvent.globalY = lambda self: self.globalPosition().toPoint().y() for _class in (QNativeGestureEvent, QEnterEvent, QTabletEvent, QHoverEvent, QMouseEvent): for _obsolete_function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY'): From c0c75bd1d43c0933d0ca0a92d94f0b9b5d4b35a1 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Wed, 5 Apr 2023 11:03:22 +0300 Subject: [PATCH 545/703] Symmetrize `QDateTime.toPython` and `toPyDateTime`, etc. --- qtpy/QtCore.py | 10 ++++++++++ qtpy/tests/test_qtcore.py | 41 +++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 46bca19c..8257dcd1 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -110,6 +110,16 @@ QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) +# For issue #153 and updated for issue #305 +if PYQT5 or PYQT6: + QDate.toPython = lambda self, *args, **kwargs: self.toPyDate(*args, **kwargs) + QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) + QTime.toPython = lambda self, *args, **kwargs: self.toPyTime(*args, **kwargs) +if PYSIDE2 or PYSIDE6: + QDate.toPyDate = lambda self, *args, **kwargs: self.toPython(*args, **kwargs) + QDateTime.toPyDateTime = lambda self, *args, **kwargs: self.toPython(*args, **kwargs) + QTime.toPyTime = lambda self, *args, **kwargs: self.toPython(*args, **kwargs) + # Mirror https://github.com/spyder-ide/qtpy/pull/393 if PYQT5 or PYSIDE2: QLibraryInfo.path = QLibraryInfo.location diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 80acfd9f..c4bb83ec 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,7 +1,7 @@ """Test QtCore.""" -from datetime import date, datetime, time import sys +from datetime import datetime import pytest @@ -16,34 +16,41 @@ ) from qtpy.tests.utils import not_using_conda +NOW = datetime.now() +# Make integer milliseconds; `floor` here, don't `round`! +NOW = NOW.replace(microsecond=(NOW.microsecond // 1000 * 1000)) + def test_qtmsghandler(): """Test qtpy.QtMsgHandler""" assert QtCore.qInstallMessageHandler is not None -def test_qdatetime_toPython(): - """Test QDateTime.toPython""" - q_date = QtCore.QDateTime.currentDateTime() - assert QtCore.QDateTime.toPython is not None - py_date = q_date.toPython() - assert isinstance(py_date, datetime) +def test_QDateTime_toPython_and_toPyDateTime(): + """Test `QDateTime.toPython` and `QDateTime.toPyDateTime`""" + q_datetime = QtCore.QDateTime(NOW) + py_date = q_datetime.toPython() + assert py_date == NOW + py_date = q_datetime.toPyDateTime() + assert py_date == NOW -def test_qdate_toPython(): - """Test QDate.toPython""" - q_date = QtCore.QDate.currentDate() - assert QtCore.QDate.toPython is not None +def test_QDate_toPython_and_toPyDate(): + """Test `QDate.toPython` and `QDate.toPyDate`""" + q_date = QtCore.QDateTime(NOW).date() py_date = q_date.toPython() - assert isinstance(py_date, date) + assert py_date == NOW.date() + py_date = q_date.toPyDate() + assert py_date == NOW.date() -def test_qtime_toPython(): - """Test QTime.toPython""" - q_time = QtCore.QTime.currentTime() - assert QtCore.QTime.toPython is not None +def test_QTime_toPython_and_toPyTime(): + """Test `QTime.toPython` and `QTime.toPyTime`""" + q_time = QtCore.QDateTime(NOW).time() py_time = q_time.toPython() - assert isinstance(py_time, time) + assert py_time == NOW.time() + py_time = q_time.toPyTime() + assert py_time == NOW.time() @pytest.mark.skipif( From ebcdb65d09959bf9f3411f60959a2ddeb2c761a5 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Wed, 5 Apr 2023 11:18:49 +0300 Subject: [PATCH 546/703] Remove forgotten code --- qtpy/QtCore.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 8257dcd1..e6798ba8 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -19,12 +19,6 @@ from PyQt5.QtCore import pyqtProperty as Property from PyQt5.QtCore import QT_VERSION_STR as __version__ - # For issue #153 and updated for issue #305 - from PyQt5.QtCore import QDate, QDateTime, QTime - QDate.toPython = lambda self, *args, **kwargs: self.toPyDate(*args, **kwargs) - QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) - QTime.toPython = lambda self, *args, **kwargs: self.toPyTime(*args, **kwargs) - # Those are imported from `import *` del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR @@ -37,12 +31,6 @@ from PyQt6.QtCore import pyqtProperty as Property from PyQt6.QtCore import QT_VERSION_STR as __version__ - # For issue #153 and updated for issue #305 - from PyQt6.QtCore import QDate, QDateTime, QTime - QDate.toPython = lambda self, *args, **kwargs: self.toPyDate(*args, **kwargs) - QDateTime.toPython = lambda self, *args, **kwargs: self.toPyDateTime(*args, **kwargs) - QTime.toPython = lambda self, *args, **kwargs: self.toPyTime(*args, **kwargs) - # For issue #311 # Seems like there is an error with sip. Without first # trying to import `PyQt6.QtGui.Qt`, some functions like From 1af0dd1243830dcf5c0f5f4aa9f07e27a2ad9393 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Wed, 5 Apr 2023 12:02:32 +0300 Subject: [PATCH 547/703] Test static `QMenu.exec_` call --- qtpy/tests/test_qtwidgets.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 8670fdf6..b0b81db0 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -98,12 +98,25 @@ def __init__(self): @pytest.mark.skipif( sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), reason="Stalls on macOS CI with Python 3.7") -def test_qmenu_functions(qtbot): - """Test functions mapping for QtWidgets.QDialog.""" - assert QtWidgets.QMenu.exec_ - menu = QtWidgets.QMenu(None) - QtCore.QTimer.singleShot(100, menu.close) - menu.exec_() +def test_QMenu_functions(qtbot): + """Test functions mapping for `QtWidgets.QMenu`.""" + # A window is required for static calls + window = QtWidgets.QMainWindow() + menu = QtWidgets.QMenu(window) + menu.addAction('QtPy') + window.show() + + with qtbot.waitExposed(window): + # Call `exec_` of a `QMenu` instance + QtCore.QTimer.singleShot(100, menu.close) + menu.exec_() + + # Call static `QMenu.exec_` + QtCore.QTimer.singleShot(100, lambda: qtbot.keyClick( + QtWidgets.QApplication.widgetAt(1, 1), + QtCore.Qt.Key.Key_Escape) + ) + QtWidgets.QMenu.exec_(menu.actions(), QtCore.QPoint(1, 1)) @pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), From 32e2f04b0a36c0211322d7b22cddfe1571fc2a21 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Wed, 5 Apr 2023 12:33:21 +0300 Subject: [PATCH 548/703] Call static `QMenu.exec` when necessary --- qtpy/QtWidgets.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 1e4a0340..ff5298a9 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -10,6 +10,18 @@ from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6 + +def _possibly_static_exec(cls, *args, **kwargs): + """ Call `self.exec` when `self` is given or a static method otherwise. """ + if isinstance(args[0], cls): + if len(args) == 1 and not kwargs: + # A special case to avoid the function resolving error + return args[0].exec() + return args[0].exec(*args[1:], **kwargs) + else: + return cls.exec(*args, **kwargs) + + if PYQT5: from PyQt5.QtWidgets import * elif PYQT6: @@ -27,7 +39,7 @@ QPlainTextEdit.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) QApplication.exec_ = QApplication.exec QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QMenu.exec_ = lambda *args, **kwargs: _possibly_static_exec(QMenu, *args, **kwargs) QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom()) # Allow unscoped access for enums inside the QtWidgets module @@ -51,4 +63,4 @@ # Map DeprecationWarning methods QApplication.exec_ = QApplication.exec QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) + QMenu.exec_ = lambda *args, **kwargs: _possibly_static_exec(QMenu, *args, **kwargs) From f54b4cc1d696b12245465c66c97a37a36c142799 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Wed, 5 Apr 2023 14:20:28 +0300 Subject: [PATCH 549/703] Try 1 to fix a stall on Windows --- qtpy/tests/test_qtwidgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index b0b81db0..2d5b344a 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -1,5 +1,5 @@ """Test QtWidgets.""" - +import contextlib import sys import pytest @@ -106,7 +106,7 @@ def test_QMenu_functions(qtbot): menu.addAction('QtPy') window.show() - with qtbot.waitExposed(window): + with qtbot.waitExposed(window) if sys.platform == 'linux' else contextlib.nullcontext(): # Call `exec_` of a `QMenu` instance QtCore.QTimer.singleShot(100, menu.close) menu.exec_() From 247569bf96e8c89c006554b54436c6c21facbbbf Mon Sep 17 00:00:00 2001 From: stsav012 Date: Wed, 5 Apr 2023 14:55:53 +0300 Subject: [PATCH 550/703] Try 2 to fix a stall on Windows --- qtpy/tests/test_qtwidgets.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 2d5b344a..d174df7b 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -1,5 +1,4 @@ """Test QtWidgets.""" -import contextlib import sys import pytest @@ -91,10 +90,10 @@ def __init__(self): QtCore.QTimer.singleShot(100, dialog.accept) dialog.exec_() - -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") +# +# @pytest.mark.skipif( +# sys.platform.startswith('linux') and not_using_conda(), +# reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), reason="Stalls on macOS CI with Python 3.7") @@ -106,7 +105,7 @@ def test_QMenu_functions(qtbot): menu.addAction('QtPy') window.show() - with qtbot.waitExposed(window) if sys.platform == 'linux' else contextlib.nullcontext(): + with qtbot.waitExposed(window): # Call `exec_` of a `QMenu` instance QtCore.QTimer.singleShot(100, menu.close) menu.exec_() @@ -114,7 +113,7 @@ def test_QMenu_functions(qtbot): # Call static `QMenu.exec_` QtCore.QTimer.singleShot(100, lambda: qtbot.keyClick( QtWidgets.QApplication.widgetAt(1, 1), - QtCore.Qt.Key.Key_Escape) + QtCore.Qt.Key_Escape) ) QtWidgets.QMenu.exec_(menu.actions(), QtCore.QPoint(1, 1)) From de808707b9db9e8aef9dfa8059a429cbe9e5678d Mon Sep 17 00:00:00 2001 From: stsav012 Date: Wed, 5 Apr 2023 15:01:32 +0300 Subject: [PATCH 551/703] Try 3 to fix a stall on Windows --- qtpy/tests/test_qtwidgets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index d174df7b..26e543aa 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -90,10 +90,10 @@ def __init__(self): QtCore.QTimer.singleShot(100, dialog.accept) dialog.exec_() -# -# @pytest.mark.skipif( -# sys.platform.startswith('linux') and not_using_conda(), -# reason="Fatal Python error: Aborted on Linux CI when not using conda") + +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), reason="Stalls on macOS CI with Python 3.7") From 189f2c17044e168b63c573dca4ec59277daa1019 Mon Sep 17 00:00:00 2001 From: Daelon Suzuka Date: Thu, 6 Apr 2023 11:10:03 -0700 Subject: [PATCH 552/703] Move module __getattr__ to top for better visibility and remove underscore from _getattr_missing_optional_dep Co-authored-by: Carlos Cordoba --- qtpy/QtGui.py | 11 ++++++----- qtpy/QtWidgets.py | 12 ++++++------ qtpy/tests/optional_deps/__init__.py | 4 ++-- qtpy/utils.py | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 0f9928dd..7a1623cf 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -9,7 +9,7 @@ """Provides QtGui classes and functions.""" from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import _getattr_missing_optional_dep +from .utils import getattr_missing_optional_dep _missing_optional_names = {} @@ -32,6 +32,11 @@ 'QOpenGLWindow', } +def __getattr__(name): + """Custom getattr to chain and wrap errors due to missing optional deps.""" + raise getattr_missing_optional_dep( + name, module_name=__name__, optional_names=_missing_optional_names) + if PYQT5: from PyQt5.QtGui import * @@ -170,7 +175,3 @@ def movePositionPatched( QSinglePointEvent.globalX = lambda self: self.globalPosition().toPoint().x() QSinglePointEvent.globalY = lambda self: self.globalPosition().toPoint().y() -def __getattr__(name): - """Custom getattr to chain and wrap errors due to missing optional deps.""" - raise _getattr_missing_optional_dep( - name, module_name=__name__, optional_names=_missing_optional_names) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 56a18068..2e339a78 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -9,11 +9,16 @@ """Provides widget classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import _getattr_missing_optional_dep +from .utils import getattr_missing_optional_dep _missing_optional_names = {} +def __getattr__(name): + """Custom getattr to chain and wrap errors due to missing optional deps.""" + raise getattr_missing_optional_dep( + name, module_name=__name__, optional_names=_missing_optional_names) + if PYQT5: from PyQt5.QtWidgets import * @@ -80,8 +85,3 @@ QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - -def __getattr__(name): - """Custom getattr to chain and wrap errors due to missing optional deps.""" - raise _getattr_missing_optional_dep( - name, module_name=__name__, optional_names=_missing_optional_names) diff --git a/qtpy/tests/optional_deps/__init__.py b/qtpy/tests/optional_deps/__init__.py index 372c9a84..a209c6b0 100644 --- a/qtpy/tests/optional_deps/__init__.py +++ b/qtpy/tests/optional_deps/__init__.py @@ -4,7 +4,7 @@ # See https://github.com/spyder-ide/qtpy/pull/387/ -from qtpy.utils import _getattr_missing_optional_dep +from qtpy.utils import getattr_missing_optional_dep from .optional_dep import ExampleClass @@ -23,5 +23,5 @@ def __getattr__(name): """Custom getattr to chain and wrap errors due to missing optional deps.""" - raise _getattr_missing_optional_dep( + raise getattr_missing_optional_dep( name, module_name=__name__, optional_names=_missing_optional_names) diff --git a/qtpy/utils.py b/qtpy/utils.py index 24dbf747..0bf5e79b 100644 --- a/qtpy/utils.py +++ b/qtpy/utils.py @@ -24,7 +24,7 @@ def _wrap_missing_optional_dep_error( return qtpy_error -def _getattr_missing_optional_dep(name, module_name, optional_names): +def getattr_missing_optional_dep(name, module_name, optional_names): """Wrap AttributeError in a special error if it matches.""" attr_error = AttributeError(f'module {module_name!r} has no attribute {name!r}') if name in optional_names: From 59a3bc899ed898d77ec65e771b084a9e47f2945a Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 7 Apr 2023 11:49:53 +0300 Subject: [PATCH 553/703] Resolve the merging conflict --- qtpy/QtWidgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index ff5298a9..e746a32d 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -64,3 +64,4 @@ def _possibly_static_exec(cls, *args, **kwargs): QApplication.exec_ = QApplication.exec QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda *args, **kwargs: _possibly_static_exec(QMenu, *args, **kwargs) + From ec2932334ceb56b8d1e0c015e8ef70cf96198a28 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 7 Apr 2023 11:50:52 +0300 Subject: [PATCH 554/703] Resolve the merging conflict --- qtpy/QtWidgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index e746a32d..a546d0a2 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -63,5 +63,5 @@ def _possibly_static_exec(cls, *args, **kwargs): # Map DeprecationWarning methods QApplication.exec_ = QApplication.exec QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QMenu.exec_ = lambda *args, **kwargs: _possibly_static_exec(QMenu, *args, **kwargs) + QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) From 9c6b503ae3c93d308570e46516729651d567ec0c Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 7 Apr 2023 11:52:35 +0300 Subject: [PATCH 555/703] Resolve the merging conflict --- qtpy/QtWidgets.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 11d65cd8..f9f2aabd 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -14,13 +14,13 @@ _missing_optional_names = {} + def __getattr__(name): """Custom getattr to chain and wrap errors due to missing optional deps.""" raise getattr_missing_optional_dep( name, module_name=__name__, optional_names=_missing_optional_names) - def _possibly_static_exec(cls, *args, **kwargs): """ Call `self.exec` when `self` is given or a static method otherwise. """ if isinstance(args[0], cls): @@ -95,5 +95,4 @@ def _possibly_static_exec(cls, *args, **kwargs): # Map DeprecationWarning methods QApplication.exec_ = QApplication.exec QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QMenu.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - + QMenu.exec_ = lambda *args, **kwargs: _possibly_static_exec(QMenu, *args, **kwargs) From 92f67fba9a7f5135159d6c3a3c65f10dc46f39be Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sat, 8 Apr 2023 23:19:38 -0500 Subject: [PATCH 556/703] CI: Use unique envs for each binding & tweak shell options --- .github/workflows/ci.yml | 2 +- .github/workflows/test.sh | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9da129c..5cef2da4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,7 +121,7 @@ jobs: if: always() && (! (matrix.skip-pyside6)) run: ./.github/workflows/test.sh pyside6 - name: Upload coverage data to coveralls.io - shell: bash + shell: bash -e {0} env: COVERALLS_FLAG_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 977e7644..05a59ffe 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -1,11 +1,7 @@ #!/bin/bash -ex -# Activate conda properly eval "$(conda shell.bash hook)" -# Remove any existing env -conda remove -q -n test-env --all || true - # Create and activate conda environment for this test BINDING=$(echo "$1" | tr '[:lower:]' '[:upper:]') QT_VERSION_VAR=${BINDING}_QT_VERSION @@ -16,8 +12,8 @@ if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTEST_VERSION=">=6,!=7.0.0,!=7.0.1,<7.2.0" fi -conda create -q -n test-env python=${PYTHON_VERSION} pytest${PYTEST_VERSION:-">=6,!=7.0.0,!=7.0.1"} "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} -conda activate test-env +conda create -q -n test-env-${BINDING} python=${PYTHON_VERSION} pytest${PYTEST_VERSION:-">=6,!=7.0.0,!=7.0.1"} "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} +conda activate test-env-${BINDING} if [ "$USE_CONDA" = "Yes" ]; then From d575bd26af7c10eb58130cdd7a8721c7d00de492 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sun, 9 Apr 2023 00:41:34 -0500 Subject: [PATCH 557/703] CI: Replace Conda with Mamba for faster & more stable/accurate solves --- .github/workflows/ci.yml | 6 ++++-- .github/workflows/test.sh | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5cef2da4..10d20c53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,11 +103,13 @@ jobs: auto-update-conda: true channels: conda-forge channel-priority: strict + miniforge-variant: Mambaforge + use-mamba: true - name: Print Conda info shell: bash -l {0} run: | - conda info - conda list + mamba info + mamba list - name: Test PyQt5 if: (! matrix.skip-pyqt5) run: ./.github/workflows/test.sh pyqt5 diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 05a59ffe..ef88e560 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -2,7 +2,7 @@ eval "$(conda shell.bash hook)" -# Create and activate conda environment for this test +# Create and activate mamba environment for this test BINDING=$(echo "$1" | tr '[:lower:]' '[:upper:]') QT_VERSION_VAR=${BINDING}_QT_VERSION @@ -12,17 +12,17 @@ if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTEST_VERSION=">=6,!=7.0.0,!=7.0.1,<7.2.0" fi -conda create -q -n test-env-${BINDING} python=${PYTHON_VERSION} pytest${PYTEST_VERSION:-">=6,!=7.0.0,!=7.0.1"} "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} +mamba create -y -n test-env-${BINDING} python=${PYTHON_VERSION} pytest${PYTEST_VERSION:-">=6,!=7.0.0,!=7.0.1"} "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} conda activate test-env-${BINDING} if [ "$USE_CONDA" = "Yes" ]; then if [ "${1}" = "pyqt5" ]; then - conda install -q qt=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} + mamba install -y qt=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} elif [ "${1}" = "pyside2" ]; then - conda install -q qt=${PYSIDE2_QT_VERSION} pyside2=${PYSIDE2_VERSION} + mamba install -y qt=${PYSIDE2_QT_VERSION} pyside2=${PYSIDE2_VERSION} elif [ "${1}" = "pyside6" ]; then - conda install -q qt6-main=${PYSIDE6_QT_VERSION} pyside6=${PYSIDE6_VERSION} + mamba install -y qt6-main=${PYSIDE6_QT_VERSION} pyside6=${PYSIDE6_VERSION} else exit 1 fi @@ -56,7 +56,7 @@ python -bb -X dev -W error -m build echo dist/*.whl | xargs -I % python -bb -X dev -W error -W "ignore::DeprecationWarning:pip._internal.locations._distutils" -W "ignore::DeprecationWarning:distutils.command.install" -m pip install --upgrade % # Print environment information -conda list +mamba list # Run tests mkdir -p temp_test_dir From 243f418a2f1b13deba674abcb003f1d967b1ea20 Mon Sep 17 00:00:00 2001 From: "C.A.M. Gerlach" Date: Sun, 9 Apr 2023 01:26:26 -0500 Subject: [PATCH 558/703] CI: Create env & install Qt deps in one pass for further speedups --- .github/workflows/test.sh | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index ef88e560..5ccfbc10 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -12,22 +12,26 @@ if [ "${!QT_VERSION_VAR:0:3}" = "5.9" ]; then PYTEST_VERSION=">=6,!=7.0.0,!=7.0.1,<7.2.0" fi -mamba create -y -n test-env-${BINDING} python=${PYTHON_VERSION} pytest${PYTEST_VERSION:-">=6,!=7.0.0,!=7.0.1"} "pytest-cov>=3.0.0" pytest-qt${PYTESTQT_VERSION:-} -conda activate test-env-${BINDING} if [ "$USE_CONDA" = "Yes" ]; then if [ "${1}" = "pyqt5" ]; then - mamba install -y qt=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION} + QT_SPECS="qt=${PYQT5_QT_VERSION} pyqt=${PYQT5_VERSION}" elif [ "${1}" = "pyside2" ]; then - mamba install -y qt=${PYSIDE2_QT_VERSION} pyside2=${PYSIDE2_VERSION} + QT_SPECS="qt=${PYSIDE2_QT_VERSION} pyside2=${PYSIDE2_VERSION}" elif [ "${1}" = "pyside6" ]; then - mamba install -y qt6-main=${PYSIDE6_QT_VERSION} pyside6=${PYSIDE6_VERSION} + QT_SPECS="qt6-main=${PYSIDE6_QT_VERSION} pyside6=${PYSIDE6_VERSION}" else exit 1 fi -else +fi + +mamba create -y -n test-env-${BINDING} python=${PYTHON_VERSION} pytest${PYTEST_VERSION:->=6,!=7.0.0,!=7.0.1} pytest-cov>=3.0.0 pytest-qt${PYTESTQT_VERSION:-} ${QT_SPECS:-} + +conda activate test-env-${BINDING} + +if [ "$USE_CONDA" = "No" ]; then if [ "${1}" = "pyqt5" ]; then pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.* From 553338d443794c662bbe4a8ae8e287cf463535fe Mon Sep 17 00:00:00 2001 From: stsav012 Date: Mon, 10 Apr 2023 16:31:41 +0300 Subject: [PATCH 559/703] Parametrize tests, as @CAM-Gerlach suggested --- qtpy/tests/test_qtcore.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index c4bb83ec..5628aa86 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -26,30 +26,27 @@ def test_qtmsghandler(): assert QtCore.qInstallMessageHandler is not None -def test_QDateTime_toPython_and_toPyDateTime(): +@pytest.mark.parametrize('method', ['toPython', 'toPyDateTime']) +def test_QDateTime_toPython_and_toPyDateTime(method): """Test `QDateTime.toPython` and `QDateTime.toPyDateTime`""" q_datetime = QtCore.QDateTime(NOW) - py_date = q_datetime.toPython() - assert py_date == NOW - py_date = q_datetime.toPyDateTime() - assert py_date == NOW + py_datetime = getattr(q_datetime, method)() + assert py_datetime == NOW -def test_QDate_toPython_and_toPyDate(): +@pytest.mark.parametrize('method', ['toPython', 'toPyDate']) +def test_QDate_toPython_and_toPyDate(method): """Test `QDate.toPython` and `QDate.toPyDate`""" q_date = QtCore.QDateTime(NOW).date() - py_date = q_date.toPython() - assert py_date == NOW.date() - py_date = q_date.toPyDate() + py_date = getattr(q_date, method)() assert py_date == NOW.date() -def test_QTime_toPython_and_toPyTime(): +@pytest.mark.parametrize('method', ['toPython', 'toPyTime']) +def test_QTime_toPython_and_toPyTime(method): """Test `QTime.toPython` and `QTime.toPyTime`""" q_time = QtCore.QDateTime(NOW).time() - py_time = q_time.toPython() - assert py_time == NOW.time() - py_time = q_time.toPyTime() + py_time = getattr(q_time, method)() assert py_time == NOW.time() From 6ee0931475bc2c33c93d9365ed1d51b7a65f11a1 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Mon, 10 Apr 2023 16:32:17 +0300 Subject: [PATCH 560/703] Avoid double-assignment of a module-level constant, as @CAM-Gerlach suggested --- qtpy/tests/test_qtcore.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 5628aa86..659de9f9 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -16,9 +16,9 @@ ) from qtpy.tests.utils import not_using_conda -NOW = datetime.now() +_now = datetime.now() # Make integer milliseconds; `floor` here, don't `round`! -NOW = NOW.replace(microsecond=(NOW.microsecond // 1000 * 1000)) +NOW = _now.replace(microsecond=(_now.microsecond // 1000 * 1000)) def test_qtmsghandler(): From b3e831d1daa92adbcf5282ceb05817a7721d2e03 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Thu, 13 Apr 2023 10:31:08 +0300 Subject: [PATCH 561/703] Add `datetime.*` type checks, as @CAM-Gerlach suggested --- qtpy/tests/test_qtcore.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 659de9f9..de12b2ee 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,7 +1,7 @@ """Test QtCore.""" import sys -from datetime import datetime +from datetime import date, datetime, time import pytest @@ -31,6 +31,7 @@ def test_QDateTime_toPython_and_toPyDateTime(method): """Test `QDateTime.toPython` and `QDateTime.toPyDateTime`""" q_datetime = QtCore.QDateTime(NOW) py_datetime = getattr(q_datetime, method)() + assert isinstance(py_datetime, datetime) assert py_datetime == NOW @@ -39,6 +40,7 @@ def test_QDate_toPython_and_toPyDate(method): """Test `QDate.toPython` and `QDate.toPyDate`""" q_date = QtCore.QDateTime(NOW).date() py_date = getattr(q_date, method)() + assert isinstance(py_date, date) assert py_date == NOW.date() @@ -47,6 +49,7 @@ def test_QTime_toPython_and_toPyTime(method): """Test `QTime.toPython` and `QTime.toPyTime`""" q_time = QtCore.QDateTime(NOW).time() py_time = getattr(q_time, method)() + assert isinstance(py_time, time) assert py_time == NOW.time() From 92a225919f0a39ed9a4eccdda0fa25b424c70b5f Mon Sep 17 00:00:00 2001 From: Anton Yablokov Date: Thu, 13 Apr 2023 11:53:38 +0300 Subject: [PATCH 562/703] Approve style correction suggested by @CAM-Gerlach Co-authored-by: C.A.M. Gerlach --- qtpy/QtWidgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index f9f2aabd..a1bda7db 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -22,7 +22,7 @@ def __getattr__(name): def _possibly_static_exec(cls, *args, **kwargs): - """ Call `self.exec` when `self` is given or a static method otherwise. """ + """Call `self.exec` when `self` is given or a static method otherwise.""" if isinstance(args[0], cls): if len(args) == 1 and not kwargs: # A special case to avoid the function resolving error From 2bf89f8e722a193ea9c1fe29830145d7922b152e Mon Sep 17 00:00:00 2001 From: stsav012 Date: Thu, 13 Apr 2023 13:46:27 +0300 Subject: [PATCH 563/703] Call static `Q*Application.exec` when necessary --- qtpy/QtCore.py | 5 +++-- qtpy/QtGui.py | 6 +++--- qtpy/QtWidgets.py | 17 +++-------------- qtpy/tests/test_qtcore.py | 13 +++++++++++++ qtpy/tests/test_qtgui.py | 12 ++++++++++-- qtpy/tests/test_qtwidgets.py | 14 +++++++++++--- qtpy/utils.py | 11 +++++++++++ 7 files changed, 54 insertions(+), 24 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 46bca19c..74932d56 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -10,6 +10,7 @@ from typing import TYPE_CHECKING from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6 +from .utils import _possibly_static_exec if PYQT5: from PyQt5.QtCore import * @@ -54,7 +55,7 @@ pass # Map missing methods - QCoreApplication.exec_ = QCoreApplication.exec + QCoreApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QCoreApplication, *args, **kwargs) QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) @@ -105,7 +106,7 @@ Qt.MidButton = Qt.MiddleButton # Map DeprecationWarning methods - QCoreApplication.exec_ = QCoreApplication.exec + QCoreApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QCoreApplication, *args, **kwargs) QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 7a1623cf..f2579199 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -9,7 +9,7 @@ """Provides QtGui classes and functions.""" from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import getattr_missing_optional_dep +from .utils import _possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} @@ -65,7 +65,7 @@ def __getattr__(name): # Map missing/renamed methods QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QGuiApplication.exec_ = QGuiApplication.exec + QGuiApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QGuiApplication, *args, **kwargs) QTextDocument.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) # Allow unscoped access for enums inside the QtGui module @@ -105,7 +105,7 @@ def __getattr__(name): # Map DeprecationWarning methods QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QGuiApplication.exec_ = QGuiApplication.exec + QGuiApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QGuiApplication, *args, **kwargs) if PYSIDE2 or PYSIDE6: # PySide{2,6} do not accept the `mode` keyword argument in diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index a1bda7db..79271640 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -9,7 +9,7 @@ """Provides widget classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import getattr_missing_optional_dep +from .utils import _possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} @@ -21,17 +21,6 @@ def __getattr__(name): name, module_name=__name__, optional_names=_missing_optional_names) -def _possibly_static_exec(cls, *args, **kwargs): - """Call `self.exec` when `self` is given or a static method otherwise.""" - if isinstance(args[0], cls): - if len(args) == 1 and not kwargs: - # A special case to avoid the function resolving error - return args[0].exec() - return args[0].exec(*args[1:], **kwargs) - else: - return cls.exec(*args, **kwargs) - - if PYQT5: from PyQt5.QtWidgets import * elif PYQT6: @@ -58,7 +47,7 @@ def _possibly_static_exec(cls, *args, **kwargs): QPlainTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) QPlainTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) QPlainTextEdit.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) - QApplication.exec_ = QApplication.exec + QApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QApplication, *args, **kwargs) QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda *args, **kwargs: _possibly_static_exec(QMenu, *args, **kwargs) QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom()) @@ -93,6 +82,6 @@ def _possibly_static_exec(cls, *args, **kwargs): QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom()) # Map DeprecationWarning methods - QApplication.exec_ = QApplication.exec + QApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QApplication, *args, **kwargs) QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda *args, **kwargs: _possibly_static_exec(QMenu, *args, **kwargs) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 80acfd9f..ae8d8152 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -76,6 +76,19 @@ def test_QLibraryInfo_LibraryLocation_and_LibraryPath(): assert QtCore.QLibraryInfo.LibraryPath is not None +def test_QCoreApplication_exec_(): + """Test `QtCore.QCoreApplication.exec_`""" + assert QtCore.QCoreApplication.exec_ is not None + app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication([sys.executable, __file__]) + assert app is not None + QtCore.QTimer.singleShot(100, QtCore.QCoreApplication.instance().quit) + QtCore.QCoreApplication.exec_() + app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication([sys.executable, __file__]) + assert app is not None + QtCore.QTimer.singleShot(100, QtCore.QCoreApplication.instance().quit) + app.exec_() + + @pytest.mark.skipif(PYQT5 or PYQT6, reason="Doesn't seem to be present on PyQt5 and PyQt6") def test_qtextstreammanipulator_exec_(): diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 8b05b64c..e971bcc8 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -34,9 +34,17 @@ def test_qdrag_functions(qtbot): drag.exec_() -def test_qguiapplication_functions(): - """Test functions mapping for QtGui.QGuiApplication.""" +def test_QGuiApplication_exec_(): + """Test `QtGui.QGuiApplication._exec_`""" assert QtGui.QGuiApplication.exec_ is not None + app = QtGui.QGuiApplication.instance() or QtGui.QGuiApplication([sys.executable, __file__]) + assert app is not None + QtCore.QTimer.singleShot(100, QtGui.QGuiApplication.instance().quit) + QtGui.QGuiApplication.exec_() + app = QtGui.QGuiApplication.instance() or QtGui.QGuiApplication([sys.executable, __file__]) + assert app is not None + QtCore.QTimer.singleShot(100, QtGui.QGuiApplication.instance().quit) + app.exec_() def test_what_moved_to_qtgui_in_qt6(): diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index c1cab5d3..ce25f460 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -53,9 +53,17 @@ def test_qplaintextedit_functions(qtbot, pdf_writer): assert output_path.exists() -def test_qapplication_functions(): - """Test functions mapping for QtWidgets.QApplication.""" - assert QtWidgets.QApplication.exec_ +def test_QApplication_exec_(): + """Test `QtWidgets.QApplication._exec_`""" + assert QtWidgets.QApplication.exec_ is not None + app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([sys.executable, __file__]) + assert app is not None + QtCore.QTimer.singleShot(100, QtWidgets.QApplication.instance().quit) + QtWidgets.QApplication.exec_() + app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([sys.executable, __file__]) + assert app is not None + QtCore.QTimer.singleShot(100, QtWidgets.QApplication.instance().quit) + app.exec_() @pytest.mark.skipif( diff --git a/qtpy/utils.py b/qtpy/utils.py index 0bf5e79b..8f04e610 100644 --- a/qtpy/utils.py +++ b/qtpy/utils.py @@ -30,3 +30,14 @@ def getattr_missing_optional_dep(name, module_name, optional_names): if name in optional_names: return _wrap_missing_optional_dep_error(attr_error, **optional_names[name]) return attr_error + + +def _possibly_static_exec(cls, *args, **kwargs): + """Call `self.exec` when `self` is given or a static method otherwise.""" + if isinstance(args[0], cls): + if len(args) == 1 and not kwargs: + # A special case to avoid the function resolving error + return args[0].exec() + return args[0].exec(*args[1:], **kwargs) + else: + return cls.exec(*args, **kwargs) From b3705bc38232c899b1ab170414b7bda12a2988f2 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Thu, 13 Apr 2023 13:55:27 +0300 Subject: [PATCH 564/703] Fix the error when an `exec_` has no arguments at all --- qtpy/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qtpy/utils.py b/qtpy/utils.py index 8f04e610..38f96674 100644 --- a/qtpy/utils.py +++ b/qtpy/utils.py @@ -34,9 +34,12 @@ def getattr_missing_optional_dep(name, module_name, optional_names): def _possibly_static_exec(cls, *args, **kwargs): """Call `self.exec` when `self` is given or a static method otherwise.""" + if not args and not kwargs: + # A special case (`cls.exec_()`) to avoid the function resolving error + return cls.exec() if isinstance(args[0], cls): if len(args) == 1 and not kwargs: - # A special case to avoid the function resolving error + # A special case (`self.exec_()`) to avoid the function resolving error return args[0].exec() return args[0].exec(*args[1:], **kwargs) else: From ffcea7c3ed13689bd8390db46b343de2b8afc789 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Thu, 13 Apr 2023 14:09:09 +0300 Subject: [PATCH 565/703] Skip tests for `QGuiApplication.exec_` and `QApplication.exec_` with `conda=No` --- qtpy/tests/test_qtgui.py | 5 ++++- qtpy/tests/test_qtwidgets.py | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index e971bcc8..2653e412 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -34,8 +34,11 @@ def test_qdrag_functions(qtbot): drag.exec_() +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_QGuiApplication_exec_(): - """Test `QtGui.QGuiApplication._exec_`""" + """Test `QtGui.QGuiApplication.exec_`""" assert QtGui.QGuiApplication.exec_ is not None app = QtGui.QGuiApplication.instance() or QtGui.QGuiApplication([sys.executable, __file__]) assert app is not None diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index ce25f460..b04f72b4 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -53,8 +53,11 @@ def test_qplaintextedit_functions(qtbot, pdf_writer): assert output_path.exists() +@pytest.mark.skipif( + sys.platform.startswith('linux') and not_using_conda(), + reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_QApplication_exec_(): - """Test `QtWidgets.QApplication._exec_`""" + """Test `QtWidgets.QApplication.exec_`""" assert QtWidgets.QApplication.exec_ is not None app = QtWidgets.QApplication.instance() or QtWidgets.QApplication([sys.executable, __file__]) assert app is not None From b59f7c0ba2803660df63ef158751fa0b31fefd3a Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 14 Apr 2023 20:50:18 +0300 Subject: [PATCH 566/703] Remove underscore prefix from `possibly_static_exec` --- qtpy/QtCore.py | 6 +++--- qtpy/QtGui.py | 6 +++--- qtpy/QtWidgets.py | 10 +++++----- qtpy/utils.py | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 74932d56..8e833028 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6 -from .utils import _possibly_static_exec +from .utils import possibly_static_exec if PYQT5: from PyQt5.QtCore import * @@ -55,7 +55,7 @@ pass # Map missing methods - QCoreApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QCoreApplication, *args, **kwargs) + QCoreApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QCoreApplication, *args, **kwargs) QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) @@ -106,7 +106,7 @@ Qt.MidButton = Qt.MiddleButton # Map DeprecationWarning methods - QCoreApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QCoreApplication, *args, **kwargs) + QCoreApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QCoreApplication, *args, **kwargs) QEventLoop.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QTextStreamManipulator.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index f2579199..df634bb4 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -9,7 +9,7 @@ """Provides QtGui classes and functions.""" from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import _possibly_static_exec, getattr_missing_optional_dep +from .utils import possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} @@ -65,7 +65,7 @@ def __getattr__(name): # Map missing/renamed methods QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QGuiApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QGuiApplication, *args, **kwargs) + QGuiApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QGuiApplication, *args, **kwargs) QTextDocument.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) # Allow unscoped access for enums inside the QtGui module @@ -105,7 +105,7 @@ def __getattr__(name): # Map DeprecationWarning methods QDrag.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QGuiApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QGuiApplication, *args, **kwargs) + QGuiApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QGuiApplication, *args, **kwargs) if PYSIDE2 or PYSIDE6: # PySide{2,6} do not accept the `mode` keyword argument in diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 79271640..54c3ffa2 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -9,7 +9,7 @@ """Provides widget classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import _possibly_static_exec, getattr_missing_optional_dep +from .utils import possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} @@ -47,9 +47,9 @@ def __getattr__(name): QPlainTextEdit.setTabStopWidth = lambda self, *args, **kwargs: self.setTabStopDistance(*args, **kwargs) QPlainTextEdit.tabStopWidth = lambda self, *args, **kwargs: self.tabStopDistance(*args, **kwargs) QPlainTextEdit.print_ = lambda self, *args, **kwargs: self.print(*args, **kwargs) - QApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QApplication, *args, **kwargs) + QApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QApplication, *args, **kwargs) QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QMenu.exec_ = lambda *args, **kwargs: _possibly_static_exec(QMenu, *args, **kwargs) + QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec(QMenu, *args, **kwargs) QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom()) # Allow unscoped access for enums inside the QtWidgets module @@ -82,6 +82,6 @@ def __getattr__(name): QLineEdit.getTextMargins = lambda self: (self.textMargins().left(), self.textMargins().top(), self.textMargins().right(), self.textMargins().bottom()) # Map DeprecationWarning methods - QApplication.exec_ = lambda *args, **kwargs: _possibly_static_exec(QApplication, *args, **kwargs) + QApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QApplication, *args, **kwargs) QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) - QMenu.exec_ = lambda *args, **kwargs: _possibly_static_exec(QMenu, *args, **kwargs) + QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec(QMenu, *args, **kwargs) diff --git a/qtpy/utils.py b/qtpy/utils.py index 38f96674..c009f17b 100644 --- a/qtpy/utils.py +++ b/qtpy/utils.py @@ -32,7 +32,7 @@ def getattr_missing_optional_dep(name, module_name, optional_names): return attr_error -def _possibly_static_exec(cls, *args, **kwargs): +def possibly_static_exec(cls, *args, **kwargs): """Call `self.exec` when `self` is given or a static method otherwise.""" if not args and not kwargs: # A special case (`cls.exec_()`) to avoid the function resolving error From 7acfb532b8f22a29dea2d8a83ffa0a958e1e6932 Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 14 Apr 2023 20:54:51 +0300 Subject: [PATCH 567/703] Clean up imports --- qtpy/QtCore.py | 5 +++++ qtpy/QtGui.py | 16 +++++++++++----- qtpy/QtWidgets.py | 8 +++++++- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 8e833028..59830f9a 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -118,3 +118,8 @@ if PYQT6 or PYSIDE6: QLibraryInfo.location = QLibraryInfo.path QLibraryInfo.LibraryLocation = QLibraryInfo.LibraryPath + +# Delete imported items, which are not the part of QtCore +del TYPE_CHECKING +del PYQT6, PYQT5, PYSIDE2, PYSIDE6 +del possibly_static_exec diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index df634bb4..34ad2109 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -9,7 +9,7 @@ """Provides QtGui classes and functions.""" from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import possibly_static_exec, getattr_missing_optional_dep +from .utils import possibly_static_exec _missing_optional_names = {} @@ -32,8 +32,11 @@ 'QOpenGLWindow', } + def __getattr__(name): """Custom getattr to chain and wrap errors due to missing optional deps.""" + from .utils import getattr_missing_optional_dep + raise getattr_missing_optional_dep( name, module_name=__name__, optional_names=_missing_optional_names) @@ -53,8 +56,8 @@ def __getattr__(name): try: from PyQt6.QtOpenGL import * except ImportError as error: - for name in _QTOPENGL_NAMES: - _missing_optional_names[name] = { + for _name in _QTOPENGL_NAMES: + _missing_optional_names[_name] = { 'name': 'PyQt6.QtOpenGL', 'missing_package': 'pyopengl', 'import_error': error, @@ -90,8 +93,8 @@ def __getattr__(name): try: from PySide6.QtOpenGL import * except ImportError as error: - for name in _QTOPENGL_NAMES: - _missing_optional_names[name] = { + for _name in _QTOPENGL_NAMES: + _missing_optional_names[_name] = { 'name': 'PySide6.QtOpenGL', 'missing_package': 'pyopengl', 'import_error': error, @@ -175,3 +178,6 @@ def movePositionPatched( QSinglePointEvent.globalX = lambda self: self.globalPosition().toPoint().x() QSinglePointEvent.globalY = lambda self: self.globalPosition().toPoint().y() +# Delete imported items, which are not the part of QtGui +del PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError +del possibly_static_exec diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 54c3ffa2..f25c82c9 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -9,7 +9,7 @@ """Provides widget classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import possibly_static_exec, getattr_missing_optional_dep +from .utils import possibly_static_exec _missing_optional_names = {} @@ -17,6 +17,8 @@ def __getattr__(name): """Custom getattr to chain and wrap errors due to missing optional deps.""" + from .utils import getattr_missing_optional_dep + raise getattr_missing_optional_dep( name, module_name=__name__, optional_names=_missing_optional_names) @@ -85,3 +87,7 @@ def __getattr__(name): QApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QApplication, *args, **kwargs) QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec(QMenu, *args, **kwargs) + +# Delete imported items, which are not the part of QtWidgets +del PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError +del possibly_static_exec From 8cbff62d79e4867e8f1f53fccb65cdc72bf9d2cc Mon Sep 17 00:00:00 2001 From: stsav012 Date: Fri, 14 Apr 2023 21:07:51 +0300 Subject: [PATCH 568/703] Revert "Clean up imports" This reverts commit 7acfb532b8f22a29dea2d8a83ffa0a958e1e6932. --- qtpy/QtCore.py | 5 ----- qtpy/QtGui.py | 16 +++++----------- qtpy/QtWidgets.py | 8 +------- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 59830f9a..8e833028 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -118,8 +118,3 @@ if PYQT6 or PYSIDE6: QLibraryInfo.location = QLibraryInfo.path QLibraryInfo.LibraryLocation = QLibraryInfo.LibraryPath - -# Delete imported items, which are not the part of QtCore -del TYPE_CHECKING -del PYQT6, PYQT5, PYSIDE2, PYSIDE6 -del possibly_static_exec diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index 34ad2109..df634bb4 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -9,7 +9,7 @@ """Provides QtGui classes and functions.""" from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import possibly_static_exec +from .utils import possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} @@ -32,11 +32,8 @@ 'QOpenGLWindow', } - def __getattr__(name): """Custom getattr to chain and wrap errors due to missing optional deps.""" - from .utils import getattr_missing_optional_dep - raise getattr_missing_optional_dep( name, module_name=__name__, optional_names=_missing_optional_names) @@ -56,8 +53,8 @@ def __getattr__(name): try: from PyQt6.QtOpenGL import * except ImportError as error: - for _name in _QTOPENGL_NAMES: - _missing_optional_names[_name] = { + for name in _QTOPENGL_NAMES: + _missing_optional_names[name] = { 'name': 'PyQt6.QtOpenGL', 'missing_package': 'pyopengl', 'import_error': error, @@ -93,8 +90,8 @@ def __getattr__(name): try: from PySide6.QtOpenGL import * except ImportError as error: - for _name in _QTOPENGL_NAMES: - _missing_optional_names[_name] = { + for name in _QTOPENGL_NAMES: + _missing_optional_names[name] = { 'name': 'PySide6.QtOpenGL', 'missing_package': 'pyopengl', 'import_error': error, @@ -178,6 +175,3 @@ def movePositionPatched( QSinglePointEvent.globalX = lambda self: self.globalPosition().toPoint().x() QSinglePointEvent.globalY = lambda self: self.globalPosition().toPoint().y() -# Delete imported items, which are not the part of QtGui -del PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -del possibly_static_exec diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index f25c82c9..54c3ffa2 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -9,7 +9,7 @@ """Provides widget classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import possibly_static_exec +from .utils import possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} @@ -17,8 +17,6 @@ def __getattr__(name): """Custom getattr to chain and wrap errors due to missing optional deps.""" - from .utils import getattr_missing_optional_dep - raise getattr_missing_optional_dep( name, module_name=__name__, optional_names=_missing_optional_names) @@ -87,7 +85,3 @@ def __getattr__(name): QApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QApplication, *args, **kwargs) QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec(QMenu, *args, **kwargs) - -# Delete imported items, which are not the part of QtWidgets -del PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -del possibly_static_exec From 59414bac20a6504bc2627098f68cc98bd64a30d7 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 14 Apr 2023 17:38:01 -0500 Subject: [PATCH 569/703] CI: Remove basedir flag for coverage command --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10d20c53..53c07afa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,4 +130,4 @@ jobs: run: | cd temp_test_dir # Switch to test working dir per non-src-layout hack cat qtpy_basedir.txt - pipx run coveralls --service=github --rcfile="../.coveragerc" --basedir="$(cat qtpy_basedir.txt)" + pipx run coveralls --service=github --rcfile="../.coveragerc" From b9f4fcc9fdfbac09bdc5dbd04f4957e8888014c6 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 14 Apr 2023 17:55:31 -0500 Subject: [PATCH 570/703] Testing --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 53c07afa..b32c4048 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -130,4 +130,4 @@ jobs: run: | cd temp_test_dir # Switch to test working dir per non-src-layout hack cat qtpy_basedir.txt - pipx run coveralls --service=github --rcfile="../.coveragerc" + pipx run coveralls --service=github --rcfile="../.coveragerc" --basedir="../qtpy" From 7c20c2d0c68aae4ce0c01d9225452eeaf37174b0 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 14 Apr 2023 18:19:09 -0500 Subject: [PATCH 571/703] Testing --- .github/workflows/ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b32c4048..b07a5a77 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,9 +125,19 @@ jobs: - name: Upload coverage data to coveralls.io shell: bash -e {0} env: + COVERALLS_PARALLEL: true COVERALLS_FLAG_NAME: ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | cd temp_test_dir # Switch to test working dir per non-src-layout hack cat qtpy_basedir.txt pipx run coveralls --service=github --rcfile="../.coveragerc" --basedir="../qtpy" + finish: + needs: test + if: ${{ always() }} + runs-on: ubuntu-latest + steps: + - name: Coveralls Finished + uses: coverallsapp/github-action@v2 + with: + parallel-finished: true \ No newline at end of file From 5319ed4a32a08b64faacd6da87c4f225fac4e0c1 Mon Sep 17 00:00:00 2001 From: Philipp Temminghoff Date: Sat, 8 Apr 2023 03:40:39 +0200 Subject: [PATCH 572/703] add QEnum macro for PyQt bindings --- qtpy/QtCore.py | 9 ++++++++- qtpy/tests/test_qtcore.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index e6798ba8..2a98af8c 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -17,6 +17,12 @@ from PyQt5.QtCore import pyqtBoundSignal as SignalInstance from PyQt5.QtCore import pyqtSlot as Slot from PyQt5.QtCore import pyqtProperty as Property + try: + from PyQt5.QtCore import Q_ENUM as QEnum + del Q_ENUM + except ImportError: # fallback for Qt5.9 + from PyQt5.QtCore import Q_ENUMS as QEnum + del Q_ENUMS from PyQt5.QtCore import QT_VERSION_STR as __version__ # Those are imported from `import *` @@ -29,6 +35,7 @@ from PyQt6.QtCore import pyqtBoundSignal as SignalInstance from PyQt6.QtCore import pyqtSlot as Slot from PyQt6.QtCore import pyqtProperty as Property + from PyQt6.QtCore import pyqtEnum as QEnum from PyQt6.QtCore import QT_VERSION_STR as __version__ # For issue #311 @@ -47,7 +54,7 @@ QThread.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) # Those are imported from `import *` - del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, QT_VERSION_STR + del pyqtSignal, pyqtBoundSignal, pyqtSlot, pyqtProperty, pyqtEnum, QT_VERSION_STR # Allow unscoped access for enums inside the QtCore module from .enums_compat import promote_enums diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index de12b2ee..689100c4 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -1,5 +1,6 @@ """Test QtCore.""" +import enum import sys from datetime import date, datetime, time @@ -69,6 +70,21 @@ def test_qthread_exec_(): assert QtCore.QThread.exec_ is not None +def test_qenum(): + """Test QEnum macro""" + class EnumTest(QtCore.QObject): + class Position(enum.IntEnum): + West = 0 + North = 1 + South = 2 + East = 3 + + QtCore.QEnum(Position) + + obj = EnumTest() + assert obj.metaObject().enumerator(0).name() == "Position" + + def test_QLibraryInfo_location_and_path(): """Test `QLibraryInfo.location` and `QLibraryInfo.path`""" assert QtCore.QLibraryInfo.location is not None From ed4715f8b8d7251436181952b27ff8ed0c357b4f Mon Sep 17 00:00:00 2001 From: stsav012 Date: Wed, 17 May 2023 18:01:17 +0300 Subject: [PATCH 573/703] =?UTF-8?q?Rename=20`utils.py`=20=E2=86=92=20`=5Fu?= =?UTF-8?q?tils.py`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- qtpy/QtCore.py | 2 +- qtpy/QtGui.py | 2 +- qtpy/QtWidgets.py | 2 +- qtpy/{utils.py => _utils.py} | 0 qtpy/tests/optional_deps/__init__.py | 2 +- 5 files changed, 4 insertions(+), 4 deletions(-) rename qtpy/{utils.py => _utils.py} (100%) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 12307e65..1d6b1b9d 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6 -from .utils import possibly_static_exec +from ._utils import possibly_static_exec if PYQT5: from PyQt5.QtCore import * diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index df634bb4..d5490997 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -9,7 +9,7 @@ """Provides QtGui classes and functions.""" from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import possibly_static_exec, getattr_missing_optional_dep +from ._utils import possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 54c3ffa2..8ca6c94e 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -9,7 +9,7 @@ """Provides widget classes and functions.""" from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from .utils import possibly_static_exec, getattr_missing_optional_dep +from ._utils import possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} diff --git a/qtpy/utils.py b/qtpy/_utils.py similarity index 100% rename from qtpy/utils.py rename to qtpy/_utils.py diff --git a/qtpy/tests/optional_deps/__init__.py b/qtpy/tests/optional_deps/__init__.py index a209c6b0..27c8c4c9 100644 --- a/qtpy/tests/optional_deps/__init__.py +++ b/qtpy/tests/optional_deps/__init__.py @@ -4,7 +4,7 @@ # See https://github.com/spyder-ide/qtpy/pull/387/ -from qtpy.utils import getattr_missing_optional_dep +from qtpy._utils import getattr_missing_optional_dep from .optional_dep import ExampleClass From 0b059d7ffc6bb557cf7f49496862faa1d36bfa51 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sat, 3 Jun 2023 17:04:04 +0200 Subject: [PATCH 574/703] enable more test on CI --- .github/workflows/ci.yml | 1 + qtpy/tests/test_qtcore.py | 3 --- qtpy/tests/test_qtgui.py | 15 --------------- qtpy/tests/test_qtprintsupport.py | 5 ----- qtpy/tests/test_qtwidgets.py | 19 ------------------- qtpy/tests/test_uic.py | 10 ---------- 6 files changed, 1 insertion(+), 52 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 10d20c53..f4bd7284 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -95,6 +95,7 @@ jobs: run: | sudo apt update sudo apt install libpulse-dev libegl1-mesa libopengl0 gstreamer1.0-gl + - uses: tlambert03/setup-qt-libs@v1 - name: Install Conda uses: conda-incubator/setup-miniconda@v2 with: diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 3b748724..ec18b784 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -53,9 +53,6 @@ def test_QTime_toPython_and_toPyTime(method): assert py_time == NOW.time() -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qeventloop_exec_(qtbot): """Test QEventLoop.exec_""" assert QtCore.QEventLoop.exec_ is not None diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 2653e412..380c2367 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -8,9 +8,6 @@ from qtpy.tests.utils import not_using_conda -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qfontmetrics_width(qtbot): """Test QFontMetrics and QFontMetricsF width""" assert QtGui.QFontMetrics.width is not None @@ -24,9 +21,6 @@ def test_qfontmetrics_width(qtbot): assert 39 <= widthF <= 63 -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qdrag_functions(qtbot): """Test functions mapping for QtGui.QDrag.""" assert QtGui.QDrag.exec_ is not None @@ -34,9 +28,6 @@ def test_qdrag_functions(qtbot): drag.exec_() -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_QGuiApplication_exec_(): """Test `QtGui.QGuiApplication.exec_`""" assert QtGui.QGuiApplication.exec_ is not None @@ -59,9 +50,6 @@ def test_what_moved_to_qtgui_in_qt6(): assert QtGui.QUndoCommand is not None -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Segmentation fault/Aborted on Linux CI when not using conda") def test_qtextdocument_functions(pdf_writer): """Test functions mapping for QtGui.QTextDocument.""" assert QtGui.QTextDocument.print_ is not None @@ -82,9 +70,6 @@ def test_enum_access(): assert QtGui.QImage.Format_Invalid == QtGui.QImage.Format.Format_Invalid -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), reason="Stalls on macOS CI with Python 3.7") diff --git a/qtpy/tests/test_qtprintsupport.py b/qtpy/tests/test_qtprintsupport.py index 952909fb..6a36aa16 100644 --- a/qtpy/tests/test_qtprintsupport.py +++ b/qtpy/tests/test_qtprintsupport.py @@ -5,7 +5,6 @@ import pytest from qtpy import QtPrintSupport -from qtpy.tests.utils import not_using_conda def test_qtprintsupport(): @@ -30,10 +29,6 @@ def test_qprintdialog_exec_(): assert QtPrintSupport.QPrintDialog.exec_ is not None -@pytest.mark.skipif( - sys.platform.startswith("linux") and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda", -) def test_qprintpreviewwidget_print_(qtbot): """Test qtpy.QtPrintSupport.QPrintPreviewWidget print_""" assert QtPrintSupport.QPrintPreviewWidget.print_ is not None diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index b04f72b4..be04f0a0 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -4,12 +4,8 @@ import pytest from qtpy import PYQT5, PYQT_VERSION, QtCore, QtGui, QtWidgets -from qtpy.tests.utils import using_conda, not_using_conda -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qtextedit_functions(qtbot, pdf_writer): """Test functions mapping for QtWidgets.QTextEdit.""" assert QtWidgets.QTextEdit.setTabStopWidth @@ -37,9 +33,6 @@ def test_what_moved_to_qtgui_in_qt6(): assert QtWidgets.QUndoCommand is not None -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_qplaintextedit_functions(qtbot, pdf_writer): """Test functions mapping for QtWidgets.QPlainTextEdit.""" assert QtWidgets.QPlainTextEdit.setTabStopWidth @@ -53,9 +46,6 @@ def test_qplaintextedit_functions(qtbot, pdf_writer): assert output_path.exists() -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") def test_QApplication_exec_(): """Test `QtWidgets.QApplication.exec_`""" assert QtWidgets.QApplication.exec_ is not None @@ -69,9 +59,6 @@ def test_QApplication_exec_(): app.exec_() -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), reason="Stalls on macOS CI with Python 3.7") @@ -83,9 +70,6 @@ def test_qdialog_functions(qtbot): dialog.exec_() -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), reason="Stalls on macOS CI with Python 3.7") @@ -102,9 +86,6 @@ def __init__(self): dialog.exec_() -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Fatal Python error: Aborted on Linux CI when not using conda") @pytest.mark.skipif( sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), reason="Stalls on macOS CI with Python 3.7") diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 1d9a791f..578093e3 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -12,7 +12,6 @@ pytest.importorskip("pyside2uic", reason="pyside2uic not installed") from qtpy import uic -from qtpy.tests.utils import not_using_conda QCOMBOBOX_SUBCLASS = """ @@ -42,9 +41,6 @@ def enabled_qcombobox_subclass(temp_dir_path): sys.path.pop(0) -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Segfaults on Linux when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") def test_load_ui(qtbot): """ Make sure that the patched loadUi function behaves as expected with a @@ -62,9 +58,6 @@ def test_load_ui(qtbot): @pytest.mark.skipif( PYSIDE2 or PYSIDE6, reason="PySide2uic not consistently installed across platforms/versions") -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Segfaults on Linux when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") def test_load_ui_type(qtbot): """ Make sure that the patched loadUiType function behaves as expected with a @@ -88,9 +81,6 @@ def __init__(self): assert isinstance(ui.comboBox, QComboBox) -@pytest.mark.skipif( - sys.platform.startswith('linux') and not_using_conda(), - reason="Segfaults on Linux when not using conda under all bindings (PYSIDE2/6 & PYQT5/6)") def test_load_ui_custom_auto(qtbot, tmp_path): """ Test that we can load a .ui file with custom widgets without having to From ff85334586800740e9212bf0215607ea42d27082 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sat, 3 Jun 2023 17:20:10 +0200 Subject: [PATCH 575/703] try uncomment additional test --- qtpy/tests/test_qtmultimedia.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 5da7ea4b..379a1ad5 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -2,13 +2,7 @@ import pytest -from qtpy import PYSIDE6, PYQT6 - -@pytest.mark.skipif( - sys.platform.startswith("linux") and (PYSIDE6 or PYQT6), - reason="Needs to setup GStreamer on Linux", -) def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" from qtpy import QtMultimedia From f80ce128c13dac1d321e12b8fff1c0afee70053e Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sat, 3 Jun 2023 17:22:46 +0200 Subject: [PATCH 576/703] add concurecy calculation --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4bd7284..5f1ffcc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,10 @@ on: - main - '*.x' +concurrency: + group: test-${{ github.ref }} + cancel-in-progress: true + jobs: test: name: Test ${{ matrix.os }} Python ${{ matrix.python-version }} conda=${{ matrix.use-conda }} From 18e1671d293a043e615e6e96da0a69aabaf8fa86 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sat, 3 Jun 2023 17:25:21 +0200 Subject: [PATCH 577/703] fix import --- qtpy/tests/test_qtmultimedia.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/tests/test_qtmultimedia.py b/qtpy/tests/test_qtmultimedia.py index 379a1ad5..29e78ae5 100644 --- a/qtpy/tests/test_qtmultimedia.py +++ b/qtpy/tests/test_qtmultimedia.py @@ -2,6 +2,8 @@ import pytest +from qtpy import PYSIDE6, PYQT6 + def test_qtmultimedia(): """Test the qtpy.QtMultimedia namespace""" From 3ee0b4ff75210d89c4c7ce3760fbc6f31bc9267c Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Mon, 5 Jun 2023 15:15:08 +0000 Subject: [PATCH 578/703] enable test_isalive --- qtpy/tests/test_compat.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qtpy/tests/test_compat.py b/qtpy/tests/test_compat.py index 856f20bc..7ca9d1de 100644 --- a/qtpy/tests/test_compat.py +++ b/qtpy/tests/test_compat.py @@ -7,10 +7,8 @@ @pytest.mark.skipif( ((sys.version_info.major == 3 and sys.version_info.minor == 7) - and sys.platform.startswith('win') and not not_using_conda()) - or - (sys.platform.startswith('linux') and not_using_conda()), - reason="sip not included in Python3.7 on Windows, or in non-conda test suite on Linux" + and sys.platform.startswith('win') and not not_using_conda()), + reason="sip not included in Python3.7 on Windows" ) def test_isalive(qtbot): """Test compat.isalive""" From f04fe62b2abee8ddc450fbd66ca4d60e5e32d906 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Sun, 25 Jun 2023 23:42:34 +0200 Subject: [PATCH 579/703] Add compat layer for QFileDialog add tests --- .github/workflows/ci.yml | 4 ++ qtpy/QtWidgets.py | 29 ++++++++++++++ qtpy/tests/test_qtcore.py | 2 +- qtpy/tests/test_qtwidgets.py | 78 +++++++++++++++++++++++++++++++++++- 4 files changed, 111 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f1ffcc7..f2573ccd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,6 +100,10 @@ jobs: sudo apt update sudo apt install libpulse-dev libegl1-mesa libopengl0 gstreamer1.0-gl - uses: tlambert03/setup-qt-libs@v1 + - name: Install Linux system packages + if: contains(matrix.os, 'ubuntu') + shell: bash + run: sudo apt install libpulse-dev libegl1-mesa libopengl0 gstreamer1.0-gl - name: Install Conda uses: conda-incubator/setup-miniconda@v2 with: diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 54c3ffa2..3309a3fa 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -7,6 +7,7 @@ # ----------------------------------------------------------------------------- """Provides widget classes and functions.""" +from functools import wraps from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError from .utils import possibly_static_exec, getattr_missing_optional_dep @@ -20,6 +21,22 @@ def __getattr__(name): raise getattr_missing_optional_dep( name, module_name=__name__, optional_names=_missing_optional_names) +def _dir_to_directory(func): + @wraps(func) + def _dir_to_directory_(*args, **kwargs): + if "dir" in kwargs: + kwargs["directory"] = kwargs.pop("dir") + return func(*args, **kwargs) + return _dir_to_directory_ + +def _directory_to_dir(func): + @wraps(func) + def _directory_to_dir_(*args, **kwargs): + if "directory" in kwargs: + kwargs["dir"] = kwargs.pop("directory") + return func(*args, **kwargs) + return _directory_to_dir_ + if PYQT5: from PyQt5.QtWidgets import * @@ -85,3 +102,15 @@ def __getattr__(name): QApplication.exec_ = lambda *args, **kwargs: possibly_static_exec(QApplication, *args, **kwargs) QDialog.exec_ = lambda self, *args, **kwargs: self.exec(*args, **kwargs) QMenu.exec_ = lambda *args, **kwargs: possibly_static_exec(QMenu, *args, **kwargs) + + +if PYSIDE2 or PYSIDE6: + QFileDialog.getExistingDirectory = _directory_to_dir(QFileDialog.getExistingDirectory) + QFileDialog.getOpenFileName = _directory_to_dir(QFileDialog.getOpenFileName) + QFileDialog.getOpenFileNames = _directory_to_dir(QFileDialog.getOpenFileNames) + QFileDialog.getSaveFileName = _directory_to_dir(QFileDialog.getSaveFileName) +else: + QFileDialog.getExistingDirectory = _dir_to_directory(QFileDialog.getExistingDirectory) + QFileDialog.getOpenFileName = _dir_to_directory(QFileDialog.getOpenFileName) + QFileDialog.getOpenFileNames = _dir_to_directory(QFileDialog.getOpenFileNames) + QFileDialog.getSaveFileName = _dir_to_directory(QFileDialog.getSaveFileName) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index ec18b784..50a1be3a 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -80,7 +80,7 @@ def test_QLibraryInfo_LibraryLocation_and_LibraryPath(): assert QtCore.QLibraryInfo.LibraryPath is not None -def test_QCoreApplication_exec_(): +def test_QCoreApplication_exec_(qapp): """Test `QtCore.QCoreApplication.exec_`""" assert QtCore.QCoreApplication.exec_ is not None app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication([sys.executable, __file__]) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index be04f0a0..1ea816e5 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -1,9 +1,14 @@ """Test QtWidgets.""" +import contextlib import sys +from time import sleep import pytest -from qtpy import PYQT5, PYQT_VERSION, QtCore, QtGui, QtWidgets +from qtpy import PYQT5, PYQT_VERSION, QtCore, QtGui, QtWidgets, PYQT6, PYSIDE6, PYSIDE2 +from qtpy.tests.utils import using_conda, not_using_conda + +from pytestqt.exceptions import TimeoutError def test_qtextedit_functions(qtbot, pdf_writer): @@ -130,3 +135,74 @@ def test_opengl_imports(): QtPy makes QOpenGLWidget available in QtWidgets to maintain compatibility. """ assert QtWidgets.QOpenGLWidget is not None + +@pytest.mark.skipif(sys.platform == 'darwin' and sys.version_info[:2] == (3, 7) and (PYQT5 or PYSIDE2), reason="test crashes on macOS CI with Python 3.7 with 'Illegal instruction: 4'") +@pytest.mark.parametrize("keyword", ["dir", "directory"]) +@pytest.mark.parametrize("instance", [True, False]) +def test_qfiledialog_compat(tmp_path, qtbot, keyword, instance): + """ + This function is testing if the decorators that renamed keyword are working. + It mas stop working if the Qt bindings do some overwriting of the methods in constructor. + It should not happen, but we already meet that PySide team did similar things in the past + (like overwriting enum module in PySide6==6.3.2). + + keyword: str + The keyword that should be used in the function call. + instance: bool + If True, the function is called on the instance of the QFileDialog, otherwise on the class. + """ + + class CloseThread(QtCore.QThread): + """ + On some implementations the `getExistingDirectory` functions starts own + event loop that will not trigger QTimer started before the call. Until the + dialog is closed the main event loop will be stopped. + + Because of this it is required to use the thread to interact with the dialog. + """ + def run(self, allow_restart=True): + sleep(0.5) + need_restart = allow_restart + app = QtWidgets.QApplication.instance() + for dlg in app.topLevelWidgets(): + if not isinstance(dlg, QtWidgets.QFileDialog) or dlg.isHidden(): + continue + # when implement this I try to use: + # * dlg.close() - On Qt6 it will close the dialog, but it will not restart the main event loop + # * dlg.accept() - It ends with information thar `accept` and `reject` of such created dialog can not be called + # * accept dialog with enter - It works, but it cannot be called to early after dialog is shown + qtbot.keyClick(dlg, QtCore.Qt.Key_Enter) + need_restart = False + sleep(0.1) + for dlg in app.topLevelWidgets(): + # As described above, it may happen that dialog is not closed after first using enter. + # in such case we call `run` function again. The 0.5s sleep is enough for the second enter to close the dialog. + if not isinstance(dlg, QtWidgets.QFileDialog) or dlg.isHidden(): + continue + self.run(allow_restart=False) + return + + if need_restart: + self.run() + + # we need to use the `DontUseNativeDialog` option to be able to interact with it from code. + try: + opt = QtWidgets.QFileDialog.Option.DontUseNativeDialog + except AttributeError: + # old qt5 bindings + opt = QtWidgets.QFileDialog.DontUseNativeDialog + + kwargs = { + "caption": "Select a directory", + keyword: str(tmp_path), + "options": opt, + } + + + thr = CloseThread() + thr.start() + qtbot.waitUntil(thr.isRunning, timeout=1000) + dlg = QtWidgets.QFileDialog() if instance else QtWidgets.QFileDialog + dlg.getExistingDirectory(**kwargs) + qtbot.waitUntil(thr.isFinished, timeout=3000) + \ No newline at end of file From 16398cc5b1cda43588628884bab46611378d3f62 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Wed, 28 Jun 2023 10:57:37 +0200 Subject: [PATCH 580/703] Apply suggestions from code review Co-authored-by: Carlos Cordoba --- qtpy/tests/test_qtwidgets.py | 46 +++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 1ea816e5..fc8d2e1f 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -4,11 +4,11 @@ from time import sleep import pytest +from pytestqt.exceptions import TimeoutError from qtpy import PYQT5, PYQT_VERSION, QtCore, QtGui, QtWidgets, PYQT6, PYSIDE6, PYSIDE2 from qtpy.tests.utils import using_conda, not_using_conda -from pytestqt.exceptions import TimeoutError def test_qtextedit_functions(qtbot, pdf_writer): @@ -136,29 +136,38 @@ def test_opengl_imports(): """ assert QtWidgets.QOpenGLWidget is not None -@pytest.mark.skipif(sys.platform == 'darwin' and sys.version_info[:2] == (3, 7) and (PYQT5 or PYSIDE2), reason="test crashes on macOS CI with Python 3.7 with 'Illegal instruction: 4'") + +@pytest.mark.skipif( + sys.platform == 'darwin' and sys.version_info[:2] == (3, 7) and + (PYQT5 or PYSIDE2), + reason="Crashes on macOS with Python 3.7 with 'Illegal instruction: 4'") @pytest.mark.parametrize("keyword", ["dir", "directory"]) @pytest.mark.parametrize("instance", [True, False]) def test_qfiledialog_compat(tmp_path, qtbot, keyword, instance): """ - This function is testing if the decorators that renamed keyword are working. - It mas stop working if the Qt bindings do some overwriting of the methods in constructor. - It should not happen, but we already meet that PySide team did similar things in the past - (like overwriting enum module in PySide6==6.3.2). + This function is testing if the decorators that renamed keyword are + working. + + It mas stop working if the Qt bindings do some overwriting of the methods + in constructor. It should not happen, but we already meet that PySide team + did similar things in the past (like overwriting enum module in + PySide6==6.3.2). keyword: str The keyword that should be used in the function call. instance: bool - If True, the function is called on the instance of the QFileDialog, otherwise on the class. + If True, the function is called on the instance of the QFileDialog, + otherwise on the class. """ class CloseThread(QtCore.QThread): """ On some implementations the `getExistingDirectory` functions starts own - event loop that will not trigger QTimer started before the call. Until the - dialog is closed the main event loop will be stopped. + event loop that will not trigger QTimer started before the call. Until + the dialog is closed the main event loop will be stopped. - Because of this it is required to use the thread to interact with the dialog. + Because of this it is required to use the thread to interact with the + dialog. """ def run(self, allow_restart=True): sleep(0.5) @@ -167,10 +176,13 @@ def run(self, allow_restart=True): for dlg in app.topLevelWidgets(): if not isinstance(dlg, QtWidgets.QFileDialog) or dlg.isHidden(): continue - # when implement this I try to use: - # * dlg.close() - On Qt6 it will close the dialog, but it will not restart the main event loop - # * dlg.accept() - It ends with information thar `accept` and `reject` of such created dialog can not be called - # * accept dialog with enter - It works, but it cannot be called to early after dialog is shown + # "when implemented this I try to use: + # * dlg.close() - On Qt6 it will close the dialog, but it will + # not restart the main event loop. + # * dlg.accept() - It ends with information thar `accept` and + # `reject` of such created dialog can not be called. + # * accept dialog with enter - It works, but it cannot be + # called to early after dialog is shown qtbot.keyClick(dlg, QtCore.Qt.Key_Enter) need_restart = False sleep(0.1) @@ -185,7 +197,8 @@ def run(self, allow_restart=True): if need_restart: self.run() - # we need to use the `DontUseNativeDialog` option to be able to interact with it from code. + # We need to use the `DontUseNativeDialog` option to be able to interact + # with it from code. try: opt = QtWidgets.QFileDialog.Option.DontUseNativeDialog except AttributeError: @@ -204,5 +217,4 @@ def run(self, allow_restart=True): qtbot.waitUntil(thr.isRunning, timeout=1000) dlg = QtWidgets.QFileDialog() if instance else QtWidgets.QFileDialog dlg.getExistingDirectory(**kwargs) - qtbot.waitUntil(thr.isFinished, timeout=3000) - \ No newline at end of file + qtbot.waitUntil(thr.isFinished, timeout=3000) \ No newline at end of file From 6c97f134008a1bc64a1640081aa8a6bb253bd569 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Thu, 29 Jun 2023 00:44:38 +0200 Subject: [PATCH 581/703] Update qtpy/tests/test_qtwidgets.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Daniel Althviz Moré --- qtpy/tests/test_qtwidgets.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index fc8d2e1f..fa982c4e 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -145,11 +145,11 @@ def test_opengl_imports(): @pytest.mark.parametrize("instance", [True, False]) def test_qfiledialog_compat(tmp_path, qtbot, keyword, instance): """ - This function is testing if the decorators that renamed keyword are - working. + This function is testing if the decorators that renamed the dir/directory + keyword are working. - It mas stop working if the Qt bindings do some overwriting of the methods - in constructor. It should not happen, but we already meet that PySide team + It may stop working if the Qt bindings do some overwriting of the methods + in constructor. It should not happen, but the PySide team did similar things in the past (like overwriting enum module in PySide6==6.3.2). From 3a8c2fe7cb1f3dd71f6d735d91db8a789d78a4c7 Mon Sep 17 00:00:00 2001 From: Grzegorz Bokota Date: Thu, 29 Jun 2023 00:45:45 +0200 Subject: [PATCH 582/703] Update .github/workflows/ci.yml --- .github/workflows/ci.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f2573ccd..5f1ffcc7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -100,10 +100,6 @@ jobs: sudo apt update sudo apt install libpulse-dev libegl1-mesa libopengl0 gstreamer1.0-gl - uses: tlambert03/setup-qt-libs@v1 - - name: Install Linux system packages - if: contains(matrix.os, 'ubuntu') - shell: bash - run: sudo apt install libpulse-dev libegl1-mesa libopengl0 gstreamer1.0-gl - name: Install Conda uses: conda-incubator/setup-miniconda@v2 with: From df60d73d62b7d5c98eab8b84bb5617ec5f8e5249 Mon Sep 17 00:00:00 2001 From: StSav012 Date: Wed, 5 Jul 2023 14:08:29 +0300 Subject: [PATCH 583/703] Make `QtWidgets.QToolBar.addAction` compatible with Qt6 arguments' order --- qtpy/QtWidgets.py | 10 ++++++- qtpy/_utils.py | 54 ++++++++++++++++++++++++++++++++++++ qtpy/tests/test_qtwidgets.py | 12 ++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 9a83ee6f..d5d58bbe 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -10,7 +10,7 @@ from functools import wraps from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError -from ._utils import possibly_static_exec, getattr_missing_optional_dep +from ._utils import add_action, possibly_static_exec, getattr_missing_optional_dep _missing_optional_names = {} @@ -114,3 +114,11 @@ def _directory_to_dir_(*args, **kwargs): QFileDialog.getOpenFileName = _dir_to_directory(QFileDialog.getOpenFileName) QFileDialog.getOpenFileNames = _dir_to_directory(QFileDialog.getOpenFileNames) QFileDialog.getSaveFileName = _dir_to_directory(QFileDialog.getSaveFileName) + + +# Make `addAction` compatible with Qt6 +if PYQT5 or PYSIDE2: + from functools import partialmethod + + QMenu.addAction = partialmethod(add_action, old_add_action=QMenu.addAction) + QToolBar.addAction = partialmethod(add_action, old_add_action=QToolBar.addAction) diff --git a/qtpy/_utils.py b/qtpy/_utils.py index c009f17b..90211863 100644 --- a/qtpy/_utils.py +++ b/qtpy/_utils.py @@ -44,3 +44,57 @@ def possibly_static_exec(cls, *args, **kwargs): return args[0].exec(*args[1:], **kwargs) else: return cls.exec(*args, **kwargs) + + +def add_action(self, *args, old_add_action): + from qtpy.QtCore import QObject + from qtpy.QtGui import QIcon, QKeySequence + from qtpy.QtWidgets import QAction + + action: QAction + icon: QIcon + text: str + shortcut: QKeySequence | QKeySequence.StandardKey | str | int + receiver: QObject + member: bytes + if all(isinstance(arg, t) + for arg, t in zip(args, [str, + (QKeySequence, QKeySequence.StandardKey, str, int), + QObject, + bytes])): + if len(args) == 2: + text, shortcut = args + action = old_add_action(self, text) + action.setShortcut(shortcut) + elif len(args) == 3: + text, shortcut, receiver = args + action = old_add_action(self, text, receiver) + action.setShortcut(shortcut) + elif len(args) == 4: + text, shortcut, receiver, member = args + action = old_add_action(self, text, receiver, member, shortcut) + else: + return old_add_action(self, *args) + return action + elif all(isinstance(arg, t) + for arg, t in zip(args, [QIcon, + str, + (QKeySequence, QKeySequence.StandardKey, str, int), + QObject, + bytes])): + if len(args) == 3: + icon, text, shortcut = args + action = old_add_action(self, icon, text) + action.setShortcut(QKeySequence(shortcut)) + elif len(args) == 4: + icon, text, shortcut, receiver = args + action = old_add_action(self, icon, text, receiver) + action.setShortcut(QKeySequence(shortcut)) + elif len(args) == 5: + icon, text, shortcut, receiver, member = args + action = old_add_action(self, icon, text, receiver, member, QKeySequence(shortcut)) + else: + return old_add_action(self, *args) + return action + return old_add_action(self, *args) + diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index fa982c4e..cb7e8b5e 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -100,6 +100,8 @@ def test_QMenu_functions(qtbot): window = QtWidgets.QMainWindow() menu = QtWidgets.QMenu(window) menu.addAction('QtPy') + menu.addAction('QtPy with a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) + menu.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) window.show() with qtbot.waitExposed(window): @@ -115,6 +117,16 @@ def test_QMenu_functions(qtbot): QtWidgets.QMenu.exec_(menu.actions(), QtCore.QPoint(1, 1)) +@pytest.mark.skipif( + sys.platform == 'darwin' and sys.version_info[:2] == (3, 7), + reason="Stalls on macOS CI with Python 3.7") +def test_QToolBar_functions(qtbot): + """Test `QtWidgets.QToolBar.addAction` compatibility with Qt6 arguments' order.""" + toolbar = QtWidgets.QToolBar() + toolbar.addAction('QtPy with a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) + toolbar.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) + + @pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), reason="A specific setup with at least sip 4.9.9 is needed for PyQt5 5.9.*" "to work with scoped enum access") From 6d6f8bd558a2804a63cf9d43050edec890c94dfa Mon Sep 17 00:00:00 2001 From: StSav012 Date: Wed, 5 Jul 2023 14:24:28 +0300 Subject: [PATCH 584/703] Make `exec()` for PySide2 --- qtpy/QtCore.py | 7 ++++++- qtpy/_utils.py | 14 ++++++++++++++ qtpy/tests/test_qtcore.py | 32 +++++++++++++++++++++++++------- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 1d6b1b9d..dc33ac83 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -10,7 +10,7 @@ from typing import TYPE_CHECKING from . import PYQT6, PYQT5, PYSIDE2, PYSIDE6 -from ._utils import possibly_static_exec +from ._utils import possibly_static_exec, possibly_static_exec_ if PYQT5: from PyQt5.QtCore import * @@ -77,6 +77,11 @@ # Fails with PySide2 5.12.0 pass + QCoreApplication.exec = lambda *args, **kwargs: possibly_static_exec_(QCoreApplication, *args, **kwargs) + QEventLoop.exec = lambda self, *args, **kwargs: self.exec_(*args, **kwargs) + QThread.exec = lambda self, *args, **kwargs: self.exec_(*args, **kwargs) + QTextStreamManipulator.exec = lambda self, *args, **kwargs: self.exec_(*args, **kwargs) + elif PYSIDE6: from PySide6.QtCore import * import PySide6.QtCore diff --git a/qtpy/_utils.py b/qtpy/_utils.py index c009f17b..ce1ba461 100644 --- a/qtpy/_utils.py +++ b/qtpy/_utils.py @@ -44,3 +44,17 @@ def possibly_static_exec(cls, *args, **kwargs): return args[0].exec(*args[1:], **kwargs) else: return cls.exec(*args, **kwargs) + + +def possibly_static_exec_(cls, *args, **kwargs): + """Call `self.exec` when `self` is given or a static method otherwise.""" + if not args and not kwargs: + # A special case (`cls.exec()`) to avoid the function resolving error + return cls.exec_() + if isinstance(args[0], cls): + if len(args) == 1 and not kwargs: + # A special case (`self.exec()`) to avoid the function resolving error + return args[0].exec_() + return args[0].exec_(*args[1:], **kwargs) + else: + return cls.exec_(*args, **kwargs) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 50a1be3a..0fc40a92 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -53,17 +53,21 @@ def test_QTime_toPython_and_toPyTime(method): assert py_time == NOW.time() -def test_qeventloop_exec_(qtbot): - """Test QEventLoop.exec_""" +def test_qeventloop_exec(qtbot): + """Test `QEventLoop.exec_` and `QEventLoop.exec`""" assert QtCore.QEventLoop.exec_ is not None + assert QtCore.QEventLoop.exec is not None event_loop = QtCore.QEventLoop(None) QtCore.QTimer.singleShot(100, event_loop.quit) event_loop.exec_() + QtCore.QTimer.singleShot(100, event_loop.quit) + event_loop.exec() -def test_qthread_exec_(): - """Test QThread.exec_""" +def test_qthread_exec(): + """Test `QThread.exec_` and `QThread.exec_`""" assert QtCore.QThread.exec_ is not None + assert QtCore.QThread.exec is not None def test_QLibraryInfo_location_and_path(): @@ -93,11 +97,25 @@ def test_QCoreApplication_exec_(qapp): app.exec_() +def test_QCoreApplication_exec(qapp): + """Test `QtCore.QCoreApplication.exec`""" + assert QtCore.QCoreApplication.exec is not None + app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication([sys.executable, __file__]) + assert app is not None + QtCore.QTimer.singleShot(100, QtCore.QCoreApplication.instance().quit) + QtCore.QCoreApplication.exec() + app = QtCore.QCoreApplication.instance() or QtCore.QCoreApplication([sys.executable, __file__]) + assert app is not None + QtCore.QTimer.singleShot(100, QtCore.QCoreApplication.instance().quit) + app.exec() + + @pytest.mark.skipif(PYQT5 or PYQT6, reason="Doesn't seem to be present on PyQt5 and PyQt6") -def test_qtextstreammanipulator_exec_(): - """Test QTextStreamManipulator.exec_""" - QtCore.QTextStreamManipulator.exec_ is not None +def test_qtextstreammanipulator_exec(): + """Test `QTextStreamManipulator.exec_` and `QTextStreamManipulator.exec`""" + assert QtCore.QTextStreamManipulator.exec_ is not None + assert QtCore.QTextStreamManipulator.exec is not None @pytest.mark.skipif(PYSIDE2 or PYQT6, From 878e22734d5a4000e3e4df938744b24a063f0801 Mon Sep 17 00:00:00 2001 From: StSav012 Date: Wed, 5 Jul 2023 14:32:38 +0300 Subject: [PATCH 585/703] Fix a test failure on PyQt5 5.9 --- qtpy/tests/test_qtwidgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index cb7e8b5e..74461e11 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -123,8 +123,8 @@ def test_QMenu_functions(qtbot): def test_QToolBar_functions(qtbot): """Test `QtWidgets.QToolBar.addAction` compatibility with Qt6 arguments' order.""" toolbar = QtWidgets.QToolBar() - toolbar.addAction('QtPy with a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) - toolbar.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) + toolbar.addAction('QtPy with a shortcut', QtGui.QKeySequence.UnknownKey) + toolbar.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.UnknownKey) @pytest.mark.skipif(PYQT5 and PYQT_VERSION.startswith('5.9'), From 0e79728fd47e0dc9463a25d0ddcde4bf42b72cbc Mon Sep 17 00:00:00 2001 From: StSav012 Date: Wed, 5 Jul 2023 14:46:31 +0300 Subject: [PATCH 586/703] Make `Q*.addAction` compatible for Qt < 6.3 --- qtpy/QtWidgets.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index d5d58bbe..8d6d8edf 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -7,10 +7,13 @@ # ----------------------------------------------------------------------------- """Provides widget classes and functions.""" -from functools import wraps +from functools import partialmethod, wraps + +from packaging.version import parse from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError from ._utils import add_action, possibly_static_exec, getattr_missing_optional_dep +from .QtCore import __version__ as _qt_version _missing_optional_names = {} @@ -115,10 +118,7 @@ def _directory_to_dir_(*args, **kwargs): QFileDialog.getOpenFileNames = _dir_to_directory(QFileDialog.getOpenFileNames) QFileDialog.getSaveFileName = _dir_to_directory(QFileDialog.getSaveFileName) - -# Make `addAction` compatible with Qt6 -if PYQT5 or PYSIDE2: - from functools import partialmethod - +# Make `addAction` compatible with Qt6 >= 6.3 +if PYQT5 or PYSIDE2 or parse(_qt_version) < parse('6.3'): QMenu.addAction = partialmethod(add_action, old_add_action=QMenu.addAction) QToolBar.addAction = partialmethod(add_action, old_add_action=QToolBar.addAction) From 9b90a97d14f19e136635aae228d884d95f41aa8e Mon Sep 17 00:00:00 2001 From: StSav012 Date: Wed, 5 Jul 2023 14:55:47 +0300 Subject: [PATCH 587/703] Fix a test failure on PyQt5 5.9 --- qtpy/tests/test_qtwidgets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 74461e11..2858a3f3 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -100,8 +100,8 @@ def test_QMenu_functions(qtbot): window = QtWidgets.QMainWindow() menu = QtWidgets.QMenu(window) menu.addAction('QtPy') - menu.addAction('QtPy with a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) - menu.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.StandardKey.UnknownKey) + menu.addAction('QtPy with a shortcut', QtGui.QKeySequence.UnknownKey) + menu.addAction(QtGui.QIcon(), 'QtPy with an icon and a shortcut', QtGui.QKeySequence.UnknownKey) window.show() with qtbot.waitExposed(window): From bb524b6913538196d1e959168783f488804c2fcc Mon Sep 17 00:00:00 2001 From: StSav012 Date: Thu, 6 Jul 2023 10:48:39 +0300 Subject: [PATCH 588/703] Add a description for `add_action` --- qtpy/_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qtpy/_utils.py b/qtpy/_utils.py index de860037..4dce8bb0 100644 --- a/qtpy/_utils.py +++ b/qtpy/_utils.py @@ -61,6 +61,7 @@ def possibly_static_exec_(cls, *args, **kwargs): def add_action(self, *args, old_add_action): + """Re-order arguments of `addAction` to backport compatibility with Qt>=6.3.""" from qtpy.QtCore import QObject from qtpy.QtGui import QIcon, QKeySequence from qtpy.QtWidgets import QAction From 419bd90a04dc8cd36f21c5b028e593bb772f742f Mon Sep 17 00:00:00 2001 From: Jan Robert Schmidt Date: Thu, 6 Jul 2023 11:03:46 +0200 Subject: [PATCH 589/703] replace custom implementation with loadUiType from PySide6 --- qtpy/uic.py | 60 +++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/qtpy/uic.py b/qtpy/uic.py index 6f53e4f5..a51b193f 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -81,6 +81,7 @@ if PYSIDE6: from PySide6.QtCore import QMetaObject from PySide6.QtUiTools import QUiLoader + from PySide6.QtUiTools import loadUiType elif PYSIDE2: from PySide2.QtCore import QMetaObject from PySide2.QtUiTools import QUiLoader @@ -245,41 +246,42 @@ def loadUi(uifile, baseinstance=None, workingDirectory=None): QMetaObject.connectSlotsByName(widget) return widget - def loadUiType(uifile, from_imports=False): - """Load a .ui file and return the generated form class and - the Qt base class. + if PYSIDE2: + def loadUiType(uifile, from_imports=False): + """Load a .ui file and return the generated form class and + the Qt base class. - The "loadUiType" command convert the ui file to py code - in-memory first and then execute it in a special frame to - retrieve the form_class. + The "loadUiType" command convert the ui file to py code + in-memory first and then execute it in a special frame to + retrieve the form_class. - Credit: https://stackoverflow.com/a/14195313/15954282 - """ + Credit: https://stackoverflow.com/a/14195313/15954282 + """ - import sys - from io import StringIO - from xml.etree.ElementTree import ElementTree - - from . import QtWidgets + import sys + from io import StringIO + from xml.etree.ElementTree import ElementTree - # Parse the UI file - etree = ElementTree() - ui = etree.parse(uifile) + from . import QtWidgets + + # Parse the UI file + etree = ElementTree() + ui = etree.parse(uifile) - widget_class = ui.find('widget').get('class') - form_class = ui.find('class').text + widget_class = ui.find('widget').get('class') + form_class = ui.find('class').text - with open(uifile, encoding="utf-8") as fd: - code_stream = StringIO() - frame = {} + with open(uifile, encoding="utf-8") as fd: + code_stream = StringIO() + frame = {} - compileUi(fd, code_stream, indent=0, from_imports=from_imports) - pyc = compile(code_stream.getvalue(), '', 'exec') - exec(pyc, frame) + compileUi(fd, code_stream, indent=0, from_imports=from_imports) + pyc = compile(code_stream.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 = getattr(QtWidgets, 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 = getattr(QtWidgets, widget_class) - return form_class, base_class + return form_class, base_class From 278d064d8b5cca62a2c7176e288866059cac5385 Mon Sep 17 00:00:00 2001 From: Jan Robert Schmidt Date: Tue, 11 Jul 2023 14:43:00 +0200 Subject: [PATCH 590/703] skip test_load_ui_type if pyside6-uic not available --- qtpy/tests/test_uic.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/qtpy/tests/test_uic.py b/qtpy/tests/test_uic.py index 578093e3..8cde0311 100644 --- a/qtpy/tests/test_uic.py +++ b/qtpy/tests/test_uic.py @@ -4,9 +4,11 @@ import warnings import pytest +from packaging.version import parse -from qtpy import PYSIDE6, PYSIDE2, QtWidgets +from qtpy import PYSIDE6, PYSIDE2, QtWidgets, PYSIDE_VERSION from qtpy.QtWidgets import QComboBox +from qtpy.tests.utils import using_conda if PYSIDE2: pytest.importorskip("pyside2uic", reason="pyside2uic not installed") @@ -56,8 +58,9 @@ def test_load_ui(qtbot): @pytest.mark.skipif( - PYSIDE2 or PYSIDE6, - reason="PySide2uic not consistently installed across platforms/versions") + PYSIDE6 and using_conda() and parse(PYSIDE_VERSION) < parse("6.5") + and (sys.platform in ("darwin", "linux")), + reason="pyside6-uic command not contained in all conda-forge packages.") def test_load_ui_type(qtbot): """ Make sure that the patched loadUiType function behaves as expected with a From 570801869066671c6381b120b8a8052c7a627613 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 11 Aug 2023 14:08:16 -0500 Subject: [PATCH 591/703] CI: Update bindings upper bound version to 6.5 --- .github/workflows/ci.yml | 5 +++-- .github/workflows/test.sh | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f1ffcc7..4576b83d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,7 +57,7 @@ jobs: pyside6-version: '6.4' # Python 3.11 needs 6.4+ - use-conda: 'Yes' skip-pyqt6: true # No PyQt6 conda packages yet - pyside6-version: '6.4' # Conda only has 6.4+ + pyside6-version: '6.4' # Conda only has 6.4+ for Python <3.8 - use-conda: 'No' pyqt5-version: '5.15' # Test with latest optional packages - python-version: '3.7' @@ -67,6 +67,7 @@ jobs: - python-version: '3.11' use-conda: 'No' skip-pyside2: true # Pyside2 wheels don't support Python 3.11+ + pyside6-version: '6.5' # Test upper bound - os: windows-latest python-version: '3.7' use-conda: 'Yes' @@ -84,7 +85,7 @@ jobs: - os: macos-latest python-version: '3.7' use-conda: 'No' - pyqt6-version: 6.4 # Test upper bound + pyqt6-version: 6.5 # Test upper bound pyside2-version: 5.15 # Test upper bound steps: - name: Checkout branch diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 5ccfbc10..69267f57 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -53,11 +53,12 @@ fi # Build wheel of package git clean -xdf -e *.coverage +python -m pip install --upgrade pip python -m pip install --upgrade build python -bb -X dev -W error -m build # Install package from built wheel -echo dist/*.whl | xargs -I % python -bb -X dev -W error -W "ignore::DeprecationWarning:pip._internal.locations._distutils" -W "ignore::DeprecationWarning:distutils.command.install" -m pip install --upgrade % +echo dist/*.whl | xargs -I % python -bb -X dev -W error -W "ignore::DeprecationWarning:pip._internal.locations._distutils" -W "ignore::DeprecationWarning:distutils.command.install" -W "ignore::DeprecationWarning:pip._internal.metadata.importlib._envs" -m pip install --upgrade % # Print environment information mamba list From 6d7b3c24231f7bbdb20c15e292405420e3bb859e Mon Sep 17 00:00:00 2001 From: StSav012 Date: Mon, 14 Aug 2023 19:58:14 +0300 Subject: [PATCH 592/703] Use `QT_VERSION` instead of `QtCore.__version__` --- qtpy/QtWidgets.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qtpy/QtWidgets.py b/qtpy/QtWidgets.py index 8d6d8edf..6dbeec2e 100644 --- a/qtpy/QtWidgets.py +++ b/qtpy/QtWidgets.py @@ -11,9 +11,8 @@ from packaging.version import parse -from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QtModuleNotInstalledError +from . import PYQT5, PYQT6, PYSIDE2, PYSIDE6, QT_VERSION as _qt_version from ._utils import add_action, possibly_static_exec, getattr_missing_optional_dep -from .QtCore import __version__ as _qt_version _missing_optional_names = {} From 6a594e51c4b451fc27fded87d61340c923d89e75 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 16 Aug 2023 11:31:20 -0500 Subject: [PATCH 593/703] Check 'Qt.ItemFlags' access --- qtpy/tests/test_qtcore.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 0fc40a92..20f21cbc 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -146,7 +146,6 @@ def test_enum_access(): assert QtCore.Qt.TextColorRole == QtCore.Qt.ItemDataRole.TextColorRole assert QtCore.Qt.MidButton == QtCore.Qt.MouseButton.MiddleButton - @pytest.mark.skipif(PYSIDE2 and PYSIDE_VERSION.startswith('5.12.0'), reason="Utility functions unavailable for PySide2 5.12.0") def test_qtgui_namespace_mightBeRichText(): @@ -156,3 +155,10 @@ def test_qtgui_namespace_mightBeRichText(): See: https://doc.qt.io/qt-5/qt-sub-qtgui.html """ assert QtCore.Qt.mightBeRichText is not None + + +def test_itemflags_typedef(): + """ + Test existence of `QFlags` typedef `ItemFlags` that was removed from PyQt6 + """ + assert QtCore.Qt.ItemFlags is not None From e4efdfdab9feea839730335575c13efb7d392a8a Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 16 Aug 2023 12:12:29 -0500 Subject: [PATCH 594/703] Add 'Qt.ItemFlags' definition as 'Qt.ItemFlag' alias for PyQt6 Co-authored-by: Chris-N-K --- qtpy/QtCore.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index dc33ac83..8f7585b0 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -60,6 +60,10 @@ Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole # Alias for MiddleButton removed in PyQt6 but available in PyQt5, PySide2 and PySide6 Qt.MidButton = Qt.MiddleButton + # Add removed definition for `Qt.ItemFlags` as an alias of `Qt.ItemFlag` as PySide6 does. + # Note that for PyQt5 and PySide2 those definitions are two different classes + # (one is the flag definition and the other the enum definition) + Qt.ItemFlags = Qt.ItemFlag elif PYSIDE2: from PySide2.QtCore import * From 8eb297552e9bdadc41ede36422d512e6ae52a036 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 16 Aug 2023 12:50:45 -0500 Subject: [PATCH 595/703] Add mappings for deprecated QDropEvent pos and posF methods --- qtpy/QtGui.py | 9 ++++++++- qtpy/tests/test_qtgui.py | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/qtpy/QtGui.py b/qtpy/QtGui.py index d5490997..b03b0528 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -137,8 +137,8 @@ def movePositionPatched( return movePosition(self, operation, mode, n) QTextCursor.movePosition = movePositionPatched -# Fix https://github.com/spyder-ide/qtpy/issues/394 if PYQT5 or PYSIDE2: + # Part of the fix for https://github.com/spyder-ide/qtpy/issues/394 from qtpy.QtCore import QPointF as __QPointF QNativeGestureEvent.x = lambda self: self.localPos().toPoint().x() QNativeGestureEvent.y = lambda self: self.localPos().toPoint().y() @@ -160,7 +160,11 @@ def movePositionPatched( QMouseEvent.position = lambda self: self.localPos() QMouseEvent.globalPosition = lambda self: __QPointF( float(self.globalX()), float(self.globalY())) + + # Follow similar approach for `QDropEvent` and child classes + QDropEvent.position = lambda self: self.posF() if PYQT6 or PYSIDE6: + # Part of the fix for https://github.com/spyder-ide/qtpy/issues/394 for _class in (QNativeGestureEvent, QEnterEvent, QTabletEvent, QHoverEvent, QMouseEvent): for _obsolete_function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY'): @@ -175,3 +179,6 @@ def movePositionPatched( QSinglePointEvent.globalX = lambda self: self.globalPosition().toPoint().x() QSinglePointEvent.globalY = lambda self: self.globalPosition().toPoint().y() + # Follow similar approach for `QDropEvent` and child classes + QDropEvent.pos = lambda self: self.position().toPoint() + QDropEvent.posF = lambda self: self.position() diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 380c2367..92589424 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -102,12 +102,18 @@ def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None: qtbot.mouseDClick(window, QtCore.Qt.LeftButton) # the rest of the functions are not actually tested + # QSinglePointEvent (Qt6) child classes checks for _class in ('QNativeGestureEvent', 'QEnterEvent', 'QTabletEvent'): for _function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY', 'position', 'globalPosition'): assert hasattr(getattr(QtGui, _class), _function) + # QHoverEvent checks for _function in ('pos', 'x', 'y', 'position'): assert hasattr(QtGui.QHoverEvent, _function) + # QDropEvent and child classes checks + for _class in ('QDropEvent', 'QDragMoveEvent', 'QDragEnterEvent'): + for _function in ('pos', 'posF', 'position'): + assert hasattr(getattr(QtGui, _class), _function) @pytest.mark.skipif(not (PYSIDE2 or PYSIDE6), reason="PySide{2,6} specific test") From 9348591433052b87f1e0c48f7ae5e458c1cafd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Wed, 16 Aug 2023 13:15:32 -0500 Subject: [PATCH 596/703] Apply suggestions from code review Co-authored-by: Carlos Cordoba --- qtpy/QtCore.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/QtCore.py b/qtpy/QtCore.py index 8f7585b0..1cf60668 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -58,8 +58,10 @@ # Alias deprecated ItemDataRole enum values removed in Qt6 Qt.BackgroundColorRole = Qt.ItemDataRole.BackgroundColorRole = Qt.BackgroundRole Qt.TextColorRole = Qt.ItemDataRole.TextColorRole = Qt.ForegroundRole + # Alias for MiddleButton removed in PyQt6 but available in PyQt5, PySide2 and PySide6 Qt.MidButton = Qt.MiddleButton + # Add removed definition for `Qt.ItemFlags` as an alias of `Qt.ItemFlag` as PySide6 does. # Note that for PyQt5 and PySide2 those definitions are two different classes # (one is the flag definition and the other the enum definition) From 154f6c72d14aad90c2032bf1c503d05209110750 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Wed, 16 Aug 2023 13:43:59 -0500 Subject: [PATCH 597/703] Skip QEnum test for PySide2 < 5.15 --- qtpy/tests/test_qtcore.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtcore.py b/qtpy/tests/test_qtcore.py index 3220780f..00ae0be2 100644 --- a/qtpy/tests/test_qtcore.py +++ b/qtpy/tests/test_qtcore.py @@ -5,17 +5,16 @@ from datetime import date, datetime, time import pytest +from packaging.version import parse from qtpy import ( PYQT5, PYQT6, PYSIDE2, - PYSIDE6, PYQT_VERSION, PYSIDE_VERSION, QtCore, ) -from qtpy.tests.utils import not_using_conda _now = datetime.now() # Make integer milliseconds; `floor` here, don't `round`! @@ -71,6 +70,8 @@ def test_qthread_exec(): assert QtCore.QThread.exec is not None +@pytest.mark.skipif(PYSIDE2 and parse(PYSIDE_VERSION) < parse("5.15"), + reason="QEnum macro doesn't seem to be present on PySide2 <5.15") def test_qenum(): """Test QEnum macro""" class EnumTest(QtCore.QObject): From a4340ffd930a2225ea9a69a05576e6c1a576158c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Althviz=20Mor=C3=A9?= Date: Thu, 17 Aug 2023 14:13:26 -0500 Subject: [PATCH 598/703] Apply suggestions from code review Co-authored-by: Carlos Cordoba --- qtpy/tests/test_qtgui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qtpy/tests/test_qtgui.py b/qtpy/tests/test_qtgui.py index 92589424..2b66d671 100644 --- a/qtpy/tests/test_qtgui.py +++ b/qtpy/tests/test_qtgui.py @@ -107,9 +107,11 @@ def mouseDoubleClickEvent(self, event: QtGui.QMouseEvent) -> None: for _function in ('pos', 'x', 'y', 'globalPos', 'globalX', 'globalY', 'position', 'globalPosition'): assert hasattr(getattr(QtGui, _class), _function) + # QHoverEvent checks for _function in ('pos', 'x', 'y', 'position'): assert hasattr(QtGui.QHoverEvent, _function) + # QDropEvent and child classes checks for _class in ('QDropEvent', 'QDragMoveEvent', 'QDragEnterEvent'): for _function in ('pos', 'posF', 'position'): From aab310d38453917f087c67de9e0b25bb666aaae7 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 18 Aug 2023 17:09:09 -0500 Subject: [PATCH 599/703] Test using PyQt extra packages --- .github/workflows/ci.yml | 2 ++ .github/workflows/test.sh | 16 ++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4576b83d..381ecb47 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,7 @@ jobs: PYSIDE6_VERSION: ${{ matrix.pyside6-version || matrix.qt6-version-default }} PYSIDE6_QT_VERSION: ${{ matrix.pyside6-qt-version || matrix.pyside6-version || matrix.qt6-version-default }} QSCINTILLA_VERSION: ${{ matrix.qscintilla-version || matrix.qscintilla-version-default }} + PYQT_EXTRAS: ${{ matrix.pyqt-extras || matrix.pyqt-extras-default }} SKIP_PIP_CHECK: ${{ matrix.skip-pip-check }} strategy: fail-fast: false @@ -48,6 +49,7 @@ jobs: qt5-version-default: ['5.12'] qt6-version-default: ['6.3'] qscintilla-version-default: ['2.13'] + pyqt-extras-default: ['Yes'] include: - os: ubuntu-latest special-invocation: 'xvfb-run --auto-servernum ' # Needed for GUI tests to work diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index 69267f57..a872711e 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -34,9 +34,21 @@ conda activate test-env-${BINDING} if [ "$USE_CONDA" = "No" ]; then if [ "${1}" = "pyqt5" ]; then - pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.* + + if [ "$PYQT_EXTRAS" = "Yes" ]; then + pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.* PyQt3D PyQtChart PyQtDataVisualization PyQtNetworkAuth PyQtPurchasing + else + pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.* + fi + elif [ "${1}" = "pyqt6" ]; then - pip install pyqt6==${PYQT6_VERSION}.* PyQt6-WebEngine==${PYQT6_VERSION}.* PyQt6-Qt6==${PYQT6_QT_VERSION}.* PyQt6-QScintilla==${QSCINTILLA_VERSION}.* + + if [ "$PYQT_EXTRAS" = "Yes" ]; then + pip install pyqt6==${PYQT6_VERSION}.* PyQt6-WebEngine==${PYQT6_VERSION}.* PyQt6-Qt6==${PYQT6_QT_VERSION}.* PyQt6-QScintilla==${QSCINTILLA_VERSION}.* PyQt6-3D PyQt6-Charts PyQt6-DataVisualization PyQt6-NetworkAuth + else + pip install pyqt6==${PYQT6_VERSION}.* PyQt6-WebEngine==${PYQT6_VERSION}.* PyQt6-Qt6==${PYQT6_QT_VERSION}.* PyQt6-QScintilla==${QSCINTILLA_VERSION}.* + fi + elif [ "${1}" = "pyside2" ]; then pip install pyside2==${PYSIDE2_VERSION}.* elif [ "${1}" = "pyside6" ]; then From 4e44f7db8eb46a543c95730406cc02b418248a86 Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 18 Aug 2023 17:23:14 -0500 Subject: [PATCH 600/703] Testing --- .github/workflows/test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.sh b/.github/workflows/test.sh index a872711e..7cce3e8e 100755 --- a/.github/workflows/test.sh +++ b/.github/workflows/test.sh @@ -36,7 +36,7 @@ if [ "$USE_CONDA" = "No" ]; then if [ "${1}" = "pyqt5" ]; then if [ "$PYQT_EXTRAS" = "Yes" ]; then - pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.* PyQt3D PyQtChart PyQtDataVisualization PyQtNetworkAuth PyQtPurchasing + pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.* PyQt3D==${PYQT5_VERSION}.* PyQtChart==${PYQT5_VERSION}.* PyQtDataVisualization==${PYQT5_VERSION}.* PyQtNetworkAuth==${PYQT5_VERSION}.* PyQtPurchasing==${PYQT5_VERSION}.* else pip install pyqt5==${PYQT5_VERSION}.* PyQtWebEngine==${PYQT5_VERSION}.* QScintilla==${QSCINTILLA_VERSION}.* fi @@ -44,7 +44,7 @@ if [ "$USE_CONDA" = "No" ]; then elif [ "${1}" = "pyqt6" ]; then if [ "$PYQT_EXTRAS" = "Yes" ]; then - pip install pyqt6==${PYQT6_VERSION}.* PyQt6-WebEngine==${PYQT6_VERSION}.* PyQt6-Qt6==${PYQT6_QT_VERSION}.* PyQt6-QScintilla==${QSCINTILLA_VERSION}.* PyQt6-3D PyQt6-Charts PyQt6-DataVisualization PyQt6-NetworkAuth + pip install pyqt6==${PYQT6_VERSION}.* PyQt6-WebEngine==${PYQT6_VERSION}.* PyQt6-Qt6==${PYQT6_QT_VERSION}.* PyQt6-QScintilla==${QSCINTILLA_VERSION}.* PyQt6-3D==${PYQT6_VERSION}.* PyQt6-Charts==${PYQT6_VERSION}.* PyQt6-DataVisualization==${PYQT6_VERSION}.* PyQt6-NetworkAuth==${PYQT6_VERSION}.* else pip install pyqt6==${PYQT6_VERSION}.* PyQt6-WebEngine==${PYQT6_VERSION}.* PyQt6-Qt6==${PYQT6_QT_VERSION}.* PyQt6-QScintilla==${QSCINTILLA_VERSION}.* fi From 66dcd92a773ce7e6363cd70420f6296d8120a9dc Mon Sep 17 00:00:00 2001 From: dalthviz Date: Fri, 18 Aug 2023 17:46:21 -0500 Subject: [PATCH 601/703] Modify 'QDatavisualization' ('QDataVisualization' with lower 'v') access test --- qtpy/tests/test_qtdatavisualization.py | 73 +++++++++++++------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/qtpy/tests/test_qtdatavisualization.py b/qtpy/tests/test_qtdatavisualization.py index 09f19f31..5bbf7caf 100644 --- a/qtpy/tests/test_qtdatavisualization.py +++ b/qtpy/tests/test_qtdatavisualization.py @@ -46,40 +46,41 @@ def test_qtdatavisualization(): assert QtDataVisualization.QLogValue3DAxisFormatter is not None # QtDatavisualization - QtDatavisualization = pytest.importorskip("qtpy.QtDatavisualization") + # import qtpy to get alias for `QtDataVisualization` with lower `v` + qtpy = pytest.importorskip("qtpy") - assert QtDatavisualization.QScatter3DSeries is not None - assert QtDatavisualization.QSurfaceDataItem is not None - assert QtDatavisualization.QSurface3DSeries is not None - assert QtDatavisualization.QAbstract3DInputHandler is not None - assert QtDatavisualization.QHeightMapSurfaceDataProxy is not None - assert QtDatavisualization.QAbstractDataProxy is not None - assert QtDatavisualization.Q3DCamera is not None - assert QtDatavisualization.QAbstract3DGraph is not None - assert QtDatavisualization.QCustom3DVolume is not None - assert QtDatavisualization.Q3DInputHandler is not None - assert QtDatavisualization.QBarDataProxy is not None - assert QtDatavisualization.QSurfaceDataProxy is not None - assert QtDatavisualization.QScatterDataItem is not None - assert QtDatavisualization.Q3DLight is not None - assert QtDatavisualization.QScatterDataProxy is not None - assert QtDatavisualization.QValue3DAxis is not None - assert QtDatavisualization.Q3DBars is not None - assert QtDatavisualization.QBarDataItem is not None - assert QtDatavisualization.QItemModelBarDataProxy is not None - assert QtDatavisualization.Q3DTheme is not None - assert QtDatavisualization.QCustom3DItem is not None - assert QtDatavisualization.QItemModelScatterDataProxy is not None - assert QtDatavisualization.QValue3DAxisFormatter is not None - assert QtDatavisualization.QItemModelSurfaceDataProxy is not None - assert QtDatavisualization.Q3DScatter is not None - assert QtDatavisualization.QTouch3DInputHandler is not None - assert QtDatavisualization.QBar3DSeries is not None - assert QtDatavisualization.QAbstract3DAxis is not None - assert QtDatavisualization.Q3DScene is not None - assert QtDatavisualization.QCategory3DAxis is not None - assert QtDatavisualization.QAbstract3DSeries is not None - assert QtDatavisualization.Q3DObject is not None - assert QtDatavisualization.QCustom3DLabel is not None - assert QtDatavisualization.Q3DSurface is not None - assert QtDatavisualization.QLogValue3DAxisFormatter is not None + assert qtpy.QtDatavisualization.QScatter3DSeries is not None + assert qtpy.QtDatavisualization.QSurfaceDataItem is not None + assert qtpy.QtDatavisualization.QSurface3DSeries is not None + assert qtpy.QtDatavisualization.QAbstract3DInputHandler is not None + assert qtpy.QtDatavisualization.QHeightMapSurfaceDataProxy is not None + assert qtpy.QtDatavisualization.QAbstractDataProxy is not None + assert qtpy.QtDatavisualization.Q3DCamera is not None + assert qtpy.QtDatavisualization.QAbstract3DGraph is not None + assert qtpy.QtDatavisualization.QCustom3DVolume is not None + assert qtpy.QtDatavisualization.Q3DInputHandler is not None + assert qtpy.QtDatavisualization.QBarDataProxy is not None + assert qtpy.QtDatavisualization.QSurfaceDataProxy is not None + assert qtpy.QtDatavisualization.QScatterDataItem is not None + assert qtpy.QtDatavisualization.Q3DLight is not None + assert qtpy.QtDatavisualization.QScatterDataProxy is not None + assert qtpy.QtDatavisualization.QValue3DAxis is not None + assert qtpy.QtDatavisualization.Q3DBars is not None + assert qtpy.QtDatavisualization.QBarDataItem is not None + assert qtpy.QtDatavisualization.QItemModelBarDataProxy is not None + assert qtpy.QtDatavisualization.Q3DTheme is not None + assert qtpy.QtDatavisualization.QCustom3DItem is not None + assert qtpy.QtDatavisualization.QItemModelScatterDataProxy is not None + assert qtpy.QtDatavisualization.QValue3DAxisFormatter is not None + assert qtpy.QtDatavisualization.QItemModelSurfaceDataProxy is not None + assert qtpy.QtDatavisualization.Q3DScatter is not None + assert qtpy.QtDatavisualization.QTouch3DInputHandler is not None + assert qtpy.QtDatavisualization.QBar3DSeries is not None + assert qtpy.QtDatavisualization.QAbstract3DAxis is not None + assert qtpy.QtDatavisualization.Q3DScene is not None + assert qtpy.QtDatavisualization.QCategory3DAxis is not None + assert qtpy.QtDatavisualization.QAbstract3DSeries is not None + assert qtpy.QtDatavisualization.Q3DObject is not None + assert qtpy.QtDatavisualization.QCustom3DLabel is not None + assert qtpy.QtDatavisualization.Q3DSurface is not None + assert qtpy.QtDatavisualization.QLogValue3DAxisFormatter is not None From 255c6b4e0bf9c6338c9f37e9f1acb9df0925ef1f Mon Sep 17 00:00:00 2001 From: dalthviz Date: Mon, 21 Aug 2023 12:19:08 -0500 Subject: [PATCH 602/703] Check QtWidgets.QFileDialog.Options definition --- qtpy/tests/test_qtwidgets.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/qtpy/tests/test_qtwidgets.py b/qtpy/tests/test_qtwidgets.py index 2858a3f3..203c11b7 100644 --- a/qtpy/tests/test_qtwidgets.py +++ b/qtpy/tests/test_qtwidgets.py @@ -155,7 +155,7 @@ def test_opengl_imports(): reason="Crashes on macOS with Python 3.7 with 'Illegal instruction: 4'") @pytest.mark.parametrize("keyword", ["dir", "directory"]) @pytest.mark.parametrize("instance", [True, False]) -def test_qfiledialog_compat(tmp_path, qtbot, keyword, instance): +def test_qfiledialog_dir_compat(tmp_path, qtbot, keyword, instance): """ This function is testing if the decorators that renamed the dir/directory keyword are working. @@ -229,4 +229,11 @@ def run(self, allow_restart=True): qtbot.waitUntil(thr.isRunning, timeout=1000) dlg = QtWidgets.QFileDialog() if instance else QtWidgets.QFileDialog dlg.getExistingDirectory(**kwargs) - qtbot.waitUntil(thr.isFinished, timeout=3000) \ No newline at end of file + qtbot.waitUntil(thr.isFinished, timeout=3000) + + +def test_qfiledialog_flags_typedef(): + """ + Test existence of `QFlags