diff options
Diffstat (limited to 'silx/gui/hdf5/Hdf5TreeModel.py')
-rw-r--r-- | silx/gui/hdf5/Hdf5TreeModel.py | 135 |
1 files changed, 114 insertions, 21 deletions
diff --git a/silx/gui/hdf5/Hdf5TreeModel.py b/silx/gui/hdf5/Hdf5TreeModel.py index 41fa91c..2d62429 100644 --- a/silx/gui/hdf5/Hdf5TreeModel.py +++ b/silx/gui/hdf5/Hdf5TreeModel.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2016-2017 European Synchrotron Radiation Facility +# 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 @@ -25,11 +25,12 @@ __authors__ = ["V. Valls"] __license__ = "MIT" -__date__ = "22/09/2017" +__date__ = "29/11/2017" import os import logging +import functools from .. import qt from .. import icons from .Hdf5Node import Hdf5Node @@ -130,7 +131,6 @@ class LoadingItemRunnable(qt.QRunnable): item = Hdf5Item(text=text, obj=h5obj, parent=oldItem.parent, populateAll=True) return item - @qt.Slot() 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. @@ -237,25 +237,32 @@ class Hdf5TreeModel(qt.QAbstractItemModel): self.__openedFiles = [] """Store the list of files opened by the model itself.""" - # FIXME: It should managed one by one by Hdf5Item itself - - def __del__(self): - self._closeOpened() - s = super(Hdf5TreeModel, self) - if hasattr(s, "__del__"): - # else it fail on Python 3 - s.__del__() + # 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. - This function may be removed in the future. - File are opened by the model when it was inserted using `insertFileAsync`, `insertFile`, `appendFile`.""" - for h5file in self.__openedFiles: - h5file.close() - self.__openedFiles = [] + self._closeFileList(self.__openedFiles) def __updateLoadingItems(self, icon): for i in range(self.__root.childCount()): @@ -283,6 +290,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel): self.__root.removeChildAtIndex(row) self.endRemoveRows() if newItem is not None: + rootIndex = qt.QModelIndex() self.__openedFiles.append(newItem.obj) self.beginInsertRows(rootIndex, row, row) self.__root.insertChild(row, newItem) @@ -325,7 +333,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel): Returns an object that contains serialized items of data corresponding to the list of indexes specified. - :param list(qt.QModelIndex) indexes: List of indexes + :param List[qt.QModelIndex] indexes: List of indexes :rtype: qt.QMimeData """ if not self.__fileMoveEnabled or len(indexes) == 0: @@ -512,6 +520,16 @@ class Hdf5TreeModel(qt.QAbstractItemModel): 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 in 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. @@ -524,9 +542,8 @@ class Hdf5TreeModel(qt.QAbstractItemModel): if node.parent is not self.__root: return - self.removeIndex(index) filename = node.obj.filename - node.obj.close() + self.removeIndex(index) self.insertFileAsync(filename, index.row()) def synchronizeH5pyObject(self, h5pyObject): @@ -555,6 +572,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel): node = self.nodeFromIndex(index) if node.parent is not self.__root: return + self._closeFileIfOwned(node) self.beginRemoveRows(qt.QModelIndex(), index.row(), index.row()) self.__root.removeChildAtIndex(index.row()) self.endRemoveRows() @@ -587,6 +605,9 @@ class Hdf5TreeModel(qt.QAbstractItemModel): 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): if not os.path.isfile(filename): raise IOError("Filename '%s' must be a file path" % filename) @@ -599,9 +620,9 @@ class Hdf5TreeModel(qt.QAbstractItemModel): # start loading the real one runnable = LoadingItemRunnable(filename, item) runnable.itemReady.connect(self.__itemReady) - self.__runnerSet.add(runnable) runnable.runnerFinished.connect(self.__releaseRunner) - qt.QThreadPool.globalInstance().start(runnable) + self.__runnerSet.add(runnable) + qt.silxGlobalThreadPool().start(runnable) def __releaseRunner(self, runner): self.__runnerSet.remove(runner) @@ -621,3 +642,75 @@ class Hdf5TreeModel(qt.QAbstractItemModel): 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() |