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.py135
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()