summaryrefslogtreecommitdiff
path: root/silx/gui/plot/PlotWidget.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/PlotWidget.py')
-rwxr-xr-x[-rw-r--r--]silx/gui/plot/PlotWidget.py180
1 files changed, 78 insertions, 102 deletions
diff --git a/silx/gui/plot/PlotWidget.py b/silx/gui/plot/PlotWidget.py
index 9b9b4d2..e47249e 100644..100755
--- a/silx/gui/plot/PlotWidget.py
+++ b/silx/gui/plot/PlotWidget.py
@@ -39,10 +39,6 @@ _logger = logging.getLogger(__name__)
from collections import OrderedDict, namedtuple
-try:
- from collections import abc
-except ImportError: # Python2 support
- import collections as abc
from contextlib import contextmanager
import datetime as dt
import itertools
@@ -60,6 +56,7 @@ try:
except ImportError:
_logger.debug("matplotlib not available")
+import six
from ..colors import Colormap
from .. import colors
from . import PlotInteraction
@@ -311,7 +308,7 @@ class PlotWidget(qt.QMainWindow):
if callable(backend):
return backend
- elif isinstance(backend, str):
+ elif isinstance(backend, six.string_types):
backend = backend.lower()
if backend in ('matplotlib', 'mpl'):
try:
@@ -337,7 +334,7 @@ class PlotWidget(qt.QMainWindow):
return backendClass
- elif isinstance(backend, abc.Iterable):
+ elif isinstance(backend, (tuple, list)):
for b in backend:
try:
return self.__getBackendClass(b)
@@ -606,7 +603,7 @@ class PlotWidget(qt.QMainWindow):
elif isinstance(item, (items.Marker,
items.XMarker, items.YMarker)):
kind = 'marker'
- elif isinstance(item, items.Shape):
+ elif isinstance(item, (items.Shape, items.BoundingRect)):
kind = 'item'
elif isinstance(item, items.Histogram):
kind = 'histogram'
@@ -627,8 +624,7 @@ class PlotWidget(qt.QMainWindow):
# Add item to plot
self._content[key] = item
item._setPlot(self)
- if item.isVisible():
- self._itemRequiresUpdate(item)
+ self._itemRequiresUpdate(item)
if isinstance(item, items.DATA_ITEMS):
self._invalidateDataRange() # TODO handle this automatically
@@ -710,7 +706,8 @@ class PlotWidget(qt.QMainWindow):
xlabel=None, ylabel=None, yaxis=None,
xerror=None, yerror=None, z=None, selectable=None,
fill=None, resetzoom=True,
- histogram=None, copy=True):
+ histogram=None, copy=True,
+ baseline=None):
"""Add a 1D curve given by x an y to the graph.
Curves are uniquely identified by their legend.
@@ -791,6 +788,8 @@ class PlotWidget(qt.QMainWindow):
- 'center'
:param bool copy: True make a copy of the data (default),
False to use provided arrays.
+ :param baseline: curve baseline
+ :type: Union[None,float,numpy.ndarray]
:returns: The key string identify this curve
"""
# This is an histogram, use addHistogram
@@ -845,6 +844,7 @@ class PlotWidget(qt.QMainWindow):
curve.setColor(default_color)
curve.setLineStyle(default_linestyle)
curve.setSymbol(self._defaultPlotPoints)
+ curve._setBaseline(baseline=baseline)
# Do not emit sigActiveCurveChanged,
# it will be sent once with _setActiveItem
@@ -887,7 +887,7 @@ class PlotWidget(qt.QMainWindow):
if len(x) > 0 and isinstance(x[0], dt.datetime):
x = [timestamp(d) for d in x]
- curve.setData(x, y, xerror, yerror, copy=copy)
+ curve.setData(x, y, xerror, yerror, baseline=baseline, copy=copy)
if replace: # Then remove all other curves
for c in self.getAllCurves(withhidden=True):
@@ -924,7 +924,9 @@ class PlotWidget(qt.QMainWindow):
fill=None,
align='center',
resetzoom=True,
- copy=True):
+ copy=True,
+ z=None,
+ baseline=None):
"""Add an histogram to the graph.
This is NOT computing the histogram, this method takes as parameter
@@ -955,6 +957,9 @@ class PlotWidget(qt.QMainWindow):
:param bool resetzoom: True (the default) to reset the zoom.
:param bool copy: True make a copy of the data (default),
False to use provided arrays.
+ :param int z: Layer on which to draw the histogram
+ :param baseline: histogram baseline
+ :type: Union[None,float,numpy.ndarray]
:returns: The key string identify this histogram
"""
legend = 'Unnamed histogram' if legend is None else str(legend)
@@ -974,9 +979,12 @@ class PlotWidget(qt.QMainWindow):
histo.setColor(color)
if fill is not None:
histo.setFill(fill)
+ if z is not None:
+ histo.setZValue(z=z)
# Set histogram data
- histo.setData(histogram, edges, align=align, copy=copy)
+ histo.setData(histogram=histogram, edges=edges, baseline=baseline,
+ align=align, copy=copy)
if mustBeAdded:
self._add(histo)
@@ -1307,7 +1315,8 @@ class PlotWidget(qt.QMainWindow):
color=None,
selectable=False,
draggable=False,
- constraint=None):
+ constraint=None,
+ yaxis='left'):
"""Add a vertical line marker to the plot.
Markers are uniquely identified by their legend.
@@ -1333,12 +1342,14 @@ class PlotWidget(qt.QMainWindow):
:type constraint: None or a callable that takes the coordinates of
the current cursor position in the plot as input
and that returns the filtered coordinates.
+ :param str yaxis: The Y axis this marker belongs to in: 'left', 'right'
:return: The key string identify this marker
"""
return self._addMarker(x=x, y=None, legend=legend,
text=text, color=color,
selectable=selectable, draggable=draggable,
- symbol=None, constraint=constraint)
+ symbol=None, constraint=constraint,
+ yaxis=yaxis)
def addYMarker(self, y,
legend=None,
@@ -1346,7 +1357,8 @@ class PlotWidget(qt.QMainWindow):
color=None,
selectable=False,
draggable=False,
- constraint=None):
+ constraint=None,
+ yaxis='left'):
"""Add a horizontal line marker to the plot.
Markers are uniquely identified by their legend.
@@ -1372,12 +1384,14 @@ class PlotWidget(qt.QMainWindow):
:type constraint: None or a callable that takes the coordinates of
the current cursor position in the plot as input
and that returns the filtered coordinates.
+ :param str yaxis: The Y axis this marker belongs to in: 'left', 'right'
:return: The key string identify this marker
"""
return self._addMarker(x=None, y=y, legend=legend,
text=text, color=color,
selectable=selectable, draggable=draggable,
- symbol=None, constraint=constraint)
+ symbol=None, constraint=constraint,
+ yaxis=yaxis)
def addMarker(self, x, y, legend=None,
text=None,
@@ -1385,7 +1399,8 @@ class PlotWidget(qt.QMainWindow):
selectable=False,
draggable=False,
symbol='+',
- constraint=None):
+ constraint=None,
+ yaxis='left'):
"""Add a point marker to the plot.
Markers are uniquely identified by their legend.
@@ -1423,6 +1438,7 @@ class PlotWidget(qt.QMainWindow):
:type constraint: None or a callable that takes the coordinates of
the current cursor position in the plot as input
and that returns the filtered coordinates.
+ :param str yaxis: The Y axis this marker belongs to in: 'left', 'right'
:return: The key string identify this marker
"""
if x is None:
@@ -1436,12 +1452,14 @@ class PlotWidget(qt.QMainWindow):
return self._addMarker(x=x, y=y, legend=legend,
text=text, color=color,
selectable=selectable, draggable=draggable,
- symbol=symbol, constraint=constraint)
+ symbol=symbol, constraint=constraint,
+ yaxis=yaxis)
def _addMarker(self, x, y, legend,
text, color,
selectable, draggable,
- symbol, constraint):
+ symbol, constraint,
+ yaxis=None):
"""Common method for adding point, vline and hline marker.
See :meth:`addMarker` for argument documentation.
@@ -1487,6 +1505,7 @@ class PlotWidget(qt.QMainWindow):
marker._setDraggable(draggable)
if symbol is not None:
marker.setSymbol(symbol)
+ marker.setYAxis(yaxis)
# TODO to improve, but this ensure constraint is applied
marker.setPosition(x, y)
@@ -2692,6 +2711,7 @@ class PlotWidget(qt.QMainWindow):
"""Redraw the plot immediately."""
for item in self._contentToUpdate:
item._update(self._backend)
+
self._contentToUpdate = []
self._backend.replot()
self._dirty = False # reset dirty flag
@@ -2862,7 +2882,18 @@ class PlotWidget(qt.QMainWindow):
:rtype: A tuple of 2 floats: (xData, yData) or None.
"""
assert axis in ("left", "right")
- return self._backend.pixelToData(x, y, axis=axis, check=check)
+
+ if x is None:
+ x = self.width() // 2
+ if y is None:
+ y = self.height() // 2
+
+ if check:
+ left, top, width, height = self.getPlotBoundsInPixels()
+ if not (left <= x <= left + width and top <= y <= top + height):
+ return None
+
+ return self._backend.pixelToData(x, y, axis)
def getPlotBoundsInPixels(self):
"""Plot area bounds in widget coordinates in pixels.
@@ -2880,29 +2911,6 @@ class PlotWidget(qt.QMainWindow):
"""
self._backend.setGraphCursorShape(cursor)
- def _pickMarker(self, x, y, test=None):
- """Pick a marker at the given position.
-
- To use for interaction implementation.
-
- :param float x: X position in pixels.
- :param float y: Y position in pixels.
- :param test: A callable to call for each picked marker to filter
- picked markers. If None (default), do not filter markers.
- """
- if test is None:
- def test(mark):
- return True
-
- markers = self._backend.pickItems(x, y, kinds=('marker',))
- legends = [m['legend'] for m in markers if m['kind'] == 'marker']
-
- for legend in reversed(legends):
- marker = self._getMarker(legend)
- if marker is not None and test(marker):
- return marker
- return None
-
def _getAllMarkers(self, just_legend=False):
"""Returns all markers' legend or objects
@@ -2924,73 +2932,41 @@ class PlotWidget(qt.QMainWindow):
"""
return self._getItem(kind='marker', legend=legend)
- def _pickImageOrCurve(self, x, y, test=None):
- """Pick an image or a curve at the given position.
+ def pickItems(self, x, y, condition=None):
+ """Generator of picked items in the plot at given position.
- To use for interaction implementation.
+ Items are returned from front to back.
:param float x: X position in pixels
:param float y: Y position in pixels
- :param test: A callable to call for each picked item to filter
- picked items. If None (default), do not filter items.
+ :param callable condition:
+ Callable taking an item as input and returning False for items
+ to skip during picking. If None (default) no item is skipped.
+ :return: Iterable of :class:`PickingResult` objects at picked position.
+ Items are ordered from front to back.
"""
- if test is None:
- def test(i):
- return True
+ for item in reversed(self._backend.getItemsFromBackToFront(condition=condition)):
+ result = item.pick(x, y)
+ if result is not None:
+ yield result
- allItems = self._backend.pickItems(x, y, kinds=('curve', 'image'))
- allItems = [item for item in allItems
- if item['kind'] in ['curve', 'image']]
+ def _pickTopMost(self, x, y, condition=None):
+ """Returns top-most picked item in the plot at given position.
- for item in reversed(allItems):
- kind, legend = item['kind'], item['legend']
- if kind == 'curve':
- curve = self.getCurve(legend)
- if curve is not None and test(curve):
- return kind, curve, item['indices']
-
- elif kind == 'image':
- image = self.getImage(legend)
- if image is not None and test(image):
- return kind, image, None
-
- else:
- _logger.warning('Unsupported kind: %s', kind)
-
- return None
-
- def _pick(self, x, y):
- """Pick items in the plot at given position.
+ Items are checked from front to back.
:param float x: X position in pixels
:param float y: Y position in pixels
- :return: Iterable of (plot item, indices) at picked position.
- Items are ordered from back to front.
- """
- items = []
-
- # Convert backend result to plot items
- for itemInfo in self._backend.pickItems(
- x, y, kinds=('marker', 'curve', 'image')):
- kind, legend = itemInfo['kind'], itemInfo['legend']
-
- if kind in ('marker', 'image'):
- item = self._getItem(kind=kind, legend=legend)
- indices = None # TODO compute indices for images
-
- else: # backend kind == 'curve'
- for kind in ('curve', 'histogram', 'scatter'):
- item = self._getItem(kind=kind, legend=legend)
- if item is not None:
- indices = itemInfo['indices']
- break
- else:
- _logger.error(
- 'Cannot find corresponding picked item')
- continue
- items.append((item, indices))
-
- return tuple(items)
+ :param callable condition:
+ Callable taking an item as input and returning False for items
+ to skip during picking. If None (default) no item is skipped.
+ :return: :class:`PickingResult` object at picked position.
+ If no item is picked, it returns None
+ :rtype: Union[None,PickingResult]
+ """
+ for result in self.pickItems(x, y, condition):
+ return result
+ return None
# User event handling #
@@ -3123,7 +3099,7 @@ class PlotWidget(qt.QMainWindow):
# Panning with arrow keys
def isPanWithArrowKeys(self):
- """Returns whether or not panning the graph with arrow keys is enable.
+ """Returns whether or not panning the graph with arrow keys is enabled.
See :meth:`setPanWithArrowKeys`.
"""