summaryrefslogtreecommitdiff
path: root/src/silx/gui/hdf5
diff options
context:
space:
mode:
Diffstat (limited to 'src/silx/gui/hdf5')
-rw-r--r--src/silx/gui/hdf5/Hdf5Formatter.py9
-rw-r--r--src/silx/gui/hdf5/Hdf5HeaderView.py60
-rwxr-xr-xsrc/silx/gui/hdf5/Hdf5Item.py157
-rw-r--r--src/silx/gui/hdf5/Hdf5Node.py3
-rw-r--r--src/silx/gui/hdf5/Hdf5TreeModel.py100
-rw-r--r--src/silx/gui/hdf5/Hdf5TreeView.py17
-rw-r--r--src/silx/gui/hdf5/NexusSortFilterProxyModel.py7
-rw-r--r--src/silx/gui/hdf5/__init__.py8
-rw-r--r--src/silx/gui/hdf5/_utils.py15
-rwxr-xr-xsrc/silx/gui/hdf5/test/test_hdf5.py282
10 files changed, 440 insertions, 218 deletions
diff --git a/src/silx/gui/hdf5/Hdf5Formatter.py b/src/silx/gui/hdf5/Hdf5Formatter.py
index 4dbb0fc..99e0bb6 100644
--- a/src/silx/gui/hdf5/Hdf5Formatter.py
+++ b/src/silx/gui/hdf5/Hdf5Formatter.py
@@ -37,8 +37,7 @@ import h5py
class Hdf5Formatter(qt.QObject):
- """Formatter to convert HDF5 data to string.
- """
+ """Formatter to convert HDF5 data to string."""
formatChanged = qt.Signal()
"""Emitted when properties of the formatter change."""
@@ -87,7 +86,7 @@ class Hdf5Formatter(qt.QObject):
if dataset.shape == tuple():
return "scalar"
shape = [str(i) for i in dataset.shape]
- text = u" \u00D7 ".join(shape)
+ text = " \u00D7 ".join(shape)
return text
def humanReadableValue(self, dataset):
@@ -162,7 +161,7 @@ class Hdf5Formatter(qt.QObject):
if enumType is not None:
return "enum"
- text = str(dtype.newbyteorder('N'))
+ text = str(dtype.newbyteorder("N"))
if numpy.issubdtype(dtype, numpy.floating):
if hasattr(numpy, "float128") and dtype == numpy.float128:
text = "float80"
@@ -181,7 +180,7 @@ class Hdf5Formatter(qt.QObject):
elif dtype.byteorder == "=":
text = "Native " + text
- dtype = dtype.newbyteorder('N')
+ dtype = dtype.newbyteorder("N")
return text
def humanReadableHdf5Type(self, dataset):
diff --git a/src/silx/gui/hdf5/Hdf5HeaderView.py b/src/silx/gui/hdf5/Hdf5HeaderView.py
index 6d306e5..16323dd 100644
--- a/src/silx/gui/hdf5/Hdf5HeaderView.py
+++ b/src/silx/gui/hdf5/Hdf5HeaderView.py
@@ -72,21 +72,49 @@ class Hdf5HeaderView(qt.QHeaderView):
def __updateAutoResize(self):
"""Update the view according to the state of the auto-resize"""
if self.__auto_resize:
- self.setSectionResizeMode(Hdf5TreeModel.NAME_COLUMN, qt.QHeaderView.ResizeToContents)
- self.setSectionResizeMode(Hdf5TreeModel.TYPE_COLUMN, qt.QHeaderView.ResizeToContents)
- self.setSectionResizeMode(Hdf5TreeModel.SHAPE_COLUMN, qt.QHeaderView.ResizeToContents)
- self.setSectionResizeMode(Hdf5TreeModel.VALUE_COLUMN, qt.QHeaderView.Interactive)
- self.setSectionResizeMode(Hdf5TreeModel.DESCRIPTION_COLUMN, qt.QHeaderView.Interactive)
- self.setSectionResizeMode(Hdf5TreeModel.NODE_COLUMN, qt.QHeaderView.ResizeToContents)
- self.setSectionResizeMode(Hdf5TreeModel.LINK_COLUMN, qt.QHeaderView.ResizeToContents)
+ self.setSectionResizeMode(
+ Hdf5TreeModel.NAME_COLUMN, qt.QHeaderView.ResizeToContents
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.TYPE_COLUMN, qt.QHeaderView.ResizeToContents
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.SHAPE_COLUMN, qt.QHeaderView.ResizeToContents
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.VALUE_COLUMN, qt.QHeaderView.Interactive
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.DESCRIPTION_COLUMN, qt.QHeaderView.Interactive
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.NODE_COLUMN, qt.QHeaderView.ResizeToContents
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.LINK_COLUMN, qt.QHeaderView.ResizeToContents
+ )
else:
- self.setSectionResizeMode(Hdf5TreeModel.NAME_COLUMN, qt.QHeaderView.Interactive)
- self.setSectionResizeMode(Hdf5TreeModel.TYPE_COLUMN, qt.QHeaderView.Interactive)
- self.setSectionResizeMode(Hdf5TreeModel.SHAPE_COLUMN, qt.QHeaderView.Interactive)
- self.setSectionResizeMode(Hdf5TreeModel.VALUE_COLUMN, qt.QHeaderView.Interactive)
- self.setSectionResizeMode(Hdf5TreeModel.DESCRIPTION_COLUMN, qt.QHeaderView.Interactive)
- self.setSectionResizeMode(Hdf5TreeModel.NODE_COLUMN, qt.QHeaderView.Interactive)
- self.setSectionResizeMode(Hdf5TreeModel.LINK_COLUMN, qt.QHeaderView.Interactive)
+ self.setSectionResizeMode(
+ Hdf5TreeModel.NAME_COLUMN, qt.QHeaderView.Interactive
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.TYPE_COLUMN, qt.QHeaderView.Interactive
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.SHAPE_COLUMN, qt.QHeaderView.Interactive
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.VALUE_COLUMN, qt.QHeaderView.Interactive
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.DESCRIPTION_COLUMN, qt.QHeaderView.Interactive
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.NODE_COLUMN, qt.QHeaderView.Interactive
+ )
+ self.setSectionResizeMode(
+ Hdf5TreeModel.LINK_COLUMN, qt.QHeaderView.Interactive
+ )
def setAutoResizeColumns(self, autoResize):
"""Enable/disable auto-resize. When auto-resized, the header take care
@@ -125,7 +153,9 @@ class Hdf5HeaderView(qt.QHeaderView):
"""
return self.__hide_columns_popup
- enableHideColumnsPopup = qt.Property(bool, hasHideColumnsPopup, setAutoResizeColumns)
+ enableHideColumnsPopup = qt.Property(
+ bool, hasHideColumnsPopup, setAutoResizeColumns
+ )
"""Property to enable/disable popup allowing to hide/show columns."""
def __genHideSectionEvent(self, column):
diff --git a/src/silx/gui/hdf5/Hdf5Item.py b/src/silx/gui/hdf5/Hdf5Item.py
index 8f20649..2777a94 100755
--- a/src/silx/gui/hdf5/Hdf5Item.py
+++ b/src/silx/gui/hdf5/Hdf5Item.py
@@ -1,6 +1,6 @@
# /*##########################################################################
#
-# Copyright (c) 2016-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2016-2023 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
@@ -28,7 +28,6 @@ __date__ = "17/01/2019"
import logging
-import collections
import enum
from typing import Optional
@@ -39,6 +38,7 @@ from .Hdf5Node import Hdf5Node
import silx.io.utils
from silx.gui.data.TextFormatter import TextFormatter
from ..hdf5.Hdf5Formatter import Hdf5Formatter
+
_logger = logging.getLogger(__name__)
_formatter = TextFormatter()
_hdf5Formatter = Hdf5Formatter(textFormatter=_formatter)
@@ -46,8 +46,8 @@ _hdf5Formatter = Hdf5Formatter(textFormatter=_formatter)
class DescriptionType(enum.Enum):
- """List of available kind of description.
- """
+ """List of available kind of description."""
+
ERROR = "error"
DESCRIPTION = "description"
TITLE = "title"
@@ -210,9 +210,14 @@ class Hdf5Item(Hdf5Node):
class_ = silx.io.utils.get_h5_class(self.__obj)
if class_ == silx.io.utils.H5Type.EXTERNAL_LINK:
- message = "External link broken. Path %s::%s does not exist" % (self.__obj.filename, self.__obj.path)
+ message = "External link broken. Path %s::%s does not exist" % (
+ self.__obj.filename,
+ self.__obj.path,
+ )
elif class_ == silx.io.utils.H5Type.SOFT_LINK:
- message = "Soft link broken. Path %s does not exist" % (self.__obj.path)
+ message = "Soft link broken. Path %s does not exist" % (
+ self.__obj.path
+ )
else:
name = self.__obj.__class__.__name__.split(".")[-1].capitalize()
message = "%s broken" % (name)
@@ -220,7 +225,10 @@ class Hdf5Item(Hdf5Node):
self.__isBroken = True
else:
self.__obj = obj
- if silx.io.utils.get_h5_class(obj) not in [silx.io.utils.H5Type.GROUP, silx.io.utils.H5Type.FILE]:
+ if silx.io.utils.get_h5_class(obj) not in [
+ silx.io.utils.H5Type.GROUP,
+ silx.io.utils.H5Type.FILE,
+ ]:
try:
# pre-fetch of the data
if obj.shape is None:
@@ -257,7 +265,10 @@ class Hdf5Item(Hdf5Node):
keys.append(name)
except Exception:
lib_name = self.obj.__class__.__module__.split(".")[0]
- _logger.error("Internal %s error (second time). The file is corrupted.", lib_name)
+ _logger.error(
+ "Internal %s error (second time). The file is corrupted.",
+ lib_name,
+ )
_logger.debug("Backtrace", exc_info=True)
for name in keys:
try:
@@ -281,7 +292,14 @@ class Hdf5Item(Hdf5Node):
h5class = silx.io.utils.get_h5_class(class_=class_)
if h5class is None:
_logger.error("Class %s unsupported", class_)
- item = Hdf5Item(text=name, obj=None, parent=self, key=name, h5Class=h5class, linkClass=link)
+ item = Hdf5Item(
+ text=name,
+ obj=None,
+ parent=self,
+ key=name,
+ h5Class=h5class,
+ linkClass=link,
+ )
self.appendChild(item)
def hasChildren(self):
@@ -330,7 +348,7 @@ class Hdf5Item(Hdf5Node):
:param Dict[str,str] attributeDict: Key/value attributes
"""
- attributeDict = collections.OrderedDict()
+ attributeDict = {}
if self.h5Class == silx.io.utils.H5Type.DATASET:
attributeDict["#Title"] = "HDF5 Dataset"
@@ -338,7 +356,9 @@ class Hdf5Item(Hdf5Node):
attributeDict["Path"] = self.obj.name
attributeDict["Shape"] = self._getFormatter().humanReadableShape(self.obj)
attributeDict["Value"] = self._getFormatter().humanReadableValue(self.obj)
- attributeDict["Data type"] = self._getFormatter().humanReadableType(self.obj, full=True)
+ attributeDict["Data type"] = self._getFormatter().humanReadableType(
+ self.obj, full=True
+ )
elif self.h5Class == silx.io.utils.H5Type.GROUP:
attributeDict["#Title"] = "HDF5 Group"
if self.nexusClassName:
@@ -395,14 +415,18 @@ class Hdf5Item(Hdf5Node):
# Check NX_class formatting
lower = text.lower()
formatedNX_class = ""
- if lower.startswith('nx'):
- formatedNX_class = 'NX' + lower[2:]
- if lower == 'nxcansas':
- formatedNX_class = 'NXcanSAS' # That's the only class with capital letters...
+ if lower.startswith("nx"):
+ formatedNX_class = "NX" + lower[2:]
+ if lower == "nxcansas":
+ formatedNX_class = (
+ "NXcanSAS" # That's the only class with capital letters...
+ )
if text != formatedNX_class:
- _logger.error("NX_class: '%s' is malformed (should be '%s')",
- text,
- formatedNX_class)
+ _logger.error(
+ "NX_class: '%s' is malformed (should be '%s')",
+ text,
+ formatedNX_class,
+ )
text = formatedNX_class
self.__nx_class = text
@@ -469,59 +493,44 @@ class Hdf5Item(Hdf5Node):
return None
_NEXUS_CLASS_TO_VALUE_CHILDREN = {
- 'NXaperture': (
- (DescriptionType.DESCRIPTION, 'description'),
- ),
- 'NXbeam_stop': (
- (DescriptionType.DESCRIPTION, 'description'),
- ),
- 'NXdetector': (
- (DescriptionType.NAME, 'local_name'),
- (DescriptionType.DESCRIPTION, 'description')
- ),
- 'NXentry': (
- (DescriptionType.TITLE, 'title'),
- ),
- 'NXenvironment': (
- (DescriptionType.NAME, 'short_name'),
- (DescriptionType.NAME, 'name'),
- (DescriptionType.DESCRIPTION, 'description')
- ),
- 'NXinstrument': (
- (DescriptionType.NAME, 'name'),
- ),
- 'NXlog': (
- (DescriptionType.DESCRIPTION, 'description'),
- ),
- 'NXmirror': (
- (DescriptionType.DESCRIPTION, 'description'),
- ),
- 'NXpositioner': (
- (DescriptionType.NAME, 'name'),
+ "NXaperture": ((DescriptionType.DESCRIPTION, "description"),),
+ "NXbeam_stop": ((DescriptionType.DESCRIPTION, "description"),),
+ "NXdetector": (
+ (DescriptionType.NAME, "local_name"),
+ (DescriptionType.DESCRIPTION, "description"),
),
- 'NXprocess': (
- (DescriptionType.PROGRAM, 'program'),
+ "NXentry": ((DescriptionType.TITLE, "title"),),
+ "NXenvironment": (
+ (DescriptionType.NAME, "short_name"),
+ (DescriptionType.NAME, "name"),
+ (DescriptionType.DESCRIPTION, "description"),
),
- 'NXsample': (
- (DescriptionType.TITLE, 'short_title'),
- (DescriptionType.NAME, 'name'),
- (DescriptionType.DESCRIPTION, 'description')
+ "NXinstrument": ((DescriptionType.NAME, "name"),),
+ "NXlog": ((DescriptionType.DESCRIPTION, "description"),),
+ "NXmirror": ((DescriptionType.DESCRIPTION, "description"),),
+ "NXnote": ((DescriptionType.DESCRIPTION, "description"),),
+ "NXpositioner": ((DescriptionType.NAME, "name"),),
+ "NXprocess": ((DescriptionType.PROGRAM, "program"),),
+ "NXsample": (
+ (DescriptionType.TITLE, "short_title"),
+ (DescriptionType.NAME, "name"),
+ (DescriptionType.DESCRIPTION, "description"),
),
- 'NXsample_component': (
- (DescriptionType.NAME, 'name'),
- (DescriptionType.DESCRIPTION, 'description')
+ "NXsample_component": (
+ (DescriptionType.NAME, "name"),
+ (DescriptionType.DESCRIPTION, "description"),
),
- 'NXsensor': (
- (DescriptionType.NAME, 'short_name'),
- (DescriptionType.NAME, 'name')
+ "NXsensor": (
+ (DescriptionType.NAME, "short_name"),
+ (DescriptionType.NAME, "name"),
),
- 'NXsource': (
- (DescriptionType.NAME, 'name'),
+ "NXsource": (
+ (DescriptionType.NAME, "name"),
), # or its 'short_name' attribute... This is not supported
- 'NXsubentry': (
- (DescriptionType.DESCRIPTION, 'definition'),
- (DescriptionType.PROGRAM, 'program_name'),
- (DescriptionType.TITLE, 'title'),
+ "NXsubentry": (
+ (DescriptionType.DESCRIPTION, "definition"),
+ (DescriptionType.PROGRAM, "program_name"),
+ (DescriptionType.TITLE, "title"),
),
}
"""Mapping from NeXus class to child names containing data to use as value"""
@@ -536,19 +545,25 @@ class Hdf5Item(Hdf5Node):
return DescriptionType.ERROR, self.__error
if self.h5Class == silx.io.utils.H5Type.DATASET:
- return DescriptionType.VALUE, self._getFormatter().humanReadableValue(self.obj)
+ return DescriptionType.VALUE, self._getFormatter().humanReadableValue(
+ self.obj
+ )
elif self.isGroupObj() and self.nexusClassName:
# For NeXus groups, try to find a title or name
# By default, look for a title (most application definitions should have one)
- defaultSequence = ((DescriptionType.TITLE, 'title'),)
- sequence = self._NEXUS_CLASS_TO_VALUE_CHILDREN.get(self.nexusClassName, defaultSequence)
+ defaultSequence = ((DescriptionType.TITLE, "title"),)
+ sequence = self._NEXUS_CLASS_TO_VALUE_CHILDREN.get(
+ self.nexusClassName, defaultSequence
+ )
for kind, child_name in sequence:
for index in range(self.childCount()):
child = self.child(index)
- if (isinstance(child, Hdf5Item) and
- child.h5Class == silx.io.utils.H5Type.DATASET and
- child.basename == child_name):
+ if (
+ isinstance(child, Hdf5Item)
+ and child.h5Class == silx.io.utils.H5Type.DATASET
+ and child.basename == child_name
+ ):
return kind, self._getFormatter().humanReadableValue(child.obj)
description = self.obj.attrs.get("desc", None)
diff --git a/src/silx/gui/hdf5/Hdf5Node.py b/src/silx/gui/hdf5/Hdf5Node.py
index 0d58748..db49594 100644
--- a/src/silx/gui/hdf5/Hdf5Node.py
+++ b/src/silx/gui/hdf5/Hdf5Node.py
@@ -36,11 +36,12 @@ class Hdf5Node(object):
It provides link to the childs and to the parents, and a link to an
external object.
"""
+
def __init__(
self,
parent=None,
populateAll=False,
- openedPath: Optional[str]=None,
+ openedPath: Optional[str] = None,
):
"""
Constructor
diff --git a/src/silx/gui/hdf5/Hdf5TreeModel.py b/src/silx/gui/hdf5/Hdf5TreeModel.py
index 8ac800a..3353ab3 100644
--- a/src/silx/gui/hdf5/Hdf5TreeModel.py
+++ b/src/silx/gui/hdf5/Hdf5TreeModel.py
@@ -1,6 +1,6 @@
# /*##########################################################################
#
-# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
+# Copyright (c) 2016-2023 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
@@ -38,6 +38,7 @@ from .Hdf5Item import Hdf5Item
from .Hdf5LoadingItem import Hdf5LoadingItem
from . import _utils
from ... import io as silx_io
+from ...io._sliceh5 import DatasetSlice
import h5py
@@ -61,6 +62,8 @@ def _createRootLabel(h5obj):
if path.startswith("/"):
path = path[1:]
label = "%s::%s" % (filename, path)
+ if isinstance(h5obj, DatasetSlice):
+ label += str(list(h5obj.indices))
return label
@@ -69,7 +72,8 @@ class LoadingItemRunnable(qt.QRunnable):
class __Signals(qt.QObject):
"""Signal holder"""
- itemReady = qt.Signal(object, object, object)
+
+ itemReady = qt.Signal(object, object, object, str)
runnerFinished = qt.Signal(object)
def __init__(self, filename, item):
@@ -126,7 +130,7 @@ class LoadingItemRunnable(qt.QRunnable):
if h5file is not None:
h5file.close()
- self.itemReady.emit(self.oldItem, newItem, error)
+ self.itemReady.emit(self.oldItem, newItem, error, self.filename)
self.runnerFinished.emit(self)
def autoDelete(self):
@@ -181,7 +185,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
]
"""List of logical columns available"""
- sigH5pyObjectLoaded = qt.Signal(object)
+ sigH5pyObjectLoaded = qt.Signal(object, str)
"""Emitted when a new root item was loaded and inserted to the model."""
sigH5pyObjectRemoved = qt.Signal(object)
@@ -201,13 +205,13 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
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'
+ 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()
@@ -247,7 +251,6 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
"""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[:] = []
@@ -266,14 +269,21 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
index2 = self.index(i, self.columnCount() - 1, qt.QModelIndex())
self.dataChanged.emit(index1, index2)
- def __itemReady(self, oldItem, newItem, error):
+ def __itemReady(
+ self,
+ oldItem: Hdf5Node,
+ newItem: Optional[Hdf5Node],
+ error: Optional[Exception],
+ filename: str,
+ ):
"""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
+ :param oldItem: current displayed item
+ :param newItem: item loaded, or None if error is defined
+ :param error: An exception, or None if newItem is defined
+ :param filename: The filename used to load the new item
"""
row = self.__root.indexOfChild(oldItem)
@@ -291,7 +301,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
self.endInsertRows()
if isinstance(oldItem, Hdf5LoadingItem):
- self.sigH5pyObjectLoaded.emit(newItem.obj)
+ self.sigH5pyObjectLoaded.emit(newItem.obj, filename)
else:
self.sigH5pyObjectSynchronized.emit(oldItem.obj, newItem.obj)
@@ -384,7 +394,9 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
if action == qt.Qt.IgnoreAction:
return True
- if self.__fileMoveEnabled and mimedata.hasFormat(_utils.Hdf5DatasetMimeData.MIME_TYPE):
+ if self.__fileMoveEnabled and mimedata.hasFormat(
+ _utils.Hdf5DatasetMimeData.MIME_TYPE
+ ):
if mimedata.isRoot():
dragNode = mimedata.node()
parentNode = self.nodeFromIndex(parentIndex)
@@ -404,10 +416,9 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
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):
+ while parentNode is not self.__root:
node = parentNode
parentNode = node.parent
row = parentNode.indexOfChild(node)
@@ -424,7 +435,10 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
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))
+ message = "<html>%s:<ul><li>%s</li><ul></html>" % (
+ title,
+ "</li><li>".join(messages),
+ )
qt.QMessageBox.critical(None, title, message)
return True
@@ -443,14 +457,31 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
self.__root.insertChild(row, node)
self.endInsertRows()
- def moveRow(self, sourceParentIndex, sourceRow, destinationParentIndex, destinationRow):
+ 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)
+ return self.moveRows(
+ sourceParentIndex, sourceRow, 1, destinationParentIndex, destinationRow
+ )
- def moveRows(self, sourceParentIndex, sourceRow, count, destinationParentIndex, destinationRow):
- self.beginMoveRows(sourceParentIndex, sourceRow, sourceRow, 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)
@@ -532,14 +563,14 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
return qt.QModelIndex()
row = grandparent.indexOfChild(parent)
- assert row != - 1
+ 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
+ """Close the file if it was loaded from a filename or a
drag-and-drop"""
obj = node.obj
for f in self.__openedFiles:
@@ -572,10 +603,15 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
# else compare commonh5 objects
if not isinstance(obj2, type(obj1)):
return False
+
def key(item):
- if item.file is None:
- return item.name
- return item.file.filename, item.file.mode, item.name
+ info = [item.name]
+ if item.file is not None:
+ info += [item.file.filename, item.file.mode]
+ if isinstance(item, DatasetSlice):
+ info.append(item.indices)
+ return tuple(info)
+
return key(obj1) == key(obj2)
def h5pyObjectRow(self, h5pyObject):
@@ -655,7 +691,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
obj=h5pyObject,
parent=self.__root,
openedPath=filename,
- )
+ ),
)
def hasPendingOperations(self):
@@ -697,7 +733,7 @@ class Hdf5TreeModel(qt.QAbstractItemModel):
h5file = silx_io.open(filename)
if self.__ownFiles:
self.__openedFiles.append(h5file)
- self.sigH5pyObjectLoaded.emit(h5file)
+ self.sigH5pyObjectLoaded.emit(h5file, filename)
self.insertH5pyObject(h5file, row=row, filename=filename)
except IOError:
_logger.debug("File '%s' can't be read.", filename, exc_info=True)
diff --git a/src/silx/gui/hdf5/Hdf5TreeView.py b/src/silx/gui/hdf5/Hdf5TreeView.py
index da35d15..a477fc3 100644
--- a/src/silx/gui/hdf5/Hdf5TreeView.py
+++ b/src/silx/gui/hdf5/Hdf5TreeView.py
@@ -57,6 +57,7 @@ class Hdf5TreeView(qt.QTreeView):
:meth:`removeContextMenuCallback` to add your custum actions according
to the selected objects.
"""
+
def __init__(self, parent=None):
"""
Constructor
@@ -167,7 +168,11 @@ class Hdf5TreeView(qt.QTreeView):
def dragEnterEvent(self, event):
model = self.findHdf5TreeModel()
- if model is not None and model.isFileDropEnabled() and event.mimeData().hasFormat("text/uri-list"):
+ if (
+ model is not None
+ and model.isFileDropEnabled()
+ and event.mimeData().hasFormat("text/uri-list")
+ ):
self.setState(qt.QAbstractItemView.DraggingState)
event.accept()
else:
@@ -175,7 +180,11 @@ class Hdf5TreeView(qt.QTreeView):
def dragMoveEvent(self, event):
model = self.findHdf5TreeModel()
- if model is not None and model.isFileDropEnabled() and event.mimeData().hasFormat("text/uri-list"):
+ if (
+ model is not None
+ and model.isFileDropEnabled()
+ and event.mimeData().hasFormat("text/uri-list")
+ ):
event.setDropAction(qt.Qt.CopyAction)
event.accept()
else:
@@ -215,7 +224,9 @@ class Hdf5TreeView(qt.QTreeView):
model = model.sourceModel()
else:
break
- raise RuntimeError("Model from the requested index is not reachable from this view")
+ raise RuntimeError(
+ "Model from the requested index is not reachable from this view"
+ )
def mapToModel(self, index):
"""Map an index from any model reachable by the view to an index from
diff --git a/src/silx/gui/hdf5/NexusSortFilterProxyModel.py b/src/silx/gui/hdf5/NexusSortFilterProxyModel.py
index 1b80c3e..0bc7352 100644
--- a/src/silx/gui/hdf5/NexusSortFilterProxyModel.py
+++ b/src/silx/gui/hdf5/NexusSortFilterProxyModel.py
@@ -76,7 +76,8 @@ class NexusSortFilterProxyModel(qt.QSortFilterProxyModel):
"""
if sourceLeft.column() != Hdf5TreeModel.NAME_COLUMN:
return super(NexusSortFilterProxyModel, self).lessThan(
- sourceLeft, sourceRight)
+ sourceLeft, sourceRight
+ )
# Do not sort child of root (files)
if sourceLeft.parent() == qt.QModelIndex():
@@ -217,7 +218,9 @@ class NexusSortFilterProxyModel(qt.QSortFilterProxyModel):
if index.column() == Hdf5TreeModel.NAME_COLUMN:
if role == qt.Qt.DecorationRole:
sourceIndex = self.mapToSource(index)
- item = self.sourceModel().data(sourceIndex, Hdf5TreeModel.H5PY_ITEM_ROLE)
+ item = self.sourceModel().data(
+ sourceIndex, Hdf5TreeModel.H5PY_ITEM_ROLE
+ )
if self.__isNXnode(item):
result = self.__getNxIcon(result)
return result
diff --git a/src/silx/gui/hdf5/__init__.py b/src/silx/gui/hdf5/__init__.py
index 2243484..8e07407 100644
--- a/src/silx/gui/hdf5/__init__.py
+++ b/src/silx/gui/hdf5/__init__.py
@@ -40,4 +40,10 @@ from ._utils import Hdf5ContextMenuEvent # noqa
from .NexusSortFilterProxyModel import NexusSortFilterProxyModel # noqa
from .Hdf5TreeModel import Hdf5TreeModel # noqa
-__all__ = ['Hdf5TreeView', 'H5Node', 'Hdf5ContextMenuEvent', 'NexusSortFilterProxyModel', 'Hdf5TreeModel']
+__all__ = [
+ "Hdf5TreeView",
+ "H5Node",
+ "Hdf5ContextMenuEvent",
+ "NexusSortFilterProxyModel",
+ "Hdf5TreeModel",
+]
diff --git a/src/silx/gui/hdf5/_utils.py b/src/silx/gui/hdf5/_utils.py
index 1d1b4cb..7232bfe 100644
--- a/src/silx/gui/hdf5/_utils.py
+++ b/src/silx/gui/hdf5/_utils.py
@@ -33,7 +33,7 @@ __date__ = "17/01/2019"
from html import escape
import logging
import os.path
-
+from silx.gui import constants
import silx.io.utils
import silx.io.url
from .. import qt
@@ -109,19 +109,20 @@ class Hdf5DatasetMimeData(qt.QMimeData):
MIME_TYPE = "application/x-internal-h5py-dataset"
- SILX_URI_TYPE = "application/x-silx-uri"
+ SILX_URI_TYPE = constants.SILX_URI_MIMETYPE
+ """For compatibility with silx <= 1.1"""
def __init__(self, node=None, dataset=None, isRoot=False):
qt.QMimeData.__init__(self)
self.__dataset = dataset
self.__node = node
self.__isRoot = isRoot
- self.setData(self.MIME_TYPE, "".encode(encoding='utf-8'))
+ self.setData(self.MIME_TYPE, "".encode(encoding="utf-8"))
if node is not None:
h5Node = H5Node(node)
silxUrl = h5Node.url
self.setText(silxUrl)
- self.setData(self.SILX_URI_TYPE, silxUrl.encode(encoding='utf-8'))
+ self.setData(constants.SILX_URI_MIMETYPE, silxUrl.encode(encoding="utf-8"))
def isRoot(self):
return self.__isRoot
@@ -427,9 +428,9 @@ class H5Node(object):
:rtype: ~silx.io.url.DataUrl
"""
absolute_filename = os.path.abspath(self.local_filename)
- return silx.io.url.DataUrl(scheme="silx",
- file_path=absolute_filename,
- data_path=self.local_name)
+ return silx.io.url.DataUrl(
+ scheme="silx", file_path=absolute_filename, data_path=self.local_name
+ )
@property
def url(self):
diff --git a/src/silx/gui/hdf5/test/test_hdf5.py b/src/silx/gui/hdf5/test/test_hdf5.py
index 6e77e1d..cb08436 100755
--- a/src/silx/gui/hdf5/test/test_hdf5.py
+++ b/src/silx/gui/hdf5/test/test_hdf5.py
@@ -1,6 +1,6 @@
# /*##########################################################################
#
-# Copyright (c) 2016-2021 European Synchrotron Radiation Facility
+# Copyright (c) 2016-2023 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
@@ -30,23 +30,24 @@ __date__ = "12/03/2019"
import time
import os
-import unittest
import tempfile
import numpy
-from pkg_resources import parse_version
+from packaging.version import Version
from contextlib import contextmanager
from silx.gui import qt
from silx.gui.utils.testutils import TestCaseQt
from silx.gui import hdf5
from silx.gui.utils.testutils import SignalListener
from silx.io import commonh5
+from silx.io import h5py_utils
+from silx.io.url import DataUrl
import weakref
import h5py
import pytest
-h5py2_9 = parse_version(h5py.version.version) >= parse_version('2.9.0')
+h5py2_9 = Version(h5py.version.version) >= Version("2.9.0")
@pytest.fixture(scope="class")
@@ -54,7 +55,7 @@ def useH5File(request, tmpdir_factory):
tmp = tmpdir_factory.mktemp("test_hdf5")
request.cls.filename = os.path.join(tmp, "data.h5")
# create h5 data
- with h5py.File(request.cls.filename, "w") as f:
+ with h5py_utils.File(request.cls.filename, "w") as f:
g = f.create_group("arrays")
g.create_dataset("scalar", data=10)
yield
@@ -69,7 +70,6 @@ def create_NXentry(group, name):
@pytest.mark.usefixtures("useH5File")
class TestHdf5TreeModel(TestCaseQt):
-
def setUp(self):
super(TestHdf5TreeModel, self).setUp()
@@ -87,7 +87,7 @@ class TestHdf5TreeModel(TestCaseQt):
fd, tmp_name = tempfile.mkstemp(suffix=".h5")
os.close(fd)
# create h5 data
- h5file = h5py.File(tmp_name, "w")
+ h5file = h5py_utils.File(tmp_name, "w")
g = h5file.create_group("arrays")
g.create_dataset("scalar", data=10)
h5file.close()
@@ -134,7 +134,9 @@ class TestHdf5TreeModel(TestCaseQt):
self.assertEqual(model.rowCount(qt.QModelIndex()), 0)
model.insertFileAsync(self.filename)
index = model.index(0, 0, qt.QModelIndex())
- self.assertIsInstance(model.nodeFromIndex(index), hdf5.Hdf5LoadingItem.Hdf5LoadingItem)
+ 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)
@@ -160,7 +162,7 @@ class TestHdf5TreeModel(TestCaseQt):
self.assertEqual(model.rowCount(qt.QModelIndex()), 0)
def testSynchronizeObject(self):
- h5 = h5py.File(self.filename, mode="r")
+ h5 = h5py_utils.File(self.filename, mode="r")
model = hdf5.Hdf5TreeModel()
model.insertH5pyObject(h5)
self.assertEqual(model.rowCount(qt.QModelIndex()), 1)
@@ -235,7 +237,7 @@ class TestHdf5TreeModel(TestCaseQt):
"""A file inserted as an h5py object is not open (then not closed)
internally."""
try:
- h5File = h5py.File(self.filename, mode="r")
+ h5File = h5py_utils.File(self.filename, mode="r")
model = hdf5.Hdf5TreeModel()
self.assertEqual(model.rowCount(qt.QModelIndex()), 0)
model.insertH5pyObject(h5File)
@@ -244,7 +246,9 @@ class TestHdf5TreeModel(TestCaseQt):
h5File = model.data(index, role=hdf5.Hdf5TreeModel.H5PY_OBJECT_ROLE)
model.removeIndex(index)
self.assertEqual(model.rowCount(qt.QModelIndex()), 0)
- self.assertTrue(bool(h5File.id.valid), "The HDF5 file was unexpetedly closed")
+ self.assertTrue(
+ bool(h5File.id.valid), "The HDF5 file was unexpetedly closed"
+ )
finally:
h5File.close()
@@ -269,7 +273,12 @@ class TestHdf5TreeModel(TestCaseQt):
def getRowDataAsDict(self, model, row):
displayed = {}
- roles = [qt.Qt.DisplayRole, qt.Qt.DecorationRole, qt.Qt.ToolTipRole, qt.Qt.TextAlignmentRole]
+ roles = [
+ qt.Qt.DisplayRole,
+ qt.Qt.DecorationRole,
+ qt.Qt.ToolTipRole,
+ qt.Qt.TextAlignmentRole,
+ ]
for column in range(0, model.columnCount(qt.QModelIndex())):
index = model.index(0, column, qt.QModelIndex())
for role in roles:
@@ -286,13 +295,27 @@ class TestHdf5TreeModel(TestCaseQt):
model = hdf5.Hdf5TreeModel()
model.insertH5pyObject(h5)
displayed = self.getRowDataAsDict(model, row=0)
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DisplayRole], "1.mock")
- self.assertIsInstance(displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DecorationRole], qt.QIcon)
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.TYPE_COLUMN, qt.Qt.DisplayRole], "")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.SHAPE_COLUMN, qt.Qt.DisplayRole], "")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.VALUE_COLUMN, qt.Qt.DisplayRole], "")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.DESCRIPTION_COLUMN, qt.Qt.DisplayRole], None)
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.NODE_COLUMN, qt.Qt.DisplayRole], "File")
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DisplayRole], "1.mock"
+ )
+ self.assertIsInstance(
+ displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DecorationRole], qt.QIcon
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.TYPE_COLUMN, qt.Qt.DisplayRole], ""
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.SHAPE_COLUMN, qt.Qt.DisplayRole], ""
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.VALUE_COLUMN, qt.Qt.DisplayRole], ""
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.DESCRIPTION_COLUMN, qt.Qt.DisplayRole], None
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.NODE_COLUMN, qt.Qt.DisplayRole], "File"
+ )
def testGroupData(self):
h5 = commonh5.File("/foo/bar/1.mock", "w")
@@ -302,13 +325,27 @@ class TestHdf5TreeModel(TestCaseQt):
model = hdf5.Hdf5TreeModel()
model.insertH5pyObject(d)
displayed = self.getRowDataAsDict(model, row=0)
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DisplayRole], "1.mock::foo")
- self.assertIsInstance(displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DecorationRole], qt.QIcon)
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.TYPE_COLUMN, qt.Qt.DisplayRole], "")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.SHAPE_COLUMN, qt.Qt.DisplayRole], "")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.VALUE_COLUMN, qt.Qt.DisplayRole], "")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.DESCRIPTION_COLUMN, qt.Qt.DisplayRole], "fooo")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.NODE_COLUMN, qt.Qt.DisplayRole], "Group")
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DisplayRole], "1.mock::foo"
+ )
+ self.assertIsInstance(
+ displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DecorationRole], qt.QIcon
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.TYPE_COLUMN, qt.Qt.DisplayRole], ""
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.SHAPE_COLUMN, qt.Qt.DisplayRole], ""
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.VALUE_COLUMN, qt.Qt.DisplayRole], ""
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.DESCRIPTION_COLUMN, qt.Qt.DisplayRole], "fooo"
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.NODE_COLUMN, qt.Qt.DisplayRole], "Group"
+ )
def testDatasetData(self):
h5 = commonh5.File("/foo/bar/1.mock", "w")
@@ -318,13 +355,29 @@ class TestHdf5TreeModel(TestCaseQt):
model = hdf5.Hdf5TreeModel()
model.insertH5pyObject(d)
displayed = self.getRowDataAsDict(model, row=0)
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DisplayRole], "1.mock::foo")
- self.assertIsInstance(displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DecorationRole], qt.QIcon)
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.TYPE_COLUMN, qt.Qt.DisplayRole], value.dtype.name)
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.SHAPE_COLUMN, qt.Qt.DisplayRole], "3")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.VALUE_COLUMN, qt.Qt.DisplayRole], "[1 2 3]")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.DESCRIPTION_COLUMN, qt.Qt.DisplayRole], "[1 2 3]")
- self.assertEqual(displayed[hdf5.Hdf5TreeModel.NODE_COLUMN, qt.Qt.DisplayRole], "Dataset")
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DisplayRole], "1.mock::foo"
+ )
+ self.assertIsInstance(
+ displayed[hdf5.Hdf5TreeModel.NAME_COLUMN, qt.Qt.DecorationRole], qt.QIcon
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.TYPE_COLUMN, qt.Qt.DisplayRole],
+ value.dtype.name,
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.SHAPE_COLUMN, qt.Qt.DisplayRole], "3"
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.VALUE_COLUMN, qt.Qt.DisplayRole], "[1 2 3]"
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.DESCRIPTION_COLUMN, qt.Qt.DisplayRole],
+ "[1 2 3]",
+ )
+ self.assertEqual(
+ displayed[hdf5.Hdf5TreeModel.NODE_COLUMN, qt.Qt.DisplayRole], "Dataset"
+ )
def testDropLastAsFirst(self):
model = hdf5.Hdf5TreeModel()
@@ -365,17 +418,18 @@ class TestHdf5TreeModel(TestCaseQt):
@pytest.mark.usefixtures("useH5File")
class TestHdf5TreeModelSignals(TestCaseQt):
-
def setUp(self):
TestCaseQt.setUp(self)
self.model = hdf5.Hdf5TreeModel()
- self.h5 = h5py.File(self.filename, mode='r')
+ self.h5 = h5py_utils.File(self.filename, mode="r")
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"))
+ self.model.sigH5pyObjectSynchronized.connect(
+ self.listener.partial(signal="synchronized")
+ )
def tearDown(self):
self.signals = None
@@ -395,16 +449,28 @@ class TestHdf5TreeModelSignals(TestCaseQt):
raise RuntimeError("Still waiting for a pending operation")
def testInsert(self):
- h5 = h5py.File(self.filename, mode='r')
+ h5 = h5py_utils.File(self.filename, mode="r")
self.model.insertH5pyObject(h5)
self.assertEqual(self.listener.callCount(), 0)
def testLoaded(self):
- self.model.insertFile(self.filename)
- self.assertEqual(self.listener.callCount(), 1)
- self.assertEqual(self.listener.karguments(argumentName="signal")[0], "loaded")
- self.assertIsNot(self.listener.arguments(callIndex=0)[0], self.h5)
- self.assertEqual(self.listener.arguments(callIndex=0)[0].filename, self.filename)
+ for data_path in [None, "/arrays/scalar"]:
+ with self.subTest(data_path=data_path):
+ url = DataUrl(file_path=self.filename, data_path=data_path)
+ insertedFilename = url.path()
+ self.model.insertFile(insertedFilename)
+ self.assertEqual(self.listener.callCount(), 1)
+ self.assertEqual(
+ self.listener.karguments(argumentName="signal")[0], "loaded"
+ )
+ self.assertIsNot(self.listener.arguments(callIndex=0)[0], self.h5)
+ self.assertEqual(
+ self.listener.arguments(callIndex=0)[0].file.filename, self.filename
+ )
+ self.assertEqual(
+ self.listener.arguments(callIndex=0)[1], insertedFilename
+ )
+ self.listener.clear()
def testRemoved(self):
self.model.removeH5pyObject(self.h5)
@@ -416,13 +482,14 @@ class TestHdf5TreeModelSignals(TestCaseQt):
self.model.synchronizeH5pyObject(self.h5)
self.waitForPendingOperations(self.model)
self.assertEqual(self.listener.callCount(), 1)
- self.assertEqual(self.listener.karguments(argumentName="signal")[0], "synchronized")
+ self.assertEqual(
+ 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):
count = model.rowCount(index)
result = []
@@ -451,9 +518,15 @@ class TestNexusSortFilterProxyModel(TestCaseQt):
"""Test NXentry with start_time"""
model = hdf5.Hdf5TreeModel()
h5 = commonh5.File("/foo/bar/1.mock", "w")
- create_NXentry(h5, "a").create_dataset("start_time", data=numpy.array([numpy.string_("2015")]))
- create_NXentry(h5, "b").create_dataset("start_time", data=numpy.array([numpy.string_("2013")]))
- create_NXentry(h5, "c").create_dataset("start_time", data=numpy.array([numpy.string_("2014")]))
+ create_NXentry(h5, "a").create_dataset(
+ "start_time", data=numpy.array([numpy.string_("2015")])
+ )
+ create_NXentry(h5, "b").create_dataset(
+ "start_time", data=numpy.array([numpy.string_("2013")])
+ )
+ create_NXentry(h5, "c").create_dataset(
+ "start_time", data=numpy.array([numpy.string_("2014")])
+ )
model.insertH5pyObject(h5)
proxy = hdf5.NexusSortFilterProxyModel()
@@ -466,9 +539,15 @@ class TestNexusSortFilterProxyModel(TestCaseQt):
"""Test NXentry with end_time"""
model = hdf5.Hdf5TreeModel()
h5 = commonh5.File("/foo/bar/1.mock", "w")
- create_NXentry(h5, "a").create_dataset("end_time", data=numpy.array([numpy.string_("2015")]))
- create_NXentry(h5, "b").create_dataset("end_time", data=numpy.array([numpy.string_("2013")]))
- create_NXentry(h5, "c").create_dataset("end_time", data=numpy.array([numpy.string_("2014")]))
+ create_NXentry(h5, "a").create_dataset(
+ "end_time", data=numpy.array([numpy.string_("2015")])
+ )
+ create_NXentry(h5, "b").create_dataset(
+ "end_time", data=numpy.array([numpy.string_("2013")])
+ )
+ create_NXentry(h5, "c").create_dataset(
+ "end_time", data=numpy.array([numpy.string_("2014")])
+ )
model.insertH5pyObject(h5)
proxy = hdf5.NexusSortFilterProxyModel()
@@ -565,7 +644,7 @@ class TestNexusSortFilterProxyModel(TestCaseQt):
self.assertListEqual(names, ["100aaa", "aaa100"])
-@pytest.fixture(scope='class')
+@pytest.fixture(scope="class")
def useH5Model(request, tmpdir_factory):
# Create HDF5 files
tmp = tmpdir_factory.mktemp("test_hdf5")
@@ -573,39 +652,53 @@ def useH5Model(request, tmpdir_factory):
extH5FileName = os.path.join(tmp, "base__external.h5")
extDatFileName = os.path.join(tmp, "base__external.dat")
- externalh5 = h5py.File(extH5FileName, mode="w")
+ externalh5 = h5py_utils.File(extH5FileName, mode="w")
externalh5["target/dataset"] = 50
externalh5["target/link"] = h5py.SoftLink("/target/dataset")
externalh5["/ext/vds0"] = [0, 1]
externalh5["/ext/vds1"] = [2, 3]
externalh5.close()
- numpy.array([0,1,10,10,2,3]).tofile(extDatFileName)
+ numpy.array([0, 1, 10, 10, 2, 3]).tofile(extDatFileName)
- h5 = h5py.File(filename, mode="w")
+ h5 = h5py_utils.File(filename, mode="w")
h5["group/dataset"] = 50
h5["link/soft_link"] = h5py.SoftLink("/group/dataset")
h5["link/soft_link_to_group"] = h5py.SoftLink("/group")
- h5["link/soft_link_to_link"] = h5py.SoftLink("/link/soft_link")
+ h5["link/soft_link_to_soft_link"] = h5py.SoftLink("/link/soft_link")
+ h5["link/soft_link_to_external_link"] = h5py.SoftLink("/link/external_link")
h5["link/soft_link_to_file"] = h5py.SoftLink("/")
h5["group/soft_link_relative"] = h5py.SoftLink("dataset")
h5["link/external_link"] = h5py.ExternalLink(extH5FileName, "/target/dataset")
- h5["link/external_link_to_link"] = h5py.ExternalLink(extH5FileName, "/target/link")
- h5["broken_link/external_broken_file"] = h5py.ExternalLink(extH5FileName + "_not_exists", "/target/link")
- h5["broken_link/external_broken_link"] = h5py.ExternalLink(extH5FileName, "/target/not_exists")
+ h5["link/external_link_to_soft_link"] = h5py.ExternalLink(
+ extH5FileName, "/target/link"
+ )
+ h5["broken_link/external_broken_file"] = h5py.ExternalLink(
+ extH5FileName + "_not_exists", "/target/link"
+ )
+ h5["broken_link/external_broken_link"] = h5py.ExternalLink(
+ extH5FileName, "/target/not_exists"
+ )
h5["broken_link/soft_broken_link"] = h5py.SoftLink("/group/not_exists")
h5["broken_link/soft_link_to_broken_link"] = h5py.SoftLink("/group/not_exists")
if h5py2_9:
- layout = h5py.VirtualLayout((2,2), dtype=int)
- layout[0] = h5py.VirtualSource("base__external.h5", name="/ext/vds0", shape=(2,), dtype=int)
- layout[1] = h5py.VirtualSource("base__external.h5", name="/ext/vds1", shape=(2,), dtype=int)
+ layout = h5py.VirtualLayout((2, 2), dtype=int)
+ layout[0] = h5py.VirtualSource(
+ "base__external.h5", name="/ext/vds0", shape=(2,), dtype=int
+ )
+ layout[1] = h5py.VirtualSource(
+ "base__external.h5", name="/ext/vds1", shape=(2,), dtype=int
+ )
h5.create_group("/ext")
h5["/ext"].create_virtual_dataset("virtual", layout)
- external = [("base__external.dat", 0, 2*8), ("base__external.dat", 4*8, 2*8)]
- h5["/ext"].create_dataset("raw", shape=(2,2), dtype=int, external=external)
+ external = [
+ ("base__external.dat", 0, 2 * 8),
+ ("base__external.dat", 4 * 8, 2 * 8),
+ ]
+ h5["/ext"].create_dataset("raw", shape=(2, 2), dtype=int, external=external)
h5.close()
- with h5py.File(filename, mode="r") as h5File:
+ with h5py_utils.File(filename, mode="r") as h5File:
# Create model
request.cls.model = hdf5.Hdf5TreeModel()
request.cls.model.insertH5pyObject(h5File)
@@ -615,7 +708,7 @@ def useH5Model(request, tmpdir_factory):
TestCaseQt.qWaitForDestroy(ref)
-@pytest.mark.usefixtures('useH5Model')
+@pytest.mark.usefixtures("useH5Model")
class _TestModelBase(TestCaseQt):
def getIndexFromPath(self, model, path):
"""
@@ -639,7 +732,6 @@ class _TestModelBase(TestCaseQt):
class TestH5Item(_TestModelBase):
-
def testFile(self):
path = ["base.h5"]
h5item = self.getH5ItemFromPath(self.model, path)
@@ -665,7 +757,7 @@ class TestH5Item(_TestModelBase):
self.assertEqual(h5item.dataLink(qt.Qt.DisplayRole), "Soft")
def testSoftLinkToLink(self):
- path = ["base.h5", "link", "soft_link_to_link"]
+ path = ["base.h5", "link", "soft_link_to_soft_link"]
h5item = self.getH5ItemFromPath(self.model, path)
self.assertEqual(h5item.dataLink(qt.Qt.DisplayRole), "Soft")
@@ -683,7 +775,7 @@ class TestH5Item(_TestModelBase):
self.assertEqual(h5item.dataLink(qt.Qt.DisplayRole), "External")
def testExternalLinkToLink(self):
- path = ["base.h5", "link", "external_link_to_link"]
+ path = ["base.h5", "link", "external_link_to_soft_link"]
h5item = self.getH5ItemFromPath(self.model, path)
self.assertEqual(h5item.dataLink(qt.Qt.DisplayRole), "External")
@@ -719,7 +811,14 @@ class TestH5Item(_TestModelBase):
self.assertEqual(h5item.dataLink(qt.Qt.DisplayRole), "")
def testDatasetFromSoftLinkToFile(self):
- path = ["base.h5", "link", "soft_link_to_file", "link", "soft_link_to_group", "dataset"]
+ path = [
+ "base.h5",
+ "link",
+ "soft_link_to_file",
+ "link",
+ "soft_link_to_group",
+ "dataset",
+ ]
h5item = self.getH5ItemFromPath(self.model, path)
self.assertEqual(h5item.dataLink(qt.Qt.DisplayRole), "")
@@ -740,7 +839,6 @@ class TestH5Item(_TestModelBase):
class TestH5Node(_TestModelBase):
-
def getH5NodeFromPath(self, model, path):
item = self.getH5ItemFromPath(model, path)
h5node = hdf5.H5Node(item)
@@ -790,16 +888,30 @@ class TestH5Node(_TestModelBase):
self.assertEqual(h5node.local_basename, "soft_link")
self.assertEqual(h5node.local_name, "/link/soft_link")
- def testSoftLinkToLink(self):
- path = ["base.h5", "link", "soft_link_to_link"]
+ def testSoftLinkToSoftLink(self):
+ path = ["base.h5", "link", "soft_link_to_soft_link"]
h5node = self.getH5NodeFromPath(self.model, path)
self.assertEqual(h5node.physical_filename, h5node.local_filename)
self.assertIn("base.h5", h5node.physical_filename)
self.assertEqual(h5node.physical_basename, "dataset")
self.assertEqual(h5node.physical_name, "/group/dataset")
- self.assertEqual(h5node.local_basename, "soft_link_to_link")
- self.assertEqual(h5node.local_name, "/link/soft_link_to_link")
+ self.assertEqual(h5node.local_basename, "soft_link_to_soft_link")
+ self.assertEqual(h5node.local_name, "/link/soft_link_to_soft_link")
+
+ def testSoftLinkToExternalLink(self):
+ path = ["base.h5", "link", "soft_link_to_external_link"]
+ h5node = self.getH5NodeFromPath(self.model, path)
+
+ with self.assertRaises(KeyError):
+ # h5py bug: #1706
+ self.assertNotEqual(h5node.physical_filename, h5node.local_filename)
+ self.assertIn("base.h5", h5node.local_filename)
+ self.assertIn("base__external.h5", h5node.physical_filename)
+ self.assertEqual(h5node.physical_basename, "dataset")
+ self.assertEqual(h5node.physical_name, "/target/dataset")
+ self.assertEqual(h5node.local_basename, "soft_link_to_external_link")
+ self.assertEqual(h5node.local_name, "/link/soft_link_to_external_link")
def testSoftLinkRelative(self):
path = ["base.h5", "group", "soft_link_relative"]
@@ -824,19 +936,18 @@ class TestH5Node(_TestModelBase):
self.assertEqual(h5node.local_basename, "external_link")
self.assertEqual(h5node.local_name, "/link/external_link")
- def testExternalLinkToLink(self):
- path = ["base.h5", "link", "external_link_to_link"]
+ def testExternalLinkToSoftLink(self):
+ path = ["base.h5", "link", "external_link_to_soft_link"]
h5node = self.getH5NodeFromPath(self.model, path)
self.assertNotEqual(h5node.physical_filename, h5node.local_filename)
self.assertIn("base.h5", h5node.local_filename)
self.assertIn("base__external.h5", h5node.physical_filename)
-
self.assertNotEqual(h5node.physical_filename, h5node.local_filename)
self.assertEqual(h5node.physical_basename, "dataset")
self.assertEqual(h5node.physical_name, "/target/dataset")
- self.assertEqual(h5node.local_basename, "external_link_to_link")
- self.assertEqual(h5node.local_name, "/link/external_link_to_link")
+ self.assertEqual(h5node.local_basename, "external_link_to_soft_link")
+ self.assertEqual(h5node.local_name, "/link/external_link_to_soft_link")
def testExternalBrokenFile(self):
path = ["base.h5", "broken_link", "external_broken_file"]
@@ -896,7 +1007,14 @@ class TestH5Node(_TestModelBase):
self.assertEqual(h5node.local_name, "/link/soft_link_to_group/dataset")
def testDatasetFromSoftLinkToFile(self):
- path = ["base.h5", "link", "soft_link_to_file", "link", "soft_link_to_group", "dataset"]
+ path = [
+ "base.h5",
+ "link",
+ "soft_link_to_file",
+ "link",
+ "soft_link_to_group",
+ "dataset",
+ ]
h5node = self.getH5NodeFromPath(self.model, path)
self.assertEqual(h5node.physical_filename, h5node.local_filename)
@@ -904,7 +1022,9 @@ class TestH5Node(_TestModelBase):
self.assertEqual(h5node.physical_basename, "dataset")
self.assertEqual(h5node.physical_name, "/group/dataset")
self.assertEqual(h5node.local_basename, "dataset")
- self.assertEqual(h5node.local_name, "/link/soft_link_to_file/link/soft_link_to_group/dataset")
+ self.assertEqual(
+ h5node.local_name, "/link/soft_link_to_file/link/soft_link_to_group/dataset"
+ )
@pytest.mark.skipif(not h5py2_9, reason="requires h5py>=2.9")
def testExternalVirtual(self):