summaryrefslogtreecommitdiff
path: root/silx/gui/hdf5/Hdf5TreeModel.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/hdf5/Hdf5TreeModel.py')
-rw-r--r--silx/gui/hdf5/Hdf5TreeModel.py778
1 files changed, 0 insertions, 778 deletions
diff --git a/silx/gui/hdf5/Hdf5TreeModel.py b/silx/gui/hdf5/Hdf5TreeModel.py
deleted file mode 100644
index 152f3e5..0000000
--- a/silx/gui/hdf5/Hdf5TreeModel.py
+++ /dev/null
@@ -1,778 +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.
-#
-# ###########################################################################*/
-
-__authors__ = ["V. Valls"]
-__license__ = "MIT"
-__date__ = "12/03/2019"
-
-
-import os
-import logging
-import functools
-from .. import qt
-from .. import icons
-from .Hdf5Node import Hdf5Node
-from .Hdf5Item import Hdf5Item
-from .Hdf5LoadingItem import Hdf5LoadingItem
-from . import _utils
-from ... import io as silx_io
-
-_logger = logging.getLogger(__name__)
-
-"""Helpers to take care of None objects as signal parameters.
-PySide crash if a signal with a None parameter is emitted between threads.
-"""
-if qt.BINDING == 'PySide':
- class _NoneWraper(object):
- pass
- _NoneWraperInstance = _NoneWraper()
-
- def _wrapNone(x):
- """Wrap x if it is a None value, else returns x"""
- if x is None:
- return _NoneWraperInstance
- else:
- return x
-
- def _unwrapNone(x):
- """Unwrap x as a None if a None was stored by `wrapNone`, else returns
- x"""
- if x is _NoneWraperInstance:
- return None
- else:
- return x
-else:
- # Allow to fix None event params to avoid PySide crashes
- def _wrapNone(x):
- return x
-
- def _unwrapNone(x):
- return x
-
-
-def _createRootLabel(h5obj):
- """
- Create label for the very first npde of the tree.
-
- :param h5obj: The h5py object to display in the GUI
- :type h5obj: h5py-like object
- :rtpye: str
- """
- if silx_io.is_file(h5obj):
- label = os.path.basename(h5obj.filename)
- else:
- filename = os.path.basename(h5obj.file.filename)
- path = h5obj.name
- if path.startswith("/"):
- path = path[1:]
- label = "%s::%s" % (filename, path)
- return label
-
-
-class LoadingItemRunnable(qt.QRunnable):
- """Runner to process item loading from a file"""
-
- class __Signals(qt.QObject):
- """Signal holder"""
- itemReady = qt.Signal(object, object, object)
- runnerFinished = qt.Signal(object)
-
- def __init__(self, filename, item):
- """Constructor
-
- :param LoadingItemWorker worker: Object holding data and signals
- """
- super(LoadingItemRunnable, self).__init__()
- self.filename = filename
- self.oldItem = item
- self.signals = self.__Signals()
-
- def setFile(self, filename, item):
- self.filenames.append((filename, item))
-
- @property
- def itemReady(self):
- return self.signals.itemReady
-
- @property
- def runnerFinished(self):
- return self.signals.runnerFinished
-
- def __loadItemTree(self, oldItem, h5obj):
- """Create an item tree used by the GUI from an h5py object.
-
- :param Hdf5Node oldItem: The current item displayed the GUI
- :param h5py.File h5obj: The h5py object to display in the GUI
- :rtpye: Hdf5Node
- """
- text = _createRootLabel(h5obj)
- item = Hdf5Item(text=text, obj=h5obj, parent=oldItem.parent, populateAll=True)
- return item
-
- def run(self):
- """Process the file loading. The worker is used as holder
- of the data and the signal. The result is sent as a signal.
- """
- h5file = None
- try:
- h5file = silx_io.open(self.filename)
- newItem = self.__loadItemTree(self.oldItem, h5file)
- error = None
- except IOError as e:
- # Should be logged
- error = e
- newItem = None
- if h5file is not None:
- h5file.close()
-
- # Take care of None value in case of PySide
- newItem = _wrapNone(newItem)
- error = _wrapNone(error)
- self.itemReady.emit(self.oldItem, newItem, error)
- self.runnerFinished.emit(self)
-
- def autoDelete(self):
- return True
-
-
-class Hdf5TreeModel(qt.QAbstractItemModel):
- """Tree model storing a list of :class:`h5py.File` like objects.
-
- The main column display the :class:`h5py.File` list and there hierarchy.
- Other columns display information on node hierarchy.
- """
-
- H5PY_ITEM_ROLE = qt.Qt.UserRole
- """Role to reach h5py item from an item index"""
-
- H5PY_OBJECT_ROLE = qt.Qt.UserRole + 1
- """Role to reach h5py object from an item index"""
-
- USER_ROLE = qt.Qt.UserRole + 2
- """Start of range of available user role for derivative models"""
-
- NAME_COLUMN = 0
- """Column id containing HDF5 node names"""
-
- TYPE_COLUMN = 1
- """Column id containing HDF5 dataset types"""
-
- SHAPE_COLUMN = 2
- """Column id containing HDF5 dataset shapes"""
-
- VALUE_COLUMN = 3
- """Column id containing HDF5 dataset values"""
-
- DESCRIPTION_COLUMN = 4
- """Column id containing HDF5 node description/title/message"""
-
- NODE_COLUMN = 5
- """Column id containing HDF5 node type"""
-
- LINK_COLUMN = 6
- """Column id containing HDF5 link type"""
-
- COLUMN_IDS = [
- NAME_COLUMN,
- TYPE_COLUMN,
- SHAPE_COLUMN,
- VALUE_COLUMN,
- DESCRIPTION_COLUMN,
- NODE_COLUMN,
- LINK_COLUMN,
- ]
- """List of logical columns available"""
-
- sigH5pyObjectLoaded = qt.Signal(object)
- """Emitted when a new root item was loaded and inserted to the model."""
-
- sigH5pyObjectRemoved = qt.Signal(object)
- """Emitted when a root item is removed from the model."""
-
- sigH5pyObjectSynchronized = qt.Signal(object, object)
- """Emitted when an item was synchronized."""
-
- def __init__(self, parent=None, ownFiles=True):
- """
- Constructor
-
- :param qt.QWidget parent: Parent widget
- :param bool ownFiles: If true (default) the model will manage the files
- life cycle when they was added using path (like DnD).
- """
- super(Hdf5TreeModel, self).__init__(parent)
-
- self.header_labels = [None] * len(self.COLUMN_IDS)
- self.header_labels[self.NAME_COLUMN] = 'Name'
- self.header_labels[self.TYPE_COLUMN] = 'Type'
- self.header_labels[self.SHAPE_COLUMN] = 'Shape'
- self.header_labels[self.VALUE_COLUMN] = 'Value'
- self.header_labels[self.DESCRIPTION_COLUMN] = 'Description'
- self.header_labels[self.NODE_COLUMN] = 'Node'
- self.header_labels[self.LINK_COLUMN] = 'Link'
-
- # Create items
- self.__root = Hdf5Node()
- self.__fileDropEnabled = True
- self.__fileMoveEnabled = True
- self.__datasetDragEnabled = False
-
- self.__animatedIcon = icons.getWaitIcon()
- self.__animatedIcon.iconChanged.connect(self.__updateLoadingItems)
- self.__runnerSet = set([])
-
- # store used icons to avoid the cache to release it
- self.__icons = []
- self.__icons.append(icons.getQIcon("item-none"))
- self.__icons.append(icons.getQIcon("item-0dim"))
- self.__icons.append(icons.getQIcon("item-1dim"))
- self.__icons.append(icons.getQIcon("item-2dim"))
- self.__icons.append(icons.getQIcon("item-3dim"))
- self.__icons.append(icons.getQIcon("item-ndim"))
-
- self.__ownFiles = ownFiles
- self.__openedFiles = []
- """Store the list of files opened by the model itself."""
- # FIXME: It should be managed one by one by Hdf5Item itself
-
- # 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 Hdf5TreeModel")
- for obj in fileList:
- _logger.debug("Close file %s", obj.filename)
- obj.close()
- fileList[:] = []
-
- def _closeOpened(self):
- """Close files which was opened by this model.
-
- File are opened by the model when it was inserted using
- `insertFileAsync`, `insertFile`, `appendFile`."""
- self._closeFileList(self.__openedFiles)
-
- def __updateLoadingItems(self, icon):
- for i in range(self.__root.childCount()):
- item = self.__root.child(i)
- if isinstance(item, Hdf5LoadingItem):
- index1 = self.index(i, 0, qt.QModelIndex())
- index2 = self.index(i, self.columnCount() - 1, qt.QModelIndex())
- self.dataChanged.emit(index1, index2)
-
- def __itemReady(self, oldItem, newItem, error):
- """Called at the end of a concurent file loading, when the loading
- item is ready. AN error is defined if an exception occured when
- loading the newItem .
-
- :param Hdf5Node oldItem: current displayed item
- :param Hdf5Node newItem: item loaded, or None if error is defined
- :param Exception error: An exception, or None if newItem is defined
- """
- # Take care of None value in case of PySide
- newItem = _unwrapNone(newItem)
- error = _unwrapNone(error)
- row = self.__root.indexOfChild(oldItem)
-
- rootIndex = qt.QModelIndex()
- self.beginRemoveRows(rootIndex, row, row)
- self.__root.removeChildAtIndex(row)
- self.endRemoveRows()
-
- if newItem is not None:
- rootIndex = qt.QModelIndex()
- if self.__ownFiles:
- self.__openedFiles.append(newItem.obj)
- self.beginInsertRows(rootIndex, row, row)
- self.__root.insertChild(row, newItem)
- self.endInsertRows()
-
- if isinstance(oldItem, Hdf5LoadingItem):
- self.sigH5pyObjectLoaded.emit(newItem.obj)
- else:
- self.sigH5pyObjectSynchronized.emit(oldItem.obj, newItem.obj)
-
- # FIXME the error must be displayed
-
- def isFileDropEnabled(self):
- return self.__fileDropEnabled
-
- def setFileDropEnabled(self, enabled):
- self.__fileDropEnabled = enabled
-
- fileDropEnabled = qt.Property(bool, isFileDropEnabled, setFileDropEnabled)
- """Property to enable/disable file dropping in the model."""
-
- def isDatasetDragEnabled(self):
- return self.__datasetDragEnabled
-
- def setDatasetDragEnabled(self, enabled):
- self.__datasetDragEnabled = enabled
-
- datasetDragEnabled = qt.Property(bool, isDatasetDragEnabled, setDatasetDragEnabled)
- """Property to enable/disable drag of datasets."""
-
- def isFileMoveEnabled(self):
- return self.__fileMoveEnabled
-
- def setFileMoveEnabled(self, enabled):
- self.__fileMoveEnabled = enabled
-
- fileMoveEnabled = qt.Property(bool, isFileMoveEnabled, setFileMoveEnabled)
- """Property to enable/disable drag-and-drop of files to
- change the ordering in the model."""
-
- def supportedDropActions(self):
- if self.__fileMoveEnabled or self.__fileDropEnabled:
- return qt.Qt.CopyAction | qt.Qt.MoveAction
- else:
- return 0
-
- def mimeTypes(self):
- types = []
- if self.__fileMoveEnabled or self.__datasetDragEnabled:
- types.append(_utils.Hdf5DatasetMimeData.MIME_TYPE)
- return types
-
- def mimeData(self, indexes):
- """
- Returns an object that contains serialized items of data corresponding
- to the list of indexes specified.
-
- :param List[qt.QModelIndex] indexes: List of indexes
- :rtype: qt.QMimeData
- """
- if len(indexes) == 0:
- return None
-
- indexes = [i for i in indexes if i.column() == 0]
- if len(indexes) > 1:
- raise NotImplementedError("Drag of multi rows is not implemented")
- if len(indexes) == 0:
- raise NotImplementedError("Drag of cell is not implemented")
-
- node = self.nodeFromIndex(indexes[0])
-
- if self.__fileMoveEnabled and node.parent is self.__root:
- mimeData = _utils.Hdf5DatasetMimeData(node=node, isRoot=True)
- elif self.__datasetDragEnabled:
- mimeData = _utils.Hdf5DatasetMimeData(node=node)
- else:
- mimeData = None
- return mimeData
-
- def flags(self, index):
- defaultFlags = qt.QAbstractItemModel.flags(self, index)
-
- if index.isValid():
- node = self.nodeFromIndex(index)
- if self.__fileMoveEnabled and node.parent is self.__root:
- # that's a root
- return qt.Qt.ItemIsDragEnabled | defaultFlags
- elif self.__datasetDragEnabled:
- return qt.Qt.ItemIsDragEnabled | defaultFlags
- return defaultFlags
- elif self.__fileDropEnabled or self.__fileMoveEnabled:
- return qt.Qt.ItemIsDropEnabled | defaultFlags
- else:
- return defaultFlags
-
- def dropMimeData(self, mimedata, action, row, column, parentIndex):
- if action == qt.Qt.IgnoreAction:
- return True
-
- if self.__fileMoveEnabled and mimedata.hasFormat(_utils.Hdf5DatasetMimeData.MIME_TYPE):
- if mimedata.isRoot():
- dragNode = mimedata.node()
- parentNode = self.nodeFromIndex(parentIndex)
- if parentNode is not dragNode.parent:
- return False
-
- if row == -1:
- # append to the parent
- row = parentNode.childCount()
- else:
- # insert at row
- pass
-
- dragNodeParent = dragNode.parent
- sourceRow = dragNodeParent.indexOfChild(dragNode)
- self.moveRow(parentIndex, sourceRow, parentIndex, row)
- return True
-
- if self.__fileDropEnabled and mimedata.hasFormat("text/uri-list"):
-
- parentNode = self.nodeFromIndex(parentIndex)
- if parentNode is not self.__root:
- while(parentNode is not self.__root):
- node = parentNode
- parentNode = node.parent
- row = parentNode.indexOfChild(node)
- else:
- if row == -1:
- row = self.__root.childCount()
-
- messages = []
- for url in mimedata.urls():
- try:
- self.insertFileAsync(url.toLocalFile(), row)
- row += 1
- except IOError as e:
- messages.append(e.args[0])
- if len(messages) > 0:
- title = "Error occurred when loading files"
- message = "<html>%s:<ul><li>%s</li><ul></html>" % (title, "</li><li>".join(messages))
- qt.QMessageBox.critical(None, title, message)
- return True
-
- return False
-
- def headerData(self, section, orientation, role=qt.Qt.DisplayRole):
- if orientation == qt.Qt.Horizontal:
- if role in [qt.Qt.DisplayRole, qt.Qt.EditRole]:
- return self.header_labels[section]
- return None
-
- def insertNode(self, row, node):
- if row == -1:
- row = self.__root.childCount()
- self.beginInsertRows(qt.QModelIndex(), row, row)
- self.__root.insertChild(row, node)
- self.endInsertRows()
-
- def moveRow(self, sourceParentIndex, sourceRow, destinationParentIndex, destinationRow):
- if sourceRow == destinationRow or sourceRow == destinationRow - 1:
- # abort move, same place
- return
- return self.moveRows(sourceParentIndex, sourceRow, 1, destinationParentIndex, destinationRow)
-
- def moveRows(self, sourceParentIndex, sourceRow, count, destinationParentIndex, destinationRow):
- self.beginMoveRows(sourceParentIndex, sourceRow, sourceRow, destinationParentIndex, destinationRow)
- sourceNode = self.nodeFromIndex(sourceParentIndex)
- destinationNode = self.nodeFromIndex(destinationParentIndex)
-
- if sourceNode is destinationNode and sourceRow < destinationRow:
- item = sourceNode.child(sourceRow)
- destinationNode.insertChild(destinationRow, item)
- sourceNode.removeChildAtIndex(sourceRow)
- else:
- item = sourceNode.removeChildAtIndex(sourceRow)
- destinationNode.insertChild(destinationRow, item)
-
- self.endMoveRows()
- return True
-
- def index(self, row, column, parent=qt.QModelIndex()):
- try:
- node = self.nodeFromIndex(parent)
- return self.createIndex(row, column, node.child(row))
- except IndexError:
- return qt.QModelIndex()
-
- def data(self, index, role=qt.Qt.DisplayRole):
- node = self.nodeFromIndex(index)
-
- if role == self.H5PY_ITEM_ROLE:
- return node
-
- if role == self.H5PY_OBJECT_ROLE:
- return node.obj
-
- if index.column() == self.NAME_COLUMN:
- return node.dataName(role)
- elif index.column() == self.TYPE_COLUMN:
- return node.dataType(role)
- elif index.column() == self.SHAPE_COLUMN:
- return node.dataShape(role)
- elif index.column() == self.VALUE_COLUMN:
- return node.dataValue(role)
- elif index.column() == self.DESCRIPTION_COLUMN:
- return node.dataDescription(role)
- elif index.column() == self.NODE_COLUMN:
- return node.dataNode(role)
- elif index.column() == self.LINK_COLUMN:
- return node.dataLink(role)
- else:
- return None
-
- def columnCount(self, parent=qt.QModelIndex()):
- return len(self.COLUMN_IDS)
-
- def hasChildren(self, parent=qt.QModelIndex()):
- node = self.nodeFromIndex(parent)
- if node is None:
- return 0
- return node.hasChildren()
-
- def rowCount(self, parent=qt.QModelIndex()):
- node = self.nodeFromIndex(parent)
- if node is None:
- return 0
- return node.childCount()
-
- def parent(self, child):
- if not child.isValid():
- return qt.QModelIndex()
-
- node = self.nodeFromIndex(child)
-
- if node is None:
- return qt.QModelIndex()
-
- parent = node.parent
-
- if parent is None:
- return qt.QModelIndex()
-
- grandparent = parent.parent
- if grandparent is None:
- return qt.QModelIndex()
- row = grandparent.indexOfChild(parent)
-
- assert row != - 1
- return self.createIndex(row, 0, parent)
-
- def nodeFromIndex(self, index):
- return index.internalPointer() if index.isValid() else self.__root
-
- def _closeFileIfOwned(self, node):
- """"Close the file if it was loaded from a filename or a
- drag-and-drop"""
- obj = node.obj
- for f in self.__openedFiles:
- if f is obj:
- _logger.debug("Close file %s", obj.filename)
- obj.close()
- self.__openedFiles.remove(obj)
-
- def synchronizeIndex(self, index):
- """
- Synchronize a file a given its index.
-
- Basically close it and load it again.
-
- :param qt.QModelIndex index: Index of the item to update
- """
- node = self.nodeFromIndex(index)
- if node.parent is not self.__root:
- return
-
- filename = node.obj.filename
- self.insertFileAsync(filename, index.row(), synchronizingNode=node)
-
- def h5pyObjectRow(self, h5pyObject):
- for row in range(self.__root.childCount()):
- item = self.__root.child(row)
- if item.obj == h5pyObject:
- return row
- return -1
-
- def synchronizeH5pyObject(self, h5pyObject):
- """
- Synchronize a h5py object in all the tree.
-
- Basically close it and load it again.
-
- :param h5py.File h5pyObject: A :class:`h5py.File` object.
- """
- index = 0
- while index < self.__root.childCount():
- item = self.__root.child(index)
- if item.obj == h5pyObject:
- qindex = self.index(index, 0, qt.QModelIndex())
- self.synchronizeIndex(qindex)
- index += 1
-
- def removeIndex(self, index):
- """
- Remove an item from the model using its index.
-
- :param qt.QModelIndex index: Index of the item to remove
- """
- node = self.nodeFromIndex(index)
- if node.parent != self.__root:
- return
- self._closeFileIfOwned(node)
- self.beginRemoveRows(qt.QModelIndex(), index.row(), index.row())
- self.__root.removeChildAtIndex(index.row())
- self.endRemoveRows()
- self.sigH5pyObjectRemoved.emit(node.obj)
-
- def removeH5pyObject(self, h5pyObject):
- """
- Remove an item from the model using the holding h5py object.
- It can remove more than one item.
-
- :param h5py.File h5pyObject: A :class:`h5py.File` object.
- """
- index = 0
- while index < self.__root.childCount():
- item = self.__root.child(index)
- if item.obj == h5pyObject:
- qindex = self.index(index, 0, qt.QModelIndex())
- self.removeIndex(qindex)
- else:
- index += 1
-
- def insertH5pyObject(self, h5pyObject, text=None, row=-1):
- """Append an HDF5 object from h5py to the tree.
-
- :param h5pyObject: File handle/descriptor for a :class:`h5py.File`
- or any other class of h5py file structure.
- """
- if text is None:
- text = _createRootLabel(h5pyObject)
- if row == -1:
- row = self.__root.childCount()
- self.insertNode(row, Hdf5Item(text=text, obj=h5pyObject, parent=self.__root))
-
- def hasPendingOperations(self):
- return len(self.__runnerSet) > 0
-
- def insertFileAsync(self, filename, row=-1, synchronizingNode=None):
- if not os.path.isfile(filename):
- raise IOError("Filename '%s' must be a file path" % filename)
-
- # create temporary item
- if synchronizingNode is None:
- text = os.path.basename(filename)
- item = Hdf5LoadingItem(text=text, parent=self.__root, animatedIcon=self.__animatedIcon)
- self.insertNode(row, item)
- else:
- item = synchronizingNode
-
- # start loading the real one
- runnable = LoadingItemRunnable(filename, item)
- runnable.itemReady.connect(self.__itemReady)
- runnable.runnerFinished.connect(self.__releaseRunner)
- self.__runnerSet.add(runnable)
- qt.silxGlobalThreadPool().start(runnable)
-
- def __releaseRunner(self, runner):
- self.__runnerSet.remove(runner)
-
- def insertFile(self, filename, row=-1):
- """Load a HDF5 file into the data model.
-
- :param filename: file path.
- """
- try:
- h5file = silx_io.open(filename)
- if self.__ownFiles:
- self.__openedFiles.append(h5file)
- self.sigH5pyObjectLoaded.emit(h5file)
- self.insertH5pyObject(h5file, row=row)
- except IOError:
- _logger.debug("File '%s' can't be read.", filename, exc_info=True)
- raise
-
- def clear(self):
- """Remove all the content of the model"""
- for _ in range(self.rowCount()):
- qindex = self.index(0, 0, qt.QModelIndex())
- self.removeIndex(qindex)
-
- def appendFile(self, filename):
- self.insertFile(filename, -1)
-
- def indexFromH5Object(self, h5Object):
- """Returns a model index from an h5py-like object.
-
- :param object h5Object: An h5py-like object
- :rtype: qt.QModelIndex
- """
- if h5Object is None:
- return qt.QModelIndex()
-
- filename = h5Object.file.filename
-
- # Seach for the right roots
- rootIndices = []
- for index in range(self.rowCount(qt.QModelIndex())):
- index = self.index(index, 0, qt.QModelIndex())
- obj = self.data(index, Hdf5TreeModel.H5PY_OBJECT_ROLE)
- if obj.file.filename == filename:
- # We can have many roots with different subtree of the same
- # root
- rootIndices.append(index)
-
- if len(rootIndices) == 0:
- # No root found
- return qt.QModelIndex()
-
- path = h5Object.name + "/"
- path = path.replace("//", "/")
-
- # Search for the right node
- found = False
- foundIndices = []
- for _ in range(1000 * len(rootIndices)):
- # Avoid too much iterations, in case of recurssive links
- if len(foundIndices) == 0:
- if len(rootIndices) == 0:
- # Nothing found
- break
- # Start fron a new root
- foundIndices.append(rootIndices.pop(0))
-
- obj = self.data(index, Hdf5TreeModel.H5PY_OBJECT_ROLE)
- p = obj.name + "/"
- p = p.replace("//", "/")
- if path == p:
- found = True
- break
-
- parentIndex = foundIndices[-1]
- for index in range(self.rowCount(parentIndex)):
- index = self.index(index, 0, parentIndex)
- obj = self.data(index, Hdf5TreeModel.H5PY_OBJECT_ROLE)
-
- p = obj.name + "/"
- p = p.replace("//", "/")
- if path == p:
- foundIndices.append(index)
- found = True
- break
- elif path.startswith(p):
- foundIndices.append(index)
- break
- else:
- # Nothing found, start again with another root
- foundIndices = []
-
- if found:
- break
-
- if found:
- return foundIndices[-1]
- return qt.QModelIndex()