summaryrefslogtreecommitdiff
path: root/silx/gui/dialog/AbstractDataFileDialog.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/dialog/AbstractDataFileDialog.py')
-rw-r--r--silx/gui/dialog/AbstractDataFileDialog.py1742
1 files changed, 0 insertions, 1742 deletions
diff --git a/silx/gui/dialog/AbstractDataFileDialog.py b/silx/gui/dialog/AbstractDataFileDialog.py
deleted file mode 100644
index 29e7bb5..0000000
--- a/silx/gui/dialog/AbstractDataFileDialog.py
+++ /dev/null
@@ -1,1742 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-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 contains an :class:`AbstractDataFileDialog`.
-"""
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "05/03/2019"
-
-
-import sys
-import os
-import logging
-import functools
-from distutils.version import LooseVersion
-
-import numpy
-import six
-
-import silx.io.url
-from silx.gui import qt
-from silx.gui.hdf5.Hdf5TreeModel import Hdf5TreeModel
-from . import utils
-from .FileTypeComboBox import FileTypeComboBox
-
-import fabio
-
-
-_logger = logging.getLogger(__name__)
-
-
-DEFAULT_SIDEBAR_URL = True
-"""Set it to false to disable initilializing of the sidebar urls with the
-default Qt list. This could allow to disable a behaviour known to segfault on
-some version of PyQt."""
-
-
-class _IconProvider(object):
-
- FileDialogToParentDir = qt.QStyle.SP_CustomBase + 1
-
- FileDialogToParentFile = qt.QStyle.SP_CustomBase + 2
-
- def __init__(self):
- self.__iconFileDialogToParentDir = None
- self.__iconFileDialogToParentFile = None
-
- def _createIconToParent(self, standardPixmap):
- """
-
- FIXME: It have to be tested for some OS (arrow icon do not have always
- the same direction)
- """
- style = qt.QApplication.style()
- baseIcon = style.standardIcon(qt.QStyle.SP_FileDialogToParent)
- backgroundIcon = style.standardIcon(standardPixmap)
- icon = qt.QIcon()
-
- sizes = baseIcon.availableSizes()
- sizes = sorted(sizes, key=lambda s: s.height())
- sizes = filter(lambda s: s.height() < 100, sizes)
- sizes = list(sizes)
- if len(sizes) > 0:
- baseSize = sizes[-1]
- else:
- baseSize = baseIcon.availableSizes()[0]
- size = qt.QSize(baseSize.width(), baseSize.height() * 3 // 2)
-
- modes = [qt.QIcon.Normal, qt.QIcon.Disabled]
- for mode in modes:
- pixmap = qt.QPixmap(size)
- pixmap.fill(qt.Qt.transparent)
- painter = qt.QPainter(pixmap)
- painter.drawPixmap(0, 0, backgroundIcon.pixmap(baseSize, mode=mode))
- painter.drawPixmap(0, size.height() // 3, baseIcon.pixmap(baseSize, mode=mode))
- painter.end()
- icon.addPixmap(pixmap, mode=mode)
-
- return icon
-
- def getFileDialogToParentDir(self):
- if self.__iconFileDialogToParentDir is None:
- self.__iconFileDialogToParentDir = self._createIconToParent(qt.QStyle.SP_DirIcon)
- return self.__iconFileDialogToParentDir
-
- def getFileDialogToParentFile(self):
- if self.__iconFileDialogToParentFile is None:
- self.__iconFileDialogToParentFile = self._createIconToParent(qt.QStyle.SP_FileIcon)
- return self.__iconFileDialogToParentFile
-
- def icon(self, kind):
- if kind == self.FileDialogToParentDir:
- return self.getFileDialogToParentDir()
- elif kind == self.FileDialogToParentFile:
- return self.getFileDialogToParentFile()
- else:
- style = qt.QApplication.style()
- icon = style.standardIcon(kind)
- return icon
-
-
-class _SideBar(qt.QListView):
- """Sidebar containing shortcuts for common directories"""
-
- def __init__(self, parent=None):
- super(_SideBar, self).__init__(parent)
- self.__iconProvider = qt.QFileIconProvider()
- self.setUniformItemSizes(True)
- model = qt.QStandardItemModel(self)
- self.setModel(model)
- self._initModel()
- self.setEditTriggers(qt.QAbstractItemView.NoEditTriggers)
-
- def iconProvider(self):
- return self.__iconProvider
-
- def _initModel(self):
- urls = self._getDefaultUrls()
- self.setUrls(urls)
-
- def _getDefaultUrls(self):
- """Returns the default shortcuts.
-
- It uses the default QFileDialog shortcuts if it is possible, else
- provides a link to the computer's root and the user's home.
-
- :rtype: List[str]
- """
- urls = []
- version = LooseVersion(qt.qVersion())
- feed_sidebar = True
-
- if not DEFAULT_SIDEBAR_URL:
- _logger.debug("Skip default sidebar URLs (from setted variable)")
- feed_sidebar = False
- elif version.version[0] == 4 and sys.platform in ["win32"]:
- # Avoid locking the GUI 5min in case of use of network driver
- _logger.debug("Skip default sidebar URLs (avoid lock when using network drivers)")
- feed_sidebar = False
- elif version < LooseVersion("5.11.2") and qt.BINDING == "PyQt5" and sys.platform in ["linux", "linux2"]:
- # Avoid segfault on PyQt5 + gtk
- _logger.debug("Skip default sidebar URLs (avoid PyQt5 segfault)")
- feed_sidebar = False
-
- if feed_sidebar:
- # Get default shortcut
- # There is no other way
- d = qt.QFileDialog(self)
- # Needed to be able to reach the sidebar urls
- d.setOption(qt.QFileDialog.DontUseNativeDialog, True)
- urls = d.sidebarUrls()
- d.deleteLater()
- d = None
-
- if len(urls) == 0:
- urls.append(qt.QUrl("file://"))
- urls.append(qt.QUrl.fromLocalFile(qt.QDir.homePath()))
-
- return urls
-
- def setSelectedPath(self, path):
- selected = None
- model = self.model()
- for i in range(model.rowCount()):
- index = model.index(i, 0)
- url = model.data(index, qt.Qt.UserRole)
- urlPath = url.toLocalFile()
- if path == urlPath:
- selected = index
-
- selectionModel = self.selectionModel()
- if selected is not None:
- selectionModel.setCurrentIndex(selected, qt.QItemSelectionModel.ClearAndSelect)
- else:
- selectionModel.clear()
-
- def setUrls(self, urls):
- model = self.model()
- model.clear()
-
- names = {}
- names[qt.QDir.rootPath()] = "Computer"
- names[qt.QDir.homePath()] = "Home"
-
- style = qt.QApplication.style()
- iconProvider = self.iconProvider()
- for url in urls:
- path = url.toLocalFile()
- if path == "":
- if sys.platform != "win32":
- url = qt.QUrl(qt.QDir.rootPath())
- name = "Computer"
- icon = style.standardIcon(qt.QStyle.SP_ComputerIcon)
- else:
- fileInfo = qt.QFileInfo(path)
- name = names.get(path, fileInfo.fileName())
- icon = iconProvider.icon(fileInfo)
-
- if icon.isNull():
- icon = style.standardIcon(qt.QStyle.SP_MessageBoxCritical)
-
- item = qt.QStandardItem()
- item.setText(name)
- item.setIcon(icon)
- item.setData(url, role=qt.Qt.UserRole)
- model.appendRow(item)
-
- def urls(self):
- result = []
- model = self.model()
- for i in range(model.rowCount()):
- index = model.index(i, 0)
- url = model.data(index, qt.Qt.UserRole)
- result.append(url)
- return result
-
- def sizeHint(self):
- index = self.model().index(0, 0)
- return self.sizeHintForIndex(index) + qt.QSize(2 * self.frameWidth(), 2 * self.frameWidth())
-
-
-class _Browser(qt.QStackedWidget):
-
- activated = qt.Signal(qt.QModelIndex)
- selected = qt.Signal(qt.QModelIndex)
- rootIndexChanged = qt.Signal(qt.QModelIndex)
-
- def __init__(self, parent, listView, detailView):
- qt.QStackedWidget.__init__(self, parent)
- self.__listView = listView
- self.__detailView = detailView
- self.insertWidget(0, self.__listView)
- self.insertWidget(1, self.__detailView)
-
- self.__listView.activated.connect(self.__emitActivated)
- self.__detailView.activated.connect(self.__emitActivated)
-
- def __emitActivated(self, index):
- self.activated.emit(index)
-
- def __emitSelected(self, selected, deselected):
- index = self.selectedIndex()
- if index is not None:
- self.selected.emit(index)
-
- def selectedIndex(self):
- if self.currentIndex() == 0:
- selectionModel = self.__listView.selectionModel()
- else:
- selectionModel = self.__detailView.selectionModel()
-
- if selectionModel is None:
- return None
-
- indexes = selectionModel.selectedIndexes()
- # Filter non-main columns
- indexes = [i for i in indexes if i.column() == 0]
- if len(indexes) == 1:
- index = indexes[0]
- return index
- return None
-
- def model(self):
- """Returns the current model."""
- if self.currentIndex() == 0:
- return self.__listView.model()
- else:
- return self.__detailView.model()
-
- def selectIndex(self, index):
- if self.currentIndex() == 0:
- selectionModel = self.__listView.selectionModel()
- else:
- selectionModel = self.__detailView.selectionModel()
- if selectionModel is None:
- return
- selectionModel.setCurrentIndex(index, qt.QItemSelectionModel.ClearAndSelect)
-
- def viewMode(self):
- """Returns the current view mode.
-
- :rtype: qt.QFileDialog.ViewMode
- """
- if self.currentIndex() == 0:
- return qt.QFileDialog.List
- elif self.currentIndex() == 1:
- return qt.QFileDialog.Detail
- else:
- assert(False)
-
- def setViewMode(self, mode):
- """Set the current view mode.
-
- :param qt.QFileDialog.ViewMode mode: The new view mode
- """
- if mode == qt.QFileDialog.Detail:
- self.showDetails()
- elif mode == qt.QFileDialog.List:
- self.showList()
- else:
- assert(False)
-
- def showList(self):
- self.__listView.show()
- self.__detailView.hide()
- self.setCurrentIndex(0)
-
- def showDetails(self):
- self.__listView.hide()
- self.__detailView.show()
- self.setCurrentIndex(1)
- self.__detailView.updateGeometry()
-
- def clear(self):
- self.__listView.setRootIndex(qt.QModelIndex())
- self.__detailView.setRootIndex(qt.QModelIndex())
- selectionModel = self.__listView.selectionModel()
- if selectionModel is not None:
- selectionModel.selectionChanged.disconnect()
- selectionModel.clear()
- selectionModel = self.__detailView.selectionModel()
- if selectionModel is not None:
- selectionModel.selectionChanged.disconnect()
- selectionModel.clear()
- self.__listView.setModel(None)
- self.__detailView.setModel(None)
-
- def setRootIndex(self, index, model=None):
- """Sets the root item to the item at the given index.
- """
- rootIndex = self.__listView.rootIndex()
- newModel = model or index.model()
- assert(newModel is not None)
-
- if rootIndex is None or rootIndex.model() is not newModel:
- # update the model
- selectionModel = self.__listView.selectionModel()
- if selectionModel is not None:
- selectionModel.selectionChanged.disconnect()
- selectionModel.clear()
- selectionModel = self.__detailView.selectionModel()
- if selectionModel is not None:
- selectionModel.selectionChanged.disconnect()
- selectionModel.clear()
- pIndex = qt.QPersistentModelIndex(index)
- self.__listView.setModel(newModel)
- # changing the model of the tree view change the index mapping
- # that is why we are using a persistance model index
- self.__detailView.setModel(newModel)
- index = newModel.index(pIndex.row(), pIndex.column(), pIndex.parent())
- selectionModel = self.__listView.selectionModel()
- selectionModel.selectionChanged.connect(self.__emitSelected)
- selectionModel = self.__detailView.selectionModel()
- selectionModel.selectionChanged.connect(self.__emitSelected)
-
- self.__listView.setRootIndex(index)
- self.__detailView.setRootIndex(index)
- self.rootIndexChanged.emit(index)
-
- def rootIndex(self):
- """Returns the model index of the model's root item. The root item is
- the parent item to the view's toplevel items. The root can be invalid.
- """
- return self.__listView.rootIndex()
-
- __serialVersion = 1
- """Store the current version of the serialized data"""
-
- def visualRect(self, index):
- """Returns the rectangle on the viewport occupied by the item at index.
-
- :param qt.QModelIndex index: An index
- :rtype: QRect
- """
- if self.currentIndex() == 0:
- return self.__listView.visualRect(index)
- else:
- return self.__detailView.visualRect(index)
-
- def viewport(self):
- """Returns the viewport widget.
-
- :param qt.QModelIndex index: An index
- :rtype: QRect
- """
- if self.currentIndex() == 0:
- return self.__listView.viewport()
- else:
- return self.__detailView.viewport()
-
- def restoreState(self, state):
- """Restores the dialogs's layout, history and current directory to the
- state specified.
-
- :param qt.QByeArray state: Stream containing the new state
- :rtype: bool
- """
- stream = qt.QDataStream(state, qt.QIODevice.ReadOnly)
-
- nameId = stream.readQString()
- if nameId != "Browser":
- _logger.warning("Stored state contains an invalid name id. Browser restoration cancelled.")
- return False
-
- version = stream.readInt32()
- if version != self.__serialVersion:
- _logger.warning("Stored state contains an invalid version. Browser restoration cancelled.")
- return False
-
- headerData = stream.readQVariant()
- self.__detailView.header().restoreState(headerData)
-
- viewMode = stream.readInt32()
- self.setViewMode(viewMode)
- return True
-
- def saveState(self):
- """Saves the state of the dialog's layout.
-
- :rtype: qt.QByteArray
- """
- data = qt.QByteArray()
- stream = qt.QDataStream(data, qt.QIODevice.WriteOnly)
-
- nameId = u"Browser"
- stream.writeQString(nameId)
- stream.writeInt32(self.__serialVersion)
- stream.writeQVariant(self.__detailView.header().saveState())
- stream.writeInt32(self.viewMode())
-
- return data
-
-
-class _FabioData(object):
-
- def __init__(self, fabioFile):
- self.__fabioFile = fabioFile
-
- @property
- def dtype(self):
- # Let say it is a valid type
- return numpy.dtype("float")
-
- @property
- def shape(self):
- if self.__fabioFile.nframes == 0:
- return None
- if self.__fabioFile.nframes == 1:
- return [slice(None), slice(None)]
- return [self.__fabioFile.nframes, slice(None), slice(None)]
-
- def __getitem__(self, selector):
- if self.__fabioFile.nframes == 1 and selector == tuple():
- return self.__fabioFile.data
- if isinstance(selector, tuple) and len(selector) == 1:
- selector = selector[0]
-
- if isinstance(selector, six.integer_types):
- if 0 <= selector < self.__fabioFile.nframes:
- if self.__fabioFile.nframes == 1:
- return self.__fabioFile.data
- else:
- frame = self.__fabioFile.getframe(selector)
- return frame.data
- else:
- raise ValueError("Invalid selector %s" % selector)
- else:
- raise TypeError("Unsupported selector type %s" % type(selector))
-
-
-class _PathEdit(qt.QLineEdit):
- pass
-
-
-class _CatchResizeEvent(qt.QObject):
-
- resized = qt.Signal(qt.QResizeEvent)
-
- def __init__(self, parent, target):
- super(_CatchResizeEvent, self).__init__(parent)
- self.__target = target
- self.__target_oldResizeEvent = self.__target.resizeEvent
- self.__target.resizeEvent = self.__resizeEvent
-
- def __resizeEvent(self, event):
- result = self.__target_oldResizeEvent(event)
- self.resized.emit(event)
- return result
-
-
-class AbstractDataFileDialog(qt.QDialog):
- """The `AbstractFileDialog` provides a generic GUI to create a custom dialog
- allowing to access to file resources like HDF5 files or HDF5 datasets.
-
- .. image:: img/abstractdatafiledialog.png
-
- The dialog contains:
-
- - Shortcuts: It provides few links to have a fast access of browsing
- locations.
- - Browser: It provides a display to browse throw the file system and inside
- HDF5 files or fabio files. A file format selector is provided.
- - URL: Display the URL available to reach the data using
- :meth:`silx.io.get_data`, :meth:`silx.io.open`.
- - Data selector: A widget to apply a sub selection of the browsed dataset.
- This widget can be provided, else nothing will be used.
- - Data preview: A widget to preview the selected data, which is the result
- of the filter from the data selector.
- This widget can be provided, else nothing will be used.
- - Preview's toolbar: Provides tools used to custom data preview or data
- selector.
- This widget can be provided, else nothing will be used.
- - Buttons to validate the dialog
- """
-
- _defaultIconProvider = None
- """Lazy loaded default icon provider"""
-
- def __init__(self, parent=None):
- super(AbstractDataFileDialog, self).__init__(parent)
- self._init()
-
- def _init(self):
- self.setWindowTitle("Open")
-
- self.__openedFiles = []
- """Store the list of files opened by the model itself."""
- # FIXME: It should be managed one by one by Hdf5Item itself
-
- self.__directory = None
- self.__directoryLoadedFilter = None
- self.__errorWhileLoadingFile = None
- self.__selectedFile = None
- self.__selectedData = None
- self.__currentHistory = []
- """Store history of URLs, last index one is the latest one"""
- self.__currentHistoryLocation = -1
- """Store the location in the history. Bigger is older"""
-
- self.__processing = 0
- """Number of asynchronous processing tasks"""
- self.__h5 = None
- self.__fabio = None
-
- if qt.qVersion() < "5.0":
- # On Qt4 it is needed to provide a safe file system model
- _logger.debug("Uses SafeFileSystemModel")
- from .SafeFileSystemModel import SafeFileSystemModel
- self.__fileModel = SafeFileSystemModel(self)
- else:
- # On Qt5 a safe icon provider is still needed to avoid freeze
- _logger.debug("Uses default QFileSystemModel with a SafeFileIconProvider")
- self.__fileModel = qt.QFileSystemModel(self)
- from .SafeFileIconProvider import SafeFileIconProvider
- iconProvider = SafeFileIconProvider()
- self.__fileModel.setIconProvider(iconProvider)
-
- # The common file dialog filter only on Mac OS X
- self.__fileModel.setNameFilterDisables(sys.platform == "darwin")
- self.__fileModel.setReadOnly(True)
- self.__fileModel.directoryLoaded.connect(self.__directoryLoaded)
-
- self.__dataModel = Hdf5TreeModel(self)
-
- self.__createWidgets()
- self.__initLayout()
- self.__showAsListView()
-
- path = os.getcwd()
- self.__fileModel_setRootPath(path)
-
- self.__clearData()
- self.__updatePath()
-
- # Update the file model filter
- self.__fileTypeCombo.setCurrentIndex(0)
- self.__filterSelected(0)
-
- # It is not possible to override the QObject destructor nor
- # to access to the content of the Python object with the `destroyed`
- # signal cause the Python method was already removed with the QWidget,
- # while the QObject still exists.
- # We use a static method plus explicit references to objects to
- # release. The callback do not use any ref to self.
- onDestroy = functools.partial(self._closeFileList, self.__openedFiles)
- self.destroyed.connect(onDestroy)
-
- @staticmethod
- def _closeFileList(fileList):
- """Static method to close explicit references to internal objects."""
- _logger.debug("Clear AbstractDataFileDialog")
- for obj in fileList:
- _logger.debug("Close file %s", obj.filename)
- obj.close()
- fileList[:] = []
-
- def done(self, result):
- self._clear()
- super(AbstractDataFileDialog, self).done(result)
-
- def _clear(self):
- """Explicit method to clear data stored in the dialog.
- After this call it is not anymore possible to use the widget.
-
- This method is triggered by the destruction of the object and the
- QDialog :meth:`done`. Then it can be triggered more than once.
- """
- _logger.debug("Clear dialog")
- self.__errorWhileLoadingFile = None
- self.__clearData()
- if self.__fileModel is not None:
- # Cache the directory before cleaning the model
- self.__directory = self.directory()
- self.__browser.clear()
- self.__closeFile()
- self.__fileModel = None
- self.__dataModel = None
-
- def hasPendingEvents(self):
- """Returns true if the dialog have asynchronous tasks working on the
- background."""
- return self.__processing > 0
-
- # User interface
-
- def __createWidgets(self):
- self.__sidebar = self._createSideBar()
- if self.__sidebar is not None:
- sideBarModel = self.__sidebar.selectionModel()
- sideBarModel.selectionChanged.connect(self.__shortcutSelected)
- self.__sidebar.setSelectionMode(qt.QAbstractItemView.SingleSelection)
-
- listView = qt.QListView(self)
- listView.setSelectionBehavior(qt.QAbstractItemView.SelectRows)
- listView.setSelectionMode(qt.QAbstractItemView.SingleSelection)
- listView.setResizeMode(qt.QListView.Adjust)
- listView.setWrapping(True)
- listView.setEditTriggers(qt.QAbstractItemView.NoEditTriggers)
- listView.setContextMenuPolicy(qt.Qt.CustomContextMenu)
- utils.patchToConsumeReturnKey(listView)
-
- treeView = qt.QTreeView(self)
- treeView.setSelectionBehavior(qt.QAbstractItemView.SelectRows)
- treeView.setSelectionMode(qt.QAbstractItemView.SingleSelection)
- treeView.setRootIsDecorated(False)
- treeView.setItemsExpandable(False)
- treeView.setSortingEnabled(True)
- treeView.header().setSortIndicator(0, qt.Qt.AscendingOrder)
- treeView.header().setStretchLastSection(False)
- treeView.setTextElideMode(qt.Qt.ElideMiddle)
- treeView.setEditTriggers(qt.QAbstractItemView.NoEditTriggers)
- treeView.setContextMenuPolicy(qt.Qt.CustomContextMenu)
- treeView.setDragDropMode(qt.QAbstractItemView.InternalMove)
- utils.patchToConsumeReturnKey(treeView)
-
- self.__browser = _Browser(self, listView, treeView)
- self.__browser.activated.connect(self.__browsedItemActivated)
- self.__browser.selected.connect(self.__browsedItemSelected)
- self.__browser.rootIndexChanged.connect(self.__rootIndexChanged)
- self.__browser.setObjectName("browser")
-
- self.__previewWidget = self._createPreviewWidget(self)
-
- self.__fileTypeCombo = FileTypeComboBox(self)
- self.__fileTypeCombo.setObjectName("fileTypeCombo")
- self.__fileTypeCombo.setDuplicatesEnabled(False)
- self.__fileTypeCombo.setSizeAdjustPolicy(qt.QComboBox.AdjustToMinimumContentsLength)
- self.__fileTypeCombo.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
- self.__fileTypeCombo.activated[int].connect(self.__filterSelected)
- self.__fileTypeCombo.setFabioUrlSupproted(self._isFabioFilesSupported())
-
- self.__pathEdit = _PathEdit(self)
- self.__pathEdit.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
- self.__pathEdit.textChanged.connect(self.__textChanged)
- self.__pathEdit.setObjectName("url")
- utils.patchToConsumeReturnKey(self.__pathEdit)
-
- self.__buttons = qt.QDialogButtonBox(self)
- self.__buttons.setSizePolicy(qt.QSizePolicy.Fixed, qt.QSizePolicy.Fixed)
- types = qt.QDialogButtonBox.Open | qt.QDialogButtonBox.Cancel
- self.__buttons.setStandardButtons(types)
- self.__buttons.button(qt.QDialogButtonBox.Cancel).setObjectName("cancel")
- self.__buttons.button(qt.QDialogButtonBox.Open).setObjectName("open")
-
- self.__buttons.accepted.connect(self.accept)
- self.__buttons.rejected.connect(self.reject)
-
- self.__browseToolBar = self._createBrowseToolBar()
- self.__backwardAction.setEnabled(False)
- self.__forwardAction.setEnabled(False)
- self.__fileDirectoryAction.setEnabled(False)
- self.__parentFileDirectoryAction.setEnabled(False)
-
- self.__selectorWidget = self._createSelectorWidget(self)
- if self.__selectorWidget is not None:
- self.__selectorWidget.selectionChanged.connect(self.__selectorWidgetChanged)
-
- self.__previewToolBar = self._createPreviewToolbar(self, self.__previewWidget, self.__selectorWidget)
-
- self.__dataIcon = qt.QLabel(self)
- self.__dataIcon.setSizePolicy(qt.QSizePolicy.Fixed, qt.QSizePolicy.Fixed)
- self.__dataIcon.setScaledContents(True)
- self.__dataIcon.setMargin(2)
- self.__dataIcon.setAlignment(qt.Qt.AlignCenter)
-
- self.__dataInfo = qt.QLabel(self)
- self.__dataInfo.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
-
- def _createSideBar(self):
- sidebar = _SideBar(self)
- sidebar.setObjectName("sidebar")
- return sidebar
-
- def iconProvider(self):
- iconProvider = self.__class__._defaultIconProvider
- if iconProvider is None:
- iconProvider = _IconProvider()
- self.__class__._defaultIconProvider = iconProvider
- return iconProvider
-
- def _createBrowseToolBar(self):
- toolbar = qt.QToolBar(self)
- toolbar.setIconSize(qt.QSize(16, 16))
- iconProvider = self.iconProvider()
-
- backward = qt.QAction(toolbar)
- backward.setText("Back")
- backward.setObjectName("backwardAction")
- backward.setIcon(iconProvider.icon(qt.QStyle.SP_ArrowBack))
- backward.triggered.connect(self.__navigateBackward)
- self.__backwardAction = backward
-
- forward = qt.QAction(toolbar)
- forward.setText("Forward")
- forward.setObjectName("forwardAction")
- forward.setIcon(iconProvider.icon(qt.QStyle.SP_ArrowForward))
- forward.triggered.connect(self.__navigateForward)
- self.__forwardAction = forward
-
- parentDirectory = qt.QAction(toolbar)
- parentDirectory.setText("Go to parent")
- parentDirectory.setObjectName("toParentAction")
- parentDirectory.setIcon(iconProvider.icon(qt.QStyle.SP_FileDialogToParent))
- parentDirectory.triggered.connect(self.__navigateToParent)
- self.__toParentAction = parentDirectory
-
- fileDirectory = qt.QAction(toolbar)
- fileDirectory.setText("Root of the file")
- fileDirectory.setObjectName("toRootFileAction")
- fileDirectory.setIcon(iconProvider.icon(iconProvider.FileDialogToParentFile))
- fileDirectory.triggered.connect(self.__navigateToParentFile)
- self.__fileDirectoryAction = fileDirectory
-
- parentFileDirectory = qt.QAction(toolbar)
- parentFileDirectory.setText("Parent directory of the file")
- parentFileDirectory.setObjectName("toDirectoryAction")
- parentFileDirectory.setIcon(iconProvider.icon(iconProvider.FileDialogToParentDir))
- parentFileDirectory.triggered.connect(self.__navigateToParentDir)
- self.__parentFileDirectoryAction = parentFileDirectory
-
- listView = qt.QAction(toolbar)
- listView.setText("List view")
- listView.setObjectName("listModeAction")
- listView.setIcon(iconProvider.icon(qt.QStyle.SP_FileDialogListView))
- listView.triggered.connect(self.__showAsListView)
- listView.setCheckable(True)
-
- detailView = qt.QAction(toolbar)
- detailView.setText("Detail view")
- detailView.setObjectName("detailModeAction")
- detailView.setIcon(iconProvider.icon(qt.QStyle.SP_FileDialogDetailedView))
- detailView.triggered.connect(self.__showAsDetailedView)
- detailView.setCheckable(True)
-
- self.__listViewAction = listView
- self.__detailViewAction = detailView
-
- toolbar.addAction(backward)
- toolbar.addAction(forward)
- toolbar.addSeparator()
- toolbar.addAction(parentDirectory)
- toolbar.addAction(fileDirectory)
- toolbar.addAction(parentFileDirectory)
- toolbar.addSeparator()
- toolbar.addAction(listView)
- toolbar.addAction(detailView)
-
- toolbar.setStyleSheet("QToolBar { border: 0px }")
-
- return toolbar
-
- def __initLayout(self):
- sideBarLayout = qt.QVBoxLayout()
- sideBarLayout.setContentsMargins(0, 0, 0, 0)
- dummyToolBar = qt.QWidget(self)
- dummyToolBar.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
- dummyCombo = qt.QWidget(self)
- dummyCombo.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
- sideBarLayout.addWidget(dummyToolBar)
- if self.__sidebar is not None:
- sideBarLayout.addWidget(self.__sidebar)
- sideBarLayout.addWidget(dummyCombo)
- sideBarWidget = qt.QWidget(self)
- sideBarWidget.setLayout(sideBarLayout)
-
- dummyCombo.setFixedHeight(self.__fileTypeCombo.height())
- self.__resizeCombo = _CatchResizeEvent(self, self.__fileTypeCombo)
- self.__resizeCombo.resized.connect(lambda e: dummyCombo.setFixedHeight(e.size().height()))
-
- dummyToolBar.setFixedHeight(self.__browseToolBar.height())
- self.__resizeToolbar = _CatchResizeEvent(self, self.__browseToolBar)
- self.__resizeToolbar.resized.connect(lambda e: dummyToolBar.setFixedHeight(e.size().height()))
-
- datasetSelection = qt.QWidget(self)
- layoutLeft = qt.QVBoxLayout()
- layoutLeft.setContentsMargins(0, 0, 0, 0)
- layoutLeft.addWidget(self.__browseToolBar)
- layoutLeft.addWidget(self.__browser)
- layoutLeft.addWidget(self.__fileTypeCombo)
- datasetSelection.setLayout(layoutLeft)
- datasetSelection.setSizePolicy(qt.QSizePolicy.MinimumExpanding, qt.QSizePolicy.Expanding)
-
- infoLayout = qt.QHBoxLayout()
- infoLayout.setContentsMargins(0, 0, 0, 0)
- infoLayout.addWidget(self.__dataIcon)
- infoLayout.addWidget(self.__dataInfo)
-
- dataFrame = qt.QFrame(self)
- dataFrame.setFrameShape(qt.QFrame.StyledPanel)
- layout = qt.QVBoxLayout()
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setSpacing(0)
- layout.addWidget(self.__previewWidget)
- layout.addLayout(infoLayout)
- dataFrame.setLayout(layout)
-
- dataSelection = qt.QWidget(self)
- dataLayout = qt.QVBoxLayout()
- dataLayout.setContentsMargins(0, 0, 0, 0)
- if self.__previewToolBar is not None:
- dataLayout.addWidget(self.__previewToolBar)
- else:
- # Add dummy space
- dummyToolbar2 = qt.QWidget(self)
- dummyToolbar2.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
- dummyToolbar2.setFixedHeight(self.__browseToolBar.height())
- self.__resizeToolbar = _CatchResizeEvent(self, self.__browseToolBar)
- self.__resizeToolbar.resized.connect(lambda e: dummyToolbar2.setFixedHeight(e.size().height()))
- dataLayout.addWidget(dummyToolbar2)
-
- dataLayout.addWidget(dataFrame)
- if self.__selectorWidget is not None:
- dataLayout.addWidget(self.__selectorWidget)
- else:
- # Add dummy space
- dummyCombo2 = qt.QWidget(self)
- dummyCombo2.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
- dummyCombo2.setFixedHeight(self.__fileTypeCombo.height())
- self.__resizeToolbar = _CatchResizeEvent(self, self.__fileTypeCombo)
- self.__resizeToolbar.resized.connect(lambda e: dummyCombo2.setFixedHeight(e.size().height()))
- dataLayout.addWidget(dummyCombo2)
- dataSelection.setLayout(dataLayout)
-
- self.__splitter = qt.QSplitter(self)
- self.__splitter.setContentsMargins(0, 0, 0, 0)
- self.__splitter.addWidget(sideBarWidget)
- self.__splitter.addWidget(datasetSelection)
- self.__splitter.addWidget(dataSelection)
- self.__splitter.setStretchFactor(1, 10)
-
- bottomLayout = qt.QHBoxLayout()
- bottomLayout.setContentsMargins(0, 0, 0, 0)
- bottomLayout.addWidget(self.__pathEdit)
- bottomLayout.addWidget(self.__buttons)
-
- layout = qt.QVBoxLayout(self)
- layout.addWidget(self.__splitter)
- layout.addLayout(bottomLayout)
-
- self.setLayout(layout)
- self.updateGeometry()
-
- # Logic
-
- def __navigateBackward(self):
- """Navigate through the history one step backward."""
- if len(self.__currentHistory) > 0 and self.__currentHistoryLocation > 0:
- self.__currentHistoryLocation -= 1
- url = self.__currentHistory[self.__currentHistoryLocation]
- self.selectUrl(url)
-
- def __navigateForward(self):
- """Navigate through the history one step forward."""
- if len(self.__currentHistory) > 0 and self.__currentHistoryLocation < len(self.__currentHistory) - 1:
- self.__currentHistoryLocation += 1
- url = self.__currentHistory[self.__currentHistoryLocation]
- self.selectUrl(url)
-
- def __navigateToParent(self):
- index = self.__browser.rootIndex()
- if index.model() is self.__fileModel:
- # browse throw the file system
- index = index.parent()
- path = self.__fileModel.filePath(index)
- self.__fileModel_setRootPath(path)
- self.__browser.selectIndex(qt.QModelIndex())
- self.__updatePath()
- elif index.model() is self.__dataModel:
- index = index.parent()
- if index.isValid():
- # browse throw the hdf5
- self.__browser.setRootIndex(index)
- self.__browser.selectIndex(qt.QModelIndex())
- self.__updatePath()
- else:
- # go back to the file system
- self.__navigateToParentDir()
- else:
- # Root of the file system (my computer)
- pass
-
- def __navigateToParentFile(self):
- index = self.__browser.rootIndex()
- if index.model() is self.__dataModel:
- index = self.__dataModel.indexFromH5Object(self.__h5)
- self.__browser.setRootIndex(index)
- self.__browser.selectIndex(qt.QModelIndex())
- self.__updatePath()
-
- def __navigateToParentDir(self):
- index = self.__browser.rootIndex()
- if index.model() is self.__dataModel:
- path = os.path.dirname(self.__h5.file.filename)
- index = self.__fileModel.index(path)
- self.__browser.setRootIndex(index)
- self.__browser.selectIndex(qt.QModelIndex())
- self.__closeFile()
- self.__updatePath()
-
- def viewMode(self):
- """Returns the current view mode.
-
- :rtype: qt.QFileDialog.ViewMode
- """
- return self.__browser.viewMode()
-
- def setViewMode(self, mode):
- """Set the current view mode.
-
- :param qt.QFileDialog.ViewMode mode: The new view mode
- """
- if mode == qt.QFileDialog.Detail:
- self.__browser.showDetails()
- self.__listViewAction.setChecked(False)
- self.__detailViewAction.setChecked(True)
- elif mode == qt.QFileDialog.List:
- self.__browser.showList()
- self.__listViewAction.setChecked(True)
- self.__detailViewAction.setChecked(False)
- else:
- assert(False)
-
- def __showAsListView(self):
- self.setViewMode(qt.QFileDialog.List)
-
- def __showAsDetailedView(self):
- self.setViewMode(qt.QFileDialog.Detail)
-
- def __shortcutSelected(self):
- self.__browser.selectIndex(qt.QModelIndex())
- self.__clearData()
- self.__updatePath()
- selectionModel = self.__sidebar.selectionModel()
- indexes = selectionModel.selectedIndexes()
- if len(indexes) == 1:
- index = indexes[0]
- url = self.__sidebar.model().data(index, role=qt.Qt.UserRole)
- path = url.toLocalFile()
- self.__fileModel_setRootPath(path)
-
- def __browsedItemActivated(self, index):
- if not index.isValid():
- return
- if index.model() is self.__fileModel:
- path = self.__fileModel.filePath(index)
- if self.__fileModel.isDir(index):
- self.__fileModel_setRootPath(path)
- if os.path.isfile(path):
- self.__fileActivated(index)
- elif index.model() is self.__dataModel:
- obj = self.__dataModel.data(index, role=Hdf5TreeModel.H5PY_OBJECT_ROLE)
- if silx.io.is_group(obj):
- self.__browser.setRootIndex(index)
- else:
- assert(False)
-
- def __browsedItemSelected(self, index):
- self.__dataSelected(index)
- self.__updatePath()
-
- def __fileModel_setRootPath(self, path):
- """Set the root path of the fileModel with a filter on the
- directoryLoaded event.
-
- Without this filter an extra event is received (at least with PyQt4)
- when we use for the first time the sidebar.
-
- :param str path: Path to load
- """
- assert(path is not None)
- if path != "" and not os.path.exists(path):
- return
- if self.hasPendingEvents():
- # Make sure the asynchronous fileModel setRootPath is finished
- qt.QApplication.instance().processEvents()
-
- if self.__directoryLoadedFilter is not None:
- if utils.samefile(self.__directoryLoadedFilter, path):
- return
- self.__directoryLoadedFilter = path
- self.__processing += 1
- if self.__fileModel is None:
- return
- index = self.__fileModel.setRootPath(path)
- if not index.isValid():
- # There is a problem with this path
- # No asynchronous process will be waked up
- self.__processing -= 1
- self.__browser.setRootIndex(index, model=self.__fileModel)
- self.__clearData()
- self.__updatePath()
-
- def __directoryLoaded(self, path):
- if self.__directoryLoadedFilter is not None:
- if not utils.samefile(self.__directoryLoadedFilter, path):
- # Filter event which should not arrive in PyQt4
- # The first click on the sidebar sent 2 events
- self.__processing -= 1
- return
- if self.__fileModel is None:
- return
- index = self.__fileModel.index(path)
- self.__browser.setRootIndex(index, model=self.__fileModel)
- self.__updatePath()
- self.__processing -= 1
-
- def __closeFile(self):
- self.__openedFiles[:] = []
- self.__fileDirectoryAction.setEnabled(False)
- self.__parentFileDirectoryAction.setEnabled(False)
- if self.__h5 is not None:
- self.__dataModel.removeH5pyObject(self.__h5)
- self.__h5.close()
- self.__h5 = None
- if self.__fabio is not None:
- if hasattr(self.__fabio, "close"):
- self.__fabio.close()
- self.__fabio = None
-
- def __openFabioFile(self, filename):
- self.__closeFile()
- try:
- self.__fabio = fabio.open(filename)
- self.__openedFiles.append(self.__fabio)
- self.__selectedFile = filename
- except Exception as e:
- _logger.error("Error while loading file %s: %s", filename, e.args[0])
- _logger.debug("Backtrace", exc_info=True)
- self.__errorWhileLoadingFile = filename, e.args[0]
- return False
- else:
- return True
-
- def __openSilxFile(self, filename):
- self.__closeFile()
- try:
- self.__h5 = silx.io.open(filename)
- self.__openedFiles.append(self.__h5)
- self.__selectedFile = filename
- except IOError as e:
- _logger.error("Error while loading file %s: %s", filename, e.args[0])
- _logger.debug("Backtrace", exc_info=True)
- self.__errorWhileLoadingFile = filename, e.args[0]
- return False
- else:
- self.__fileDirectoryAction.setEnabled(True)
- self.__parentFileDirectoryAction.setEnabled(True)
- self.__dataModel.insertH5pyObject(self.__h5)
- return True
-
- def __isSilxHavePriority(self, filename):
- """Silx have priority when there is a specific decoder
- """
- _, ext = os.path.splitext(filename)
- ext = "*%s" % ext
- formats = silx.io.supported_extensions(flat_formats=False)
- for extensions in formats.values():
- if ext in extensions:
- return True
- return False
-
- def __openFile(self, filename):
- codec = self.__fileTypeCombo.currentCodec()
- openners = []
- if codec.is_autodetect():
- if self.__isSilxHavePriority(filename):
- openners.append(self.__openSilxFile)
- if self._isFabioFilesSupported():
- openners.append(self.__openFabioFile)
- else:
- if self._isFabioFilesSupported():
- openners.append(self.__openFabioFile)
- openners.append(self.__openSilxFile)
- elif codec.is_silx_codec():
- openners.append(self.__openSilxFile)
- elif self._isFabioFilesSupported() and codec.is_fabio_codec():
- # It is requested to use fabio, anyway fabio is here or not
- openners.append(self.__openFabioFile)
-
- for openner in openners:
- ref = openner(filename)
- if ref is not None:
- return True
- return False
-
- def __fileActivated(self, index):
- self.__selectedFile = None
- path = self.__fileModel.filePath(index)
- if os.path.isfile(path):
- loaded = self.__openFile(path)
- if loaded:
- if self.__h5 is not None:
- index = self.__dataModel.indexFromH5Object(self.__h5)
- self.__browser.setRootIndex(index)
- elif self.__fabio is not None:
- data = _FabioData(self.__fabio)
- self.__setData(data)
- self.__updatePath()
- else:
- self.__clearData()
-
- def __dataSelected(self, index):
- selectedData = None
- if index is not None:
- if index.model() is self.__dataModel:
- obj = self.__dataModel.data(index, self.__dataModel.H5PY_OBJECT_ROLE)
- if self._isDataSupportable(obj):
- selectedData = obj
- elif index.model() is self.__fileModel:
- self.__closeFile()
- if self._isFabioFilesSupported():
- path = self.__fileModel.filePath(index)
- if os.path.isfile(path):
- codec = self.__fileTypeCombo.currentCodec()
- is_fabio_decoder = codec.is_fabio_codec()
- is_fabio_have_priority = not codec.is_silx_codec() and not self.__isSilxHavePriority(path)
- if is_fabio_decoder or is_fabio_have_priority:
- # Then it's flat frame container
- self.__openFabioFile(path)
- if self.__fabio is not None:
- selectedData = _FabioData(self.__fabio)
- else:
- assert(False)
-
- self.__setData(selectedData)
-
- def __filterSelected(self, index):
- filters = self.__fileTypeCombo.itemExtensions(index)
- self.__fileModel.setNameFilters(list(filters))
-
- def __setData(self, data):
- self.__data = data
-
- if data is not None and self._isDataSupportable(data):
- if self.__selectorWidget is not None:
- self.__selectorWidget.setData(data)
- if not self.__selectorWidget.isUsed():
- # Needed to fake the fact we have to reset the zoom in preview
- self.__selectedData = None
- self.__setSelectedData(data)
- self.__selectorWidget.hide()
- else:
- self.__selectorWidget.setVisible(self.__selectorWidget.hasVisibleSelectors())
- # Needed to fake the fact we have to reset the zoom in preview
- self.__selectedData = None
- self.__selectorWidget.selectionChanged.emit()
- else:
- # Needed to fake the fact we have to reset the zoom in preview
- self.__selectedData = None
- self.__setSelectedData(data)
- else:
- self.__clearData()
- self.__updatePath()
-
- def _isDataSupported(self, data):
- """Check if the data can be returned by the dialog.
-
- If true, this data can be returned by the dialog and the open button
- while be enabled. If false the button will be disabled.
-
- :rtype: bool
- """
- raise NotImplementedError()
-
- def _isDataSupportable(self, data):
- """Check if the selected data can be supported at one point.
-
- If true, the data selector will be checked and it will update the data
- preview. Else the selecting is disabled.
-
- :rtype: bool
- """
- raise NotImplementedError()
-
- def __clearData(self):
- """Clear the data part of the GUI"""
- if self.__previewWidget is not None:
- self.__previewWidget.setData(None)
- if self.__selectorWidget is not None:
- self.__selectorWidget.setData(None)
- self.__selectorWidget.hide()
- self.__selectedData = None
- self.__data = None
- self.__updateDataInfo()
- button = self.__buttons.button(qt.QDialogButtonBox.Open)
- button.setEnabled(False)
-
- def __selectorWidgetChanged(self):
- data = self.__selectorWidget.getSelectedData(self.__data)
- self.__setSelectedData(data)
-
- def __setSelectedData(self, data):
- """Set the data selected by the dialog.
-
- If :meth:`_isDataSupported` returns false, this function will be
- inhibited and no data will be selected.
- """
- if isinstance(data, _FabioData):
- data = data[()]
- if self.__previewWidget is not None:
- fromDataSelector = self.__selectedData is not None
- self.__previewWidget.setData(data, fromDataSelector=fromDataSelector)
- if self._isDataSupported(data):
- self.__selectedData = data
- else:
- self.__clearData()
- return
- self.__updateDataInfo()
- self.__updatePath()
- button = self.__buttons.button(qt.QDialogButtonBox.Open)
- button.setEnabled(True)
-
- def __updateDataInfo(self):
- if self.__errorWhileLoadingFile is not None:
- filename, message = self.__errorWhileLoadingFile
- message = "<b>Error while loading file '%s'</b><hr/>%s" % (filename, message)
- size = self.__dataInfo.height()
- icon = self.style().standardIcon(qt.QStyle.SP_MessageBoxCritical)
- pixmap = icon.pixmap(size, size)
-
- self.__dataInfo.setText("Error while loading file")
- self.__dataInfo.setToolTip(message)
- self.__dataIcon.setToolTip(message)
- self.__dataIcon.setVisible(True)
- self.__dataIcon.setPixmap(pixmap)
-
- self.__errorWhileLoadingFile = None
- return
-
- self.__dataIcon.setVisible(False)
- self.__dataInfo.setToolTip("")
- if self.__selectedData is None:
- self.__dataInfo.setText("No data selected")
- else:
- text = self._displayedDataInfo(self.__data, self.__selectedData)
- self.__dataInfo.setVisible(text is not None)
- if text is not None:
- self.__dataInfo.setText(text)
-
- def _displayedDataInfo(self, dataBeforeSelection, dataAfterSelection):
- """Returns the text displayed under the data preview.
-
- This zone is used to display error in case or problem of data selection
- or problems with IO.
-
- :param numpy.ndarray dataAfterSelection: Data as it is after the
- selection widget (basically the data from the preview widget)
- :param numpy.ndarray dataAfterSelection: Data as it is before the
- selection widget (basically the data from the browsing widget)
- :rtype: bool
- """
- return None
-
- def __createUrlFromIndex(self, index, useSelectorWidget=True):
- if index.model() is self.__fileModel:
- filename = self.__fileModel.filePath(index)
- dataPath = None
- elif index.model() is self.__dataModel:
- obj = self.__dataModel.data(index, role=Hdf5TreeModel.H5PY_OBJECT_ROLE)
- filename = obj.file.filename
- dataPath = obj.name
- else:
- # root of the computer
- filename = ""
- dataPath = None
-
- if useSelectorWidget and self.__selectorWidget is not None and self.__selectorWidget.isUsed():
- slicing = self.__selectorWidget.slicing()
- if slicing == tuple():
- slicing = None
- else:
- slicing = None
-
- if self.__fabio is not None:
- scheme = "fabio"
- elif self.__h5 is not None:
- scheme = "silx"
- else:
- if os.path.isfile(filename):
- codec = self.__fileTypeCombo.currentCodec()
- if codec.is_fabio_codec():
- scheme = "fabio"
- elif codec.is_silx_codec():
- scheme = "silx"
- else:
- scheme = None
- else:
- scheme = None
-
- url = silx.io.url.DataUrl(file_path=filename, data_path=dataPath, data_slice=slicing, scheme=scheme)
- return url
-
- def __updatePath(self):
- index = self.__browser.selectedIndex()
- if index is None:
- index = self.__browser.rootIndex()
- url = self.__createUrlFromIndex(index)
- if url.path() != self.__pathEdit.text():
- old = self.__pathEdit.blockSignals(True)
- self.__pathEdit.setText(url.path())
- self.__pathEdit.blockSignals(old)
-
- def __rootIndexChanged(self, index):
- url = self.__createUrlFromIndex(index, useSelectorWidget=False)
-
- currentUrl = None
- if 0 <= self.__currentHistoryLocation < len(self.__currentHistory):
- currentUrl = self.__currentHistory[self.__currentHistoryLocation]
-
- if currentUrl is None or currentUrl != url.path():
- # clean up the forward history
- self.__currentHistory = self.__currentHistory[0:self.__currentHistoryLocation + 1]
- self.__currentHistory.append(url.path())
- self.__currentHistoryLocation += 1
-
- if index.model() != self.__dataModel:
- if sys.platform == "win32":
- # path == ""
- isRoot = not index.isValid()
- else:
- # path in ["", "/"]
- isRoot = not index.isValid() or not index.parent().isValid()
- else:
- isRoot = False
-
- if index.isValid():
- self.__dataSelected(index)
- self.__toParentAction.setEnabled(not isRoot)
- self.__updateActionHistory()
- self.__updateSidebar()
-
- def __updateSidebar(self):
- """Called when the current directory location change"""
- if self.__sidebar is None:
- return
- selectionModel = self.__sidebar.selectionModel()
- selectionModel.selectionChanged.disconnect(self.__shortcutSelected)
- index = self.__browser.rootIndex()
- if index.model() == self.__fileModel:
- path = self.__fileModel.filePath(index)
- self.__sidebar.setSelectedPath(path)
- elif index.model() is None:
- path = ""
- self.__sidebar.setSelectedPath(path)
- else:
- selectionModel.clear()
- selectionModel.selectionChanged.connect(self.__shortcutSelected)
-
- def __updateActionHistory(self):
- self.__forwardAction.setEnabled(len(self.__currentHistory) - 1 > self.__currentHistoryLocation)
- self.__backwardAction.setEnabled(self.__currentHistoryLocation > 0)
-
- def __textChanged(self, text):
- self.__pathChanged()
-
- def _isFabioFilesSupported(self):
- """Returns true fabio files can be loaded.
- """
- return True
-
- def _isLoadableUrl(self, url):
- """Returns true if the URL is loadable by this dialog.
-
- :param DataUrl url: The requested URL
- """
- return True
-
- def __pathChanged(self):
- url = silx.io.url.DataUrl(path=self.__pathEdit.text())
- if url.is_valid() or url.path() == "":
- if url.path() in ["", "/"] or url.file_path() in ["", "/"]:
- self.__fileModel_setRootPath(qt.QDir.rootPath())
- elif os.path.exists(url.file_path()):
- rootIndex = None
- if os.path.isdir(url.file_path()):
- self.__fileModel_setRootPath(url.file_path())
- index = self.__fileModel.index(url.file_path())
- elif os.path.isfile(url.file_path()):
- if self._isLoadableUrl(url):
- if url.scheme() == "silx":
- loaded = self.__openSilxFile(url.file_path())
- elif url.scheme() == "fabio" and self._isFabioFilesSupported():
- loaded = self.__openFabioFile(url.file_path())
- else:
- loaded = self.__openFile(url.file_path())
- else:
- loaded = False
- if loaded:
- if self.__h5 is not None:
- rootIndex = self.__dataModel.indexFromH5Object(self.__h5)
- elif self.__fabio is not None:
- index = self.__fileModel.index(url.file_path())
- rootIndex = index
- if rootIndex is None:
- index = self.__fileModel.index(url.file_path())
- index = index.parent()
-
- if rootIndex is not None:
- if rootIndex.model() == self.__dataModel:
- if url.data_path() is not None:
- dataPath = url.data_path()
- if dataPath in self.__h5:
- obj = self.__h5[dataPath]
- else:
- path = utils.findClosestSubPath(self.__h5, dataPath)
- if path is None:
- path = "/"
- obj = self.__h5[path]
-
- if silx.io.is_file(obj):
- self.__browser.setRootIndex(rootIndex)
- elif silx.io.is_group(obj):
- index = self.__dataModel.indexFromH5Object(obj)
- self.__browser.setRootIndex(index)
- else:
- index = self.__dataModel.indexFromH5Object(obj)
- self.__browser.setRootIndex(index.parent())
- self.__browser.selectIndex(index)
- else:
- self.__browser.setRootIndex(rootIndex)
- self.__clearData()
- elif rootIndex.model() == self.__fileModel:
- # that's a fabio file
- self.__browser.setRootIndex(rootIndex.parent())
- self.__browser.selectIndex(rootIndex)
- # data = _FabioData(self.__fabio)
- # self.__setData(data)
- else:
- assert(False)
- else:
- self.__browser.setRootIndex(index, model=self.__fileModel)
- self.__clearData()
-
- if self.__selectorWidget is not None:
- self.__selectorWidget.selectSlicing(url.data_slice())
- else:
- self.__errorWhileLoadingFile = (url.file_path(), "File not found")
- self.__clearData()
- else:
- self.__errorWhileLoadingFile = (url.file_path(), "Path invalid")
- self.__clearData()
-
- def previewToolbar(self):
- return self.__previewToolbar
-
- def previewWidget(self):
- return self.__previewWidget
-
- def selectorWidget(self):
- return self.__selectorWidget
-
- def _createPreviewToolbar(self, parent, dataPreviewWidget, dataSelectorWidget):
- return None
-
- def _createPreviewWidget(self, parent):
- return None
-
- def _createSelectorWidget(self, parent):
- return None
-
- # Selected file
-
- def setDirectory(self, path):
- """Sets the data dialog's current directory."""
- self.__fileModel_setRootPath(path)
-
- def selectedFile(self):
- """Returns the file path containing the selected data.
-
- :rtype: str
- """
- return self.__selectedFile
-
- def selectFile(self, filename):
- """Sets the data dialog's current file."""
- self.__directoryLoadedFilter = ""
- old = self.__pathEdit.blockSignals(True)
- try:
- self.__pathEdit.setText(filename)
- finally:
- self.__pathEdit.blockSignals(old)
- self.__pathChanged()
-
- # Selected data
-
- def selectUrl(self, url):
- """Sets the data dialog's current data url.
-
- :param Union[str,DataUrl] url: URL identifying a data (it can be a
- `DataUrl` object)
- """
- if isinstance(url, silx.io.url.DataUrl):
- url = url.path()
- self.__directoryLoadedFilter = ""
- old = self.__pathEdit.blockSignals(True)
- try:
- self.__pathEdit.setText(url)
- finally:
- self.__pathEdit.blockSignals(old)
- self.__pathChanged()
-
- def selectedUrl(self):
- """Returns the URL from the file system to the data.
-
- If the dialog is not validated, the path can be an intermediat
- selected path, or an invalid path.
-
- :rtype: str
- """
- return self.__pathEdit.text()
-
- def selectedDataUrl(self):
- """Returns the URL as a :class:`DataUrl` from the file system to the
- data.
-
- If the dialog is not validated, the path can be an intermediat
- selected path, or an invalid path.
-
- :rtype: DataUrl
- """
- url = self.selectedUrl()
- return silx.io.url.DataUrl(url)
-
- def directory(self):
- """Returns the path from the current browsed directory.
-
- :rtype: str
- """
- if self.__directory is not None:
- # At post execution, returns the cache
- return self.__directory
-
- index = self.__browser.rootIndex()
- if index.model() is self.__fileModel:
- path = self.__fileModel.filePath(index)
- return path
- elif index.model() is self.__dataModel:
- path = os.path.dirname(self.__h5.file.filename)
- return path
- else:
- return ""
-
- def _selectedData(self):
- """Returns the internal selected data
-
- :rtype: numpy.ndarray
- """
- return self.__selectedData
-
- # Filters
-
- def selectedNameFilter(self):
- """Returns the filter that the user selected in the file dialog."""
- return self.__fileTypeCombo.currentText()
-
- # History
-
- def history(self):
- """Returns the browsing history of the filedialog as a list of paths.
-
- :rtype: List<str>
- """
- if len(self.__currentHistory) <= 1:
- return []
- history = self.__currentHistory[0:self.__currentHistoryLocation]
- return list(history)
-
- def setHistory(self, history):
- self.__currentHistory = []
- self.__currentHistory.extend(history)
- self.__currentHistoryLocation = len(self.__currentHistory) - 1
- self.__updateActionHistory()
-
- # Colormap
-
- def colormap(self):
- if self.__previewWidget is None:
- return None
- return self.__previewWidget.colormap()
-
- def setColormap(self, colormap):
- if self.__previewWidget is None:
- raise RuntimeError("No preview widget defined")
- self.__previewWidget.setColormap(colormap)
-
- # Sidebar
-
- def setSidebarUrls(self, urls):
- """Sets the urls that are located in the sidebar."""
- if self.__sidebar is None:
- return
- self.__sidebar.setUrls(urls)
-
- def sidebarUrls(self):
- """Returns a list of urls that are currently in the sidebar."""
- if self.__sidebar is None:
- return []
- return self.__sidebar.urls()
-
- # State
-
- __serialVersion = 1
- """Store the current version of the serialized data"""
-
- @classmethod
- def qualifiedName(cls):
- return "%s.%s" % (cls.__module__, cls.__name__)
-
- def restoreState(self, state):
- """Restores the dialogs's layout, history and current directory to the
- state specified.
-
- :param qt.QByteArray state: Stream containing the new state
- :rtype: bool
- """
- stream = qt.QDataStream(state, qt.QIODevice.ReadOnly)
-
- qualifiedName = stream.readQString()
- if qualifiedName != self.qualifiedName():
- _logger.warning("Stored state contains an invalid qualified name. %s restoration cancelled.", self.__class__.__name__)
- return False
-
- version = stream.readInt32()
- if version != self.__serialVersion:
- _logger.warning("Stored state contains an invalid version. %s restoration cancelled.", self.__class__.__name__)
- return False
-
- result = True
-
- splitterData = stream.readQVariant()
- sidebarUrls = stream.readQStringList()
- history = stream.readQStringList()
- workingDirectory = stream.readQString()
- browserData = stream.readQVariant()
- viewMode = stream.readInt32()
- colormapData = stream.readQVariant()
-
- result &= self.__splitter.restoreState(splitterData)
- sidebarUrls = [qt.QUrl(s) for s in sidebarUrls]
- self.setSidebarUrls(list(sidebarUrls))
- history = [s for s in history]
- self.setHistory(list(history))
- if workingDirectory is not None:
- self.setDirectory(workingDirectory)
- result &= self.__browser.restoreState(browserData)
- self.setViewMode(viewMode)
- colormap = self.colormap()
- if colormap is not None:
- result &= self.colormap().restoreState(colormapData)
-
- return result
-
- def saveState(self):
- """Saves the state of the dialog's layout, history and current
- directory.
-
- :rtype: qt.QByteArray
- """
- data = qt.QByteArray()
- stream = qt.QDataStream(data, qt.QIODevice.WriteOnly)
-
- s = self.qualifiedName()
- stream.writeQString(u"%s" % s)
- stream.writeInt32(self.__serialVersion)
- stream.writeQVariant(self.__splitter.saveState())
- strings = [u"%s" % s.toString() for s in self.sidebarUrls()]
- stream.writeQStringList(strings)
- strings = [u"%s" % s for s in self.history()]
- stream.writeQStringList(strings)
- stream.writeQString(u"%s" % self.directory())
- stream.writeQVariant(self.__browser.saveState())
- stream.writeInt32(self.viewMode())
- colormap = self.colormap()
- if colormap is not None:
- stream.writeQVariant(self.colormap().saveState())
- else:
- stream.writeQVariant(None)
-
- return data