Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

silx.gui.plot.items: Refactoring PlotWidget items by adding a DataItem base class #3212

Merged
merged 7 commits into from
Oct 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion silx/gui/plot/items/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
__license__ = "MIT"
__date__ = "22/06/2017"

from .core import (Item, LabelsMixIn, DraggableMixIn, ColormapMixIn, # noqa
from .core import (Item, DataItem, # noqa
LabelsMixIn, DraggableMixIn, ColormapMixIn, # noqa
SymbolMixIn, ColorMixIn, YAxisMixIn, FillMixIn, # noqa
AlphaMixIn, LineMixIn, ScatterVisualizationMixIn, # noqa
ComplexMixIn, ItemChangedType, PointsBase) # noqa
Expand Down
15 changes: 3 additions & 12 deletions silx/gui/plot/items/complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,9 @@ class ImageComplexData(ImageBase, ColormapMixIn, ComplexMixIn):
"""Overrides supported ComplexMode"""

def __init__(self):
ImageBase.__init__(self)
ImageBase.__init__(self, numpy.zeros((0, 0), dtype=numpy.complex64))
ColormapMixIn.__init__(self)
ComplexMixIn.__init__(self)
self._data = numpy.zeros((0, 0), dtype=numpy.complex64)
self._dataByModesCache = {}
self._amplitudeRangeInfo = None, 2

Expand Down Expand Up @@ -264,17 +263,9 @@ def setData(self, data, copy=True):
'Image is not complex, converting it to complex to plot it.')
data = numpy.array(data, dtype=numpy.complex64)

self._data = data
self._dataByModesCache = {}
self._setColormappedData(self.getData(copy=False), copy=False)

# TODO hackish data range implementation
if self.isVisible():
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()

self._updated(ItemChangedType.DATA)
super().setData(data)

def getComplexData(self, copy=True):
"""Returns the image complex data
Expand All @@ -283,7 +274,7 @@ def getComplexData(self, copy=True):
False to use internal representation (do not modify!)
:rtype: numpy.ndarray of complex
"""
return numpy.array(self._data, copy=copy)
return super().getData(copy=copy)

def getData(self, copy=True, mode=None):
"""Returns the image data corresponding to (current) mode.
Expand Down
34 changes: 27 additions & 7 deletions silx/gui/plot/items/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import six

from ....utils.deprecation import deprecated
from ....utils.proxy import docstring
from ....utils.enum import Enum as _Enum
from ....math.combo import min_max
from ... import qt
Expand Down Expand Up @@ -376,6 +377,27 @@ def pick(self, x, y):
return PickingResult(self, indices)


class DataItem(Item):
"""Item with a data extent in the plot"""

def _boundsChanged(self, checkVisibility: bool=True) -> None:
"""Call this method in subclass when data bounds has changed.

:param bool checkVisibility:
"""
if not checkVisibility or self.isVisible():
# TODO hackish data range implementation
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()

@docstring(Item)
def setVisible(self, visible: bool):
if visible != self.isVisible():
self._boundsChanged(checkVisibility=False)
super().setVisible(visible)


# Mix-in classes ##############################################################

class ItemMixInBase(object):
Expand Down Expand Up @@ -837,6 +859,8 @@ def setYAxis(self, yaxis):
assert yaxis in ('left', 'right')
if yaxis != self._yaxis:
self._yaxis = yaxis
if isinstance(self, DataItem):
self._boundsChanged()
self._updated(ItemChangedType.YAXIS)


Expand Down Expand Up @@ -1202,7 +1226,7 @@ def getCurrentVisualizationParameter(self, parameter):
return self.getVisualizationParameter(parameter)


class PointsBase(Item, SymbolMixIn, AlphaMixIn):
class PointsBase(DataItem, SymbolMixIn, AlphaMixIn):
"""Base class for :class:`Curve` and :class:`Scatter`"""
# note: _logFilterData must be overloaded if you overload
# getData to change its signature
Expand All @@ -1212,7 +1236,7 @@ class PointsBase(Item, SymbolMixIn, AlphaMixIn):
on top of images."""

def __init__(self):
Item.__init__(self)
DataItem.__init__(self)
SymbolMixIn.__init__(self)
AlphaMixIn.__init__(self)
self._x = ()
Expand Down Expand Up @@ -1484,11 +1508,7 @@ def setData(self, x, y, xerror=None, yerror=None, copy=True):
self._filteredCache = {} # Reset cached filtered data
self._clippedCache = {} # Reset cached clipped bool array

# TODO hackish data range implementation
if self.isVisible():
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()
self._boundsChanged()
self._updated(ItemChangedType.DATA)


Expand Down
23 changes: 0 additions & 23 deletions silx/gui/plot/items/curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,15 +185,6 @@ def __init__(self):

self._setBaseline(Curve._DEFAULT_BASELINE)

self.sigItemChanged.connect(self.__itemChanged)

def __itemChanged(self, event):
if event == ItemChangedType.YAXIS:
# TODO hackish data range implementation
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()

def _addBackendRenderer(self, backend):
"""Update backend renderer"""
# Filter-out values <= 0
Expand Down Expand Up @@ -251,20 +242,6 @@ def __getitem__(self, item):
else:
raise IndexError("Index out of range: %s", str(item))

def setVisible(self, visible):
"""Set visibility of item.

:param bool visible: True to display it, False otherwise
"""
visible = bool(visible)
# TODO hackish data range implementation
if self.isVisible() != visible:
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()

super(Curve, self).setVisible(visible)

@deprecated(replacement='Curve.getHighlightedStyle().getColor()',
since_version='0.9.0')
def getHighlightedColor(self):
Expand Down
25 changes: 4 additions & 21 deletions silx/gui/plot/items/histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
except ImportError: # Python2 support
import collections as abc

from .core import (Item, AlphaMixIn, BaselineMixIn, ColorMixIn, FillMixIn,
from .core import (DataItem, AlphaMixIn, BaselineMixIn, ColorMixIn, FillMixIn,
LineMixIn, YAxisMixIn, ItemChangedType)

_logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -100,7 +100,7 @@ def _getHistogramCurve(histogram, edges):


# TODO: Yerror, test log scale
class Histogram(Item, AlphaMixIn, ColorMixIn, FillMixIn,
class Histogram(DataItem, AlphaMixIn, ColorMixIn, FillMixIn,
LineMixIn, YAxisMixIn, BaselineMixIn):
"""Description of an histogram"""

Expand All @@ -119,7 +119,7 @@ class Histogram(Item, AlphaMixIn, ColorMixIn, FillMixIn,
_DEFAULT_BASELINE = None

def __init__(self):
Item.__init__(self)
DataItem.__init__(self)
AlphaMixIn.__init__(self)
BaselineMixIn.__init__(self)
ColorMixIn.__init__(self)
Expand Down Expand Up @@ -219,19 +219,6 @@ def _getBounds(self):
min(0, numpy.nanmin(values)),
max(0, numpy.nanmax(values)))

def setVisible(self, visible):
"""Set visibility of item.

:param bool visible: True to display it, False otherwise
"""
visible = bool(visible)
# TODO hackish data range implementation
if self.isVisible() != visible:
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()
super(Histogram, self).setVisible(visible)

def getValueData(self, copy=True):
"""The values of the histogram

Expand Down Expand Up @@ -314,11 +301,7 @@ def setData(self, histogram, edges, align='center', baseline=None,
self._alignement = align
self._setBaseline(baseline)

if self.isVisible():
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()

self._boundsChanged()
self._updated(ItemChangedType.DATA)

def getAlignment(self):
Expand Down
79 changes: 26 additions & 53 deletions silx/gui/plot/items/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
import numpy

from ....utils.proxy import docstring
from .core import (Item, LabelsMixIn, DraggableMixIn, ColormapMixIn,
from .core import (DataItem, LabelsMixIn, DraggableMixIn, ColormapMixIn,
AlphaMixIn, ItemChangedType)


Expand Down Expand Up @@ -87,15 +87,20 @@ def _convertImageToRgba32(image, copy=True):
return numpy.array(image, copy=copy)


class ImageBase(Item, LabelsMixIn, DraggableMixIn, AlphaMixIn):
"""Description of an image"""
class ImageBase(DataItem, LabelsMixIn, DraggableMixIn, AlphaMixIn):
"""Description of an image

def __init__(self):
Item.__init__(self)
:param numpy.ndarray data: Initial image data
"""

def __init__(self, data=None):
DataItem.__init__(self)
LabelsMixIn.__init__(self)
DraggableMixIn.__init__(self)
AlphaMixIn.__init__(self)
self._data = numpy.zeros((0, 0, 4), dtype=numpy.uint8)
if data is None:
data = numpy.zeros((0, 0, 4), dtype=numpy.uint8)
self._data = data

self._origin = (0., 0.)
self._scale = (1., 1.)
Expand Down Expand Up @@ -129,19 +134,6 @@ def __getitem__(self, item):
else:
raise IndexError("Index out of range: %s" % str(item))

def setVisible(self, visible):
"""Set visibility of item.

:param bool visible: True to display it, False otherwise
"""
visible = bool(visible)
# TODO hackish data range implementation
if self.isVisible() != visible:
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()
super(ImageBase, self).setVisible(visible)

def _isPlotLinear(self, plot):
"""Return True if plot only uses linear scale for both of x and y
axes."""
Expand Down Expand Up @@ -189,6 +181,15 @@ def getData(self, copy=True):
"""
return numpy.array(self._data, copy=copy)

def setData(self, data):
"""Set the image data

:param Union[numpy.ndarray,None] data:
"""
self._data = data
self._boundsChanged()
self._updated(ItemChangedType.DATA)

def getRgbaImageData(self, copy=True):
"""Get the displayed RGB(A) image

Expand All @@ -215,13 +216,7 @@ def setOrigin(self, origin):
origin = float(origin), float(origin)
if origin != self._origin:
self._origin = origin

# TODO hackish data range implementation
if self.isVisible():
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()

self._boundsChanged()
self._updated(ItemChangedType.POSITION)

def getScale(self):
Expand All @@ -244,23 +239,16 @@ def setScale(self, scale):

if scale != self._scale:
self._scale = scale

# TODO hackish data range implementation
if self.isVisible():
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()

self._boundsChanged()
self._updated(ItemChangedType.SCALE)


class ImageData(ImageBase, ColormapMixIn):
"""Description of a data image with a colormap"""

def __init__(self):
ImageBase.__init__(self)
ImageBase.__init__(self, numpy.zeros((0, 0), dtype=numpy.float32))
ColormapMixIn.__init__(self)
self._data = numpy.zeros((0, 0), dtype=numpy.float32)
self._alternativeImage = None
self.__alpha = None

Expand Down Expand Up @@ -370,7 +358,6 @@ def setData(self, data, alternative=None, alpha=None, copy=True):
_logger.warning(
'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:
Expand All @@ -389,20 +376,14 @@ def setData(self, data, alternative=None, alpha=None, copy=True):
alpha = numpy.clip(alpha, 0., 1.)
self.__alpha = alpha

# TODO hackish data range implementation
if self.isVisible():
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()

self._updated(ItemChangedType.DATA)
super().setData(data)


class ImageRgba(ImageBase):
"""Description of an RGB(A) image"""

def __init__(self):
ImageBase.__init__(self)
ImageBase.__init__(self, numpy.zeros((0, 0, 4), dtype=numpy.uint8))

def _addBackendRenderer(self, backend):
"""Update backend renderer"""
Expand Down Expand Up @@ -440,15 +421,7 @@ def setData(self, data, copy=True):
data = numpy.array(data, copy=copy)
assert data.ndim == 3
assert data.shape[-1] in (3, 4)
self._data = data

# TODO hackish data range implementation
if self.isVisible():
plot = self.getPlot()
if plot is not None:
plot._invalidateDataRange()

self._updated(ItemChangedType.DATA)
super().setData(data)


class MaskImageData(ImageData):
Expand Down
Loading