From 7b79676577fae6e0fa1d300e754064bfc8eef887 Mon Sep 17 00:00:00 2001 From: David Aguilar Date: Tue, 19 Oct 2021 10:47:09 -0700 Subject: [PATCH] qtpy: update to spider-ide/qtpy v1.11.2 Signed-off-by: David Aguilar --- extras/qtpy/AUTHORS.md | 19 +++++----- extras/qtpy/CHANGELOG.md | 91 +++++++++++++++++++++++++++++++++++++++++++++ qtpy/QtCore.py | 6 ++- qtpy/QtDataVisualization.py | 22 +++++++++++ qtpy/QtGui.py | 6 ++- qtpy/QtPositioning.py | 18 +++++++++ qtpy/QtSerialPort.py | 17 +++++++++ qtpy/QtWebEngineWidgets.py | 4 ++ qtpy/QtWidgets.py | 2 + qtpy/QtWinExtras.py | 16 ++++++++ qtpy/__init__.py | 21 +++++++++-- qtpy/_version.py | 2 +- qtpy/compat.py | 5 +-- qtpy/py3compat.py | 4 +- qtpy/uic.py | 53 +++++++++++++++++++++++++- 15 files changed, 262 insertions(+), 24 deletions(-) create mode 100644 qtpy/QtDataVisualization.py create mode 100644 qtpy/QtPositioning.py create mode 100644 qtpy/QtSerialPort.py create mode 100644 qtpy/QtWinExtras.py diff --git a/extras/qtpy/AUTHORS.md b/extras/qtpy/AUTHORS.md index a6b4d15e..cb19d7b5 100644 --- a/extras/qtpy/AUTHORS.md +++ b/extras/qtpy/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/extras/qtpy/CHANGELOG.md b/extras/qtpy/CHANGELOG.md index 253f739c..4141eb32 100644 --- a/extras/qtpy/CHANGELOG.md +++ b/extras/qtpy/CHANGELOG.md @@ -1,5 +1,96 @@ # 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 + +* [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 + +* [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 + +* [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/QtCore.py b/qtpy/QtCore.py index f583b05f..d4bbd0d3 100644 --- a/qtpy/QtCore.py +++ b/qtpy/QtCore.py @@ -16,6 +16,7 @@ from . import PYQT5, PYSIDE2, PYQT4, PYSIDE, 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__ @@ -25,7 +26,7 @@ if PYQT5: 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 @@ elif PYQT4: 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 @@ elif PYQT4: 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, 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/QtGui.py b/qtpy/QtGui.py index 071be132..be8f5688 100644 --- a/qtpy/QtGui.py +++ b/qtpy/QtGui.py @@ -33,7 +33,11 @@ elif PYQT4: qFuzzyCompare) except ImportError: pass - from PyQt4.Qt import QKeySequence, QTextCursor + 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, 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/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/QtWebEngineWidgets.py b/qtpy/QtWebEngineWidgets.py index d1df5bfb..33a66575 100644 --- a/qtpy/QtWebEngineWidgets.py +++ b/qtpy/QtWebEngineWidgets.py @@ -22,6 +22,8 @@ if PYQT5: 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 @@ elif PYSIDE2: 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 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 @@ elif PYQT4: from PyQt4.QtGui import * QStyleOptionViewItem = QStyleOptionViewItemV4 del QStyleOptionViewItemV4 + QStyleOptionFrame = QStyleOptionFrameV3 + del QStyleOptionFrameV3 # These objects belong to QtGui try: 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/__init__.py b/qtpy/__init__.py index ddbe4529..ccb7d417 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. @@ -70,6 +70,7 @@ import warnings # Version of QtPy from ._version import __version__ +from .py3compat import PY2 class PythonQtError(RuntimeError): @@ -116,7 +117,7 @@ PYQT4 = PYSIDE = PYSIDE2 = False # 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: @@ -188,8 +189,13 @@ if API in PYQT4_API: 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: + # 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 @@ -224,3 +230,10 @@ if PYQT4: API_NAME += (" (API v{0})".format(sip.getapi('QString'))) except AttributeError: pass + +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/_version.py b/qtpy/_version.py index a8ef90de..3745f022 100644 --- a/qtpy/_version.py +++ b/qtpy/_version.py @@ -1,2 +1,2 @@ -version_info = (1, 9, 0) +version_info = (1, 11, 2) __version__ = '.'.join(map(str, version_info)) 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 @@ 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 +from .py3compat import Callable, is_text_string, to_text_string, TEXT_TYPES # ============================================================================= @@ -46,7 +45,7 @@ if PYQT4: 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 @@ else: 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 diff --git a/qtpy/uic.py b/qtpy/uic.py index 07d7a787..d26a25a1 100644 --- a/qtpy/uic.py +++ b/qtpy/uic.py @@ -14,7 +14,7 @@ elif PYQT4: 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,17 @@ else: if PYSIDE: from PySide.QtCore import QMetaObject from PySide.QtUiTools import QUiLoader + try: + from pysideuic import compileUi + except ImportError: + pass elif PYSIDE2: from PySide2.QtCore import QMetaObject from PySide2.QtUiTools import QUiLoader + try: + from pyside2uic import compileUi + except ImportError: + pass class UiLoader(QUiLoader): """ @@ -181,7 +189,7 @@ else: 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 @@ -226,3 +234,44 @@ else: widget = loader.load(uifile) 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. + + 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 + """ + + 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 -- 2.11.4.GIT