summaryrefslogtreecommitdiff
path: root/silx/gui/hdf5
diff options
context:
space:
mode:
authorPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2018-07-31 16:22:25 +0200
committerPicca Frédéric-Emmanuel <picca@synchrotron-soleil.fr>2018-07-31 16:22:25 +0200
commit159ef14fb9e198bb0066ea14e6b980f065de63dd (patch)
treebc37c7d4ba09ee59deb708897fa0571709aec293 /silx/gui/hdf5
parent270d5ddc31c26b62379e3caa9044dd75ccc71847 (diff)
New upstream version 0.8.0+dfsg
Diffstat (limited to 'silx/gui/hdf5')
-rw-r--r--silx/gui/hdf5/Hdf5Formatter.py18
-rw-r--r--silx/gui/hdf5/Hdf5TreeModel.py90
-rw-r--r--silx/gui/hdf5/Hdf5TreeView.py17
-rw-r--r--silx/gui/hdf5/NexusSortFilterProxyModel.py59
-rw-r--r--silx/gui/hdf5/_utils.py22
-rw-r--r--silx/gui/hdf5/test/test_hdf5.py256
6 files changed, 351 insertions, 111 deletions
diff --git a/silx/gui/hdf5/Hdf5Formatter.py b/silx/gui/hdf5/Hdf5Formatter.py
index 0e3697f..6802142 100644
--- a/silx/gui/hdf5/Hdf5Formatter.py
+++ b/silx/gui/hdf5/Hdf5Formatter.py
@@ -27,7 +27,7 @@ text."""
__authors__ = ["V. Valls"]
__license__ = "MIT"
-__date__ = "23/01/2018"
+__date__ = "06/06/2018"
import numpy
from silx.third_party import six
@@ -119,7 +119,11 @@ class Hdf5Formatter(qt.QObject):
return text
def humanReadableType(self, dataset, full=False):
- dtype = dataset.dtype
+ if hasattr(dataset, "dtype"):
+ dtype = dataset.dtype
+ else:
+ # Fallback...
+ dtype = type(dataset)
return self.humanReadableDType(dtype, full)
def humanReadableDType(self, dtype, full=False):
@@ -164,6 +168,16 @@ class Hdf5Formatter(qt.QObject):
return "enum"
text = str(dtype.newbyteorder('N'))
+ if numpy.issubdtype(dtype, numpy.floating):
+ if hasattr(numpy, "float128") and dtype == numpy.float128:
+ text = "float80"
+ if full:
+ text += " (padding 128bits)"
+ elif hasattr(numpy, "float96") and dtype == numpy.float96:
+ text = "float80"
+ if full:
+ text += " (padding 96bits)"
+
if full:
if dtype.byteorder == "<":
text = "Little-endian " + text
diff --git a/silx/gui/hdf5/Hdf5TreeModel.py b/silx/gui/hdf5/Hdf5TreeModel.py
index 2d62429..835708a 100644
--- a/silx/gui/hdf5/Hdf5TreeModel.py
+++ b/silx/gui/hdf5/Hdf5TreeModel.py
@@ -25,7 +25,7 @@
__authors__ = ["V. Valls"]
__license__ = "MIT"
-__date__ = "29/11/2017"
+__date__ = "11/06/2018"
import os
@@ -205,7 +205,23 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
]
"""List of logical columns available"""
- def __init__(self, parent=None):
+ 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)
@@ -221,6 +237,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
self.__root = Hdf5Node()
self.__fileDropEnabled = True
self.__fileMoveEnabled = True
+ self.__datasetDragEnabled = False
self.__animatedIcon = icons.getWaitIcon()
self.__animatedIcon.iconChanged.connect(self.__updateLoadingItems)
@@ -235,6 +252,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
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
@@ -285,16 +303,25 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
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()
- self.__openedFiles.append(newItem.obj)
+ 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):
@@ -306,6 +333,15 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
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
@@ -323,10 +359,12 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
return 0
def mimeTypes(self):
+ types = []
if self.__fileMoveEnabled:
- return [_utils.Hdf5NodeMimeData.MIME_TYPE]
- else:
- return []
+ types.append(_utils.Hdf5NodeMimeData.MIME_TYPE)
+ if self.__datasetDragEnabled:
+ types.append(_utils.Hdf5DatasetMimeData.MIME_TYPE)
+ return types
def mimeData(self, indexes):
"""
@@ -336,7 +374,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
:param List[qt.QModelIndex] indexes: List of indexes
:rtype: qt.QMimeData
"""
- if not self.__fileMoveEnabled or len(indexes) == 0:
+ if len(indexes) == 0:
return None
indexes = [i for i in indexes if i.column() == 0]
@@ -346,7 +384,13 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
raise NotImplementedError("Drag of cell is not implemented")
node = self.nodeFromIndex(indexes[0])
- mimeData = _utils.Hdf5NodeMimeData(node)
+
+ if self.__fileMoveEnabled and node.parent is self.__root:
+ mimeData = _utils.Hdf5NodeMimeData(node=node)
+ elif self.__datasetDragEnabled:
+ mimeData = _utils.Hdf5DatasetMimeData(node=node)
+ else:
+ mimeData = None
return mimeData
def flags(self, index):
@@ -357,6 +401,8 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
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
@@ -543,8 +589,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
return
filename = node.obj.filename
- self.removeIndex(index)
- self.insertFileAsync(filename, index.row())
+ self.insertFileAsync(filename, index.row(), synchronizingNode=node)
def synchronizeH5pyObject(self, h5pyObject):
"""
@@ -560,8 +605,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
if item.obj is h5pyObject:
qindex = self.index(index, 0, qt.QModelIndex())
self.synchronizeIndex(qindex)
- else:
- index += 1
+ index += 1
def removeIndex(self, index):
"""
@@ -576,6 +620,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
self.beginRemoveRows(qt.QModelIndex(), index.row(), index.row())
self.__root.removeChildAtIndex(index.row())
self.endRemoveRows()
+ self.sigH5pyObjectRemoved.emit(node.obj)
def removeH5pyObject(self, h5pyObject):
"""
@@ -608,14 +653,17 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
def hasPendingOperations(self):
return len(self.__runnerSet) > 0
- def insertFileAsync(self, filename, row=-1):
+ 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
- text = os.path.basename(filename)
- item = Hdf5LoadingItem(text=text, parent=self.__root, animatedIcon=self.__animatedIcon)
- self.insertNode(row, 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)
@@ -634,12 +682,20 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
"""
try:
h5file = silx_io.open(filename)
- self.__openedFiles.append(h5file)
+ 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)
diff --git a/silx/gui/hdf5/Hdf5TreeView.py b/silx/gui/hdf5/Hdf5TreeView.py
index 78b5c19..a86140a 100644
--- a/silx/gui/hdf5/Hdf5TreeView.py
+++ b/silx/gui/hdf5/Hdf5TreeView.py
@@ -25,7 +25,7 @@
__authors__ = ["V. Valls"]
__license__ = "MIT"
-__date__ = "20/02/2018"
+__date__ = "30/04/2018"
import logging
@@ -66,10 +66,8 @@ class Hdf5TreeView(qt.QTreeView):
"""
qt.QTreeView.__init__(self, parent)
- model = Hdf5TreeModel(self)
- proxy_model = NexusSortFilterProxyModel(self)
- proxy_model.setSourceModel(model)
- self.setModel(proxy_model)
+ model = self.createDefaultModel()
+ self.setModel(model)
self.setHeader(Hdf5HeaderView(qt.Qt.Horizontal, self))
self.setSelectionBehavior(qt.QAbstractItemView.SelectRows)
@@ -87,6 +85,15 @@ class Hdf5TreeView(qt.QTreeView):
self.setContextMenuPolicy(qt.Qt.CustomContextMenu)
self.customContextMenuRequested.connect(self._createContextMenu)
+ def createDefaultModel(self):
+ """Creates and returns the default model.
+
+ Inherite to custom the default model"""
+ model = Hdf5TreeModel(self)
+ proxy_model = NexusSortFilterProxyModel(self)
+ proxy_model.setSourceModel(model)
+ return proxy_model
+
def __removeContextMenuProxies(self, ref):
"""Callback to remove dead proxy from the list"""
self.__context_menu_callbacks.remove(ref)
diff --git a/silx/gui/hdf5/NexusSortFilterProxyModel.py b/silx/gui/hdf5/NexusSortFilterProxyModel.py
index 9a27968..3f2cf8d 100644
--- a/silx/gui/hdf5/NexusSortFilterProxyModel.py
+++ b/silx/gui/hdf5/NexusSortFilterProxyModel.py
@@ -25,7 +25,7 @@
__authors__ = ["V. Valls"]
__license__ = "MIT"
-__date__ = "10/10/2017"
+__date__ = "25/06/2018"
import logging
@@ -34,6 +34,7 @@ import numpy
from .. import qt
from .Hdf5TreeModel import Hdf5TreeModel
import silx.io.utils
+from silx.gui import icons
_logger = logging.getLogger(__name__)
@@ -45,6 +46,7 @@ class NexusSortFilterProxyModel(qt.QSortFilterProxyModel):
def __init__(self, parent=None):
qt.QSortFilterProxyModel.__init__(self, parent)
self.__split = re.compile("(\\d+|\\D+)")
+ self.__iconCache = {}
def lessThan(self, sourceLeft, sourceRight):
"""Returns True if the value of the item referred to by the given
@@ -86,6 +88,14 @@ class NexusSortFilterProxyModel(qt.QSortFilterProxyModel):
nxClass = node.obj.attrs.get("NX_class", None)
return nxClass == "NXentry"
+ def __isNXnode(self, node):
+ """Returns true if the node is an NX concept"""
+ class_ = node.h5Class
+ if class_ is None or class_ != silx.io.utils.H5Type.GROUP:
+ return False
+ nxClass = node.obj.attrs.get("NX_class", None)
+ return nxClass is not None
+
def getWordsAndNumbers(self, name):
"""
Returns a list of words and integers composing the name.
@@ -96,11 +106,14 @@ class NexusSortFilterProxyModel(qt.QSortFilterProxyModel):
:param str name: A name
:rtype: List
"""
+ nonSensitive = self.sortCaseSensitivity() == qt.Qt.CaseInsensitive
words = self.__split.findall(name)
result = []
for i in words:
if i[0].isdigit():
i = int(i)
+ elif nonSensitive:
+ i = i.lower()
result.append(i)
return result
@@ -145,3 +158,47 @@ class NexusSortFilterProxyModel(qt.QSortFilterProxyModel):
except Exception:
_logger.debug("Exception occurred", exc_info=True)
return None
+
+ def __createCompoundIcon(self, backgroundIcon, foregroundIcon):
+ icon = qt.QIcon()
+
+ sizes = backgroundIcon.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 = qt.QSize(32, 32)
+
+ modes = [qt.QIcon.Normal, qt.QIcon.Disabled]
+ for mode in modes:
+ pixmap = qt.QPixmap(baseSize)
+ pixmap.fill(qt.Qt.transparent)
+ painter = qt.QPainter(pixmap)
+ painter.drawPixmap(0, 0, backgroundIcon.pixmap(baseSize, mode=mode))
+ painter.drawPixmap(0, 0, foregroundIcon.pixmap(baseSize, mode=mode))
+ painter.end()
+ icon.addPixmap(pixmap, mode=mode)
+
+ return icon
+
+ def __getNxIcon(self, baseIcon):
+ iconHash = baseIcon.cacheKey()
+ icon = self.__iconCache.get(iconHash, None)
+ if icon is None:
+ nxIcon = icons.getQIcon("layer-nx")
+ icon = self.__createCompoundIcon(baseIcon, nxIcon)
+ self.__iconCache[iconHash] = icon
+ return icon
+
+ def data(self, index, role=qt.Qt.DisplayRole):
+ result = super(NexusSortFilterProxyModel, self).data(index, role)
+
+ if index.column() == Hdf5TreeModel.NAME_COLUMN:
+ if role == qt.Qt.DecorationRole:
+ sourceIndex = self.mapToSource(index)
+ item = self.sourceModel().data(sourceIndex, Hdf5TreeModel.H5PY_ITEM_ROLE)
+ if self.__isNXnode(item):
+ result = self.__getNxIcon(result)
+ return result
diff --git a/silx/gui/hdf5/_utils.py b/silx/gui/hdf5/_utils.py
index ddf4db5..8385129 100644
--- a/silx/gui/hdf5/_utils.py
+++ b/silx/gui/hdf5/_utils.py
@@ -28,7 +28,7 @@ package `silx.gui.hdf5` package.
__authors__ = ["V. Valls"]
__license__ = "MIT"
-__date__ = "20/12/2017"
+__date__ = "04/05/2018"
import logging
@@ -102,6 +102,26 @@ def htmlFromDict(dictionary, title=None):
return result
+class Hdf5DatasetMimeData(qt.QMimeData):
+ """Mimedata class to identify an internal drag and drop of a Hdf5Node."""
+
+ MIME_TYPE = "application/x-internal-h5py-dataset"
+
+ def __init__(self, node=None, dataset=None):
+ qt.QMimeData.__init__(self)
+ self.__dataset = dataset
+ self.__node = node
+ self.setData(self.MIME_TYPE, "".encode(encoding='utf-8'))
+
+ def node(self):
+ return self.__node
+
+ def dataset(self):
+ if self.__node is not None:
+ return self.__node.obj
+ return self.__dataset
+
+
class Hdf5NodeMimeData(qt.QMimeData):
"""Mimedata class to identify an internal drag and drop of a Hdf5Node."""
diff --git a/silx/gui/hdf5/test/test_hdf5.py b/silx/gui/hdf5/test/test_hdf5.py
index 44c4456..fc27f6b 100644
--- a/silx/gui/hdf5/test/test_hdf5.py
+++ b/silx/gui/hdf5/test/test_hdf5.py
@@ -26,7 +26,7 @@
__authors__ = ["V. Valls"]
__license__ = "MIT"
-__date__ = "20/02/2018"
+__date__ = "03/05/2018"
import time
@@ -39,6 +39,7 @@ from contextlib import contextmanager
from silx.gui import qt
from silx.gui.test.utils import TestCaseQt
from silx.gui import hdf5
+from silx.gui.test.utils import SignalListener
from silx.io import commonh5
import weakref
@@ -48,6 +49,29 @@ except ImportError:
h5py = None
+_tmpDirectory = None
+
+
+def setUpModule():
+ global _tmpDirectory
+ _tmpDirectory = tempfile.mkdtemp(prefix=__name__)
+
+ if h5py is not None:
+ filename = _tmpDirectory + "/data.h5"
+
+ # create h5 data
+ f = h5py.File(filename, "w")
+ g = f.create_group("arrays")
+ g.create_dataset("scalar", data=10)
+ f.close()
+
+
+def tearDownModule():
+ global _tmpDirectory
+ shutil.rmtree(_tmpDirectory)
+ _tmpDirectory = None
+
+
_called = 0
@@ -71,7 +95,7 @@ class TestHdf5TreeModel(TestCaseQt):
self.skipTest("h5py is not available")
def waitForPendingOperations(self, model):
- for i in range(10):
+ for _ in range(10):
if not model.hasPendingOperations():
break
self.qWait(10)
@@ -97,53 +121,53 @@ class TestHdf5TreeModel(TestCaseQt):
self.assertIsNotNone(model)
def testAppendFilename(self):
- with self.h5TempFile() as filename:
+ filename = _tmpDirectory + "/data.h5"
+ model = hdf5.Hdf5TreeModel()
+ self.assertEquals(model.rowCount(qt.QModelIndex()), 0)
+ model.appendFile(filename)
+ self.assertEquals(model.rowCount(qt.QModelIndex()), 1)
+ # clean up
+ index = model.index(0, 0, qt.QModelIndex())
+ h5File = model.data(index, hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE)
+ ref = weakref.ref(model)
+ model = None
+ self.qWaitForDestroy(ref)
+
+ def testAppendBadFilename(self):
+ model = hdf5.Hdf5TreeModel()
+ self.assertRaises(IOError, model.appendFile, "#%$")
+
+ def testInsertFilename(self):
+ filename = _tmpDirectory + "/data.h5"
+ try:
model = hdf5.Hdf5TreeModel()
self.assertEquals(model.rowCount(qt.QModelIndex()), 0)
- model.appendFile(filename)
+ model.insertFile(filename)
self.assertEquals(model.rowCount(qt.QModelIndex()), 1)
# clean up
index = model.index(0, 0, qt.QModelIndex())
h5File = model.data(index, hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE)
+ self.assertIsNotNone(h5File)
+ finally:
ref = weakref.ref(model)
model = None
self.qWaitForDestroy(ref)
- def testAppendBadFilename(self):
- model = hdf5.Hdf5TreeModel()
- self.assertRaises(IOError, model.appendFile, "#%$")
-
- def testInsertFilename(self):
- with self.h5TempFile() as filename:
- try:
- model = hdf5.Hdf5TreeModel()
- self.assertEquals(model.rowCount(qt.QModelIndex()), 0)
- model.insertFile(filename)
- self.assertEquals(model.rowCount(qt.QModelIndex()), 1)
- # clean up
- index = model.index(0, 0, qt.QModelIndex())
- h5File = model.data(index, hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE)
- self.assertIsNotNone(h5File)
- finally:
- ref = weakref.ref(model)
- model = None
- self.qWaitForDestroy(ref)
-
def testInsertFilenameAsync(self):
- with self.h5TempFile() as filename:
- try:
- model = hdf5.Hdf5TreeModel()
- self.assertEquals(model.rowCount(qt.QModelIndex()), 0)
- model.insertFileAsync(filename)
- index = model.index(0, 0, qt.QModelIndex())
- self.assertIsInstance(model.nodeFromIndex(index), hdf5.Hdf5LoadingItem.Hdf5LoadingItem)
- self.waitForPendingOperations(model)
- index = model.index(0, 0, qt.QModelIndex())
- self.assertIsInstance(model.nodeFromIndex(index), hdf5.Hdf5Item.Hdf5Item)
- finally:
- ref = weakref.ref(model)
- model = None
- self.qWaitForDestroy(ref)
+ filename = _tmpDirectory + "/data.h5"
+ try:
+ model = hdf5.Hdf5TreeModel()
+ self.assertEquals(model.rowCount(qt.QModelIndex()), 0)
+ model.insertFileAsync(filename)
+ index = model.index(0, 0, qt.QModelIndex())
+ self.assertIsInstance(model.nodeFromIndex(index), hdf5.Hdf5LoadingItem.Hdf5LoadingItem)
+ self.waitForPendingOperations(model)
+ index = model.index(0, 0, qt.QModelIndex())
+ self.assertIsInstance(model.nodeFromIndex(index), hdf5.Hdf5Item.Hdf5Item)
+ finally:
+ ref = weakref.ref(model)
+ model = None
+ self.qWaitForDestroy(ref)
def testInsertObject(self):
h5 = commonh5.File("/foo/bar/1.mock", "w")
@@ -162,36 +186,37 @@ class TestHdf5TreeModel(TestCaseQt):
self.assertEquals(model.rowCount(qt.QModelIndex()), 0)
def testSynchronizeObject(self):
- with self.h5TempFile() as filename:
- h5 = h5py.File(filename)
- model = hdf5.Hdf5TreeModel()
- model.insertH5pyObject(h5)
- self.assertEquals(model.rowCount(qt.QModelIndex()), 1)
- index = model.index(0, 0, qt.QModelIndex())
- node1 = model.nodeFromIndex(index)
- model.synchronizeH5pyObject(h5)
- # Now h5 was loaded from it's filename
- # Another ref is owned by the model
- h5.close()
+ filename = _tmpDirectory + "/data.h5"
+ h5 = h5py.File(filename)
+ model = hdf5.Hdf5TreeModel()
+ model.insertH5pyObject(h5)
+ self.assertEquals(model.rowCount(qt.QModelIndex()), 1)
+ index = model.index(0, 0, qt.QModelIndex())
+ node1 = model.nodeFromIndex(index)
+ model.synchronizeH5pyObject(h5)
+ self.waitForPendingOperations(model)
+ # Now h5 was loaded from it's filename
+ # Another ref is owned by the model
+ h5.close()
- index = model.index(0, 0, qt.QModelIndex())
- node2 = model.nodeFromIndex(index)
- self.assertIsNot(node1, node2)
- # after sync
- time.sleep(0.1)
- self.qapp.processEvents()
- time.sleep(0.1)
- index = model.index(0, 0, qt.QModelIndex())
- self.assertIsInstance(model.nodeFromIndex(index), hdf5.Hdf5Item.Hdf5Item)
- # clean up
- index = model.index(0, 0, qt.QModelIndex())
- h5File = model.data(index, hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE)
- self.assertIsNotNone(h5File)
- h5File = None
- # delete the model
- ref = weakref.ref(model)
- model = None
- self.qWaitForDestroy(ref)
+ index = model.index(0, 0, qt.QModelIndex())
+ node2 = model.nodeFromIndex(index)
+ self.assertIsNot(node1, node2)
+ # after sync
+ time.sleep(0.1)
+ self.qapp.processEvents()
+ time.sleep(0.1)
+ index = model.index(0, 0, qt.QModelIndex())
+ self.assertIsInstance(model.nodeFromIndex(index), hdf5.Hdf5Item.Hdf5Item)
+ # clean up
+ index = model.index(0, 0, qt.QModelIndex())
+ h5File = model.data(index, hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE)
+ self.assertIsNotNone(h5File)
+ h5File = None
+ # delete the model
+ ref = weakref.ref(model)
+ model = None
+ self.qWaitForDestroy(ref)
def testFileMoveState(self):
model = hdf5.Hdf5TreeModel()
@@ -222,24 +247,24 @@ class TestHdf5TreeModel(TestCaseQt):
self.assertNotEquals(model.supportedDropActions(), 0)
def testDropExternalFile(self):
- with self.h5TempFile() as filename:
- model = hdf5.Hdf5TreeModel()
- mimeData = qt.QMimeData()
- mimeData.setUrls([qt.QUrl.fromLocalFile(filename)])
- model.dropMimeData(mimeData, qt.Qt.CopyAction, 0, 0, qt.QModelIndex())
- self.assertEquals(model.rowCount(qt.QModelIndex()), 1)
- # after sync
- self.waitForPendingOperations(model)
- index = model.index(0, 0, qt.QModelIndex())
- self.assertIsInstance(model.nodeFromIndex(index), hdf5.Hdf5Item.Hdf5Item)
- # clean up
- index = model.index(0, 0, qt.QModelIndex())
- h5File = model.data(index, role=hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE)
- self.assertIsNotNone(h5File)
- h5File = None
- ref = weakref.ref(model)
- model = None
- self.qWaitForDestroy(ref)
+ filename = _tmpDirectory + "/data.h5"
+ model = hdf5.Hdf5TreeModel()
+ mimeData = qt.QMimeData()
+ mimeData.setUrls([qt.QUrl.fromLocalFile(filename)])
+ model.dropMimeData(mimeData, qt.Qt.CopyAction, 0, 0, qt.QModelIndex())
+ self.assertEquals(model.rowCount(qt.QModelIndex()), 1)
+ # after sync
+ self.waitForPendingOperations(model)
+ index = model.index(0, 0, qt.QModelIndex())
+ self.assertIsInstance(model.nodeFromIndex(index), hdf5.Hdf5Item.Hdf5Item)
+ # clean up
+ index = model.index(0, 0, qt.QModelIndex())
+ h5File = model.data(index, role=hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE)
+ self.assertIsNotNone(h5File)
+ h5File = None
+ ref = weakref.ref(model)
+ model = None
+ self.qWaitForDestroy(ref)
def getRowDataAsDict(self, model, row):
displayed = {}
@@ -337,6 +362,66 @@ class TestHdf5TreeModel(TestCaseQt):
self.assertEquals(index, qt.QModelIndex())
+class TestHdf5TreeModelSignals(TestCaseQt):
+
+ def setUp(self):
+ TestCaseQt.setUp(self)
+ self.model = hdf5.Hdf5TreeModel()
+ filename = _tmpDirectory + "/data.h5"
+ self.h5 = h5py.File(filename)
+ self.model.insertH5pyObject(self.h5)
+
+ self.listener = SignalListener()
+ self.model.sigH5pyObjectLoaded.connect(self.listener.partial(signal="loaded"))
+ self.model.sigH5pyObjectRemoved.connect(self.listener.partial(signal="removed"))
+ self.model.sigH5pyObjectSynchronized.connect(self.listener.partial(signal="synchronized"))
+
+ def tearDown(self):
+ self.signals = None
+ ref = weakref.ref(self.model)
+ self.model = None
+ self.qWaitForDestroy(ref)
+ self.h5.close()
+ self.h5 = None
+ TestCaseQt.tearDown(self)
+
+ def waitForPendingOperations(self, model):
+ for _ in range(10):
+ if not model.hasPendingOperations():
+ break
+ self.qWait(10)
+ else:
+ raise RuntimeError("Still waiting for a pending operation")
+
+ def testInsert(self):
+ filename = _tmpDirectory + "/data.h5"
+ h5 = h5py.File(filename)
+ self.model.insertH5pyObject(h5)
+ self.assertEquals(self.listener.callCount(), 0)
+
+ def testLoaded(self):
+ filename = _tmpDirectory + "/data.h5"
+ self.model.insertFile(filename)
+ self.assertEquals(self.listener.callCount(), 1)
+ self.assertEquals(self.listener.karguments(argumentName="signal")[0], "loaded")
+ self.assertIsNot(self.listener.arguments(callIndex=0)[0], self.h5)
+ self.assertEquals(self.listener.arguments(callIndex=0)[0].filename, filename)
+
+ def testRemoved(self):
+ self.model.removeH5pyObject(self.h5)
+ self.assertEquals(self.listener.callCount(), 1)
+ self.assertEquals(self.listener.karguments(argumentName="signal")[0], "removed")
+ self.assertIs(self.listener.arguments(callIndex=0)[0], self.h5)
+
+ def testSynchonized(self):
+ self.model.synchronizeH5pyObject(self.h5)
+ self.waitForPendingOperations(self.model)
+ self.assertEquals(self.listener.callCount(), 1)
+ self.assertEquals(self.listener.karguments(argumentName="signal")[0], "synchronized")
+ self.assertIs(self.listener.arguments(callIndex=0)[0], self.h5)
+ self.assertIsNot(self.listener.arguments(callIndex=0)[1], self.h5)
+
+
class TestNexusSortFilterProxyModel(TestCaseQt):
def getChildNames(self, model, index):
@@ -873,6 +958,7 @@ def suite():
test_suite = unittest.TestSuite()
loadTests = unittest.defaultTestLoader.loadTestsFromTestCase
test_suite.addTest(loadTests(TestHdf5TreeModel))
+ test_suite.addTest(loadTests(TestHdf5TreeModelSignals))
test_suite.addTest(loadTests(TestNexusSortFilterProxyModel))
test_suite.addTest(loadTests(TestHdf5TreeView))
test_suite.addTest(loadTests(TestH5Node))