diff options
Diffstat (limited to 'silx/gui/plot/items/image.py')
-rw-r--r-- | silx/gui/plot/items/image.py | 116 |
1 files changed, 90 insertions, 26 deletions
diff --git a/silx/gui/plot/items/image.py b/silx/gui/plot/items/image.py index 44cb70f..91c051d 100644 --- a/silx/gui/plot/items/image.py +++ b/silx/gui/plot/items/image.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2017-2019 European Synchrotron Radiation Facility +# Copyright (c) 2017-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 @@ -42,7 +42,6 @@ import numpy from ....utils.proxy import docstring from .core import (Item, LabelsMixIn, DraggableMixIn, ColormapMixIn, AlphaMixIn, ItemChangedType) -from ._pick import PickingResult _logger = logging.getLogger(__name__) @@ -108,7 +107,7 @@ class ImageBase(Item, LabelsMixIn, DraggableMixIn, AlphaMixIn): elif item == 0: return self.getData(copy=False) elif item == 1: - return self.getLegend() + return self.getName() elif item == 2: info = self.getInfo(copy=False) return {} if info is None else info @@ -143,25 +142,6 @@ class ImageBase(Item, LabelsMixIn, DraggableMixIn, AlphaMixIn): plot._invalidateDataRange() super(ImageBase, self).setVisible(visible) - @docstring(Item) - def pick(self, x, y): - if super(ImageBase, self).pick(x, y) is not None: - plot = self.getPlot() - if plot is None: - return None - - dataPos = plot.pixelToData(x, y) - if dataPos is None: - return None - - origin = self.getOrigin() - scale = self.getScale() - column = int((dataPos[0] - origin[0]) / float(scale[0])) - row = int((dataPos[1] - origin[1]) / float(scale[1])) - return PickingResult(self, ([row], [column])) - - return None - def _isPlotLinear(self, plot): """Return True if plot only uses linear scale for both of x and y axes.""" @@ -301,11 +281,16 @@ class ImageData(ImageBase, ColormapMixIn): if dataToUse.size == 0: return None # No data to display + colormap = self.getColormap() + if colormap.isAutoscale(): + # Avoid backend to compute autoscale: use item cache + colormap = colormap.copy() + colormap.setVRange(*colormap.getColormapRange(self)) + return backend.addImage(dataToUse, origin=self.getOrigin(), scale=self.getScale(), - z=self.getZValue(), - colormap=self.getColormap(), + colormap=colormap, alpha=self.getAlpha()) def __getitem__(self, item): @@ -331,7 +316,7 @@ class ImageData(ImageBase, ColormapMixIn): else: # Apply colormap, in this case an new array is always returned colormap = self.getColormap() - image = colormap.applyToData(self.getData(copy=False)) + image = colormap.applyToData(self) alphaImage = self.getAlphaData(copy=False) if alphaImage is not None: # Apply transparency @@ -386,6 +371,7 @@ class ImageData(ImageBase, ColormapMixIn): 'Converting complex image to absolute value to plot it.') data = numpy.absolute(data) self._data = data + self._setColormappedData(data, copy=False) if alternative is not None: alternative = numpy.array(alternative, copy=copy) @@ -434,7 +420,6 @@ class ImageRgba(ImageBase): return backend.addImage(data, origin=self.getOrigin(), scale=self.getScale(), - z=self.getZValue(), colormap=None, alpha=self.getAlpha()) @@ -473,3 +458,82 @@ class MaskImageData(ImageData): internal silx widgets. """ pass + + +class ImageStack(ImageData): + """Item to store a stack of images and to show it in the plot as one + of the images of the stack. + + The stack is a 3D array ordered this way: `frame id, y, x`. + So the first image of the stack can be reached this way: `stack[0, :, :]` + """ + + def __init__(self): + ImageData.__init__(self) + self.__stack = None + """A 3D numpy array (or a mimic one, see ListOfImages)""" + self.__stackPosition = None + """Displayed position in the cube""" + + def setStackData(self, stack, position=None, copy=True): + """Set the stack data + + :param stack: A 3D numpy array like + :param int position: The position of the displayed image in the stack + :param bool copy: True (Default) to get a copy, + False to use internal representation (do not modify!) + """ + if self.__stack is stack: + return + if copy: + stack = numpy.array(stack) + assert stack.ndim == 3 + self.__stack = stack + if position is not None: + self.__stackPosition = position + if self.__stackPosition is None: + self.__stackPosition = 0 + self.__updateDisplayedData() + + def getStackData(self, copy=True): + """Get the stored stack array. + + :param bool copy: True (Default) to get a copy, + False to use internal representation (do not modify!) + :rtype: A 3D numpy array, or numpy array like + """ + if copy: + return numpy.array(self.__stack) + else: + return self.__stack + + def setStackPosition(self, pos): + """Set the displayed position on the stack. + + This function will clamp the stack position according to + the real size of the first axis of the stack. + + :param int pos: A position on the first axis of the stack. + """ + if self.__stackPosition == pos: + return + self.__stackPosition = pos + self.__updateDisplayedData() + + def getStackPosition(self): + """Get the displayed position of the stack. + + :rtype: int + """ + return self.__stackPosition + + def __updateDisplayedData(self): + """Update the displayed frame whenever the stack or the stack + position are updated.""" + if self.__stack is None or self.__stackPosition is None: + empty = numpy.array([]).reshape(0, 0) + self.setData(empty, copy=False) + return + size = len(self.__stack) + self.__stackPosition = numpy.clip(self.__stackPosition, 0, size) + self.setData(self.__stack[self.__stackPosition], copy=False) |