summaryrefslogtreecommitdiff
path: root/silx/gui/plot/items
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/items')
-rw-r--r--silx/gui/plot/items/__init__.py7
-rw-r--r--silx/gui/plot/items/axis.py8
-rw-r--r--silx/gui/plot/items/complex.py356
-rw-r--r--silx/gui/plot/items/core.py95
-rw-r--r--silx/gui/plot/items/image.py7
-rw-r--r--silx/gui/plot/items/marker.py3
6 files changed, 451 insertions, 25 deletions
diff --git a/silx/gui/plot/items/__init__.py b/silx/gui/plot/items/__init__.py
index bf39c87..e7957ac 100644
--- a/silx/gui/plot/items/__init__.py
+++ b/silx/gui/plot/items/__init__.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2018 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
@@ -35,6 +35,7 @@ __date__ = "22/06/2017"
from .core import (Item, LabelsMixIn, DraggableMixIn, ColormapMixIn, # noqa
SymbolMixIn, ColorMixIn, YAxisMixIn, FillMixIn, # noqa
AlphaMixIn, LineMixIn, ItemChangedType) # noqa
+from .complex import ImageComplexData # noqa
from .curve import Curve # noqa
from .histogram import Histogram # noqa
from .image import ImageBase, ImageData, ImageRgba, MaskImageData # noqa
@@ -42,3 +43,7 @@ from .shape import Shape # noqa
from .scatter import Scatter # noqa
from .marker import Marker, XMarker, YMarker # noqa
from .axis import Axis, XAxis, YAxis, YRightAxis
+
+DATA_ITEMS = ImageComplexData, Curve, Histogram, ImageBase, Scatter
+"""Classes of items representing data and to consider to compute data bounds.
+"""
diff --git a/silx/gui/plot/items/axis.py b/silx/gui/plot/items/axis.py
index ff36512..d7e6eff 100644
--- a/silx/gui/plot/items/axis.py
+++ b/silx/gui/plot/items/axis.py
@@ -27,7 +27,7 @@
__authors__ = ["V. Valls"]
__license__ = "MIT"
-__date__ = "30/08/2017"
+__date__ = "06/12/2017"
import logging
from ... import qt
@@ -66,7 +66,7 @@ class Axis(qt.QObject):
"""Signal emitted when axis autoscale has changed"""
sigLimitsChanged = qt.Signal(float, float)
- """Signal emitted when axis autoscale has changed"""
+ """Signal emitted when axis limits have changed"""
def __init__(self, plot):
"""Constructor
@@ -262,7 +262,7 @@ class Axis(qt.QObject):
def setLimitsConstraints(self, minPos=None, maxPos=None):
"""
- Set a constaints on the position of the axes.
+ Set a constraint on the position of the axes.
:param float minPos: Minimum allowed axis value.
:param float maxPos: Maximum allowed axis value.
@@ -283,7 +283,7 @@ class Axis(qt.QObject):
def setRangeConstraints(self, minRange=None, maxRange=None):
"""
- Set a constaints on the position of the axes.
+ Set a constraint on the position of the axes.
:param float minRange: Minimum allowed left-to-right span across the
view
diff --git a/silx/gui/plot/items/complex.py b/silx/gui/plot/items/complex.py
new file mode 100644
index 0000000..ba57e85
--- /dev/null
+++ b/silx/gui/plot/items/complex.py
@@ -0,0 +1,356 @@
+# coding: utf-8
+# /*##########################################################################
+#
+# Copyright (c) 2017-2018 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 provides the :class:`ImageComplexData` of the :class:`Plot`.
+"""
+
+from __future__ import absolute_import
+
+__authors__ = ["Vincent Favre-Nicolin", "T. Vincent"]
+__license__ = "MIT"
+__date__ = "19/01/2018"
+
+
+import logging
+import numpy
+
+from silx.third_party import enum
+
+from ..Colormap import Colormap
+from .core import ColormapMixIn, ItemChangedType
+from .image import ImageBase
+
+
+_logger = logging.getLogger(__name__)
+
+
+# Complex colormap functions
+
+def _phase2rgb(colormap, data):
+ """Creates RGBA image with colour-coded phase.
+
+ :param Colormap colormap: The colormap to use
+ :param numpy.ndarray data: The data to convert
+ :return: Array of RGBA colors
+ :rtype: numpy.ndarray
+ """
+ if data.size == 0:
+ return numpy.zeros((0, 0, 4), dtype=numpy.uint8)
+
+ phase = numpy.angle(data)
+ return colormap.applyToData(phase)
+
+
+def _complex2rgbalog(phaseColormap, data, amin=0., dlogs=2, smax=None):
+ """Returns RGBA colors: colour-coded phases and log10(amplitude) in alpha.
+
+ :param Colormap phaseColormap: Colormap to use for the phase
+ :param numpy.ndarray data: the complex data array to convert to RGBA
+ :param float amin: the minimum value for the alpha channel
+ :param float dlogs: amplitude range displayed, in log10 units
+ :param float smax:
+ if specified, all values above max will be displayed with an alpha=1
+ """
+ if data.size == 0:
+ return numpy.zeros((0, 0, 4), dtype=numpy.uint8)
+
+ rgba = _phase2rgb(phaseColormap, data)
+ sabs = numpy.absolute(data)
+ if smax is not None:
+ sabs[sabs > smax] = smax
+ a = numpy.log10(sabs + 1e-20)
+ a -= a.max() - dlogs # display dlogs orders of magnitude
+ rgba[..., 3] = 255 * (amin + a / dlogs * (1 - amin) * (a > 0))
+ return rgba
+
+
+def _complex2rgbalin(phaseColormap, data, gamma=1.0, smax=None):
+ """Returns RGBA colors: colour-coded phase and linear amplitude in alpha.
+
+ :param Colormap phaseColormap: Colormap to use for the phase
+ :param numpy.ndarray data:
+ :param float gamma: Optional exponent gamma applied to the amplitude
+ :param float smax:
+ """
+ if data.size == 0:
+ return numpy.zeros((0, 0, 4), dtype=numpy.uint8)
+
+ rgba = _phase2rgb(phaseColormap, data)
+ a = numpy.absolute(data)
+ if smax is not None:
+ a[a > smax] = smax
+ a /= a.max()
+ rgba[..., 3] = 255 * a**gamma
+ return rgba
+
+
+class ImageComplexData(ImageBase, ColormapMixIn):
+ """Specific plot item to force colormap when using complex colormap.
+
+ This is returning the specific colormap when displaying
+ colored phase + amplitude.
+ """
+
+ class Mode(enum.Enum):
+ """Identify available display mode for complex"""
+ ABSOLUTE = 'absolute'
+ PHASE = 'phase'
+ REAL = 'real'
+ IMAGINARY = 'imaginary'
+ AMPLITUDE_PHASE = 'amplitude_phase'
+ LOG10_AMPLITUDE_PHASE = 'log10_amplitude_phase'
+ SQUARE_AMPLITUDE = 'square_amplitude'
+
+ def __init__(self):
+ ImageBase.__init__(self)
+ ColormapMixIn.__init__(self)
+ self._data = numpy.zeros((0, 0), dtype=numpy.complex64)
+ self._dataByModesCache = {}
+ self._mode = self.Mode.ABSOLUTE
+ self._amplitudeRangeInfo = None, 2
+
+ # Use default from ColormapMixIn
+ colormap = super(ImageComplexData, self).getColormap()
+
+ phaseColormap = Colormap(
+ name='hsv',
+ vmin=-numpy.pi,
+ vmax=numpy.pi)
+ phaseColormap.setEditable(False)
+
+ self._colormaps = { # Default colormaps for all modes
+ self.Mode.ABSOLUTE: colormap,
+ self.Mode.PHASE: phaseColormap,
+ self.Mode.REAL: colormap,
+ self.Mode.IMAGINARY: colormap,
+ self.Mode.AMPLITUDE_PHASE: phaseColormap,
+ self.Mode.LOG10_AMPLITUDE_PHASE: phaseColormap,
+ self.Mode.SQUARE_AMPLITUDE: colormap,
+ }
+
+ def _addBackendRenderer(self, backend):
+ """Update backend renderer"""
+ plot = self.getPlot()
+ assert plot is not None
+ if not self._isPlotLinear(plot):
+ # Do not render with non linear scales
+ return None
+
+ mode = self.getVisualizationMode()
+ if mode in (self.Mode.AMPLITUDE_PHASE,
+ self.Mode.LOG10_AMPLITUDE_PHASE):
+ # For those modes, compute RGBA image here
+ colormap = None
+ data = self.getRgbaImageData(copy=False)
+ else:
+ colormap = self.getColormap()
+ data = self.getData(copy=False)
+
+ if data.size == 0:
+ return None # No data to display
+
+ return backend.addImage(data,
+ legend=self.getLegend(),
+ origin=self.getOrigin(),
+ scale=self.getScale(),
+ z=self.getZValue(),
+ selectable=self.isSelectable(),
+ draggable=self.isDraggable(),
+ colormap=colormap,
+ alpha=self.getAlpha())
+
+
+ def setVisualizationMode(self, mode):
+ """Set the visualization mode to use.
+
+ :param Mode mode:
+ """
+ assert isinstance(mode, self.Mode)
+ assert mode in self._colormaps
+
+ if mode != self._mode:
+ self._mode = mode
+
+ self._updated(ItemChangedType.VISUALIZATION_MODE)
+
+ # Send data updated as value returned by getData has changed
+ self._updated(ItemChangedType.DATA)
+
+ # Update ColormapMixIn colormap
+ colormap = self._colormaps[self._mode]
+ if colormap is not super(ImageComplexData, self).getColormap():
+ super(ImageComplexData, self).setColormap(colormap)
+
+ def getVisualizationMode(self):
+ """Returns the visualization mode in use.
+
+ :rtype: Mode
+ """
+ return self._mode
+
+ def _setAmplitudeRangeInfo(self, max_=None, delta=2):
+ """Set the amplitude range to display for 'log10_amplitude_phase' mode.
+
+ :param max_: Max of the amplitude range.
+ If None it autoscales to data max.
+ :param float delta: Delta range in log10 to display
+ """
+ self._amplitudeRangeInfo = max_, float(delta)
+ self._updated(ItemChangedType.VISUALIZATION_MODE)
+
+ def _getAmplitudeRangeInfo(self):
+ """Returns the amplitude range to use for 'log10_amplitude_phase' mode.
+
+ :return: (max, delta), if max is None, then it autoscales to data max
+ :rtype: 2-tuple"""
+ return self._amplitudeRangeInfo
+
+ def setColormap(self, colormap, mode=None):
+ """Set the colormap for this specific mode.
+
+ :param ~silx.gui.plot.Colormap.Colormap colormap: The colormap
+ :param Mode mode:
+ If specified, set the colormap of this specific mode.
+ Default: current mode.
+ """
+ if mode is None:
+ mode = self.getVisualizationMode()
+
+ self._colormaps[mode] = colormap
+ if mode is self.getVisualizationMode():
+ super(ImageComplexData, self).setColormap(colormap)
+ else:
+ self._updated(ItemChangedType.COLORMAP)
+
+ def getColormap(self, mode=None):
+ """Get the colormap for the (current) mode.
+
+ :param Mode mode:
+ If specified, get the colormap of this specific mode.
+ Default: current mode.
+ :rtype: ~silx.gui.plot.Colormap.Colormap
+ """
+ if mode is None:
+ mode = self.getVisualizationMode()
+
+ return self._colormaps[mode]
+
+ def setData(self, data, copy=True):
+ """"Set the image complex data
+
+ :param numpy.ndarray data: 2D array of complex with 2 dimensions (h, w)
+ :param bool copy: True (Default) to get a copy,
+ False to use internal representation (do not modify!)
+ """
+ data = numpy.array(data, copy=copy)
+ assert data.ndim == 2
+ if not numpy.issubdtype(data.dtype, numpy.complexfloating):
+ _logger.warning(
+ 'Image is not complex, converting it to complex to plot it.')
+ data = numpy.array(data, dtype=numpy.complex64)
+
+ self._data = data
+ self._dataByModesCache = {}
+
+ # TODO hackish data range implementation
+ if self.isVisible():
+ plot = self.getPlot()
+ if plot is not None:
+ plot._invalidateDataRange()
+
+ self._updated(ItemChangedType.DATA)
+
+ def getComplexData(self, copy=True):
+ """Returns the image complex data
+
+ :param bool copy: True (Default) to get a copy,
+ False to use internal representation (do not modify!)
+ :rtype: numpy.ndarray of complex
+ """
+ return numpy.array(self._data, copy=copy)
+
+ def getData(self, copy=True, mode=None):
+ """Returns the image data corresponding to (current) mode.
+
+ The returned data is always floats, to get the complex data, use
+ :meth:`getComplexData`.
+
+ :param bool copy: True (Default) to get a copy,
+ False to use internal representation (do not modify!)
+ :param Mode mode:
+ If specified, get data corresponding to the mode.
+ Default: Current mode.
+ :rtype: numpy.ndarray of float
+ """
+ if mode is None:
+ mode = self.getVisualizationMode()
+
+ if mode not in self._dataByModesCache:
+ # Compute data for mode and store it in cache
+ complexData = self.getComplexData(copy=False)
+ if mode is self.Mode.PHASE:
+ data = numpy.angle(complexData)
+ elif mode is self.Mode.REAL:
+ data = numpy.real(complexData)
+ elif mode is self.Mode.IMAGINARY:
+ data = numpy.imag(complexData)
+ elif mode in (self.Mode.ABSOLUTE,
+ self.Mode.LOG10_AMPLITUDE_PHASE,
+ self.Mode.AMPLITUDE_PHASE):
+ data = numpy.absolute(complexData)
+ elif mode is self.Mode.SQUARE_AMPLITUDE:
+ data = numpy.absolute(complexData) ** 2
+ else:
+ _logger.error(
+ 'Unsupported conversion mode: %s, fallback to absolute',
+ str(mode))
+ data = numpy.absolute(complexData)
+
+ self._dataByModesCache[mode] = data
+
+ return numpy.array(self._dataByModesCache[mode], copy=copy)
+
+ def getRgbaImageData(self, copy=True, mode=None):
+ """Get the displayed RGB(A) image for (current) mode
+
+ :param bool copy: Ignored for this class
+ :param Mode mode:
+ If specified, get data corresponding to the mode.
+ Default: Current mode.
+ :rtype: numpy.ndarray of uint8 of shape (height, width, 4)
+ """
+ if mode is None:
+ mode = self.getVisualizationMode()
+
+ colormap = self.getColormap(mode=mode)
+ if mode is self.Mode.AMPLITUDE_PHASE:
+ data = self.getComplexData(copy=False)
+ return _complex2rgbalin(colormap, data)
+ elif mode is self.Mode.LOG10_AMPLITUDE_PHASE:
+ data = self.getComplexData(copy=False)
+ max_, delta = self._getAmplitudeRangeInfo()
+ return _complex2rgbalog(colormap, data, dlogs=delta, smax=max_)
+ else:
+ data = self.getData(copy=False, mode=mode)
+ return colormap.applyToData(data)
diff --git a/silx/gui/plot/items/core.py b/silx/gui/plot/items/core.py
index 34ac700..bcb6dd1 100644
--- a/silx/gui/plot/items/core.py
+++ b/silx/gui/plot/items/core.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2018 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
@@ -115,6 +115,9 @@ class ItemChangedType(enum.Enum):
OVERLAY = 'overlayChanged'
"""Item's overlay state changed flag."""
+ VISUALIZATION_MODE = 'visualizationModeChanged'
+ """Item's visualization mode changed flag."""
+
class Item(qt.QObject):
"""Description of an item of the plot"""
@@ -136,7 +139,7 @@ class Item(qt.QObject):
"""
def __init__(self):
- super(Item, self).__init__()
+ qt.QObject.__init__(self)
self._dirty = True
self._plotRef = None
self._visible = True
@@ -312,7 +315,24 @@ class Item(qt.QObject):
# Mix-in classes ##############################################################
-class LabelsMixIn(object):
+class ItemMixInBase(qt.QObject):
+ """Base class for Item mix-in"""
+
+ def _updated(self, event=None, checkVisibility=True):
+ """This is implemented in :class:`Item`.
+
+ Mark the item as dirty (i.e., needing update).
+ This also triggers Plot.replot.
+
+ :param event: The event to send to :attr:`sigItemChanged` signal.
+ :param bool checkVisibility: True to only mark as dirty if visible,
+ False to always mark as dirty.
+ """
+ raise RuntimeError(
+ "Issue with Mix-In class inheritance order")
+
+
+class LabelsMixIn(ItemMixInBase):
"""Mix-in class for items with x and y labels
Setters are private, otherwise it needs to check the plot
@@ -352,7 +372,7 @@ class LabelsMixIn(object):
self._ylabel = str(label)
-class DraggableMixIn(object):
+class DraggableMixIn(ItemMixInBase):
"""Mix-in class for draggable items"""
def __init__(self):
@@ -375,7 +395,7 @@ class DraggableMixIn(object):
self._draggable = bool(draggable)
-class ColormapMixIn(object):
+class ColormapMixIn(ItemMixInBase):
"""Mix-in class for items with colormap"""
def __init__(self):
@@ -389,7 +409,7 @@ class ColormapMixIn(object):
def setColormap(self, colormap):
"""Set the colormap of this image
- :param Colormap colormap: colormap description
+ :param silx.gui.plot.Colormap.Colormap colormap: colormap description
"""
if isinstance(colormap, dict):
colormap = Colormap._fromDict(colormap)
@@ -406,7 +426,7 @@ class ColormapMixIn(object):
self._updated(ItemChangedType.COLORMAP)
-class SymbolMixIn(object):
+class SymbolMixIn(ItemMixInBase):
"""Mix-in class for items with symbol type"""
_DEFAULT_SYMBOL = ''
@@ -415,10 +435,49 @@ class SymbolMixIn(object):
_DEFAULT_SYMBOL_SIZE = 6.0
"""Default marker size of the item"""
+ _SUPPORTED_SYMBOLS = collections.OrderedDict((
+ ('o', 'Circle'),
+ ('d', 'Diamond'),
+ ('s', 'Square'),
+ ('+', 'Plus'),
+ ('x', 'Cross'),
+ ('.', 'Point'),
+ (',', 'Pixel'),
+ ('', 'None')))
+ """Dict of supported symbols"""
+
def __init__(self):
self._symbol = self._DEFAULT_SYMBOL
self._symbol_size = self._DEFAULT_SYMBOL_SIZE
+ @classmethod
+ def getSupportedSymbols(cls):
+ """Returns the list of supported symbol names.
+
+ :rtype: tuple of str
+ """
+ return tuple(cls._SUPPORTED_SYMBOLS.keys())
+
+ @classmethod
+ def getSupportedSymbolNames(cls):
+ """Returns the list of supported symbol human-readable names.
+
+ :rtype: tuple of str
+ """
+ return tuple(cls._SUPPORTED_SYMBOLS.values())
+
+ def getSymbolName(self, symbol=None):
+ """Returns human-readable name for a symbol.
+
+ :param str symbol: The symbol from which to get the name.
+ Default: current symbol.
+ :rtype: str
+ :raise KeyError: if symbol is not in :meth:`getSupportedSymbols`.
+ """
+ if symbol is None:
+ symbol = self.getSymbol()
+ return self._SUPPORTED_SYMBOLS[symbol]
+
def getSymbol(self):
"""Return the point marker type.
@@ -441,11 +500,19 @@ class SymbolMixIn(object):
See :meth:`getSymbol`.
- :param str symbol: Marker type
+ :param str symbol: Marker type or marker name
"""
- assert symbol in ('o', '.', ',', '+', 'x', 'd', 's', '', None)
if symbol is None:
symbol = self._DEFAULT_SYMBOL
+
+ elif symbol not in self.getSupportedSymbols():
+ for symbolCode, name in self._SUPPORTED_SYMBOLS.items():
+ if name.lower() == symbol.lower():
+ symbol = symbolCode
+ break
+ else:
+ raise ValueError('Unsupported symbol %s' % str(symbol))
+
if symbol != self._symbol:
self._symbol = symbol
self._updated(ItemChangedType.SYMBOL)
@@ -471,7 +538,7 @@ class SymbolMixIn(object):
self._updated(ItemChangedType.SYMBOL_SIZE)
-class LineMixIn(object):
+class LineMixIn(ItemMixInBase):
"""Mix-in class for item with line"""
_DEFAULT_LINEWIDTH = 1.
@@ -531,7 +598,7 @@ class LineMixIn(object):
self._updated(ItemChangedType.LINE_STYLE)
-class ColorMixIn(object):
+class ColorMixIn(ItemMixInBase):
"""Mix-in class for item with color"""
_DEFAULT_COLOR = (0., 0., 0., 1.)
@@ -570,7 +637,7 @@ class ColorMixIn(object):
self._updated(ItemChangedType.COLOR)
-class YAxisMixIn(object):
+class YAxisMixIn(ItemMixInBase):
"""Mix-in class for item with yaxis"""
_DEFAULT_YAXIS = 'left'
@@ -600,7 +667,7 @@ class YAxisMixIn(object):
self._updated(ItemChangedType.YAXIS)
-class FillMixIn(object):
+class FillMixIn(ItemMixInBase):
"""Mix-in class for item with fill"""
def __init__(self):
@@ -624,7 +691,7 @@ class FillMixIn(object):
self._updated(ItemChangedType.FILL)
-class AlphaMixIn(object):
+class AlphaMixIn(ItemMixInBase):
"""Mix-in class for item with opacity"""
def __init__(self):
diff --git a/silx/gui/plot/items/image.py b/silx/gui/plot/items/image.py
index acf7bf6..99a916a 100644
--- a/silx/gui/plot/items/image.py
+++ b/silx/gui/plot/items/image.py
@@ -28,7 +28,7 @@ of the :class:`Plot`.
__authors__ = ["T. Vincent"]
__license__ = "MIT"
-__date__ = "27/06/2017"
+__date__ = "20/10/2017"
from collections import Sequence
@@ -38,7 +38,6 @@ import numpy
from .core import (Item, LabelsMixIn, DraggableMixIn, ColormapMixIn,
AlphaMixIn, ItemChangedType)
-from ..Colors import applyColormapToData
_logger = logging.getLogger(__name__)
@@ -62,7 +61,7 @@ def _convertImageToRgba32(image, copy=True):
assert image.shape[-1] in (3, 4)
# Convert type to uint8
- if image.dtype.name != 'uin8':
+ if image.dtype.name != 'uint8':
if image.dtype.kind == 'f': # Float in [0, 1]
image = (numpy.clip(image, 0., 1.) * 255).astype(numpy.uint8)
elif image.dtype.kind == 'b': # boolean
@@ -334,7 +333,7 @@ class ImageData(ImageBase, ColormapMixIn):
_logger.warning(
'Converting boolean image to int8 to plot it.')
data = numpy.array(data, copy=False, dtype=numpy.int8)
- elif numpy.issubdtype(data.dtype, numpy.complex):
+ elif numpy.iscomplexobj(data):
_logger.warning(
'Converting complex image to absolute value to plot it.')
data = numpy.absolute(data)
diff --git a/silx/gui/plot/items/marker.py b/silx/gui/plot/items/marker.py
index 5f930b7..8f79033 100644
--- a/silx/gui/plot/items/marker.py
+++ b/silx/gui/plot/items/marker.py
@@ -69,8 +69,7 @@ class _BaseMarker(Item, DraggableMixIn, ColorMixIn):
selectable=self.isSelectable(),
draggable=self.isDraggable(),
symbol=symbol,
- constraint=self.getConstraint(),
- overlay=self.isOverlay())
+ constraint=self.getConstraint())
def isOverlay(self):
"""Return true if marker is drawn as an overlay.