summaryrefslogtreecommitdiff
path: root/src/silx/gui/qt/_qt.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/silx/gui/qt/_qt.py')
-rw-r--r--src/silx/gui/qt/_qt.py232
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()