summaryrefslogtreecommitdiff
path: root/silx/gui/hdf5/Hdf5Item.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/hdf5/Hdf5Item.py')
-rw-r--r--silx/gui/hdf5/Hdf5Item.py182
1 files changed, 102 insertions, 80 deletions
diff --git a/silx/gui/hdf5/Hdf5Item.py b/silx/gui/hdf5/Hdf5Item.py
index 40793a4..f131f61 100644
--- a/silx/gui/hdf5/Hdf5Item.py
+++ b/silx/gui/hdf5/Hdf5Item.py
@@ -25,10 +25,9 @@
__authors__ = ["V. Valls"]
__license__ = "MIT"
-__date__ = "20/01/2017"
+__date__ = "26/09/2017"
-import numpy
import logging
import collections
from .. import qt
@@ -37,6 +36,7 @@ from . import _utils
from .Hdf5Node import Hdf5Node
import silx.io.utils
from silx.gui.data.TextFormatter import TextFormatter
+from ..hdf5.Hdf5Formatter import Hdf5Formatter
_logger = logging.getLogger(__name__)
@@ -47,6 +47,8 @@ except ImportError as e:
raise e
_formatter = TextFormatter()
+_hdf5Formatter = Hdf5Formatter(textFormatter=_formatter)
+# FIXME: The formatter should be an attribute of the Hdf5Model
class Hdf5Item(Hdf5Node):
@@ -55,7 +57,7 @@ class Hdf5Item(Hdf5Node):
tree structure.
"""
- def __init__(self, text, obj, parent, key=None, h5pyClass=None, isBroken=False, populateAll=False):
+ def __init__(self, text, obj, parent, key=None, h5pyClass=None, linkClass=None, populateAll=False):
"""
:param str text: text displayed
:param object obj: Pointer to h5py data. See the `obj` attribute.
@@ -63,9 +65,10 @@ class Hdf5Item(Hdf5Node):
self.__obj = obj
self.__key = key
self.__h5pyClass = h5pyClass
- self.__isBroken = isBroken
+ self.__isBroken = obj is None and h5pyClass is None
self.__error = None
self.__text = text
+ self.__linkClass = linkClass
Hdf5Node.__init__(self, parent, populateAll=populateAll)
@property
@@ -88,16 +91,26 @@ class Hdf5Item(Hdf5Node):
:rtype: h5py.File or h5py.Dataset or h5py.Group
"""
- if self.__h5pyClass is None:
+ if self.__h5pyClass is None and self.obj is not None:
self.__h5pyClass = silx.io.utils.get_h5py_class(self.obj)
return self.__h5pyClass
+ @property
+ def linkClass(self):
+ """Returns the link class object of this node
+
+ :type: h5py.SoftLink or h5py.HardLink or h5py.ExternalLink or None
+ """
+ return self.__linkClass
+
def isGroupObj(self):
"""Returns true if the stored HDF5 object is a group (contains sub
groups or datasets).
:rtype: bool
"""
+ if self.h5pyClass is None:
+ return False
return issubclass(self.h5pyClass, h5py.Group)
def isBrokenObj(self):
@@ -111,6 +124,14 @@ class Hdf5Item(Hdf5Node):
"""
return self.__isBroken
+ def _getFormatter(self):
+ """
+ Returns an Hdf5Formatter
+
+ :rtype: Hdf5Formatter
+ """
+ return _hdf5Formatter
+
def _expectedChildCount(self):
if self.isGroupObj():
return len(self.obj)
@@ -158,6 +179,22 @@ class Hdf5Item(Hdf5Node):
self.__isBroken = True
else:
self.__obj = obj
+ if not self.isGroupObj():
+ try:
+ # pre-fetch of the data
+ if obj.shape is None:
+ pass
+ elif obj.shape == tuple():
+ obj[()]
+ else:
+ if obj.compression is None and obj.size > 0:
+ key = tuple([0] * len(obj.shape))
+ obj[key]
+ except Exception as e:
+ _logger.debug(e, exc_info=True)
+ message = "%s broken. %s" % (self.__obj.name, e.args[0])
+ self.__error = message
+ self.__isBroken = True
self.__key = None
@@ -166,15 +203,15 @@ class Hdf5Item(Hdf5Node):
for name in self.obj:
try:
class_ = self.obj.get(name, getclass=True)
- has_error = False
+ link = self.obj.get(name, getclass=True, getlink=True)
except Exception as e:
- _logger.error("Internal h5py error", exc_info=True)
+ _logger.warn("Internal h5py error", exc_info=True)
+ class_ = None
try:
- class_ = self.obj.get(name, getclass=True, getlink=True)
+ link = self.obj.get(name, getclass=True, getlink=True)
except Exception as e:
- class_ = h5py.HardLink
- has_error = True
- item = Hdf5Item(text=name, obj=None, parent=self, key=name, h5pyClass=class_, isBroken=has_error)
+ link = h5py.HardLink
+ item = Hdf5Item(text=name, obj=None, parent=self, key=name, h5pyClass=class_, linkClass=link)
self.appendChild(item)
def hasChildren(self):
@@ -191,6 +228,8 @@ class Hdf5Item(Hdf5Node):
:rtype: qt.QIcon
"""
+ # Pre-fetch the object, in case it is broken
+ obj = self.obj
style = qt.QApplication.style()
if self.__isBroken:
icon = style.standardIcon(qt.QStyle.SP_MessageBoxCritical)
@@ -205,99 +244,53 @@ class Hdf5Item(Hdf5Node):
elif issubclass(class_, h5py.ExternalLink):
return style.standardIcon(qt.QStyle.SP_FileLinkIcon)
elif issubclass(class_, h5py.Dataset):
- if len(self.obj.shape) < 4:
- name = "item-%ddim" % len(self.obj.shape)
+ if obj.shape is None:
+ name = "item-none"
+ elif len(obj.shape) < 4:
+ name = "item-%ddim" % len(obj.shape)
else:
name = "item-ndim"
- if str(self.obj.dtype) == "object":
- name = "item-object"
icon = icons.getQIcon(name)
return icon
return None
- def _humanReadableShape(self, dataset):
- if dataset.shape == tuple():
- return "scalar"
- shape = [str(i) for i in dataset.shape]
- text = u" \u00D7 ".join(shape)
- return text
-
- def _humanReadableValue(self, dataset):
- if dataset.shape == tuple():
- numpy_object = dataset[()]
- text = _formatter.toString(numpy_object)
- else:
- if dataset.size < 5 and dataset.compression is None:
- numpy_object = dataset[0:5]
- text = _formatter.toString(numpy_object)
- else:
- dimension = len(dataset.shape)
- if dataset.compression is not None:
- text = "Compressed %dD data" % dimension
- else:
- text = "%dD data" % dimension
- return text
-
- def _humanReadableDType(self, dtype, full=False):
- if dtype.type == numpy.string_:
- text = "string"
- elif dtype.type == numpy.unicode_:
- text = "string"
- elif dtype.type == numpy.object_:
- text = "object"
- elif dtype.type == numpy.bool_:
- text = "bool"
- elif dtype.type == numpy.void:
- if dtype.fields is None:
- text = "raw"
- else:
- if not full:
- text = "compound"
- else:
- compound = [d[0] for d in dtype.fields.values()]
- compound = [self._humanReadableDType(d) for d in compound]
- text = "compound(%s)" % ", ".join(compound)
- else:
- text = str(dtype)
- return text
-
- def _humanReadableType(self, dataset, full=False):
- return self._humanReadableDType(dataset.dtype, full)
-
- def _setTooltipAttributes(self, attributeDict):
+ def _createTooltipAttributes(self):
"""
Add key/value attributes that will be displayed in the item tooltip
:param Dict[str,str] attributeDict: Key/value attributes
"""
+ attributeDict = collections.OrderedDict()
+
if issubclass(self.h5pyClass, h5py.Dataset):
- attributeDict["Title"] = "HDF5 Dataset"
+ attributeDict["#Title"] = "HDF5 Dataset"
attributeDict["Name"] = self.basename
attributeDict["Path"] = self.obj.name
- attributeDict["Shape"] = self._humanReadableShape(self.obj)
- attributeDict["Value"] = self._humanReadableValue(self.obj)
- attributeDict["Data type"] = self._humanReadableType(self.obj, full=True)
+ attributeDict["Shape"] = self._getFormatter().humanReadableShape(self.obj)
+ attributeDict["Value"] = self._getFormatter().humanReadableValue(self.obj)
+ attributeDict["Data type"] = self._getFormatter().humanReadableType(self.obj, full=True)
elif issubclass(self.h5pyClass, h5py.Group):
- attributeDict["Title"] = "HDF5 Group"
+ attributeDict["#Title"] = "HDF5 Group"
attributeDict["Name"] = self.basename
attributeDict["Path"] = self.obj.name
elif issubclass(self.h5pyClass, h5py.File):
- attributeDict["Title"] = "HDF5 File"
+ attributeDict["#Title"] = "HDF5 File"
attributeDict["Name"] = self.basename
attributeDict["Path"] = "/"
elif isinstance(self.obj, h5py.ExternalLink):
- attributeDict["Title"] = "HDF5 External Link"
+ attributeDict["#Title"] = "HDF5 External Link"
attributeDict["Name"] = self.basename
attributeDict["Path"] = self.obj.name
attributeDict["Linked path"] = self.obj.path
attributeDict["Linked file"] = self.obj.filename
elif isinstance(self.obj, h5py.SoftLink):
- attributeDict["Title"] = "HDF5 Soft Link"
+ attributeDict["#Title"] = "HDF5 Soft Link"
attributeDict["Name"] = self.basename
attributeDict["Path"] = self.obj.name
attributeDict["Linked path"] = self.obj.path
else:
pass
+ return attributeDict
def _getDefaultTooltip(self):
"""Returns the default tooltip
@@ -308,10 +301,8 @@ class Hdf5Item(Hdf5Node):
self.obj # lazy loading of the object
return self.__error
- attrs = collections.OrderedDict()
- self._setTooltipAttributes(attrs)
-
- title = attrs.pop("Title", None)
+ attrs = self._createTooltipAttributes()
+ title = attrs.pop("#Title", None)
if len(attrs) > 0:
tooltip = _utils.htmlFromDict(attrs, title=title)
else:
@@ -342,7 +333,7 @@ class Hdf5Item(Hdf5Node):
return ""
class_ = self.h5pyClass
if issubclass(class_, h5py.Dataset):
- text = self._humanReadableType(self.obj)
+ text = self._getFormatter().humanReadableType(self.obj)
else:
text = ""
return text
@@ -361,7 +352,7 @@ class Hdf5Item(Hdf5Node):
class_ = self.h5pyClass
if not issubclass(class_, h5py.Dataset):
return ""
- return self._humanReadableShape(self.obj)
+ return self._getFormatter().humanReadableShape(self.obj)
return None
def dataValue(self, role):
@@ -375,7 +366,7 @@ class Hdf5Item(Hdf5Node):
return ""
if not issubclass(self.h5pyClass, h5py.Dataset):
return ""
- return self._humanReadableValue(self.obj)
+ return self._getFormatter().humanReadableValue(self.obj)
return None
def dataDescription(self, role):
@@ -412,10 +403,41 @@ class Hdf5Item(Hdf5Node):
if role == qt.Qt.TextAlignmentRole:
return qt.Qt.AlignTop | qt.Qt.AlignLeft
if role == qt.Qt.DisplayRole:
+ if self.isBrokenObj():
+ return ""
class_ = self.h5pyClass
text = class_.__name__.split(".")[-1]
return text
if role == qt.Qt.ToolTipRole:
class_ = self.h5pyClass
+ if class_ is None:
+ return ""
return "Class name: %s" % self.__class__
return None
+
+ def dataLink(self, role):
+ """Data for the link column
+
+ Overwrite it to implement the content of the 'link' column.
+
+ :rtype: qt.QVariant
+ """
+ if role == qt.Qt.DecorationRole:
+ return None
+ if role == qt.Qt.TextAlignmentRole:
+ return qt.Qt.AlignTop | qt.Qt.AlignLeft
+ if role == qt.Qt.DisplayRole:
+ link = self.linkClass
+ if link is None:
+ return ""
+ elif link is h5py.ExternalLink:
+ return "External"
+ elif link is h5py.SoftLink:
+ return "Soft"
+ elif link is h5py.HardLink:
+ return ""
+ else:
+ return link.__name__
+ if role == qt.Qt.ToolTipRole:
+ return None
+ return None