summaryrefslogtreecommitdiff
path: root/silx/gui/data/DataViews.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/data/DataViews.py')
-rw-r--r--silx/gui/data/DataViews.py2059
1 files changed, 0 insertions, 2059 deletions
diff --git a/silx/gui/data/DataViews.py b/silx/gui/data/DataViews.py
deleted file mode 100644
index b18a813..0000000
--- a/silx/gui/data/DataViews.py
+++ /dev/null
@@ -1,2059 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016-2020 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
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""This module defines a views used by :class:`silx.gui.data.DataViewer`.
-"""
-
-from collections import OrderedDict
-import logging
-import numbers
-import numpy
-import os
-
-import silx.io
-from silx.utils import deprecation
-from silx.gui import qt, icons
-from silx.gui.data.TextFormatter import TextFormatter
-from silx.io import nxdata
-from silx.gui.hdf5 import H5Node
-from silx.io.nxdata import get_attr_as_unicode
-from silx.gui.colors import Colormap
-from silx.gui.dialog.ColormapDialog import ColormapDialog
-
-__authors__ = ["V. Valls", "P. Knobel"]
-__license__ = "MIT"
-__date__ = "19/02/2019"
-
-_logger = logging.getLogger(__name__)
-
-
-# DataViewer modes
-EMPTY_MODE = 0
-PLOT1D_MODE = 10
-RECORD_PLOT_MODE = 15
-IMAGE_MODE = 20
-PLOT2D_MODE = 21
-COMPLEX_IMAGE_MODE = 22
-PLOT3D_MODE = 30
-RAW_MODE = 40
-RAW_ARRAY_MODE = 41
-RAW_RECORD_MODE = 42
-RAW_SCALAR_MODE = 43
-RAW_HEXA_MODE = 44
-STACK_MODE = 50
-HDF5_MODE = 60
-NXDATA_MODE = 70
-NXDATA_INVALID_MODE = 71
-NXDATA_SCALAR_MODE = 72
-NXDATA_CURVE_MODE = 73
-NXDATA_XYVSCATTER_MODE = 74
-NXDATA_IMAGE_MODE = 75
-NXDATA_STACK_MODE = 76
-NXDATA_VOLUME_MODE = 77
-NXDATA_VOLUME_AS_STACK_MODE = 78
-
-
-def _normalizeData(data):
- """Returns a normalized data.
-
- If the data embed a numpy data or a dataset it is returned.
- Else returns the input data."""
- if isinstance(data, H5Node):
- if data.is_broken:
- return None
- return data.h5py_object
- return data
-
-
-def _normalizeComplex(data):
- """Returns a normalized complex data.
-
- If the data is a numpy data with complex, returns the
- absolute value.
- Else returns the input data."""
- if hasattr(data, "dtype"):
- isComplex = numpy.issubdtype(data.dtype, numpy.complexfloating)
- else:
- isComplex = isinstance(data, numbers.Complex)
- if isComplex:
- data = numpy.absolute(data)
- return data
-
-
-class DataInfo(object):
- """Store extracted information from a data"""
-
- def __init__(self, data):
- self.__priorities = {}
- data = self.normalizeData(data)
- self.isArray = False
- self.interpretation = None
- self.isNumeric = False
- self.isVoid = False
- self.isComplex = False
- self.isBoolean = False
- self.isRecord = False
- self.hasNXdata = False
- self.isInvalidNXdata = False
- self.countNumericColumns = 0
- self.shape = tuple()
- self.dim = 0
- self.size = 0
-
- if data is None:
- return
-
- if silx.io.is_group(data):
- nxd = nxdata.get_default(data)
- nx_class = get_attr_as_unicode(data, "NX_class")
- if nxd is not None:
- self.hasNXdata = True
- # can we plot it?
- is_scalar = nxd.signal_is_0d or nxd.interpretation in ["scalar", "scaler"]
- if not (is_scalar or nxd.is_curve or nxd.is_x_y_value_scatter or
- nxd.is_image or nxd.is_stack):
- # invalid: cannot be plotted by any widget
- self.isInvalidNXdata = True
- elif nx_class == "NXdata":
- # group claiming to be NXdata could not be parsed
- self.isInvalidNXdata = True
- elif nx_class == "NXroot" or silx.io.is_file(data):
- # root claiming to have a default entry
- if "default" in data.attrs:
- def_entry = data.attrs["default"]
- if def_entry in data and "default" in data[def_entry].attrs:
- # and entry claims to have default NXdata
- self.isInvalidNXdata = True
- elif "default" in data.attrs:
- # group claiming to have a default NXdata could not be parsed
- self.isInvalidNXdata = True
-
- if isinstance(data, numpy.ndarray):
- self.isArray = True
- elif silx.io.is_dataset(data) and data.shape != tuple():
- self.isArray = True
- else:
- self.isArray = False
-
- if silx.io.is_dataset(data):
- if "interpretation" in data.attrs:
- self.interpretation = get_attr_as_unicode(data, "interpretation")
- else:
- self.interpretation = None
- elif self.hasNXdata:
- self.interpretation = nxd.interpretation
- else:
- self.interpretation = None
-
- if hasattr(data, "dtype"):
- if numpy.issubdtype(data.dtype, numpy.void):
- # That's a real opaque type, else it is a structured type
- self.isVoid = data.dtype.fields is None
- self.isNumeric = numpy.issubdtype(data.dtype, numpy.number)
- self.isRecord = data.dtype.fields is not None
- self.isComplex = numpy.issubdtype(data.dtype, numpy.complexfloating)
- self.isBoolean = numpy.issubdtype(data.dtype, numpy.bool_)
- elif self.hasNXdata:
- self.isNumeric = numpy.issubdtype(nxd.signal.dtype,
- numpy.number)
- self.isComplex = numpy.issubdtype(nxd.signal.dtype, numpy.complexfloating)
- self.isBoolean = numpy.issubdtype(nxd.signal.dtype, numpy.bool_)
- else:
- self.isNumeric = isinstance(data, numbers.Number)
- self.isComplex = isinstance(data, numbers.Complex)
- self.isBoolean = isinstance(data, bool)
- self.isRecord = False
-
- if hasattr(data, "shape"):
- self.shape = data.shape
- elif self.hasNXdata:
- self.shape = nxd.signal.shape
- else:
- self.shape = tuple()
- if self.shape is not None:
- self.dim = len(self.shape)
-
- if hasattr(data, "shape") and data.shape is None:
- # This test is expected to avoid to fall done on the h5py issue
- # https://github.com/h5py/h5py/issues/1044
- self.size = 0
- elif hasattr(data, "size"):
- self.size = int(data.size)
- else:
- self.size = 1
-
- if hasattr(data, "dtype"):
- if data.dtype.fields is not None:
- for field in data.dtype.fields:
- if numpy.issubdtype(data.dtype[field], numpy.number):
- self.countNumericColumns += 1
-
- def normalizeData(self, data):
- """Returns a normalized data if the embed a numpy or a dataset.
- Else returns the data."""
- return _normalizeData(data)
-
- def cachePriority(self, view, priority):
- self.__priorities[view] = priority
-
- def getPriority(self, view):
- return self.__priorities[view]
-
-
-class DataViewHooks(object):
- """A set of hooks defined to custom the behaviour of the data views."""
-
- def getColormap(self, view):
- """Returns a colormap for this view."""
- return None
-
- def getColormapDialog(self, view):
- """Returns a color dialog for this view."""
- return None
-
- def viewWidgetCreated(self, view, plot):
- """Called when the widget of the view was created"""
- return
-
-class DataView(object):
- """Holder for the data view."""
-
- UNSUPPORTED = -1
- """Priority returned when the requested data can't be displayed by the
- view."""
-
- TITLE_PATTERN = "{datapath}{slicing} {permuted}"
- """Pattern used to format the title of the plot.
-
- Supported fields: `{directory}`, `{filename}`, `{datapath}`, `{slicing}`, `{permuted}`.
- """
-
- def __init__(self, parent, modeId=None, icon=None, label=None):
- """Constructor
-
- :param qt.QWidget parent: Parent of the hold widget
- """
- self.__parent = parent
- self.__widget = None
- self.__modeId = modeId
- if label is None:
- label = self.__class__.__name__
- self.__label = label
- if icon is None:
- icon = qt.QIcon()
- self.__icon = icon
- self.__hooks = None
-
- def getHooks(self):
- """Returns the data viewer hooks used by this view.
-
- :rtype: DataViewHooks
- """
- return self.__hooks
-
- def setHooks(self, hooks):
- """Set the data view hooks to use with this view.
-
- :param DataViewHooks hooks: The data view hooks to use
- """
- self.__hooks = hooks
-
- def defaultColormap(self):
- """Returns a default colormap.
-
- :rtype: Colormap
- """
- colormap = None
- if self.__hooks is not None:
- colormap = self.__hooks.getColormap(self)
- if colormap is None:
- colormap = Colormap(name="viridis")
- return colormap
-
- def defaultColorDialog(self):
- """Returns a default color dialog.
-
- :rtype: ColormapDialog
- """
- dialog = None
- if self.__hooks is not None:
- dialog = self.__hooks.getColormapDialog(self)
- if dialog is None:
- dialog = ColormapDialog()
- dialog.setModal(False)
- return dialog
-
- def icon(self):
- """Returns the default icon"""
- return self.__icon
-
- def label(self):
- """Returns the default label"""
- return self.__label
-
- def modeId(self):
- """Returns the mode id"""
- return self.__modeId
-
- def normalizeData(self, data):
- """Returns a normalized data if the embed a numpy or a dataset.
- Else returns the data."""
- return _normalizeData(data)
-
- def customAxisNames(self):
- """Returns names of axes which can be custom by the user and provided
- to the view."""
- return []
-
- def setCustomAxisValue(self, name, value):
- """
- Set the value of a custom axis
-
- :param str name: Name of the custom axis
- :param int value: Value of the custom axis
- """
- pass
-
- def isWidgetInitialized(self):
- """Returns true if the widget is already initialized.
- """
- return self.__widget is not None
-
- def select(self):
- """Called when the view is selected to display the data.
- """
- return
-
- def getWidget(self):
- """Returns the widget hold in the view and displaying the data.
-
- :returns: qt.QWidget
- """
- if self.__widget is None:
- self.__widget = self.createWidget(self.__parent)
- hooks = self.getHooks()
- if hooks is not None:
- hooks.viewWidgetCreated(self, self.__widget)
- return self.__widget
-
- def createWidget(self, parent):
- """Create the the widget displaying the data
-
- :param qt.QWidget parent: Parent of the widget
- :returns: qt.QWidget
- """
- raise NotImplementedError()
-
- def clear(self):
- """Clear the data from the view"""
- return None
-
- def setData(self, data):
- """Set the data displayed by the view
-
- :param data: Data to display
- :type data: numpy.ndarray or h5py.Dataset
- """
- return None
-
- def __formatSlices(self, indices):
- """Format an iterable of slice objects
-
- :param indices: The slices to format
- :type indices: Union[None,List[Union[slice,int]]]
- :rtype: str
- """
- if indices is None:
- return ''
-
- def formatSlice(slice_):
- start, stop, step = slice_.start, slice_.stop, slice_.step
- string = ('' if start is None else str(start)) + ':'
- if stop is not None:
- string += str(stop)
- if step not in (None, 1):
- string += ':' + step
- return string
-
- return '[' + ', '.join(
- formatSlice(index) if isinstance(index, slice) else str(index)
- for index in indices) + ']'
-
- def titleForSelection(self, selection):
- """Build title from given selection information.
-
- :param NamedTuple selection: Data selected
- :rtype: str
- """
- if selection is None or selection.filename is None:
- return None
- else:
- directory, filename = os.path.split(selection.filename)
- try:
- slicing = self.__formatSlices(selection.slice)
- except Exception:
- _logger.debug("Error while formatting slices", exc_info=True)
- slicing = '[sliced]'
-
- permuted = '(permuted)' if selection.permutation is not None else ''
-
- try:
- title = self.TITLE_PATTERN.format(
- directory=directory,
- filename=filename,
- datapath=selection.datapath,
- slicing=slicing,
- permuted=permuted)
- except Exception:
- _logger.debug("Error while formatting title", exc_info=True)
- title = selection.datapath + slicing
-
- return title
-
- def setDataSelection(self, selection):
- """Set the data selection displayed by the view
-
- If called, it have to be called directly after `setData`.
-
- :param selection: Data selected
- :type selection: NamedTuple
- """
- pass
-
- def axesNames(self, data, info):
- """Returns names of the expected axes of the view, according to the
- input data. A none value will disable the default axes selectior.
-
- :param data: Data to display
- :type data: numpy.ndarray or h5py.Dataset
- :param DataInfo info: Pre-computed information on the data
- :rtype: list[str] or None
- """
- return []
-
- def getReachableViews(self):
- """Returns the views that can be returned by `getMatchingViews`.
-
- :param object data: Any object to be displayed
- :param DataInfo info: Information cached about this data
- :rtype: List[DataView]
- """
- return [self]
-
- def getMatchingViews(self, data, info):
- """Returns the views according to data and info from the data.
-
- :param object data: Any object to be displayed
- :param DataInfo info: Information cached about this data
- :rtype: List[DataView]
- """
- priority = self.getCachedDataPriority(data, info)
- if priority == DataView.UNSUPPORTED:
- return []
- return [self]
-
- def getCachedDataPriority(self, data, info):
- try:
- priority = info.getPriority(self)
- except KeyError:
- priority = self.getDataPriority(data, info)
- info.cachePriority(self, priority)
- return priority
-
- def getDataPriority(self, data, info):
- """
- Returns the priority of using this view according to a data.
-
- - `UNSUPPORTED` means this view can't display this data
- - `1` means this view can display the data
- - `100` means this view should be used for this data
- - `1000` max value used by the views provided by silx
- - ...
-
- :param object data: The data to check
- :param DataInfo info: Pre-computed information on the data
- :rtype: int
- """
- return DataView.UNSUPPORTED
-
- def __lt__(self, other):
- return str(self) < str(other)
-
-
-class _CompositeDataView(DataView):
- """Contains sub views"""
-
- def getViews(self):
- """Returns the direct sub views registered in this view.
-
- :rtype: List[DataView]
- """
- raise NotImplementedError()
-
- def getReachableViews(self):
- """Returns all views that can be reachable at on point.
-
- This method return any sub view provided (recursivly).
-
- :rtype: List[DataView]
- """
- raise NotImplementedError()
-
- def getMatchingViews(self, data, info):
- """Returns sub views matching this data and info.
-
- This method return any sub view provided (recursivly).
-
- :param object data: Any object to be displayed
- :param DataInfo info: Information cached about this data
- :rtype: List[DataView]
- """
- raise NotImplementedError()
-
- @deprecation.deprecated(replacement="getReachableViews", since_version="0.10")
- def availableViews(self):
- return self.getViews()
-
- def isSupportedData(self, data, info):
- """If true, the composite view allow sub views to access to this data.
- Else this this data is considered as not supported by any of sub views
- (incliding this composite view).
-
- :param object data: Any object to be displayed
- :param DataInfo info: Information cached about this data
- :rtype: bool
- """
- return True
-
-
-class SelectOneDataView(_CompositeDataView):
- """Data view which can display a data using different view according to
- the kind of the data."""
-
- def __init__(self, parent, modeId=None, icon=None, label=None):
- """Constructor
-
- :param qt.QWidget parent: Parent of the hold widget
- """
- super(SelectOneDataView, self).__init__(parent, modeId, icon, label)
- self.__views = OrderedDict()
- self.__currentView = None
-
- def setHooks(self, hooks):
- """Set the data context to use with this view.
-
- :param DataViewHooks hooks: The data view hooks to use
- """
- super(SelectOneDataView, self).setHooks(hooks)
- if hooks is not None:
- for v in self.__views:
- v.setHooks(hooks)
-
- def addView(self, dataView):
- """Add a new dataview to the available list."""
- hooks = self.getHooks()
- if hooks is not None:
- dataView.setHooks(hooks)
- self.__views[dataView] = None
-
- def getReachableViews(self):
- views = []
- addSelf = False
- for v in self.__views:
- if isinstance(v, SelectManyDataView):
- views.extend(v.getReachableViews())
- else:
- addSelf = True
- if addSelf:
- # Single views are hidden by this view
- views.insert(0, self)
- return views
-
- def getMatchingViews(self, data, info):
- if not self.isSupportedData(data, info):
- return []
- view = self.__getBestView(data, info)
- if isinstance(view, SelectManyDataView):
- return view.getMatchingViews(data, info)
- else:
- return [self]
-
- def getViews(self):
- """Returns the list of registered views
-
- :rtype: List[DataView]
- """
- return list(self.__views.keys())
-
- def __getBestView(self, data, info):
- """Returns the best view according to priorities."""
- if not self.isSupportedData(data, info):
- return None
- views = [(v.getCachedDataPriority(data, info), v) for v in self.__views.keys()]
- views = filter(lambda t: t[0] > DataView.UNSUPPORTED, views)
- views = sorted(views, key=lambda t: t[0], reverse=True)
-
- if len(views) == 0:
- return None
- elif views[0][0] == DataView.UNSUPPORTED:
- return None
- else:
- return views[0][1]
-
- def customAxisNames(self):
- if self.__currentView is None:
- return
- return self.__currentView.customAxisNames()
-
- def setCustomAxisValue(self, name, value):
- if self.__currentView is None:
- return
- self.__currentView.setCustomAxisValue(name, value)
-
- def __updateDisplayedView(self):
- widget = self.getWidget()
- if self.__currentView is None:
- return
-
- # load the widget if it is not yet done
- index = self.__views[self.__currentView]
- if index is None:
- w = self.__currentView.getWidget()
- index = widget.addWidget(w)
- self.__views[self.__currentView] = index
- if widget.currentIndex() != index:
- widget.setCurrentIndex(index)
- self.__currentView.select()
-
- def select(self):
- self.__updateDisplayedView()
- if self.__currentView is not None:
- self.__currentView.select()
-
- def createWidget(self, parent):
- return qt.QStackedWidget()
-
- def clear(self):
- for v in self.__views.keys():
- v.clear()
-
- def setData(self, data):
- if self.__currentView is None:
- return
- self.__updateDisplayedView()
- self.__currentView.setData(data)
-
- def setDataSelection(self, selection):
- if self.__currentView is None:
- return
- self.__currentView.setDataSelection(selection)
-
- def axesNames(self, data, info):
- view = self.__getBestView(data, info)
- self.__currentView = view
- return view.axesNames(data, info)
-
- def getDataPriority(self, data, info):
- view = self.__getBestView(data, info)
- self.__currentView = view
- if view is None:
- return DataView.UNSUPPORTED
- else:
- return view.getCachedDataPriority(data, info)
-
- def replaceView(self, modeId, newView):
- """Replace a data view with a custom view.
- Return True in case of success, False in case of failure.
-
- .. note::
-
- This method must be called just after instantiation, before
- the viewer is used.
-
- :param int modeId: Unique mode ID identifying the DataView to
- be replaced.
- :param DataViews.DataView newView: New data view
- :return: True if replacement was successful, else False
- """
- oldView = None
- for view in self.__views:
- if view.modeId() == modeId:
- oldView = view
- break
- elif isinstance(view, _CompositeDataView):
- # recurse
- hooks = self.getHooks()
- if hooks is not None:
- newView.setHooks(hooks)
- if view.replaceView(modeId, newView):
- return True
- if oldView is None:
- return False
-
- # replace oldView with new view in dict
- self.__views = OrderedDict(
- (newView, None) if view is oldView else (view, idx) for
- view, idx in self.__views.items())
- return True
-
-
-# NOTE: SelectOneDataView was introduced with silx 0.10
-CompositeDataView = SelectOneDataView
-
-
-class SelectManyDataView(_CompositeDataView):
- """Data view which can select a set of sub views according to
- the kind of the data.
-
- This view itself is abstract and is not exposed.
- """
-
- def __init__(self, parent, views=None):
- """Constructor
-
- :param qt.QWidget parent: Parent of the hold widget
- """
- super(SelectManyDataView, self).__init__(parent, modeId=None, icon=None, label=None)
- if views is None:
- views = []
- self.__views = views
-
- def setHooks(self, hooks):
- """Set the data context to use with this view.
-
- :param DataViewHooks hooks: The data view hooks to use
- """
- super(SelectManyDataView, self).setHooks(hooks)
- if hooks is not None:
- for v in self.__views:
- v.setHooks(hooks)
-
- def addView(self, dataView):
- """Add a new dataview to the available list."""
- hooks = self.getHooks()
- if hooks is not None:
- dataView.setHooks(hooks)
- self.__views.append(dataView)
-
- def getViews(self):
- """Returns the list of registered views
-
- :rtype: List[DataView]
- """
- return list(self.__views)
-
- def getReachableViews(self):
- views = []
- for v in self.__views:
- views.extend(v.getReachableViews())
- return views
-
- def getMatchingViews(self, data, info):
- """Returns the views according to data and info from the data.
-
- :param object data: Any object to be displayed
- :param DataInfo info: Information cached about this data
- """
- if not self.isSupportedData(data, info):
- return []
- views = [v for v in self.__views if v.getCachedDataPriority(data, info) != DataView.UNSUPPORTED]
- return views
-
- def customAxisNames(self):
- raise RuntimeError("Abstract view")
-
- def setCustomAxisValue(self, name, value):
- raise RuntimeError("Abstract view")
-
- def select(self):
- raise RuntimeError("Abstract view")
-
- def createWidget(self, parent):
- raise RuntimeError("Abstract view")
-
- def clear(self):
- for v in self.__views:
- v.clear()
-
- def setData(self, data):
- raise RuntimeError("Abstract view")
-
- def axesNames(self, data, info):
- raise RuntimeError("Abstract view")
-
- def getDataPriority(self, data, info):
- if not self.isSupportedData(data, info):
- return DataView.UNSUPPORTED
- priorities = [v.getCachedDataPriority(data, info) for v in self.__views]
- priorities = [v for v in priorities if v != DataView.UNSUPPORTED]
- priorities = sorted(priorities)
- if len(priorities) == 0:
- return DataView.UNSUPPORTED
- return priorities[-1]
-
- def replaceView(self, modeId, newView):
- """Replace a data view with a custom view.
- Return True in case of success, False in case of failure.
-
- .. note::
-
- This method must be called just after instantiation, before
- the viewer is used.
-
- :param int modeId: Unique mode ID identifying the DataView to
- be replaced.
- :param DataViews.DataView newView: New data view
- :return: True if replacement was successful, else False
- """
- oldView = None
- for iview, view in enumerate(self.__views):
- if view.modeId() == modeId:
- oldView = view
- break
- elif isinstance(view, CompositeDataView):
- # recurse
- hooks = self.getHooks()
- if hooks is not None:
- newView.setHooks(hooks)
- if view.replaceView(modeId, newView):
- return True
-
- if oldView is None:
- return False
-
- # replace oldView with new view in dict
- self.__views[iview] = newView
- return True
-
-
-class _EmptyView(DataView):
- """Dummy view to display nothing"""
-
- def __init__(self, parent):
- DataView.__init__(self, parent, modeId=EMPTY_MODE)
-
- def axesNames(self, data, info):
- return None
-
- def createWidget(self, parent):
- return qt.QLabel(parent)
-
- def getDataPriority(self, data, info):
- return DataView.UNSUPPORTED
-
-
-class _Plot1dView(DataView):
- """View displaying data using a 1d plot"""
-
- def __init__(self, parent):
- super(_Plot1dView, self).__init__(
- parent=parent,
- modeId=PLOT1D_MODE,
- label="Curve",
- icon=icons.getQIcon("view-1d"))
- self.__resetZoomNextTime = True
-
- def createWidget(self, parent):
- from silx.gui import plot
- return plot.Plot1D(parent=parent)
-
- def clear(self):
- self.getWidget().clear()
- self.__resetZoomNextTime = True
-
- def normalizeData(self, data):
- data = DataView.normalizeData(self, data)
- data = _normalizeComplex(data)
- return data
-
- def setData(self, data):
- data = self.normalizeData(data)
- plotWidget = self.getWidget()
- legend = "data"
- plotWidget.addCurve(legend=legend,
- x=range(len(data)),
- y=data,
- resetzoom=self.__resetZoomNextTime)
- plotWidget.setActiveCurve(legend)
- self.__resetZoomNextTime = True
-
- def setDataSelection(self, selection):
- self.getWidget().setGraphTitle(self.titleForSelection(selection))
-
- def axesNames(self, data, info):
- return ["y"]
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- if data is None or not info.isArray or not info.isNumeric:
- return DataView.UNSUPPORTED
- if info.dim < 1:
- return DataView.UNSUPPORTED
- if info.interpretation == "spectrum":
- return 1000
- if info.dim == 2 and info.shape[0] == 1:
- return 210
- if info.dim == 1:
- return 100
- else:
- return 10
-
-
-class _Plot2dRecordView(DataView):
- def __init__(self, parent):
- super(_Plot2dRecordView, self).__init__(
- parent=parent,
- modeId=RECORD_PLOT_MODE,
- label="Curve",
- icon=icons.getQIcon("view-1d"))
- self.__resetZoomNextTime = True
- self._data = None
- self._xAxisDropDown = None
- self._yAxisDropDown = None
- self.__fields = None
-
- def createWidget(self, parent):
- from ._RecordPlot import RecordPlot
- return RecordPlot(parent=parent)
-
- def clear(self):
- self.getWidget().clear()
- self.__resetZoomNextTime = True
-
- def normalizeData(self, data):
- data = DataView.normalizeData(self, data)
- data = _normalizeComplex(data)
- return data
-
- def setData(self, data):
- self._data = self.normalizeData(data)
-
- all_fields = sorted(self._data.dtype.fields.items(), key=lambda e: e[1][1])
- numeric_fields = [f[0] for f in all_fields if numpy.issubdtype(f[1][0], numpy.number)]
- if numeric_fields == self.__fields: # Reuse previously selected fields
- fieldNameX = self.getWidget().getXAxisFieldName()
- fieldNameY = self.getWidget().getYAxisFieldName()
- else:
- self.__fields = numeric_fields
-
- self.getWidget().setSelectableXAxisFieldNames(numeric_fields)
- self.getWidget().setSelectableYAxisFieldNames(numeric_fields)
- fieldNameX = None
- fieldNameY = numeric_fields[0]
-
- # If there is a field called time, use it for the x-axis by default
- if "time" in numeric_fields:
- fieldNameX = "time"
- # Use the first field that is not "time" for the y-axis
- if fieldNameY == "time" and len(numeric_fields) >= 2:
- fieldNameY = numeric_fields[1]
-
- self._plotData(fieldNameX, fieldNameY)
-
- if not self._xAxisDropDown:
- self._xAxisDropDown = self.getWidget().getAxesSelectionToolBar().getXAxisDropDown()
- self._yAxisDropDown = self.getWidget().getAxesSelectionToolBar().getYAxisDropDown()
- self._xAxisDropDown.activated.connect(self._onAxesSelectionChaned)
- self._yAxisDropDown.activated.connect(self._onAxesSelectionChaned)
-
- def setDataSelection(self, selection):
- self.getWidget().setGraphTitle(self.titleForSelection(selection))
-
- def _onAxesSelectionChaned(self):
- fieldNameX = self._xAxisDropDown.currentData()
- self._plotData(fieldNameX, self._yAxisDropDown.currentText())
-
- def _plotData(self, fieldNameX, fieldNameY):
- self.clear()
- ydata = self._data[fieldNameY]
- if fieldNameX is None:
- xdata = numpy.arange(len(ydata))
- else:
- xdata = self._data[fieldNameX]
- self.getWidget().addCurve(legend="data",
- x=xdata,
- y=ydata,
- resetzoom=self.__resetZoomNextTime)
- self.getWidget().setXAxisFieldName(fieldNameX)
- self.getWidget().setYAxisFieldName(fieldNameY)
- self.__resetZoomNextTime = True
-
- def axesNames(self, data, info):
- return ["data"]
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- if data is None or not info.isRecord:
- return DataView.UNSUPPORTED
- if info.dim < 1:
- return DataView.UNSUPPORTED
- if info.countNumericColumns < 2:
- return DataView.UNSUPPORTED
- if info.interpretation == "spectrum":
- return 1000
- if info.dim == 2 and info.shape[0] == 1:
- return 210
- if info.dim == 1:
- return 40
- else:
- return 10
-
-
-class _Plot2dView(DataView):
- """View displaying data using a 2d plot"""
-
- def __init__(self, parent):
- super(_Plot2dView, self).__init__(
- parent=parent,
- modeId=PLOT2D_MODE,
- label="Image",
- icon=icons.getQIcon("view-2d"))
- self.__resetZoomNextTime = True
-
- def createWidget(self, parent):
- from silx.gui import plot
- widget = plot.Plot2D(parent=parent)
- widget.setDefaultColormap(self.defaultColormap())
- widget.getColormapAction().setColorDialog(self.defaultColorDialog())
- widget.getIntensityHistogramAction().setVisible(True)
- widget.setKeepDataAspectRatio(True)
- widget.getXAxis().setLabel('X')
- widget.getYAxis().setLabel('Y')
- maskToolsWidget = widget.getMaskToolsDockWidget().widget()
- maskToolsWidget.setItemMaskUpdated(True)
- return widget
-
- def clear(self):
- self.getWidget().clear()
- self.__resetZoomNextTime = True
-
- def normalizeData(self, data):
- data = DataView.normalizeData(self, data)
- data = _normalizeComplex(data)
- return data
-
- def setData(self, data):
- data = self.normalizeData(data)
- self.getWidget().addImage(legend="data",
- data=data,
- resetzoom=self.__resetZoomNextTime)
- self.__resetZoomNextTime = False
-
- def setDataSelection(self, selection):
- self.getWidget().setGraphTitle(self.titleForSelection(selection))
-
- def axesNames(self, data, info):
- return ["y", "x"]
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- if (data is None or
- not info.isArray or
- not (info.isNumeric or info.isBoolean)):
- return DataView.UNSUPPORTED
- if info.dim < 2:
- return DataView.UNSUPPORTED
- if info.interpretation == "image":
- return 1000
- if info.dim == 2:
- return 200
- else:
- return 190
-
-
-class _Plot3dView(DataView):
- """View displaying data using a 3d plot"""
-
- def __init__(self, parent):
- super(_Plot3dView, self).__init__(
- parent=parent,
- modeId=PLOT3D_MODE,
- label="Cube",
- icon=icons.getQIcon("view-3d"))
- try:
- from ._VolumeWindow import VolumeWindow # noqa
- except ImportError:
- _logger.warning("3D visualization is not available")
- _logger.debug("Backtrace", exc_info=True)
- raise
- self.__resetZoomNextTime = True
-
- def createWidget(self, parent):
- from ._VolumeWindow import VolumeWindow
-
- plot = VolumeWindow(parent)
- plot.setAxesLabels(*reversed(self.axesNames(None, None)))
- return plot
-
- def clear(self):
- self.getWidget().clear()
- self.__resetZoomNextTime = True
-
- def setData(self, data):
- data = self.normalizeData(data)
- self.getWidget().setData(data)
- self.__resetZoomNextTime = False
-
- def axesNames(self, data, info):
- return ["z", "y", "x"]
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- if data is None or not info.isArray or not info.isNumeric:
- return DataView.UNSUPPORTED
- if info.dim < 3:
- return DataView.UNSUPPORTED
- if min(data.shape) < 2:
- return DataView.UNSUPPORTED
- if info.dim == 3:
- return 100
- else:
- return 10
-
-
-class _ComplexImageView(DataView):
- """View displaying data using a ComplexImageView"""
-
- def __init__(self, parent):
- super(_ComplexImageView, self).__init__(
- parent=parent,
- modeId=COMPLEX_IMAGE_MODE,
- label="Complex Image",
- icon=icons.getQIcon("view-2d"))
-
- def createWidget(self, parent):
- from silx.gui.plot.ComplexImageView import ComplexImageView
- widget = ComplexImageView(parent=parent)
- widget.setColormap(self.defaultColormap(), mode=ComplexImageView.ComplexMode.ABSOLUTE)
- widget.setColormap(self.defaultColormap(), mode=ComplexImageView.ComplexMode.SQUARE_AMPLITUDE)
- widget.setColormap(self.defaultColormap(), mode=ComplexImageView.ComplexMode.REAL)
- widget.setColormap(self.defaultColormap(), mode=ComplexImageView.ComplexMode.IMAGINARY)
- widget.getPlot().getColormapAction().setColorDialog(self.defaultColorDialog())
- widget.getPlot().getIntensityHistogramAction().setVisible(True)
- widget.getPlot().setKeepDataAspectRatio(True)
- widget.getXAxis().setLabel('X')
- widget.getYAxis().setLabel('Y')
- maskToolsWidget = widget.getPlot().getMaskToolsDockWidget().widget()
- maskToolsWidget.setItemMaskUpdated(True)
- return widget
-
- def clear(self):
- self.getWidget().setData(None)
-
- def normalizeData(self, data):
- data = DataView.normalizeData(self, data)
- return data
-
- def setData(self, data):
- data = self.normalizeData(data)
- self.getWidget().setData(data)
-
- def setDataSelection(self, selection):
- self.getWidget().getPlot().setGraphTitle(
- self.titleForSelection(selection))
-
- def axesNames(self, data, info):
- return ["y", "x"]
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- if data is None or not info.isArray or not info.isComplex:
- return DataView.UNSUPPORTED
- if info.dim < 2:
- return DataView.UNSUPPORTED
- if info.interpretation == "image":
- return 1000
- if info.dim == 2:
- return 200
- else:
- return 190
-
-
-class _ArrayView(DataView):
- """View displaying data using a 2d table"""
-
- def __init__(self, parent):
- DataView.__init__(self, parent, modeId=RAW_ARRAY_MODE)
-
- def createWidget(self, parent):
- from silx.gui.data.ArrayTableWidget import ArrayTableWidget
- widget = ArrayTableWidget(parent)
- widget.displayAxesSelector(False)
- return widget
-
- def clear(self):
- self.getWidget().setArrayData(numpy.array([[]]))
-
- def setData(self, data):
- data = self.normalizeData(data)
- self.getWidget().setArrayData(data)
-
- def axesNames(self, data, info):
- return ["col", "row"]
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- if data is None or not info.isArray or info.isRecord:
- return DataView.UNSUPPORTED
- if info.dim < 2:
- return DataView.UNSUPPORTED
- if info.interpretation in ["scalar", "scaler"]:
- return 1000
- return 500
-
-
-class _StackView(DataView):
- """View displaying data using a stack of images"""
-
- def __init__(self, parent):
- super(_StackView, self).__init__(
- parent=parent,
- modeId=STACK_MODE,
- label="Image stack",
- icon=icons.getQIcon("view-2d-stack"))
- self.__resetZoomNextTime = True
-
- def customAxisNames(self):
- return ["depth"]
-
- def setCustomAxisValue(self, name, value):
- if name == "depth":
- self.getWidget().setFrameNumber(value)
- else:
- raise Exception("Unsupported axis")
-
- def createWidget(self, parent):
- from silx.gui import plot
- widget = plot.StackView(parent=parent)
- widget.setColormap(self.defaultColormap())
- widget.getPlotWidget().getColormapAction().setColorDialog(self.defaultColorDialog())
- widget.setKeepDataAspectRatio(True)
- widget.setLabels(self.axesNames(None, None))
- # hide default option panel
- widget.setOptionVisible(False)
- maskToolWidget = widget.getPlotWidget().getMaskToolsDockWidget().widget()
- maskToolWidget.setItemMaskUpdated(True)
- return widget
-
- def clear(self):
- self.getWidget().clear()
- self.__resetZoomNextTime = True
-
- def normalizeData(self, data):
- data = DataView.normalizeData(self, data)
- data = _normalizeComplex(data)
- return data
-
- def setData(self, data):
- data = self.normalizeData(data)
- self.getWidget().setStack(stack=data, reset=self.__resetZoomNextTime)
- # Override the colormap, while setStack overwrite it
- self.getWidget().setColormap(self.defaultColormap())
- self.__resetZoomNextTime = False
-
- def setDataSelection(self, selection):
- title = self.titleForSelection(selection)
- self.getWidget().setTitleCallback(
- lambda idx: "%s z=%d" % (title, idx))
-
- def axesNames(self, data, info):
- return ["depth", "y", "x"]
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- if data is None or not info.isArray or not info.isNumeric:
- return DataView.UNSUPPORTED
- if info.dim < 3:
- return DataView.UNSUPPORTED
- if info.interpretation == "image":
- return 500
- return 90
-
-
-class _ScalarView(DataView):
- """View displaying data using text"""
-
- def __init__(self, parent):
- DataView.__init__(self, parent, modeId=RAW_SCALAR_MODE)
-
- def createWidget(self, parent):
- widget = qt.QTextEdit(parent)
- widget.setTextInteractionFlags(qt.Qt.TextSelectableByMouse)
- widget.setAlignment(qt.Qt.AlignLeft | qt.Qt.AlignTop)
- self.__formatter = TextFormatter(parent)
- return widget
-
- def clear(self):
- self.getWidget().setText("")
-
- def setData(self, data):
- d = self.normalizeData(data)
- if silx.io.is_dataset(d):
- d = d[()]
- dtype = None
- if data is not None:
- if hasattr(data, "dtype"):
- dtype = data.dtype
- text = self.__formatter.toString(d, dtype)
- self.getWidget().setText(text)
-
- def axesNames(self, data, info):
- return []
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- data = self.normalizeData(data)
- if info.shape is None:
- return DataView.UNSUPPORTED
- if data is None:
- return DataView.UNSUPPORTED
- if silx.io.is_group(data):
- return DataView.UNSUPPORTED
- return 2
-
-
-class _RecordView(DataView):
- """View displaying data using text"""
-
- def __init__(self, parent):
- DataView.__init__(self, parent, modeId=RAW_RECORD_MODE)
-
- def createWidget(self, parent):
- from .RecordTableView import RecordTableView
- widget = RecordTableView(parent)
- widget.setWordWrap(False)
- return widget
-
- def clear(self):
- self.getWidget().setArrayData(None)
-
- def setData(self, data):
- data = self.normalizeData(data)
- widget = self.getWidget()
- widget.setArrayData(data)
- if len(data) < 100:
- widget.resizeRowsToContents()
- widget.resizeColumnsToContents()
-
- def axesNames(self, data, info):
- return ["data"]
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- if info.isRecord:
- return 40
- if data is None or not info.isArray:
- return DataView.UNSUPPORTED
- if info.dim == 1:
- if info.interpretation in ["scalar", "scaler"]:
- return 1000
- if info.shape[0] == 1:
- return 510
- return 500
- elif info.isRecord:
- return 40
- return DataView.UNSUPPORTED
-
-
-class _HexaView(DataView):
- """View displaying data using text"""
-
- def __init__(self, parent):
- DataView.__init__(self, parent, modeId=RAW_HEXA_MODE)
-
- def createWidget(self, parent):
- from .HexaTableView import HexaTableView
- widget = HexaTableView(parent)
- return widget
-
- def clear(self):
- self.getWidget().setArrayData(None)
-
- def setData(self, data):
- data = self.normalizeData(data)
- widget = self.getWidget()
- widget.setArrayData(data)
-
- def axesNames(self, data, info):
- return []
-
- def getDataPriority(self, data, info):
- if info.size <= 0:
- return DataView.UNSUPPORTED
- if info.isVoid:
- return 2000
- return DataView.UNSUPPORTED
-
-
-class _Hdf5View(DataView):
- """View displaying data using text"""
-
- def __init__(self, parent):
- super(_Hdf5View, self).__init__(
- parent=parent,
- modeId=HDF5_MODE,
- label="HDF5",
- icon=icons.getQIcon("view-hdf5"))
-
- def createWidget(self, parent):
- from .Hdf5TableView import Hdf5TableView
- widget = Hdf5TableView(parent)
- return widget
-
- def clear(self):
- widget = self.getWidget()
- widget.setData(None)
-
- def setData(self, data):
- widget = self.getWidget()
- widget.setData(data)
-
- def axesNames(self, data, info):
- return None
-
- def getDataPriority(self, data, info):
- widget = self.getWidget()
- if widget.isSupportedData(data):
- return 1
- else:
- return DataView.UNSUPPORTED
-
-
-class _RawView(CompositeDataView):
- """View displaying data as raw data.
-
- This implementation use a 2d-array view, or a record array view, or a
- raw text output.
- """
-
- def __init__(self, parent):
- super(_RawView, self).__init__(
- parent=parent,
- modeId=RAW_MODE,
- label="Raw",
- icon=icons.getQIcon("view-raw"))
- self.addView(_HexaView(parent))
- self.addView(_ScalarView(parent))
- self.addView(_ArrayView(parent))
- self.addView(_RecordView(parent))
-
-
-class _ImageView(CompositeDataView):
- """View displaying data as 2D image
-
- It choose between Plot2D and ComplexImageView widgets
- """
-
- def __init__(self, parent):
- super(_ImageView, self).__init__(
- parent=parent,
- modeId=IMAGE_MODE,
- label="Image",
- icon=icons.getQIcon("view-2d"))
- self.addView(_ComplexImageView(parent))
- self.addView(_Plot2dView(parent))
-
-
-class _InvalidNXdataView(DataView):
- """DataView showing a simple label with an error message
- to inform that a group with @NX_class=NXdata cannot be
- interpreted by any NXDataview."""
- def __init__(self, parent):
- DataView.__init__(self, parent,
- modeId=NXDATA_INVALID_MODE)
- self._msg = ""
-
- def createWidget(self, parent):
- widget = qt.QLabel(parent)
- widget.setWordWrap(True)
- widget.setStyleSheet("QLabel { color : red; }")
- return widget
-
- def axesNames(self, data, info):
- return []
-
- def clear(self):
- self.getWidget().setText("")
-
- def setData(self, data):
- self.getWidget().setText(self._msg)
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
-
- if not info.isInvalidNXdata:
- return DataView.UNSUPPORTED
-
- if info.hasNXdata:
- self._msg = "NXdata seems valid, but cannot be displayed "
- self._msg += "by any existing plot widget."
- else:
- nx_class = get_attr_as_unicode(data, "NX_class")
- if nx_class == "NXdata":
- # invalid: could not even be parsed by NXdata
- self._msg = "Group has @NX_class = NXdata, but could not be interpreted"
- self._msg += " as valid NXdata."
- elif nx_class == "NXroot" or silx.io.is_file(data):
- default_entry = data[data.attrs["default"]]
- default_nxdata_name = default_entry.attrs["default"]
- self._msg = "NXroot group provides a @default attribute "
- self._msg += "pointing to a NXentry which defines its own "
- self._msg += "@default attribute, "
- if default_nxdata_name not in default_entry:
- self._msg += " but no corresponding NXdata group exists."
- elif get_attr_as_unicode(default_entry[default_nxdata_name],
- "NX_class") != "NXdata":
- self._msg += " but the corresponding item is not a "
- self._msg += "NXdata group."
- else:
- self._msg += " but the corresponding NXdata seems to be"
- self._msg += " malformed."
- else:
- self._msg = "Group provides a @default attribute,"
- default_nxdata_name = data.attrs["default"]
- if default_nxdata_name not in data:
- self._msg += " but no corresponding NXdata group exists."
- elif get_attr_as_unicode(data[default_nxdata_name], "NX_class") != "NXdata":
- self._msg += " but the corresponding item is not a "
- self._msg += "NXdata group."
- else:
- self._msg += " but the corresponding NXdata seems to be"
- self._msg += " malformed."
- return 100
-
-
-class _NXdataBaseDataView(DataView):
- """Base class for NXdata DataView"""
-
- def __init__(self, *args, **kwargs):
- DataView.__init__(self, *args, **kwargs)
-
- def _updateColormap(self, nxdata):
- """Update used colormap according to nxdata's SILX_style"""
- cmap_norm = nxdata.plot_style.signal_scale_type
- if cmap_norm is not None:
- self.defaultColormap().setNormalization(
- 'log' if cmap_norm == 'log' else 'linear')
-
-
-class _NXdataScalarView(_NXdataBaseDataView):
- """DataView using a table view for displaying NXdata scalars:
- 0-D signal or n-D signal with *@interpretation=scalar*"""
- def __init__(self, parent):
- _NXdataBaseDataView.__init__(
- self, parent, modeId=NXDATA_SCALAR_MODE)
-
- def createWidget(self, parent):
- from silx.gui.data.ArrayTableWidget import ArrayTableWidget
- widget = ArrayTableWidget(parent)
- # widget.displayAxesSelector(False)
- return widget
-
- def axesNames(self, data, info):
- return ["col", "row"]
-
- def clear(self):
- self.getWidget().setArrayData(numpy.array([[]]),
- labels=True)
-
- def setData(self, data):
- data = self.normalizeData(data)
- # data could be a NXdata or an NXentry
- nxd = nxdata.get_default(data, validate=False)
- signal = nxd.signal
- self.getWidget().setArrayData(signal,
- labels=True)
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
-
- if info.hasNXdata and not info.isInvalidNXdata:
- nxd = nxdata.get_default(data, validate=False)
- if nxd.signal_is_0d or nxd.interpretation in ["scalar", "scaler"]:
- return 100
- return DataView.UNSUPPORTED
-
-
-class _NXdataCurveView(_NXdataBaseDataView):
- """DataView using a Plot1D for displaying NXdata curves:
- 1-D signal or n-D signal with *@interpretation=spectrum*.
-
- It also handles basic scatter plots:
- a 1-D signal with one axis whose values are not monotonically increasing.
- """
- def __init__(self, parent):
- _NXdataBaseDataView.__init__(
- self, parent, modeId=NXDATA_CURVE_MODE)
-
- def createWidget(self, parent):
- from silx.gui.data.NXdataWidgets import ArrayCurvePlot
- widget = ArrayCurvePlot(parent)
- return widget
-
- def axesNames(self, data, info):
- # disabled (used by default axis selector widget in Hdf5Viewer)
- return None
-
- def clear(self):
- self.getWidget().clear()
-
- def setData(self, data):
- data = self.normalizeData(data)
- nxd = nxdata.get_default(data, validate=False)
- signals_names = [nxd.signal_name] + nxd.auxiliary_signals_names
- if nxd.axes_dataset_names[-1] is not None:
- x_errors = nxd.get_axis_errors(nxd.axes_dataset_names[-1])
- else:
- x_errors = None
-
- # this fix is necessary until the next release of PyMca (5.2.3 or 5.3.0)
- # see https://github.com/vasole/pymca/issues/144 and https://github.com/vasole/pymca/pull/145
- if not hasattr(self.getWidget(), "setCurvesData") and \
- hasattr(self.getWidget(), "setCurveData"):
- _logger.warning("Using deprecated ArrayCurvePlot API, "
- "without support of auxiliary signals")
- self.getWidget().setCurveData(nxd.signal, nxd.axes[-1],
- yerror=nxd.errors, xerror=x_errors,
- ylabel=nxd.signal_name, xlabel=nxd.axes_names[-1],
- title=nxd.title or nxd.signal_name)
- return
-
- self.getWidget().setCurvesData([nxd.signal] + nxd.auxiliary_signals, nxd.axes[-1],
- yerror=nxd.errors, xerror=x_errors,
- ylabels=signals_names, xlabel=nxd.axes_names[-1],
- title=nxd.title or signals_names[0],
- xscale=nxd.plot_style.axes_scale_types[-1],
- yscale=nxd.plot_style.signal_scale_type)
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
- if info.hasNXdata and not info.isInvalidNXdata:
- if nxdata.get_default(data, validate=False).is_curve:
- return 100
- return DataView.UNSUPPORTED
-
-
-class _NXdataXYVScatterView(_NXdataBaseDataView):
- """DataView using a Plot1D for displaying NXdata 3D scatters as
- a scatter of coloured points (1-D signal with 2 axes)"""
- def __init__(self, parent):
- _NXdataBaseDataView.__init__(
- self, parent, modeId=NXDATA_XYVSCATTER_MODE)
-
- def createWidget(self, parent):
- from silx.gui.data.NXdataWidgets import XYVScatterPlot
- widget = XYVScatterPlot(parent)
- widget.getScatterView().setColormap(self.defaultColormap())
- widget.getScatterView().getScatterToolBar().getColormapAction().setColorDialog(
- self.defaultColorDialog())
- return widget
-
- def axesNames(self, data, info):
- # disabled (used by default axis selector widget in Hdf5Viewer)
- return None
-
- def clear(self):
- self.getWidget().clear()
-
- def setData(self, data):
- data = self.normalizeData(data)
- nxd = nxdata.get_default(data, validate=False)
-
- x_axis, y_axis = nxd.axes[-2:]
- if x_axis is None:
- x_axis = numpy.arange(nxd.signal.size)
- if y_axis is None:
- y_axis = numpy.arange(nxd.signal.size)
-
- x_label, y_label = nxd.axes_names[-2:]
- if x_label is not None:
- x_errors = nxd.get_axis_errors(x_label)
- else:
- x_errors = None
-
- if y_label is not None:
- y_errors = nxd.get_axis_errors(y_label)
- else:
- y_errors = None
-
- self._updateColormap(nxd)
-
- self.getWidget().setScattersData(y_axis, x_axis, values=[nxd.signal] + nxd.auxiliary_signals,
- yerror=y_errors, xerror=x_errors,
- ylabel=y_label, xlabel=x_label,
- title=nxd.title,
- scatter_titles=[nxd.signal_name] + nxd.auxiliary_signals_names,
- xscale=nxd.plot_style.axes_scale_types[-2],
- yscale=nxd.plot_style.axes_scale_types[-1])
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
- if info.hasNXdata and not info.isInvalidNXdata:
- if nxdata.get_default(data, validate=False).is_x_y_value_scatter:
- # It have to be a little more than a NX curve priority
- return 110
-
- return DataView.UNSUPPORTED
-
-
-class _NXdataImageView(_NXdataBaseDataView):
- """DataView using a Plot2D for displaying NXdata images:
- 2-D signal or n-D signals with *@interpretation=image*."""
- def __init__(self, parent):
- _NXdataBaseDataView.__init__(
- self, parent, modeId=NXDATA_IMAGE_MODE)
-
- def createWidget(self, parent):
- from silx.gui.data.NXdataWidgets import ArrayImagePlot
- widget = ArrayImagePlot(parent)
- widget.getPlot().setDefaultColormap(self.defaultColormap())
- widget.getPlot().getColormapAction().setColorDialog(self.defaultColorDialog())
- return widget
-
- def axesNames(self, data, info):
- # disabled (used by default axis selector widget in Hdf5Viewer)
- return None
-
- def clear(self):
- self.getWidget().clear()
-
- def setData(self, data):
- data = self.normalizeData(data)
- nxd = nxdata.get_default(data, validate=False)
- isRgba = nxd.interpretation == "rgba-image"
-
- self._updateColormap(nxd)
-
- # last two axes are Y & X
- img_slicing = slice(-2, None) if not isRgba else slice(-3, -1)
- y_axis, x_axis = nxd.axes[img_slicing]
- y_label, x_label = nxd.axes_names[img_slicing]
- y_scale, x_scale = nxd.plot_style.axes_scale_types[img_slicing]
-
- self.getWidget().setImageData(
- [nxd.signal] + nxd.auxiliary_signals,
- x_axis=x_axis, y_axis=y_axis,
- signals_names=[nxd.signal_name] + nxd.auxiliary_signals_names,
- xlabel=x_label, ylabel=y_label,
- title=nxd.title, isRgba=isRgba,
- xscale=x_scale, yscale=y_scale)
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
-
- if info.hasNXdata and not info.isInvalidNXdata:
- if nxdata.get_default(data, validate=False).is_image:
- return 100
-
- return DataView.UNSUPPORTED
-
-
-class _NXdataComplexImageView(_NXdataBaseDataView):
- """DataView using a ComplexImageView for displaying NXdata complex images:
- 2-D signal or n-D signals with *@interpretation=image*."""
- def __init__(self, parent):
- _NXdataBaseDataView.__init__(
- self, parent, modeId=NXDATA_IMAGE_MODE)
-
- def createWidget(self, parent):
- from silx.gui.data.NXdataWidgets import ArrayComplexImagePlot
- widget = ArrayComplexImagePlot(parent, colormap=self.defaultColormap())
- widget.getPlot().getColormapAction().setColorDialog(self.defaultColorDialog())
- return widget
-
- def clear(self):
- self.getWidget().clear()
-
- def setData(self, data):
- data = self.normalizeData(data)
- nxd = nxdata.get_default(data, validate=False)
-
- self._updateColormap(nxd)
-
- # last two axes are Y & X
- img_slicing = slice(-2, None)
- y_axis, x_axis = nxd.axes[img_slicing]
- y_label, x_label = nxd.axes_names[img_slicing]
-
- self.getWidget().setImageData(
- [nxd.signal] + nxd.auxiliary_signals,
- x_axis=x_axis, y_axis=y_axis,
- signals_names=[nxd.signal_name] + nxd.auxiliary_signals_names,
- xlabel=x_label, ylabel=y_label,
- title=nxd.title)
-
- def axesNames(self, data, info):
- # disabled (used by default axis selector widget in Hdf5Viewer)
- return None
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
-
- if info.hasNXdata and not info.isInvalidNXdata:
- nxd = nxdata.get_default(data, validate=False)
- if nxd.is_image and numpy.iscomplexobj(nxd.signal):
- return 100
-
- return DataView.UNSUPPORTED
-
-
-class _NXdataStackView(_NXdataBaseDataView):
- def __init__(self, parent):
- _NXdataBaseDataView.__init__(
- self, parent, modeId=NXDATA_STACK_MODE)
-
- def createWidget(self, parent):
- from silx.gui.data.NXdataWidgets import ArrayStackPlot
- widget = ArrayStackPlot(parent)
- widget.getStackView().setColormap(self.defaultColormap())
- widget.getStackView().getPlotWidget().getColormapAction().setColorDialog(self.defaultColorDialog())
- return widget
-
- def axesNames(self, data, info):
- # disabled (used by default axis selector widget in Hdf5Viewer)
- return None
-
- def clear(self):
- self.getWidget().clear()
-
- def setData(self, data):
- data = self.normalizeData(data)
- nxd = nxdata.get_default(data, validate=False)
- signal_name = nxd.signal_name
- z_axis, y_axis, x_axis = nxd.axes[-3:]
- z_label, y_label, x_label = nxd.axes_names[-3:]
- title = nxd.title or signal_name
-
- self._updateColormap(nxd)
-
- widget = self.getWidget()
- widget.setStackData(
- nxd.signal, x_axis=x_axis, y_axis=y_axis, z_axis=z_axis,
- signal_name=signal_name,
- xlabel=x_label, ylabel=y_label, zlabel=z_label,
- title=title)
- # Override the colormap, while setStack overwrite it
- widget.getStackView().setColormap(self.defaultColormap())
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
- if info.hasNXdata and not info.isInvalidNXdata:
- if nxdata.get_default(data, validate=False).is_stack:
- return 100
-
- return DataView.UNSUPPORTED
-
-
-class _NXdataVolumeView(_NXdataBaseDataView):
- def __init__(self, parent):
- _NXdataBaseDataView.__init__(
- self, parent,
- label="NXdata (3D)",
- icon=icons.getQIcon("view-nexus"),
- modeId=NXDATA_VOLUME_MODE)
- try:
- import silx.gui.plot3d # noqa
- except ImportError:
- _logger.warning("Plot3dView is not available")
- _logger.debug("Backtrace", exc_info=True)
- raise
-
- def normalizeData(self, data):
- data = super(_NXdataVolumeView, self).normalizeData(data)
- data = _normalizeComplex(data)
- return data
-
- def createWidget(self, parent):
- from silx.gui.data.NXdataWidgets import ArrayVolumePlot
- widget = ArrayVolumePlot(parent)
- return widget
-
- def axesNames(self, data, info):
- # disabled (used by default axis selector widget in Hdf5Viewer)
- return None
-
- def clear(self):
- self.getWidget().clear()
-
- def setData(self, data):
- data = self.normalizeData(data)
- nxd = nxdata.get_default(data, validate=False)
- signal_name = nxd.signal_name
- z_axis, y_axis, x_axis = nxd.axes[-3:]
- z_label, y_label, x_label = nxd.axes_names[-3:]
- title = nxd.title or signal_name
-
- widget = self.getWidget()
- widget.setData(
- nxd.signal, x_axis=x_axis, y_axis=y_axis, z_axis=z_axis,
- signal_name=signal_name,
- xlabel=x_label, ylabel=y_label, zlabel=z_label,
- title=title)
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
- if info.hasNXdata and not info.isInvalidNXdata:
- if nxdata.get_default(data, validate=False).is_volume:
- return 150
-
- return DataView.UNSUPPORTED
-
-
-class _NXdataVolumeAsStackView(_NXdataBaseDataView):
- def __init__(self, parent):
- _NXdataBaseDataView.__init__(
- self, parent,
- label="NXdata (2D)",
- icon=icons.getQIcon("view-nexus"),
- modeId=NXDATA_VOLUME_AS_STACK_MODE)
-
- def createWidget(self, parent):
- from silx.gui.data.NXdataWidgets import ArrayStackPlot
- widget = ArrayStackPlot(parent)
- widget.getStackView().setColormap(self.defaultColormap())
- widget.getStackView().getPlotWidget().getColormapAction().setColorDialog(self.defaultColorDialog())
- return widget
-
- def axesNames(self, data, info):
- # disabled (used by default axis selector widget in Hdf5Viewer)
- return None
-
- def clear(self):
- self.getWidget().clear()
-
- def setData(self, data):
- data = self.normalizeData(data)
- nxd = nxdata.get_default(data, validate=False)
- signal_name = nxd.signal_name
- z_axis, y_axis, x_axis = nxd.axes[-3:]
- z_label, y_label, x_label = nxd.axes_names[-3:]
- title = nxd.title or signal_name
-
- self._updateColormap(nxd)
-
- widget = self.getWidget()
- widget.setStackData(
- nxd.signal, x_axis=x_axis, y_axis=y_axis, z_axis=z_axis,
- signal_name=signal_name,
- xlabel=x_label, ylabel=y_label, zlabel=z_label,
- title=title)
- # Override the colormap, while setStack overwrite it
- widget.getStackView().setColormap(self.defaultColormap())
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
- if info.isComplex:
- return DataView.UNSUPPORTED
- if info.hasNXdata and not info.isInvalidNXdata:
- if nxdata.get_default(data, validate=False).is_volume:
- return 200
-
- return DataView.UNSUPPORTED
-
-class _NXdataComplexVolumeAsStackView(_NXdataBaseDataView):
- def __init__(self, parent):
- _NXdataBaseDataView.__init__(
- self, parent,
- label="NXdata (2D)",
- icon=icons.getQIcon("view-nexus"),
- modeId=NXDATA_VOLUME_AS_STACK_MODE)
- self._is_complex_data = False
-
- def createWidget(self, parent):
- from silx.gui.data.NXdataWidgets import ArrayComplexImagePlot
- widget = ArrayComplexImagePlot(parent, colormap=self.defaultColormap())
- widget.getPlot().getColormapAction().setColorDialog(self.defaultColorDialog())
- return widget
-
- def axesNames(self, data, info):
- # disabled (used by default axis selector widget in Hdf5Viewer)
- return None
-
- def clear(self):
- self.getWidget().clear()
-
- def setData(self, data):
- data = self.normalizeData(data)
- nxd = nxdata.get_default(data, validate=False)
- signal_name = nxd.signal_name
- z_axis, y_axis, x_axis = nxd.axes[-3:]
- z_label, y_label, x_label = nxd.axes_names[-3:]
- title = nxd.title or signal_name
-
- self._updateColormap(nxd)
-
- self.getWidget().setImageData(
- [nxd.signal] + nxd.auxiliary_signals,
- x_axis=x_axis, y_axis=y_axis,
- signals_names=[nxd.signal_name] + nxd.auxiliary_signals_names,
- xlabel=x_label, ylabel=y_label, title=nxd.title)
-
- def getDataPriority(self, data, info):
- data = self.normalizeData(data)
- if not info.isComplex:
- return DataView.UNSUPPORTED
- if info.hasNXdata and not info.isInvalidNXdata:
- if nxdata.get_default(data, validate=False).is_volume:
- return 200
-
- return DataView.UNSUPPORTED
-
-
-class _NXdataView(CompositeDataView):
- """Composite view displaying NXdata groups using the most adequate
- widget depending on the dimensionality."""
- def __init__(self, parent):
- super(_NXdataView, self).__init__(
- parent=parent,
- label="NXdata",
- modeId=NXDATA_MODE,
- icon=icons.getQIcon("view-nexus"))
-
- self.addView(_InvalidNXdataView(parent))
- self.addView(_NXdataScalarView(parent))
- self.addView(_NXdataCurveView(parent))
- self.addView(_NXdataXYVScatterView(parent))
- self.addView(_NXdataComplexImageView(parent))
- self.addView(_NXdataImageView(parent))
- self.addView(_NXdataStackView(parent))
-
- # The 3D view can be displayed using 2 ways
- nx3dViews = SelectManyDataView(parent)
- nx3dViews.addView(_NXdataVolumeAsStackView(parent))
- nx3dViews.addView(_NXdataComplexVolumeAsStackView(parent))
- try:
- nx3dViews.addView(_NXdataVolumeView(parent))
- except Exception:
- _logger.warning("NXdataVolumeView is not available")
- _logger.debug("Backtrace", exc_info=True)
- self.addView(nx3dViews)