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__.py2
-rw-r--r--silx/gui/plot/items/axis.py6
-rw-r--r--silx/gui/plot/items/complex.py8
-rw-r--r--silx/gui/plot/items/core.py37
-rw-r--r--silx/gui/plot/items/curve.py5
-rw-r--r--silx/gui/plot/items/histogram.py6
-rw-r--r--silx/gui/plot/items/roi.py72
-rw-r--r--silx/gui/plot/items/scatter.py5
-rw-r--r--silx/gui/plot/items/shape.py45
9 files changed, 143 insertions, 43 deletions
diff --git a/silx/gui/plot/items/__init__.py b/silx/gui/plot/items/__init__.py
index e7957ac..f829f78 100644
--- a/silx/gui/plot/items/__init__.py
+++ b/silx/gui/plot/items/__init__.py
@@ -36,7 +36,7 @@ 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 .curve import Curve, CurveStyle # noqa
from .histogram import Histogram # noqa
from .image import ImageBase, ImageData, ImageRgba, MaskImageData # noqa
from .shape import Shape # noqa
diff --git a/silx/gui/plot/items/axis.py b/silx/gui/plot/items/axis.py
index 3d9fe14..8ea5c7a 100644
--- a/silx/gui/plot/items/axis.py
+++ b/silx/gui/plot/items/axis.py
@@ -27,16 +27,16 @@
__authors__ = ["V. Valls"]
__license__ = "MIT"
-__date__ = "06/12/2017"
+__date__ = "22/11/2018"
import datetime as dt
+import enum
import logging
import dateutil.tz
from ... import qt
-from silx.third_party import enum
_logger = logging.getLogger(__name__)
@@ -448,6 +448,8 @@ class YAxis(Axis):
False for Y axis going from bottom to top
"""
flag = bool(flag)
+ if self.isInverted() == flag:
+ return
self._getBackend().setYAxisInverted(flag)
self._getPlot()._setDirtyPlot()
self.sigInvertedChanged.emit(flag)
diff --git a/silx/gui/plot/items/complex.py b/silx/gui/plot/items/complex.py
index 535b0a9..7fffd77 100644
--- a/silx/gui/plot/items/complex.py
+++ b/silx/gui/plot/items/complex.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017-2018 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2019 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
@@ -33,9 +33,9 @@ __date__ = "14/06/2018"
import logging
-import numpy
+import enum
-from silx.third_party import enum
+import numpy
from ...colors import Colormap
from .core import ColormapMixIn, ItemChangedType
@@ -137,7 +137,6 @@ class ImageComplexData(ImageBase, ColormapMixIn):
name='hsv',
vmin=-numpy.pi,
vmax=numpy.pi)
- phaseColormap.setEditable(False)
self._colormaps = { # Default colormaps for all modes
self.Mode.ABSOLUTE: colormap,
@@ -180,7 +179,6 @@ class ImageComplexData(ImageBase, ColormapMixIn):
colormap=colormap,
alpha=self.getAlpha())
-
def setVisualizationMode(self, mode):
"""Set the visualization mode to use.
diff --git a/silx/gui/plot/items/core.py b/silx/gui/plot/items/core.py
index e000751..bf3b719 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-2018 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2019 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
@@ -27,20 +27,23 @@
__authors__ = ["T. Vincent"]
__license__ = "MIT"
-__date__ = "14/06/2018"
+__date__ = "29/01/2019"
import collections
from copy import deepcopy
import logging
+import enum
import warnings
import weakref
+
import numpy
-from silx.third_party import six, enum
+import six
from ... import qt
from ... import colors
from ...colors import Colormap
+from silx import config
_logger = logging.getLogger(__name__)
@@ -82,6 +85,9 @@ class ItemChangedType(enum.Enum):
COLOR = 'colorChanged'
"""Item's color changed flag."""
+ LINE_BG_COLOR = 'lineBgColorChanged'
+ """Item's line background color changed flag."""
+
YAXIS = 'yAxisChanged'
"""Item's Y axis binding changed flag."""
@@ -411,10 +417,12 @@ class ColormapMixIn(ItemMixInBase):
return self._colormap
def setColormap(self, colormap):
- """Set the colormap of this image
+ """Set the colormap of this item
:param silx.gui.colors.Colormap colormap: colormap description
"""
+ if self._colormap is colormap:
+ return
if isinstance(colormap, dict):
colormap = Colormap._fromDict(colormap)
@@ -433,10 +441,10 @@ class ColormapMixIn(ItemMixInBase):
class SymbolMixIn(ItemMixInBase):
"""Mix-in class for items with symbol type"""
- _DEFAULT_SYMBOL = ''
+ _DEFAULT_SYMBOL = None
"""Default marker of the item"""
- _DEFAULT_SYMBOL_SIZE = 6.0
+ _DEFAULT_SYMBOL_SIZE = config.DEFAULT_PLOT_SYMBOL_SIZE
"""Default marker size of the item"""
_SUPPORTED_SYMBOLS = collections.OrderedDict((
@@ -451,8 +459,15 @@ class SymbolMixIn(ItemMixInBase):
"""Dict of supported symbols"""
def __init__(self):
- self._symbol = self._DEFAULT_SYMBOL
- self._symbol_size = self._DEFAULT_SYMBOL_SIZE
+ if self._DEFAULT_SYMBOL is None: # Use default from config
+ self._symbol = config.DEFAULT_PLOT_SYMBOL
+ else:
+ self._symbol = self._DEFAULT_SYMBOL
+
+ if self._DEFAULT_SYMBOL_SIZE is None: # Use default from config
+ self._symbol_size = config.DEFAULT_PLOT_SYMBOL_SIZE
+ else:
+ self._symbol_size = self._DEFAULT_SYMBOL_SIZE
@classmethod
def getSupportedSymbols(cls):
@@ -892,14 +907,14 @@ class Points(Item, SymbolMixIn, AlphaMixIn):
# use the getData class method because instance method can be
# overloaded to return additional arrays
data = Points.getData(self, copy=False,
- displayed=True)
+ displayed=True)
if len(data) == 5:
# hack to avoid duplicating caching mechanism in Scatter
# (happens when cached data is used, caching done using
# Scatter._logFilterData)
- x, y, xerror, yerror = data[0], data[1], data[3], data[4]
+ x, y, _xerror, _yerror = data[0], data[1], data[3], data[4]
else:
- x, y, xerror, yerror = data
+ x, y, _xerror, _yerror = data
self._boundsCache[(xPositive, yPositive)] = (
numpy.nanmin(x),
diff --git a/silx/gui/plot/items/curve.py b/silx/gui/plot/items/curve.py
index 80d9dea..79def55 100644
--- a/silx/gui/plot/items/curve.py
+++ b/silx/gui/plot/items/curve.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017-2018 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2019 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
@@ -31,9 +31,10 @@ __date__ = "24/04/2018"
import logging
+
import numpy
+import six
-from silx.third_party import six
from ....utils.deprecation import deprecated
from ... import colors
from .core import (Points, LabelsMixIn, ColorMixIn, YAxisMixIn,
diff --git a/silx/gui/plot/items/histogram.py b/silx/gui/plot/items/histogram.py
index 389e8a6..a1d6586 100644
--- a/silx/gui/plot/items/histogram.py
+++ b/silx/gui/plot/items/histogram.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2019 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
@@ -197,13 +197,13 @@ class Histogram(Item, AlphaMixIn, ColorMixIn, FillMixIn,
values[clipped_values] = numpy.nan
- if xPositive or yPositive:
+ if yPositive:
return (numpy.nanmin(edges),
numpy.nanmax(edges),
numpy.nanmin(values),
numpy.nanmax(values))
- else: # No log scale, include 0 in bounds
+ else: # No log scale on y axis, include 0 in bounds
return (numpy.nanmin(edges),
numpy.nanmax(edges),
min(0, numpy.nanmin(values)),
diff --git a/silx/gui/plot/items/roi.py b/silx/gui/plot/items/roi.py
index f55ef91..0169439 100644
--- a/silx/gui/plot/items/roi.py
+++ b/silx/gui/plot/items/roi.py
@@ -65,7 +65,7 @@ class RegionOfInterest(qt.QObject):
# Avoid circular dependancy
from ..tools import roi as roi_tools
assert parent is None or isinstance(parent, roi_tools.RegionOfInterestManager)
- super(RegionOfInterest, self).__init__(parent)
+ qt.QObject.__init__(self, parent)
self._color = rgba('red')
self._items = WeakList()
self._editAnchors = WeakList()
@@ -108,7 +108,7 @@ class RegionOfInterest(qt.QObject):
return qt.QColor.fromRgbF(*self._color)
def _getAnchorColor(self, color):
- """Returns the anchor color from the base ROI color
+ """Returns the anchor color from the base ROI color
:param Union[numpy.array,Tuple,List]: color
:rtype: Union[numpy.array,Tuple,List]
@@ -209,7 +209,7 @@ class RegionOfInterest(qt.QObject):
def setFirstShapePoints(self, points):
""""Initialize the ROI using the points from the first interaction.
- This interaction is constains by the plot API and only supports few
+ This interaction is constrained by the plot API and only supports few
shapes.
"""
points = self._createControlPointsFromFirstShape(points)
@@ -410,6 +410,13 @@ class RegionOfInterest(qt.QObject):
plot._remove(item)
self._labelItem = None
+ def _updated(self, event=None, checkVisibility=True):
+ """Implement Item mix-in update method by updating the plot items
+
+ See :class:`~silx.gui.plot.items.Item._updated`
+ """
+ self._createPlotItems()
+
def __str__(self):
"""Returns parameters of the ROI as a string."""
points = self._getControlPoints()
@@ -417,7 +424,7 @@ class RegionOfInterest(qt.QObject):
return "%s(%s)" % (self.__class__.__name__, params)
-class PointROI(RegionOfInterest):
+class PointROI(RegionOfInterest, items.SymbolMixIn):
"""A ROI identifying a point in a 2D plot."""
_kind = "Point"
@@ -426,6 +433,10 @@ class PointROI(RegionOfInterest):
_plotShape = "point"
"""Plot shape which is used for the first interaction"""
+ def __init__(self, parent=None):
+ items.SymbolMixIn.__init__(self)
+ RegionOfInterest.__init__(self, parent=parent)
+
def getPosition(self):
"""Returns the position of this ROI
@@ -458,6 +469,8 @@ class PointROI(RegionOfInterest):
marker.setPosition(points[0][0], points[0][1])
marker.setText(self.getLabel())
marker.setColor(rgba(self.getColor()))
+ marker.setSymbol(self.getSymbol())
+ marker.setSymbolSize(self.getSymbolSize())
marker._setDraggable(False)
return [marker]
@@ -466,6 +479,8 @@ class PointROI(RegionOfInterest):
marker.setPosition(points[0][0], points[0][1])
marker.setText(self.getLabel())
marker._setDraggable(self.isEditable())
+ marker.setSymbol(self.getSymbol())
+ marker.setSymbolSize(self.getSymbolSize())
return [marker]
def __str__(self):
@@ -474,7 +489,7 @@ class PointROI(RegionOfInterest):
return "%s(%s)" % (self.__class__.__name__, params)
-class LineROI(RegionOfInterest):
+class LineROI(RegionOfInterest, items.LineMixIn):
"""A ROI identifying a line in a 2D plot.
This ROI provides 1 anchor for each boundary of the line, plus an center
@@ -487,6 +502,10 @@ class LineROI(RegionOfInterest):
_plotShape = "line"
"""Plot shape which is used for the first interaction"""
+ def __init__(self, parent=None):
+ items.LineMixIn.__init__(self)
+ RegionOfInterest.__init__(self, parent=parent)
+
def _createControlPointsFromFirstShape(self, points):
center = numpy.mean(points, axis=0)
controlPoints = numpy.array([points[0], points[1], center])
@@ -535,6 +554,8 @@ class LineROI(RegionOfInterest):
item.setColor(rgba(self.getColor()))
item.setFill(False)
item.setOverlay(True)
+ item.setLineStyle(self.getLineStyle())
+ item.setLineWidth(self.getLineWidth())
return [item]
def _createAnchorItems(self, points):
@@ -582,7 +603,7 @@ class LineROI(RegionOfInterest):
return "%s(%s)" % (self.__class__.__name__, params)
-class HorizontalLineROI(RegionOfInterest):
+class HorizontalLineROI(RegionOfInterest, items.LineMixIn):
"""A ROI identifying an horizontal line in a 2D plot."""
_kind = "HLine"
@@ -591,6 +612,10 @@ class HorizontalLineROI(RegionOfInterest):
_plotShape = "hline"
"""Plot shape which is used for the first interaction"""
+ def __init__(self, parent=None):
+ items.LineMixIn.__init__(self)
+ RegionOfInterest.__init__(self, parent=parent)
+
def _createControlPointsFromFirstShape(self, points):
points = numpy.array([(float('nan'), points[0, 1])],
dtype=numpy.float64)
@@ -636,6 +661,8 @@ class HorizontalLineROI(RegionOfInterest):
marker.setText(self.getLabel())
marker.setColor(rgba(self.getColor()))
marker._setDraggable(False)
+ marker.setLineWidth(self.getLineWidth())
+ marker.setLineStyle(self.getLineStyle())
return [marker]
def _createAnchorItems(self, points):
@@ -643,6 +670,8 @@ class HorizontalLineROI(RegionOfInterest):
marker.setPosition(points[0][0], points[0][1])
marker.setText(self.getLabel())
marker._setDraggable(self.isEditable())
+ marker.setLineWidth(self.getLineWidth())
+ marker.setLineStyle(self.getLineStyle())
return [marker]
def __str__(self):
@@ -651,7 +680,7 @@ class HorizontalLineROI(RegionOfInterest):
return "%s(%s)" % (self.__class__.__name__, params)
-class VerticalLineROI(RegionOfInterest):
+class VerticalLineROI(RegionOfInterest, items.LineMixIn):
"""A ROI identifying a vertical line in a 2D plot."""
_kind = "VLine"
@@ -660,6 +689,10 @@ class VerticalLineROI(RegionOfInterest):
_plotShape = "vline"
"""Plot shape which is used for the first interaction"""
+ def __init__(self, parent=None):
+ items.LineMixIn.__init__(self)
+ RegionOfInterest.__init__(self, parent=parent)
+
def _createControlPointsFromFirstShape(self, points):
points = numpy.array([(points[0, 0], float('nan'))],
dtype=numpy.float64)
@@ -705,6 +738,8 @@ class VerticalLineROI(RegionOfInterest):
marker.setText(self.getLabel())
marker.setColor(rgba(self.getColor()))
marker._setDraggable(False)
+ marker.setLineWidth(self.getLineWidth())
+ marker.setLineStyle(self.getLineStyle())
return [marker]
def _createAnchorItems(self, points):
@@ -712,6 +747,8 @@ class VerticalLineROI(RegionOfInterest):
marker.setPosition(points[0][0], points[0][1])
marker.setText(self.getLabel())
marker._setDraggable(self.isEditable())
+ marker.setLineWidth(self.getLineWidth())
+ marker.setLineStyle(self.getLineStyle())
return [marker]
def __str__(self):
@@ -720,7 +757,7 @@ class VerticalLineROI(RegionOfInterest):
return "%s(%s)" % (self.__class__.__name__, params)
-class RectangleROI(RegionOfInterest):
+class RectangleROI(RegionOfInterest, items.LineMixIn):
"""A ROI identifying a rectangle in a 2D plot.
This ROI provides 1 anchor for each corner, plus an anchor in the
@@ -733,6 +770,10 @@ class RectangleROI(RegionOfInterest):
_plotShape = "rectangle"
"""Plot shape which is used for the first interaction"""
+ def __init__(self, parent=None):
+ items.LineMixIn.__init__(self)
+ RegionOfInterest.__init__(self, parent=parent)
+
def _createControlPointsFromFirstShape(self, points):
point0 = points[0]
point1 = points[1]
@@ -838,6 +879,8 @@ class RectangleROI(RegionOfInterest):
item.setColor(rgba(self.getColor()))
item.setFill(False)
item.setOverlay(True)
+ item.setLineStyle(self.getLineStyle())
+ item.setLineWidth(self.getLineWidth())
return [item]
def _createAnchorItems(self, points):
@@ -894,7 +937,7 @@ class RectangleROI(RegionOfInterest):
return "%s(%s)" % (self.__class__.__name__, params)
-class PolygonROI(RegionOfInterest):
+class PolygonROI(RegionOfInterest, items.LineMixIn):
"""A ROI identifying a closed polygon in a 2D plot.
This ROI provides 1 anchor for each point of the polygon.
@@ -906,6 +949,10 @@ class PolygonROI(RegionOfInterest):
_plotShape = "polygon"
"""Plot shape which is used for the first interaction"""
+ def __init__(self, parent=None):
+ items.LineMixIn.__init__(self)
+ RegionOfInterest.__init__(self, parent=parent)
+
def getPoints(self):
"""Returns the list of the points of this polygon.
@@ -948,6 +995,8 @@ class PolygonROI(RegionOfInterest):
item.setColor(rgba(self.getColor()))
item.setFill(False)
item.setOverlay(True)
+ item.setLineStyle(self.getLineStyle())
+ item.setLineWidth(self.getLineWidth())
return [item]
def _createAnchorItems(self, points):
@@ -967,7 +1016,7 @@ class PolygonROI(RegionOfInterest):
return "%s(%s)" % (self.__class__.__name__, params)
-class ArcROI(RegionOfInterest):
+class ArcROI(RegionOfInterest, items.LineMixIn):
"""A ROI identifying an arc of a circle with a width.
This ROI provides 3 anchors to control the curvature, 1 anchor to control
@@ -986,6 +1035,7 @@ class ArcROI(RegionOfInterest):
'startAngle', 'endAngle'])
def __init__(self, parent=None):
+ items.LineMixIn.__init__(self)
RegionOfInterest.__init__(self, parent=parent)
self._geometry = None
@@ -1357,6 +1407,8 @@ class ArcROI(RegionOfInterest):
item.setColor(rgba(self.getColor()))
item.setFill(False)
item.setOverlay(True)
+ item.setLineStyle(self.getLineStyle())
+ item.setLineWidth(self.getLineWidth())
return [item]
def _createAnchorItems(self, points):
diff --git a/silx/gui/plot/items/scatter.py b/silx/gui/plot/items/scatter.py
index acc74b4..707dd3d 100644
--- a/silx/gui/plot/items/scatter.py
+++ b/silx/gui/plot/items/scatter.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017-2018 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2019 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
@@ -46,9 +46,6 @@ class Scatter(Points, ColormapMixIn):
_DEFAULT_SELECTABLE = True
"""Default selectable state for scatter plots"""
- _DEFAULT_SYMBOL = 'o'
- """Default symbol of the scatter plots"""
-
def __init__(self):
Points.__init__(self)
ColormapMixIn.__init__(self)
diff --git a/silx/gui/plot/items/shape.py b/silx/gui/plot/items/shape.py
index 65b26a1..9fc1306 100644
--- a/silx/gui/plot/items/shape.py
+++ b/silx/gui/plot/items/shape.py
@@ -1,7 +1,7 @@
# coding: utf-8
# /*##########################################################################
#
-# Copyright (c) 2017 European Synchrotron Radiation Facility
+# Copyright (c) 2017-2019 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
@@ -27,14 +27,16 @@
__authors__ = ["T. Vincent"]
__license__ = "MIT"
-__date__ = "17/05/2017"
+__date__ = "21/12/2018"
import logging
import numpy
+import six
-from .core import (Item, ColorMixIn, FillMixIn, ItemChangedType)
+from ... import colors
+from .core import Item, ColorMixIn, FillMixIn, ItemChangedType, LineMixIn
_logger = logging.getLogger(__name__)
@@ -42,7 +44,7 @@ _logger = logging.getLogger(__name__)
# TODO probably make one class for each kind of shape
# TODO check fill:polygon/polyline + fill = duplicated
-class Shape(Item, ColorMixIn, FillMixIn):
+class Shape(Item, ColorMixIn, FillMixIn, LineMixIn):
"""Description of a shape item
:param str type_: The type of shape in:
@@ -53,10 +55,12 @@ class Shape(Item, ColorMixIn, FillMixIn):
Item.__init__(self)
ColorMixIn.__init__(self)
FillMixIn.__init__(self)
+ LineMixIn.__init__(self)
self._overlay = False
assert type_ in ('hline', 'polygon', 'rectangle', 'vline', 'polylines')
self._type = type_
self._points = ()
+ self._lineBgColor = None
self._handle = None
@@ -71,7 +75,10 @@ class Shape(Item, ColorMixIn, FillMixIn):
color=self.getColor(),
fill=self.isFill(),
overlay=self.isOverlay(),
- z=self.getZValue())
+ z=self.getZValue(),
+ linestyle=self.getLineStyle(),
+ linewidth=self.getLineWidth(),
+ linebgcolor=self.getLineBgColor())
def isOverlay(self):
"""Return true if shape is drawn as an overlay
@@ -119,3 +126,31 @@ class Shape(Item, ColorMixIn, FillMixIn):
"""
self._points = numpy.array(points, copy=copy)
self._updated(ItemChangedType.DATA)
+
+ def getLineBgColor(self):
+ """Returns the RGBA color of the item
+ :rtype: 4-tuple of float in [0, 1] or array of colors
+ """
+ return self._lineBgColor
+
+ def setLineBgColor(self, color, copy=True):
+ """Set item color
+ :param color: color(s) to be used
+ :type color: str ("#RRGGBB") or (npoints, 4) unsigned byte array or
+ one of the predefined color names defined in colors.py
+ :param bool copy: True (Default) to get a copy,
+ False to use internal representation (do not modify!)
+ """
+ if color is not None:
+ if isinstance(color, six.string_types):
+ color = colors.rgba(color)
+ else:
+ color = numpy.array(color, copy=copy)
+ # TODO more checks + improve color array support
+ if color.ndim == 1: # Single RGBA color
+ color = colors.rgba(color)
+ else: # Array of colors
+ assert color.ndim == 2
+
+ self._lineBgColor = color
+ self._updated(ItemChangedType.LINE_BG_COLOR)