# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2017 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 widget designed to display data using to most adapted
view from available ones from silx.
"""
from __future__ import division
__authors__ = ["V. Valls"]
__license__ = "MIT"
__date__ = "27/01/2017"
import numpy
import numbers
import logging
import silx.io
from silx.gui import icons
from silx.gui import qt
from silx.gui.data.NumpyAxesSelector import NumpyAxesSelector
from silx.gui.data.TextFormatter import TextFormatter
from silx.gui.widgets.TableWidget import TableView
from silx.gui.hdf5 import H5Node
try:
from silx.third_party import six
except ImportError:
import six
_logger = logging.getLogger(__name__)
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):
return data.h5py_object
return data
class DataInfo(object):
"""Store extracted information from a data"""
def __init__(self, data):
data = self.normalizeData(data)
self.isArray = False
self.interpretation = None
self.isNumeric = False
self.isRecord = False
self.shape = tuple()
self.dim = 0
if data is None:
return
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):
self.interpretation = data.attrs.get("interpretation", None)
else:
self.interpretation = None
if hasattr(data, "dtype"):
self.isNumeric = numpy.issubdtype(data.dtype, numpy.number)
self.isRecord = data.dtype.fields is not None
else:
self.isNumeric = isinstance(data, numbers.Number)
self.isRecord = False
if hasattr(data, "shape"):
self.shape = data.shape
else:
self.shape = tuple()
self.dim = len(self.shape)
def normalizeData(self, data):
"""Returns a normalized data if the embed a numpy or a dataset.
Else returns the data."""
return _normalizeData(data)
class DataView(object):
"""Holder for the data view."""
UNSUPPORTED = -1
"""Priority returned when the requested data can't be displayed by the
view."""
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
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)
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 axesNames(self, data, info):
"""Returns names of the expected axes of the view, according to the
input data.
:param data: Data to display
:type data: numpy.ndarray or h5py.Dataset
:param DataInfo info: Pre-computed information on the data
:rtpye: list[str]
"""
return []
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):
"""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(CompositeDataView, self).__init__(parent, modeId, icon, label)
self.__views = {}
self.__currentView = None
def addView(self, dataView):
"""Add a new dataview to the available list."""
self.__views[dataView] = None
def getBestView(self, data, info):
"""Returns the best view according to priorities."""
info = DataInfo(data)
views = [(v.getDataPriority(data, info), v) for v in self.__views.keys()]
views = filter(lambda t: t[0] > DataView.UNSUPPORTED, views)
views = sorted(views, 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 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.getDataPriority(data, info)
class _EmptyView(DataView):
"""Dummy view to display nothing"""
def __init__(self, parent):
DataView.__init__(self, parent, modeId=DataViewer.EMPTY_MODE)
def axesNames(self, data, info):
return []
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=DataViewer.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 setData(self, data):
data = self.normalizeData(data)
self.getWidget().addCurve(legend="data",
x=range(len(data)),
y=data,
resetzoom=self.__resetZoomNextTime)
self.__resetZoomNextTime = True
def axesNames(self, data, info):
return ["y"]
def getDataPriority(self, data, info):
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 _Plot2dView(DataView):
"""View displaying data using a 2d plot"""
def __init__(self, parent):
super(_Plot2dView, self).__init__(
parent=parent,
modeId=DataViewer.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.setKeepDataAspectRatio(True)
widget.setGraphXLabel('X')
widget.setGraphYLabel('Y')
return widget
def clear(self):
self.getWidget().clear()
self.__resetZoomNextTime = True
def setData(self, data):
data = self.normalizeData(data)
self.getWidget().addImage(legend="data",
data=data,
resetzoom=self.__resetZoomNextTime)
self.__resetZoomNextTime = False
def axesNames(self, data, info):
return ["y", "x"]
def getDataPriority(self, data, info):
if data is None or not info.isArray or not info.isNumeric:
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=DataViewer.PLOT3D_MODE,
label="Cube",
icon=icons.getQIcon("view-3d"))
try:
import silx.gui.plot3d #noqa
except ImportError:
_logger.warning("Plot3dView is not available")
_logger.debug("Backtrace", exc_info=True)
raise
self.__resetZoomNextTime = True
def createWidget(self, parent):
from silx.gui.plot3d import ScalarFieldView
from silx.gui.plot3d import SFViewParamTree
plot = ScalarFieldView.ScalarFieldView(parent)
plot.setAxesLabels(*reversed(self.axesNames(None, None)))
plot.addIsosurface(
lambda data: numpy.mean(data) + numpy.std(data), '#FF0000FF')
# Create a parameter tree for the scalar field view
options = SFViewParamTree.TreeView(plot)
options.setSfView(plot)
# Add the parameter tree to the main window in a dock widget
dock = qt.QDockWidget()
dock.setWidget(options)
plot.addDockWidget(qt.Qt.RightDockWidgetArea, dock)
return plot
def clear(self):
self.getWidget().setData(None)
self.__resetZoomNextTime = True
def setData(self, data):
data = self.normalizeData(data)
plot = self.getWidget()
plot.setData(data)
self.__resetZoomNextTime = False
def axesNames(self, data, info):
return ["z", "y", "x"]
def getDataPriority(self, data, info):
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 _ArrayView(DataView):
"""View displaying data using a 2d table"""
def __init__(self, parent):
DataView.__init__(self, parent, modeId=DataViewer.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 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 50
class _StackView(DataView):
"""View displaying data using a stack of images"""
def __init__(self, parent):
super(_StackView, self).__init__(
parent=parent,
modeId=DataViewer.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.setKeepDataAspectRatio(True)
widget.setLabels(self.axesNames(None, None))
# hide default option panel
widget.setOptionVisible(False)
return widget
def clear(self):
self.getWidget().clear()
self.__resetZoomNextTime = True
def setData(self, data):
data = self.normalizeData(data)
self.getWidget().setStack(stack=data, reset=self.__resetZoomNextTime)
self.__resetZoomNextTime = False
def axesNames(self, data, info):
return ["depth", "y", "x"]
def getDataPriority(self, data, info):
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=DataViewer.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):
data = self.normalizeData(data)
if silx.io.is_dataset(data):
data = data[()]
text = self.__formatter.toString(data)
self.getWidget().setText(text)
def axesNames(self, data, info):
return []
def getDataPriority(self, data, info):
data = self.normalizeData(data)
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=DataViewer.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)
widget.resizeRowsToContents()
widget.resizeColumnsToContents()
def axesNames(self, data, info):
return ["data"]
def getDataPriority(self, data, info):
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 110
return 40
elif info.isRecord:
return 40
return DataView.UNSUPPORTED
class _Hdf5View(DataView):
"""View displaying data using text"""
def __init__(self, parent):
super(_Hdf5View, self).__init__(
parent=parent,
modeId=DataViewer.HDF5_MODE,
label="HDF5",
icon=icons.getQIcon("view-hdf5"))
def createWidget(self, parent):
from .Hdf5TableModel import Hdf5TableModel
widget = TableView()
widget.setModel(Hdf5TableModel(widget))
return widget
def clear(self):
self.getWidget().model().setObject(None)
def setData(self, data):
widget = self.getWidget()
widget.model().setObject(data)
header = widget.horizontalHeader()
if qt.qVersion() < "5.0":
setResizeMode = header.setResizeMode
else:
setResizeMode = header.setSectionResizeMode
setResizeMode(0, qt.QHeaderView.Fixed)
setResizeMode(1, qt.QHeaderView.Stretch)
header.setStretchLastSection(True)
def axesNames(self, data, info):
return []
def getDataPriority(self, data, info):
widget = self.getWidget()
if widget.model().isSupportedObject(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 tetx output.
"""
def __init__(self, parent):
super(_RawView, self).__init__(
parent=parent,
modeId=DataViewer.RAW_MODE,
label="Raw",
icon=icons.getQIcon("view-raw"))
self.addView(_ScalarView(parent))
self.addView(_ArrayView(parent))
self.addView(_RecordView(parent))
[docs]class DataViewer(qt.QFrame):
"""Widget to display any kind of data
.. image:: img/DataViewer.png
The method :meth:`setData` allows to set any data to the widget. Mostly
`numpy.array` and `h5py.Dataset` are supported with adapted views. Other
data types are displayed using a text viewer.
A default view is automatically selected when a data is set. The method
:meth:`setDisplayMode` allows to change the view. To have a graphical tool
to select the view, prefer using the widget :class:`DataViewerFrame`.
The dimension of the input data and the expected dimension of the selected
view can differ. For example you can display an image (2D) from 4D
data. In this case a :class:`NumpyAxesSelector` is displayed to allow the
user to select the axis mapping and the slicing of other axes.
.. code-block:: python
import numpy
data = numpy.random.rand(500,500)
viewer = DataViewer()
viewer.setData(data)
viewer.setVisible(True)
"""
EMPTY_MODE = 0
PLOT1D_MODE = 10
PLOT2D_MODE = 20
PLOT3D_MODE = 30
RAW_MODE = 40
RAW_ARRAY_MODE = 41
RAW_RECORD_MODE = 42
RAW_SCALAR_MODE = 43
STACK_MODE = 50
HDF5_MODE = 60
displayedViewChanged = qt.Signal(object)
"""Emitted when the displayed view changes"""
dataChanged = qt.Signal()
"""Emitted when the data changes"""
currentAvailableViewsChanged = qt.Signal()
"""Emitted when the current available views (which support the current
data) change"""
def __init__(self, parent=None):
"""Constructor
:param QWidget parent: The parent of the widget
"""
super(DataViewer, self).__init__(parent)
self.__stack = qt.QStackedWidget(self)
self.__numpySelection = NumpyAxesSelector(self)
self.__numpySelection.selectedAxisChanged.connect(self.__numpyAxisChanged)
self.__numpySelection.selectionChanged.connect(self.__numpySelectionChanged)
self.__numpySelection.customAxisChanged.connect(self.__numpyCustomAxisChanged)
self.setLayout(qt.QVBoxLayout(self))
self.layout().addWidget(self.__stack, 1)
group = qt.QGroupBox(self)
group.setLayout(qt.QVBoxLayout())
group.layout().addWidget(self.__numpySelection)
group.setTitle("Axis selection")
self.__axisSelection = group
self.layout().addWidget(self.__axisSelection)
self.__currentAvailableViews = []
self.__currentView = None
self.__data = None
self.__useAxisSelection = False
views = self.createDefaultViews()
self.__views = list(views)
# store stack index for each views
self.__index = {}
self.setDisplayMode(self.EMPTY_MODE)
[docs] def createDefaultViews(self):
"""Create and returns available views which can be displayed by default
by the data viewer.
:rtype: list[DataView]"""
viewClasses = [
_EmptyView,
_Hdf5View,
_Plot1dView,
_Plot2dView,
_Plot3dView,
_RawView,
_StackView,
]
views = []
for viewClass in viewClasses:
try:
view = viewClass(self.__stack)
views.append(view)
except Exception:
_logger.warning("%s instantiation failed. View is ignored" % viewClass.__name__)
_logger.debug("Backtrace", exc_info=True)
return views
[docs] def clear(self):
"""Clear the widget"""
self.setData(None)
[docs] def normalizeData(self, data):
"""Returns a normalized data if the embed a numpy or a dataset.
Else returns the data."""
return _normalizeData(data)
def __getStackIndex(self, view):
"""Get the stack index containing the view.
:param DataView view: The view
"""
if view not in self.__index:
widget = view.getWidget()
index = self.__stack.addWidget(widget)
self.__index[view] = index
else:
index = self.__index[view]
return index
def __clearCurrentView(self):
"""Clear the current selected view"""
view = self.__currentView
if view is not None:
view.clear()
def __numpyCustomAxisChanged(self, name, value):
view = self.__currentView
if view is not None:
view.setCustomAxisValue(name, value)
def __updateNumpySelectionAxis(self):
"""
Update the numpy-selector according to the needed axis names
"""
previous = self.__numpySelection.blockSignals(True)
self.__numpySelection.clear()
info = DataInfo(self.__data)
axisNames = self.__currentView.axesNames(self.__data, info)
if info.isArray and self.__data is not None and len(axisNames) > 0:
self.__useAxisSelection = True
self.__numpySelection.setAxisNames(axisNames)
self.__numpySelection.setCustomAxis(self.__currentView.customAxisNames())
data = self.normalizeData(self.__data)
self.__numpySelection.setData(data)
if hasattr(data, "shape"):
isVisible = not (len(axisNames) == 1 and len(data.shape) == 1)
else:
isVisible = True
self.__axisSelection.setVisible(isVisible)
else:
self.__useAxisSelection = False
self.__axisSelection.setVisible(False)
self.__numpySelection.blockSignals(previous)
def __updateDataInView(self):
"""
Update the views using the current data
"""
if self.__useAxisSelection:
self.__displayedData = self.__numpySelection.selectedData()
else:
self.__displayedData = self.__data
qt.QTimer.singleShot(10, self.__setDataInView)
def __setDataInView(self):
self.__currentView.setData(self.__displayedData)
[docs] def setDisplayedView(self, view):
"""Set the displayed view.
Change the displayed view according to the view itself.
:param DataView view: The DataView to use to display the data
"""
if self.__currentView is view:
return
self.__clearCurrentView()
self.__currentView = view
self.__updateNumpySelectionAxis()
self.__updateDataInView()
stackIndex = self.__getStackIndex(self.__currentView)
if self.__currentView is not None:
self.__currentView.select()
self.__stack.setCurrentIndex(stackIndex)
self.displayedViewChanged.emit(view)
[docs] def getViewFromModeId(self, modeId):
"""Returns the first available view which have the requested modeId.
:param int modeId: Requested mode id
:rtype: DataView
"""
for view in self.__views:
if view.modeId() == modeId:
return view
return view
[docs] def setDisplayMode(self, modeId):
"""Set the displayed view using display mode.
Change the displayed view according to the requested mode.
:param int modeId: Display mode, one of
- `EMPTY_MODE`: display nothing
- `PLOT1D_MODE`: display the data as a curve
- `PLOT2D_MODE`: display the data as an image
- `PLOT3D_MODE`: display the data as an image
- `RAW_MODE`: display the data as a table
- `STACK_MODE`: display the data as an image
- `HDF5_MODE`: display the data as an image
"""
try:
view = self.getViewFromModeId(modeId)
except KeyError:
raise ValueError("Display mode %s is unknown" % modeId)
self.setDisplayedView(view)
[docs] def displayedView(self):
"""Returns the current displayed view.
:rtype: DataView
"""
return self.__currentView
def __updateView(self):
"""Display the data using the widget which fit the best"""
data = self.__data
# sort available views according to priority
info = DataInfo(data)
priorities = [v.getDataPriority(data, info) for v in self.__views]
views = zip(priorities, self.__views)
views = filter(lambda t: t[0] > DataView.UNSUPPORTED, views)
views = sorted(views, reverse=True)
# store available views
if len(views) == 0:
self.__setCurrentAvailableViews([])
available = []
else:
available = [v[1] for v in views]
self.__setCurrentAvailableViews(available)
# display the view with the most priority (the default view)
view = self.getDefaultViewFromAvailableViews(data, available)
self.__clearCurrentView()
try:
self.setDisplayedView(view)
except Exception as e:
# in case there is a problem to read the data, try to use a safe
# view
view = self.getSafeViewFromAvailableViews(data, available)
self.setDisplayedView(view)
raise e
[docs] def getSafeViewFromAvailableViews(self, data, available):
"""Returns a view which is sure to display something without failing
on rendering.
:param object data: data which will be displayed
:param list[view] available: List of available views, from highest
priority to lowest.
:rtype: DataView
"""
hdf5View = self.getViewFromModeId(DataViewer.HDF5_MODE)
if hdf5View in available:
return hdf5View
return self.getViewFromModeId(DataViewer.EMPTY_MODE)
[docs] def getDefaultViewFromAvailableViews(self, data, available):
"""Returns the default view which will be used according to available
views.
:param object data: data which will be displayed
:param list[view] available: List of available views, from highest
priority to lowest.
:rtype: DataView
"""
if len(available) > 0:
# returns the view with the highest priority
view = available[0]
else:
# else returns the empty view
view = self.getViewFromModeId(DataViewer.EMPTY_MODE)
return view
def __setCurrentAvailableViews(self, availableViews):
"""Set the current available viewa
:param List[DataView] availableViews: Current available viewa
"""
self.__currentAvailableViews = availableViews
self.currentAvailableViewsChanged.emit()
[docs] def currentAvailableViews(self):
"""Returns the list of available views for the current data
:rtype: List[DataView]
"""
return self.__currentAvailableViews
[docs] def availableViews(self):
"""Returns the list of registered views
:rtype: List[DataView]
"""
return self.__views
[docs] def setData(self, data):
"""Set the data to view.
It mostly can be a h5py.Dataset or a numpy.ndarray. Other kind of
objects will be displayed as text rendering.
:param numpy.ndarray data: The data.
"""
self.__data = data
self.__displayedData = None
self.__updateView()
self.__updateNumpySelectionAxis()
self.__updateDataInView()
self.dataChanged.emit()
def __numpyAxisChanged(self):
"""
Called when axis selection of the numpy-selector changed
"""
self.__clearCurrentView()
def __numpySelectionChanged(self):
"""
Called when data selection of the numpy-selector changed
"""
self.__updateDataInView()
[docs] def data(self):
"""Returns the data"""
return self.__data
[docs] def displayMode(self):
"""Returns the current display mode"""
return self.__currentView.modeId()