diff options
Diffstat (limited to 'src/silx/gui/qt/_qt.py')
-rw-r--r-- | src/silx/gui/qt/_qt.py | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/src/silx/gui/qt/_qt.py b/src/silx/gui/qt/_qt.py new file mode 100644 index 0000000..f62f4c8 --- /dev/null +++ b/src/silx/gui/qt/_qt.py @@ -0,0 +1,232 @@ +# coding: utf-8 +# /*########################################################################## +# +# Copyright (c) 2004-2021 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. +# +# ###########################################################################*/ +"""Load Qt binding""" + +__authors__ = ["V.A. Sole"] +__license__ = "MIT" +__date__ = "23/05/2018" + + +import logging +import sys +import traceback + + +_logger = logging.getLogger(__name__) + + +BINDING = None +"""The name of the Qt binding in use: PyQt5, PySide2, PySide6.""" + +QtBinding = None # noqa +"""The Qt binding module in use: PyQt5, PySide2, PySide6.""" + +HAS_SVG = False +"""True if Qt provides support for Scalable Vector Graphics (QtSVG).""" + +HAS_OPENGL = False +"""True if Qt provides support for OpenGL (QtOpenGL).""" + +# First check for an already loaded wrapper +for _binding in ('PySide2', 'PyQt5', 'PySide6'): + if _binding + '.QtCore' in sys.modules: + BINDING = _binding + break +else: # Then try Qt bindings + try: + import PyQt5.QtCore # noqa + except ImportError: + if 'PyQt5' in sys.modules: + del sys.modules["PyQt5"] + try: + import PySide2.QtCore # noqa + except ImportError: + if 'PySide2' in sys.modules: + del sys.modules["PySide2"] + try: + import PySide6.QtCore # noqa + except ImportError: + if 'PySide6' in sys.modules: + del sys.modules["PySide6"] + raise ImportError( + 'No Qt wrapper found. Install PyQt5, PySide2, PySide6.') + else: + BINDING = 'PySide6' + else: + BINDING = 'PySide2' + else: + BINDING = 'PyQt5' + + +if BINDING == 'PyQt5': + _logger.debug('Using PyQt5 bindings') + + import PyQt5 as QtBinding # noqa + + from PyQt5.QtCore import * # noqa + from PyQt5.QtGui import * # noqa + from PyQt5.QtWidgets import * # noqa + from PyQt5.QtPrintSupport import * # noqa + + try: + from PyQt5.QtOpenGL import * # noqa + except ImportError: + _logger.info("PyQt5.QtOpenGL not available") + HAS_OPENGL = False + else: + HAS_OPENGL = True + + try: + from PyQt5.QtSvg import * # noqa + except ImportError: + _logger.info("PyQt5.QtSvg not available") + HAS_SVG = False + else: + HAS_SVG = True + + from PyQt5.uic import loadUi # noqa + + Signal = pyqtSignal + + Property = pyqtProperty + + Slot = pyqtSlot + + # Disable PyQt5's cooperative multi-inheritance since other bindings do not provide it. + # See https://www.riverbankcomputing.com/static/Docs/PyQt5/multiinheritance.html?highlight=inheritance + class _Foo(object): pass + class QObject(QObject, _Foo): pass + + +elif BINDING == 'PySide2': + _logger.debug('Using PySide2 bindings') + + import PySide2 as QtBinding # noqa + + from PySide2.QtCore import * # noqa + from PySide2.QtGui import * # noqa + from PySide2.QtWidgets import * # noqa + from PySide2.QtPrintSupport import * # noqa + + try: + from PySide2.QtOpenGL import * # noqa + except ImportError: + _logger.info("PySide2.QtOpenGL not available") + HAS_OPENGL = False + else: + HAS_OPENGL = True + + try: + from PySide2.QtSvg import * # noqa + except ImportError: + _logger.info("PySide2.QtSvg not available") + HAS_SVG = False + else: + HAS_SVG = True + + pyqtSignal = Signal + + # Qt6 compatibility: + # with PySide2 `exec` method has a special behavior + class _ExecMixIn: + """Mix-in class providind `exec` compatibility""" + def exec(self, *args, **kwargs): + return super().exec_(*args, **kwargs) + + # QtWidgets + class QApplication(_ExecMixIn, QApplication): pass + class QColorDialog(_ExecMixIn, QColorDialog): pass + class QDialog(_ExecMixIn, QDialog): pass + class QErrorMessage(_ExecMixIn, QErrorMessage): pass + class QFileDialog(_ExecMixIn, QFileDialog): pass + class QFontDialog(_ExecMixIn, QFontDialog): pass + class QInputDialog(_ExecMixIn, QInputDialog): pass + class QMenu(_ExecMixIn, QMenu): pass + class QMessageBox(_ExecMixIn, QMessageBox): pass + class QProgressDialog(_ExecMixIn, QProgressDialog): pass + #QtCore + class QCoreApplication(_ExecMixIn, QCoreApplication): pass + class QEventLoop(_ExecMixIn, QEventLoop): pass + if hasattr(QTextStreamManipulator, "exec_"): + # exec_ only wrapped in PySide2 and NOT in PyQt5 + class QTextStreamManipulator(_ExecMixIn, QTextStreamManipulator): pass + class QThread(_ExecMixIn, QThread): pass + + +elif BINDING == 'PySide6': + _logger.debug('Using PySide6 bindings') + + import PySide6 as QtBinding # noqa + + from PySide6.QtCore import * # noqa + from PySide6.QtGui import * # noqa + from PySide6.QtWidgets import * # noqa + from PySide6.QtPrintSupport import * # noqa + + try: + from PySide6.QtOpenGL import * # noqa + from PySide6.QtOpenGLWidgets import QOpenGLWidget # noqa + except ImportError: + _logger.info("PySide6.QtOpenGL not available") + HAS_OPENGL = False + else: + HAS_OPENGL = True + + try: + from PySide6.QtSvg import * # noqa + except ImportError: + _logger.info("PySide6.QtSvg not available") + HAS_SVG = False + else: + HAS_SVG = True + + pyqtSignal = Signal + +else: + raise ImportError('No Qt wrapper found. Install PyQt5, PySide2 or PySide6') + + +# provide a exception handler but not implement it by default +def exceptionHandler(type_, value, trace): + """ + This exception handler prevents quitting to the command line when there is + an unhandled exception while processing a Qt signal. + + The script/application willing to use it should implement code similar to: + + .. code-block:: python + + if __name__ == "__main__": + sys.excepthook = qt.exceptionHandler + + """ + _logger.error("%s %s %s", type_, value, ''.join(traceback.format_tb(trace))) + msg = QMessageBox() + msg.setWindowTitle("Unhandled exception") + msg.setIcon(QMessageBox.Critical) + msg.setInformativeText("%s %s\nPlease report details" % (type_, value)) + msg.setDetailedText(("%s " % value) + ''.join(traceback.format_tb(trace))) + msg.raise_() + msg.exec() |