diff options
Diffstat (limited to 'silx/gui/plot/PlotWidget.py')
-rwxr-xr-x[-rw-r--r--] | silx/gui/plot/PlotWidget.py | 180 |
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`. """ |