diff options
Diffstat (limited to 'silx/gui/hdf5/Hdf5Item.py')
-rw-r--r-- | silx/gui/hdf5/Hdf5Item.py | 182 |
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 |