diff options
Diffstat (limited to 'silx/gui/qt')
-rw-r--r-- | silx/gui/qt/__init__.py | 60 | ||||
-rw-r--r-- | silx/gui/qt/_macosx.py | 68 | ||||
-rw-r--r-- | silx/gui/qt/_pyside_dynamic.py | 239 | ||||
-rw-r--r-- | silx/gui/qt/_pyside_missing.py | 274 | ||||
-rw-r--r-- | silx/gui/qt/_qt.py | 289 | ||||
-rw-r--r-- | silx/gui/qt/_utils.py | 71 | ||||
-rw-r--r-- | silx/gui/qt/inspect.py | 87 |
7 files changed, 0 insertions, 1088 deletions
diff --git a/silx/gui/qt/__init__.py b/silx/gui/qt/__init__.py deleted file mode 100644 index ace2841..0000000 --- a/silx/gui/qt/__init__.py +++ /dev/null @@ -1,60 +0,0 @@ -# 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. -# -# ###########################################################################*/ -"""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/>`_ - -If a Qt binding is already loaded, it will use it, otherwise the different -Qt bindings are tried in this order: PyQt5, PyQt4, PySide2. - -The name of the loaded Qt binding is stored in the BINDING variable. - -This module provides a flat namespace over Qt bindings by importing -all symbols from **QtCore** and **QtGui** packages and if available -from **QtOpenGL** and **QtSvg** packages. -For **PyQt5**, it also imports all symbols from **QtWidgets** and -**QtPrintSupport** packages. - -Example of using :mod:`silx.gui.qt` module: - ->>> from silx.gui import qt ->>> app = qt.QApplication([]) ->>> widget = qt.QWidget() - -For an alternative solution providing a structured namespace, -see `qtpy <https://pypi.org/project/QtPy/>`_ which -provides the namespace of PyQt5 over PyQt4, PySide and PySide2. -""" - -from ._qt import * # noqa -from ._utils import * # noqa - - -if sys.platform == "darwin": - if BINDING in ["PySide", "PyQt4"]: - from . import _macosx - _macosx.patch_QUrl_toLocalFile() diff --git a/silx/gui/qt/_macosx.py b/silx/gui/qt/_macosx.py deleted file mode 100644 index 07f3143..0000000 --- a/silx/gui/qt/_macosx.py +++ /dev/null @@ -1,68 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2004-2016 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. -# -# ###########################################################################*/ -""" -Patches for Mac OS X -""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "30/11/2016" - - -def patch_QUrl_toLocalFile(): - """Apply a monkey-patch on qt.QUrl to allow to reach filename when the URL - come from a MIME data from a file drop. Without, `QUrl.toLocalName` with - some version of Mac OS X returns a path which looks like - `/.file/id=180.112`. - - Qt5 is or will be patch, but Qt4 and PySide are not. - - This fix uses the file URL and use an subprocess with an - AppleScript. The script convert the URI into a posix path. - The interpreter (osascript) is available on default OS X installs. - - See https://bugreports.qt.io/browse/QTBUG-40449 - """ - from ._qt import QUrl - import subprocess - - def QUrl_toLocalFile(self): - path = QUrl._oldToLocalFile(self) - if not path.startswith("/.file/id="): - return path - - url = self.toString() - script = 'get posix path of my posix file \"%s\" -- kthxbai' % url - try: - p = subprocess.Popen(["osascript", "-e", script], stdout=subprocess.PIPE) - out, _err = p.communicate() - if p.returncode == 0: - return out.strip() - except OSError: - pass - return path - - QUrl._oldToLocalFile = QUrl.toLocalFile - QUrl.toLocalFile = QUrl_toLocalFile diff --git a/silx/gui/qt/_pyside_dynamic.py b/silx/gui/qt/_pyside_dynamic.py deleted file mode 100644 index 6013416..0000000 --- a/silx/gui/qt/_pyside_dynamic.py +++ /dev/null @@ -1,239 +0,0 @@ -# -*- coding: utf-8 -*- - -# Taken from: https://gist.github.com/cpbotha/1b42a20c8f3eb9bb7cb8 -# Plus: https://github.com/spyder-ide/qtpy/commit/001a862c401d757feb63025f88dbb4601d353c84 - -# Copyright (c) 2011 Sebastian Wiesner <lunaryorn@gmail.com> -# Modifications by Charl Botha <cpbotha@vxlabs.com> -# * customWidgets support (registerCustomWidget() causes segfault in -# pyside 1.1.2 on Ubuntu 12.04 x86_64) -# * workingDirectory support in loadUi - -# found this here: -# https://github.com/lunaryorn/snippets/blob/master/qt4/designer/pyside_dynamic.py - -# 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. - -""" - How to load a user interface dynamically with PySide. - - .. moduleauthor:: Sebastian Wiesner <lunaryorn@gmail.com> -""" - -from __future__ import (print_function, division, unicode_literals, - absolute_import) - -import logging -import sys - -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__) - - -class UiLoader(QUiLoader): - """ - Subclass :class:`~PySide.QtUiTools.QUiLoader` to create the user interface - in a base instance. - - Unlike :class:`~PySide.QtUiTools.QUiLoader` itself this class does not - 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:`PyQt*.uic.loadUi`. - """ - - def __init__(self, baseinstance, customWidgets=None): - """ - Create a loader for the given ``baseinstance``. - - The user interface is created in ``baseinstance``, which must be an - instance of the top-level class in the user interface to load, or a - subclass thereof. - - ``customWidgets`` is a dictionary mapping from class name to class - object for widgets that you've promoted in the Qt Designer - interface. Usually, this should be done by calling - registerCustomWidget on the QUiLoader, but - with PySide 1.1.2 on Ubuntu 12.04 x86_64 this causes a segfault. - - ``parent`` is the parent object of this loader. - """ - - QUiLoader.__init__(self, baseinstance) - self.baseinstance = baseinstance - self.customWidgets = {} - self.uifile = None - self.customWidgets.update(customWidgets) - - def createWidget(self, class_name, parent=None, name=''): - """ - Function that is called for each widget defined in ui file, - overridden here to populate baseinstance instead. - """ - - if parent is None and self.baseinstance: - # supposed to create the top-level widget, return the base instance - # instead - return self.baseinstance - - else: - if class_name in self.availableWidgets(): - # create a new widget for child widgets - widget = QUiLoader.createWidget(self, class_name, parent, name) - - else: - # if not in the list of availableWidgets, - # must be a custom widget - # this will raise KeyError if the user has not supplied the - # relevant class_name in the dictionary, or TypeError, if - # customWidgets is None - if class_name not in self.customWidgets: - raise Exception('No custom widget ' + class_name + - ' found in customWidgets param of' + - 'UiFile %s.' % self.uifile) - try: - widget = self.customWidgets[class_name](parent) - except Exception: - _logger.error("Fail to instanciate widget %s from file %s", class_name, self.uifile) - raise - - if self.baseinstance: - # set an attribute for the new child widget on the base - # instance, just like PyQt*.uic.loadUi does. - setattr(self.baseinstance, name, widget) - - # this outputs the various widget names, e.g. - # sampleGraphicsView, dockWidget, samplesTableView etc. - # print(name) - - return widget - - def _parse_custom_widgets(self, ui_file): - """ - This function is used to parse a ui file and look for the <customwidgets> - section, then automatically load all the custom widget classes. - """ - import importlib - from xml.etree.ElementTree import ElementTree - - # Parse the UI file - etree = ElementTree() - ui = etree.parse(ui_file) - - # Get the customwidgets section - custom_widgets = ui.find('customwidgets') - - if custom_widgets is None: - return - - custom_widget_classes = {} - - for custom_widget in custom_widgets.getchildren(): - - cw_class = custom_widget.find('class').text - cw_header = custom_widget.find('header').text - - module = importlib.import_module(cw_header) - - custom_widget_classes[cw_class] = getattr(module, cw_class) - - self.customWidgets.update(custom_widget_classes) - - def load(self, uifile): - self._parse_custom_widgets(uifile) - self.uifile = uifile - return QUiLoader.load(self, uifile) - - -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``. - - ``uifile`` is a string containing a file name of the UI file to load. - - If ``baseinstance`` is ``None``, the a new instance of the top-level widget - will be created. Otherwise, the user interface is created within the given - ``baseinstance``. In this case ``baseinstance`` must be an instance of the - top-level widget class in the UI file to load, or a subclass thereof. In - other words, if you've created a ``QMainWindow`` interface in the designer, - ``baseinstance`` must be a ``QMainWindow`` or a subclass thereof, too. You - cannot load a ``QMainWindow`` UI file with a plain - :class:`~PySide.QtGui.QWidget` as ``baseinstance``. - - :method:`~PySide.QtCore.QMetaObject.connectSlotsByName()` is called on the - created user interface, so you can implemented your slots according to its - conventions in your widget class. - - Return ``baseinstance``, if ``baseinstance`` is not ``None``. Otherwise - return the newly created instance of the user interface. - """ - if package is not None: - _logger.warning( - "loadUi package parameter not implemented with PySide") - if resource_suffix is not None: - _logger.warning( - "loadUi resource_suffix parameter not implemented with PySide") - - loader = UiLoader(baseinstance, customWidgets=CUSTOM_WIDGETS) - widget = loader.load(uifile) - QMetaObject.connectSlotsByName(widget) - return widget diff --git a/silx/gui/qt/_pyside_missing.py b/silx/gui/qt/_pyside_missing.py deleted file mode 100644 index a7e2781..0000000 --- a/silx/gui/qt/_pyside_missing.py +++ /dev/null @@ -1,274 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2017 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. -# -# ###########################################################################*/ -""" -Python implementation of classes which are not provided by default by PySide. -""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "17/01/2017" - - -from PySide.QtGui import QAbstractProxyModel -from PySide.QtCore import QModelIndex -from PySide.QtCore import Qt -from PySide.QtGui import QItemSelection -from PySide.QtGui import QItemSelectionRange - - -class QIdentityProxyModel(QAbstractProxyModel): - """Python translation of the source code of Qt c++ file""" - - def __init__(self, parent=None): - super(QIdentityProxyModel, self).__init__(parent) - self.__ignoreNextLayoutAboutToBeChanged = False - self.__ignoreNextLayoutChanged = False - self.__persistentIndexes = [] - - def columnCount(self, parent): - parent = self.mapToSource(parent) - return self.sourceModel().columnCount(parent) - - def dropMimeData(self, data, action, row, column, parent): - parent = self.mapToSource(parent) - return self.sourceModel().dropMimeData(data, action, row, column, parent) - - def index(self, row, column, parent=QModelIndex()): - parent = self.mapToSource(parent) - i = self.sourceModel().index(row, column, parent) - return self.mapFromSource(i) - - def insertColumns(self, column, count, parent=QModelIndex()): - parent = self.mapToSource(parent) - return self.sourceModel().insertColumns(column, count, parent) - - def insertRows(self, row, count, parent=QModelIndex()): - parent = self.mapToSource(parent) - return self.sourceModel().insertRows(row, count, parent) - - def mapFromSource(self, sourceIndex): - if self.sourceModel() is None or not sourceIndex.isValid(): - return QModelIndex() - index = self.createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer()) - return index - - def mapSelectionFromSource(self, sourceSelection): - proxySelection = QItemSelection() - if self.sourceModel() is None: - return proxySelection - - cursor = sourceSelection.constBegin() - end = sourceSelection.constEnd() - while cursor != end: - topLeft = self.mapFromSource(cursor.topLeft()) - bottomRight = self.mapFromSource(cursor.bottomRight()) - proxyRange = QItemSelectionRange(topLeft, bottomRight) - proxySelection.append(proxyRange) - cursor += 1 - return proxySelection - - def mapSelectionToSource(self, proxySelection): - sourceSelection = QItemSelection() - if self.sourceModel() is None: - return sourceSelection - - cursor = proxySelection.constBegin() - end = proxySelection.constEnd() - while cursor != end: - topLeft = self.mapToSource(cursor.topLeft()) - bottomRight = self.mapToSource(cursor.bottomRight()) - sourceRange = QItemSelectionRange(topLeft, bottomRight) - sourceSelection.append(sourceRange) - cursor += 1 - return sourceSelection - - def mapToSource(self, proxyIndex): - if self.sourceModel() is None or not proxyIndex.isValid(): - return QModelIndex() - return self.sourceModel().createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer()) - - def match(self, start, role, value, hits=1, flags=Qt.MatchFlags(Qt.MatchStartsWith | Qt.MatchWrap)): - if self.sourceModel() is None: - return [] - - start = self.mapToSource(start) - sourceList = self.sourceModel().match(start, role, value, hits, flags) - proxyList = [] - for cursor in sourceList: - proxyList.append(self.mapFromSource(cursor)) - return proxyList - - def parent(self, child): - sourceIndex = self.mapToSource(child) - sourceParent = sourceIndex.parent() - index = self.mapFromSource(sourceParent) - return index - - def removeColumns(self, column, count, parent=QModelIndex()): - parent = self.mapToSource(parent) - return self.sourceModel().removeColumns(column, count, parent) - - def removeRows(self, row, count, parent=QModelIndex()): - parent = self.mapToSource(parent) - return self.sourceModel().removeRows(row, count, parent) - - def rowCount(self, parent=QModelIndex()): - parent = self.mapToSource(parent) - return self.sourceModel().rowCount(parent) - - def setSourceModel(self, newSourceModel): - """Bind and unbind the source model events""" - self.beginResetModel() - - sourceModel = self.sourceModel() - if sourceModel is not None: - sourceModel.rowsAboutToBeInserted.disconnect(self.__rowsAboutToBeInserted) - sourceModel.rowsInserted.disconnect(self.__rowsInserted) - sourceModel.rowsAboutToBeRemoved.disconnect(self.__rowsAboutToBeRemoved) - sourceModel.rowsRemoved.disconnect(self.__rowsRemoved) - sourceModel.rowsAboutToBeMoved.disconnect(self.__rowsAboutToBeMoved) - sourceModel.rowsMoved.disconnect(self.__rowsMoved) - sourceModel.columnsAboutToBeInserted.disconnect(self.__columnsAboutToBeInserted) - sourceModel.columnsInserted.disconnect(self.__columnsInserted) - sourceModel.columnsAboutToBeRemoved.disconnect(self.__columnsAboutToBeRemoved) - sourceModel.columnsRemoved.disconnect(self.__columnsRemoved) - sourceModel.columnsAboutToBeMoved.disconnect(self.__columnsAboutToBeMoved) - sourceModel.columnsMoved.disconnect(self.__columnsMoved) - sourceModel.modelAboutToBeReset.disconnect(self.__modelAboutToBeReset) - sourceModel.modelReset.disconnect(self.__modelReset) - sourceModel.dataChanged.disconnect(self.__dataChanged) - sourceModel.headerDataChanged.disconnect(self.__headerDataChanged) - sourceModel.layoutAboutToBeChanged.disconnect(self.__layoutAboutToBeChanged) - sourceModel.layoutChanged.disconnect(self.__layoutChanged) - - super(QIdentityProxyModel, self).setSourceModel(newSourceModel) - - sourceModel = self.sourceModel() - if sourceModel is not None: - sourceModel.rowsAboutToBeInserted.connect(self.__rowsAboutToBeInserted) - sourceModel.rowsInserted.connect(self.__rowsInserted) - sourceModel.rowsAboutToBeRemoved.connect(self.__rowsAboutToBeRemoved) - sourceModel.rowsRemoved.connect(self.__rowsRemoved) - sourceModel.rowsAboutToBeMoved.connect(self.__rowsAboutToBeMoved) - sourceModel.rowsMoved.connect(self.__rowsMoved) - sourceModel.columnsAboutToBeInserted.connect(self.__columnsAboutToBeInserted) - sourceModel.columnsInserted.connect(self.__columnsInserted) - sourceModel.columnsAboutToBeRemoved.connect(self.__columnsAboutToBeRemoved) - sourceModel.columnsRemoved.connect(self.__columnsRemoved) - sourceModel.columnsAboutToBeMoved.connect(self.__columnsAboutToBeMoved) - sourceModel.columnsMoved.connect(self.__columnsMoved) - sourceModel.modelAboutToBeReset.connect(self.__modelAboutToBeReset) - sourceModel.modelReset.connect(self.__modelReset) - sourceModel.dataChanged.connect(self.__dataChanged) - sourceModel.headerDataChanged.connect(self.__headerDataChanged) - sourceModel.layoutAboutToBeChanged.connect(self.__layoutAboutToBeChanged) - sourceModel.layoutChanged.connect(self.__layoutChanged) - - self.endResetModel() - - def __columnsAboutToBeInserted(self, parent, start, end): - parent = self.mapFromSource(parent) - self.beginInsertColumns(parent, start, end) - - def __columnsAboutToBeMoved(self, sourceParent, sourceStart, sourceEnd, destParent, dest): - sourceParent = self.mapFromSource(sourceParent) - destParent = self.mapFromSource(destParent) - self.beginMoveColumns(sourceParent, sourceStart, sourceEnd, destParent, dest) - - def __columnsAboutToBeRemoved(self, parent, start, end): - parent = self.mapFromSource(parent) - self.beginRemoveColumns(parent, start, end) - - def __columnsInserted(self, parent, start, end): - self.endInsertColumns() - - def __columnsMoved(self, sourceParent, sourceStart, sourceEnd, destParent, dest): - self.endMoveColumns() - - def __columnsRemoved(self, parent, start, end): - self.endRemoveColumns() - - def __dataChanged(self, topLeft, bottomRight): - topLeft = self.mapFromSource(topLeft) - bottomRight = self.mapFromSource(bottomRight) - self.dataChanged(topLeft, bottomRight) - - def __headerDataChanged(self, orientation, first, last): - self.headerDataChanged(orientation, first, last) - - def __layoutAboutToBeChanged(self): - """Store persistent indexes""" - if self.__ignoreNextLayoutAboutToBeChanged: - return - - for proxyPersistentIndex in self.persistentIndexList(): - self.__proxyIndexes.append() - sourcePersistentIndex = self.mapToSource(proxyPersistentIndex) - mapping = proxyPersistentIndex, sourcePersistentIndex - self.__persistentIndexes.append(mapping) - - self.layoutAboutToBeChanged() - - def __layoutChanged(self): - """Restore persistent indexes""" - if self.__ignoreNextLayoutChanged: - return - - for mapping in self.__persistentIndexes: - proxyIndex, sourcePersistentIndex = mapping - sourcePersistentIndex = self.mapFromSource(sourcePersistentIndex) - self.changePersistentIndex(proxyIndex, sourcePersistentIndex) - - self.__persistentIndexes = [] - - self.layoutChanged() - - def __modelAboutToBeReset(self): - self.beginResetModel() - - def __modelReset(self): - self.endResetModel() - - def __rowsAboutToBeInserted(self, parent, start, end): - parent = self.mapFromSource(parent) - self.beginInsertRows(parent, start, end) - - def __rowsAboutToBeMoved(self, sourceParent, sourceStart, sourceEnd, destParent, dest): - sourceParent = self.mapFromSource(sourceParent) - destParent = self.mapFromSource(destParent) - self.beginMoveRows(sourceParent, sourceStart, sourceEnd, destParent, dest) - - def __rowsAboutToBeRemoved(self, parent, start, end): - parent = self.mapFromSource(parent) - self.beginRemoveRows(parent, start, end) - - def __rowsInserted(self, parent, start, end): - self.endInsertRows() - - def __rowsMoved(self, sourceParent, sourceStart, sourceEnd, destParent, dest): - self.endMoveRows() - - def __rowsRemoved(self, parent, start, end): - self.endRemoveRows() diff --git a/silx/gui/qt/_qt.py b/silx/gui/qt/_qt.py deleted file mode 100644 index 29a6354..0000000 --- a/silx/gui/qt/_qt.py +++ /dev/null @@ -1,289 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2004-2020 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 - -from ...utils.deprecation import deprecated_warning - - -_logger = logging.getLogger(__name__) - - -BINDING = None -"""The name of the Qt binding in use: PyQt5, PyQt4 or PySide2.""" - -QtBinding = None # noqa -"""The Qt binding module in use: PyQt5, PyQt4 or PySide2.""" - -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 -if 'PySide2.QtCore' in sys.modules: - BINDING = 'PySide2' - -elif 'PySide.QtCore' in sys.modules: - BINDING = 'PySide' - -elif 'PyQt5.QtCore' in sys.modules: - BINDING = 'PyQt5' - -elif 'PyQt4.QtCore' in sys.modules: - BINDING = 'PyQt4' - -else: # Then try Qt bindings - try: - import PyQt5.QtCore # noqa - except ImportError: - if 'PyQt5' in sys.modules: - del sys.modules["PyQt5"] - try: - import sip - 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) - import PyQt4.QtCore # noqa - except ImportError: - if 'PyQt4' in sys.modules: - del sys.modules["sip"] - del sys.modules["PyQt4"] - try: - import PySide2.QtCore # noqa - except ImportError: - if 'PySide2' in sys.modules: - del sys.modules["PySide2"] - try: - import PySide.QtCore # noqa - except ImportError: - if 'PySide' in sys.modules: - del sys.modules["PySide"] - raise ImportError( - 'No Qt wrapper found. Install PyQt5, PyQt4 or PySide2.') - else: - BINDING = 'PySide' - else: - BINDING = 'PySide2' - else: - BINDING = 'PyQt4' - else: - BINDING = 'PyQt5' - - -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: - import sip - 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") - - import PyQt4 as QtBinding # noqa - - from PyQt4.QtCore import * # noqa - from PyQt4.QtGui import * # noqa - - try: - from PyQt4.QtOpenGL import * # noqa - except ImportError: - _logger.info("PyQt4.QtOpenGL not available") - HAS_OPENGL = False - else: - HAS_OPENGL = True - - try: - from PyQt4.QtSvg import * # noqa - except ImportError: - _logger.info("PyQt4.QtSvg not available") - HAS_SVG = False - else: - HAS_SVG = True - - from PyQt4.uic import loadUi # noqa - - Signal = pyqtSignal - - Property = pyqtProperty - - Slot = pyqtSlot - -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 - - from PySide.QtCore import * # noqa - from PySide.QtGui import * # noqa - - try: - from PySide.QtOpenGL import * # noqa - except ImportError: - _logger.info("PySide.QtOpenGL not available") - HAS_OPENGL = False - else: - HAS_OPENGL = True - - try: - from PySide.QtSvg import * # noqa - except ImportError: - _logger.info("PySide.QtSvg not available") - HAS_SVG = False - else: - HAS_SVG = True - - pyqtSignal = Signal - - # Import loadUi wrapper for PySide - from ._pyside_dynamic import loadUi # noqa - - # Import missing classes - if not hasattr(locals(), "QIdentityProxyModel"): - from ._pyside_missing import QIdentityProxyModel # noqa - -elif 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("PySide.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 - - # Import loadUi wrapper for PySide2 - from ._pyside_dynamic import loadUi # noqa - - pyqtSignal = Signal - -else: - raise ImportError('No Qt wrapper found. Install PyQt4, PyQt5, PySide2') - - -# 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_() diff --git a/silx/gui/qt/_utils.py b/silx/gui/qt/_utils.py deleted file mode 100644 index 4a7a1c0..0000000 --- a/silx/gui/qt/_utils.py +++ /dev/null @@ -1,71 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2004-2019 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 convenient functions related to Qt. -""" - -__authors__ = ["V. Valls"] -__license__ = "MIT" -__date__ = "30/11/2016" - - -import sys as _sys -from . import _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 == '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() - return set([convert(data) for data in formats]) - - -__globalThreadPoolInstance = None -"""Store the own silx global thread pool""" - - -def silxGlobalThreadPool(): - """"Manage an own QThreadPool to avoid issue on Qt5 Windows with the - default Qt global thread pool. - - A thread pool is create in lazy loading. With a maximum of 4 threads. - Else `qt.Thread.idealThreadCount()` is used. - - :rtype: qt.QThreadPool - """ - global __globalThreadPoolInstance - if __globalThreadPoolInstance is None: - tp = _qt.QThreadPool() - # Setting maxThreadCount fixes a segfault with PyQt 5.9.1 on Windows - maxThreadCount = min(4, tp.maxThreadCount()) - tp.setMaxThreadCount(maxThreadCount) - __globalThreadPoolInstance = tp - return __globalThreadPoolInstance diff --git a/silx/gui/qt/inspect.py b/silx/gui/qt/inspect.py deleted file mode 100644 index 3c08835..0000000 --- a/silx/gui/qt/inspect.py +++ /dev/null @@ -1,87 +0,0 @@ -# coding: utf-8 -# /*########################################################################## -# -# Copyright (c) 2018-2019 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': - try: - from PySide2.shiboken2 import isValid # noqa - from PySide2.shiboken2 import createdByPython # noqa - from PySide2.shiboken2 import ownedByPython # noqa - except ImportError: - from shiboken2 import isValid # noqa - from shiboken2 import createdByPython # noqa - from 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'] |