diff options
author | Alexandre Marie <alexandre.marie@synchrotron-soleil.fr> | 2018-12-17 12:28:45 +0100 |
---|---|---|
committer | Alexandre Marie <alexandre.marie@synchrotron-soleil.fr> | 2018-12-17 12:28:45 +0100 |
commit | c49572a2e90b398e90a43f86b490f27ee6c58de6 (patch) | |
tree | d130cf7dfc3cf73157e1bece8173331bb4bc7156 /silx/gui/qt | |
parent | 0bbc8ab933e62c1fa6d548e879ae6d98cbd461f1 (diff) | |
parent | cebdc9244c019224846cb8d2668080fe386a6adc (diff) |
Merge tag 'upstream/0.9.0+dfsg'
Upstream version 0.9.0+dfsg
Diffstat (limited to 'silx/gui/qt')
-rw-r--r-- | silx/gui/qt/__init__.py | 7 | ||||
-rw-r--r-- | silx/gui/qt/_pyside_dynamic.py | 55 | ||||
-rw-r--r-- | silx/gui/qt/_qt.py | 43 | ||||
-rw-r--r-- | silx/gui/qt/_utils.py | 5 | ||||
-rw-r--r-- | silx/gui/qt/inspect.py | 82 |
5 files changed, 158 insertions, 34 deletions
diff --git a/silx/gui/qt/__init__.py b/silx/gui/qt/__init__.py index f7bc916..b8c6cdd 100644 --- a/silx/gui/qt/__init__.py +++ b/silx/gui/qt/__init__.py @@ -25,12 +25,11 @@ """Common wrapper over Python Qt bindings: - `PyQt5 <http://pyqt.sourceforge.net/Docs/PyQt5/>`_ +- `PySide2 <https://wiki.qt.io/Qt_for_Python>`_ - `PyQt4 <http://pyqt.sourceforge.net/Docs/PyQt4/>`_ -- `PySide <http://www.pyside.org>`_ -- `PySide2 <https://wiki.qt.io/PySide2>`_ If a Qt binding is already loaded, it will use it, otherwise the different -Qt bindings are tried in this order: PyQt5, PyQt4, PySide, PySide2. +Qt bindings are tried in this order: PyQt5, PyQt4, PySide2. The name of the loaded Qt binding is stored in the BINDING variable. @@ -48,7 +47,7 @@ Example of using :mod:`silx.gui.qt` module: For an alternative solution providing a structured namespace, see `qtpy <https://pypi.python.org/pypi/QtPy/>`_ which -provides the namespace of PyQt5 over PyQt4 and PySide. +provides the namespace of PyQt5 over PyQt4, PySide and PySide2. """ from ._qt import * # noqa diff --git a/silx/gui/qt/_pyside_dynamic.py b/silx/gui/qt/_pyside_dynamic.py index a9246b9..13d1a9d 100644 --- a/silx/gui/qt/_pyside_dynamic.py +++ b/silx/gui/qt/_pyside_dynamic.py @@ -39,11 +39,15 @@ from __future__ import (print_function, division, unicode_literals, absolute_import) import logging +import sys -from PySide.QtCore import QMetaObject -from PySide.QtUiTools import QUiLoader -from PySide.QtGui import QMainWindow - +if "PySide.QtCore" in sys.modules: + from PySide.QtCore import QMetaObject + from PySide.QtUiTools import QUiLoader +else: # PySide2 + from PySide2.QtCore import QMetaObject, Property, Qt + from PySide2.QtWidgets import QFrame + from PySide2.QtUiTools import QUiLoader _logger = logging.getLogger(__name__) @@ -57,7 +61,7 @@ class UiLoader(QUiLoader): create a new instance of the top-level widget, but creates the user interface in an existing instance of the top-level class. - This mimics the behaviour of :func:`PyQt4.uic.loadUi`. + This mimics the behaviour of :func:`PyQt*.uic.loadUi`. """ def __init__(self, baseinstance, customWidgets=None): @@ -113,7 +117,7 @@ class UiLoader(QUiLoader): if self.baseinstance: # set an attribute for the new child widget on the base - # instance, just like PyQt4.uic.loadUi does. + # instance, just like PyQt*.uic.loadUi does. setattr(self.baseinstance, name, widget) # this outputs the various widget names, e.g. @@ -123,6 +127,43 @@ class UiLoader(QUiLoader): return widget +if "PySide2.QtCore" in sys.modules: + + class _Line(QFrame): + """Widget to use as 'Line' Qt designer""" + def __init__(self, parent=None): + super(_Line, self).__init__(parent) + self.setFrameShape(QFrame.HLine) + self.setFrameShadow(QFrame.Sunken) + + def getOrientation(self): + shape = self.frameShape() + if shape == QFrame.HLine: + return Qt.Horizontal + elif shape == QFrame.VLine: + return Qt.Vertical + else: + raise RuntimeError("Wrong shape: %d", shape) + + def setOrientation(self, orientation): + if orientation == Qt.Horizontal: + self.setFrameShape(QFrame.HLine) + elif orientation == Qt.Vertical: + self.setFrameShape(QFrame.VLine) + else: + raise ValueError("Unsupported orientation %s" % str(orientation)) + + orientation = Property("Qt::Orientation", getOrientation, setOrientation) + + + CUSTOM_WIDGETS = {"Line": _Line} + """Default custom widgets for `loadUi`""" + +else: # PySide support + CUSTOM_WIDGETS = {} + """Default custom widgets for `loadUi`""" + + def loadUi(uifile, baseinstance=None, package=None, resource_suffix=None): """ Dynamically load a user interface from the given ``uifile``. @@ -152,7 +193,7 @@ def loadUi(uifile, baseinstance=None, package=None, resource_suffix=None): _logger.warning( "loadUi resource_suffix parameter not implemented with PySide") - loader = UiLoader(baseinstance) + loader = UiLoader(baseinstance, customWidgets=CUSTOM_WIDGETS) widget = loader.load(uifile) QMetaObject.connectSlotsByName(widget) return widget diff --git a/silx/gui/qt/_qt.py b/silx/gui/qt/_qt.py index 6bf7d93..a4b9007 100644 --- a/silx/gui/qt/_qt.py +++ b/silx/gui/qt/_qt.py @@ -22,21 +22,7 @@ # THE SOFTWARE. # # ###########################################################################*/ -"""Common wrapper over Python Qt bindings: - -- `PyQt5 <http://pyqt.sourceforge.net/Docs/PyQt5/>`_, -- `PyQt4 <http://pyqt.sourceforge.net/Docs/PyQt4/>`_ or -- `PySide <http://www.pyside.org>`_. - -If a Qt binding is already loaded, it will use it, otherwise the different -Qt bindings are tried in this order: PySide2, PyQt4, PySide, PyQt5. - -The name of the loaded Qt binding is stored in the BINDING variable. - -For an alternative solution providing a structured namespace, -see `qtpy <https://pypi.python.org/pypi/QtPy/>`_ which -provides the namespace of PyQt5 over PyQt4, PySide and PySide2. -""" +"""Load Qt binding""" __authors__ = ["V.A. Sole"] __license__ = "MIT" @@ -47,15 +33,17 @@ import logging import sys import traceback +from ...utils.deprecation import deprecated_warning + _logger = logging.getLogger(__name__) BINDING = None -"""The name of the Qt binding in use: PyQt5, 'PyQt4, PySide2 or PySide.""" +"""The name of the Qt binding in use: PyQt5, PyQt4 or PySide2.""" QtBinding = None # noqa -"""The Qt binding module in use: PyQt5, PyQt4, PySide2 or PySide.""" +"""The Qt binding module in use: PyQt5, PyQt4 or PySide2.""" HAS_SVG = False """True if Qt provides support for Scalable Vector Graphics (QtSVG).""" @@ -84,17 +72,17 @@ else: # Then try Qt bindings import PyQt4 # noqa except ImportError: try: - import PySide # noqa + import PySide2 # noqa except ImportError: try: - import PySide2 # noqa + import PySide # noqa except ImportError: raise ImportError( 'No Qt wrapper found. Install PyQt5, PyQt4 or PySide2.') else: - BINDING = 'PySide2' + BINDING = 'PySide' else: - BINDING = 'PySide' + BINDING = 'PySide2' else: BINDING = 'PyQt4' else: @@ -103,6 +91,9 @@ else: # Then try Qt bindings if BINDING == 'PyQt4': _logger.debug('Using PyQt4 bindings') + deprecated_warning("Qt Binding", "PyQt4", + replacement='PyQt5', + since_version='0.9.0') if sys.version_info < (3, ): try: @@ -110,6 +101,11 @@ if BINDING == 'PyQt4': 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: _logger.warning("Cannot set sip API") @@ -144,6 +140,9 @@ if BINDING == 'PyQt4': elif BINDING == 'PySide': _logger.debug('Using PySide bindings') + deprecated_warning("Qt Binding", "PySide", + replacement='PySide2', + since_version='0.9.0') import PySide as QtBinding # noqa @@ -238,7 +237,7 @@ elif BINDING == 'PySide2': HAS_SVG = True # Import loadUi wrapper for PySide2 - # TODO from ._pyside_dynamic import loadUi # noqa + from ._pyside_dynamic import loadUi # noqa pyqtSignal = Signal diff --git a/silx/gui/qt/_utils.py b/silx/gui/qt/_utils.py index be55465..912f08c 100644 --- a/silx/gui/qt/_utils.py +++ b/silx/gui/qt/_utils.py @@ -36,8 +36,11 @@ from . import _qt as qt def supportedImageFormats(): """Return a set of string of file format extensions supported by the Qt runtime.""" - if sys.version_info[0] < 3 or qt.BINDING in ('PySide', 'PySide2'): + if sys.version_info[0] < 3 or qt.BINDING == 'PySide': convert = str + elif qt.BINDING == 'PySide2': + def convert(data): + return str(data.data(), 'ascii') else: convert = lambda data: str(data, 'ascii') formats = qt.QImageReader.supportedImageFormats() diff --git a/silx/gui/qt/inspect.py b/silx/gui/qt/inspect.py new file mode 100644 index 0000000..c6c2cbe --- /dev/null +++ b/silx/gui/qt/inspect.py @@ -0,0 +1,82 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2018 European Synchrotron Radiation Facility +# +# 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. +# +# ###########################################################################*/ +"""This module provides functions to access Qt C++ object state: + +- :func:`isValid` to check whether a QObject C++ pointer is valid. +- :func:`createdByPython` to check if a QObject was created from Python. +- :func:`ownedByPython` to check if a QObject is currently owned by Python. +""" + +__authors__ = ["T. Vincent"] +__license__ = "MIT" +__date__ = "08/10/2018" + + +from . import _qt as qt + + +if qt.BINDING in ('PyQt4', 'PyQt5'): + if qt.BINDING == 'PyQt5': + try: + from PyQt5.sip import isdeleted as _isdeleted # noqa + from PyQt5.sip import ispycreated as createdByPython # noqa + from PyQt5.sip import ispyowned as ownedByPython # noqa + except ImportError: + from sip import isdeleted as _isdeleted # noqa + from sip import ispycreated as createdByPython # noqa + from sip import ispyowned as ownedByPython # noqa + + else: # PyQt4 + from sip import isdeleted as _isdeleted # noqa + from sip import ispycreated as createdByPython # noqa + from sip import ispyowned as ownedByPython # noqa + + def isValid(obj): + """Returns True if underlying C++ object is valid. + + :param QObject obj: + :rtype: bool + """ + return not _isdeleted(obj) + +elif qt.BINDING == 'PySide2': + from PySide2.shiboken2 import isValid # noqa + from PySide2.shiboken2 import createdByPython # noqa + from PySide2.shiboken2 import ownedByPython # noqa + +elif qt.BINDING == 'PySide': + try: # Available through PySide + from PySide.shiboken import isValid # noqa + from PySide.shiboken import createdByPython # noqa + from PySide.shiboken import ownedByPython # noqa + except ImportError: # Available through standalone shiboken package + from Shiboken.shiboken import isValid # noqa + from Shiboken.shiboken import createdByPython # noqa + from Shiboken.shiboken import ownedByPython # noqa + +else: + raise ImportError("Unsupported Qt binding %s" % qt.BINDING) + +__all__ = ['isValid', 'createdByPython', 'ownedByPython'] |