From 270d5ddc31c26b62379e3caa9044dd75ccc71847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Picca=20Fr=C3=A9d=C3=A9ric-Emmanuel?= Date: Sun, 4 Mar 2018 10:20:27 +0100 Subject: New upstream version 0.7.0+dfsg --- silx/gui/plot/backends/BackendMatplotlib.py | 154 ++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 43 deletions(-) (limited to 'silx/gui/plot/backends/BackendMatplotlib.py') diff --git a/silx/gui/plot/backends/BackendMatplotlib.py b/silx/gui/plot/backends/BackendMatplotlib.py index b41f20e..f9a1fe5 100644 --- a/silx/gui/plot/backends/BackendMatplotlib.py +++ b/silx/gui/plot/backends/BackendMatplotlib.py @@ -1,7 +1,7 @@ # coding: utf-8 # /*########################################################################## # -# Copyright (c) 2004-2017 European Synchrotron Radiation Facility +# Copyright (c) 2004-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 @@ -58,6 +58,59 @@ from . import BackendBase from .._utils import FLOAT32_MINPOS +class _MarkerContainer(Container): + """Marker artists container supporting draw/remove and text position update + + :param artists: + Iterable with either one Line2D or a Line2D and a Text. + The use of an iterable if enforced by Container being + a subclass of tuple that defines a specific __new__. + :param x: X coordinate of the marker (None for horizontal lines) + :param y: Y coordinate of the marker (None for vertical lines) + """ + + def __init__(self, artists, x, y): + self.line = artists[0] + self.text = artists[1] if len(artists) > 1 else None + self.x = x + self.y = y + + Container.__init__(self, artists) + + def draw(self, *args, **kwargs): + """artist-like draw to broadcast draw to line and text""" + self.line.draw(*args, **kwargs) + if self.text is not None: + self.text.draw(*args, **kwargs) + + def updateMarkerText(self, xmin, xmax, ymin, ymax): + """Update marker text position and visibility according to plot limits + + :param xmin: X axis lower limit + :param xmax: X axis upper limit + :param ymin: Y axis lower limit + :param ymax: Y axis upprt limit + """ + if self.text is not None: + visible = ((self.x is None or xmin <= self.x <= xmax) and + (self.y is None or ymin <= self.y <= ymax)) + self.text.set_visible(visible) + + if self.x is not None and self.y is None: # vertical line + delta = abs(ymax - ymin) + if ymin > ymax: + ymax = ymin + ymax -= 0.005 * delta + self.text.set_y(ymax) + + if self.x is None and self.y is not None: # Horizontal line + delta = abs(xmax - xmin) + if xmin > xmax: + xmax = xmin + xmax -= 0.005 * delta + self.text.set_x(xmax) + + class BackendMatplotlib(BackendBase.BackendBase): """Base class for Matplotlib backend without a FigureCanvas. @@ -356,10 +409,13 @@ class BackendMatplotlib(BackendBase.BackendBase): self.ax.add_patch(item) elif shape in ('polygon', 'polylines'): - xView = xView.reshape(1, -1) - yView = yView.reshape(1, -1) - item = Polygon(numpy.vstack((xView, yView)).T, - closed=(shape == 'polygon'), + points = numpy.array((xView, yView)).T + if shape == 'polygon': + closed = True + else: # shape == 'polylines' + closed = numpy.all(numpy.equal(points[0], points[-1])) + item = Polygon(points, + closed=closed, fill=False, label=legend, color=color) @@ -381,9 +437,14 @@ class BackendMatplotlib(BackendBase.BackendBase): def addMarker(self, x, y, legend, text, color, selectable, draggable, - symbol, constraint, overlay): + symbol, constraint): legend = "__MARKER__" + legend + textArtist = None + + xmin, xmax = self.getGraphXLimits() + ymin, ymax = self.getGraphYLimits(axis='left') + if x is not None and y is not None: line = self.ax.plot(x, y, label=legend, linestyle=" ", @@ -392,49 +453,35 @@ class BackendMatplotlib(BackendBase.BackendBase): markersize=10.)[-1] if text is not None: - xtmp, ytmp = self.ax.transData.transform_point((x, y)) - inv = self.ax.transData.inverted() - xtmp, ytmp = inv.transform_point((xtmp, ytmp)) - if symbol is None: valign = 'baseline' else: valign = 'top' text = " " + text - line._infoText = self.ax.text(x, ytmp, text, - color=color, - horizontalalignment='left', - verticalalignment=valign) + textArtist = self.ax.text(x, y, text, + color=color, + horizontalalignment='left', + verticalalignment=valign) elif x is not None: line = self.ax.axvline(x, label=legend, color=color) if text is not None: - text = " " + text - ymin, ymax = self.getGraphYLimits(axis='left') - delta = abs(ymax - ymin) - if ymin > ymax: - ymax = ymin - ymax -= 0.005 * delta - line._infoText = self.ax.text(x, ymax, text, - color=color, - horizontalalignment='left', - verticalalignment='top') + # Y position will be updated in updateMarkerText call + textArtist = self.ax.text(x, 1., " " + text, + color=color, + horizontalalignment='left', + verticalalignment='top') elif y is not None: line = self.ax.axhline(y, label=legend, color=color) if text is not None: - text = " " + text - xmin, xmax = self.getGraphXLimits() - delta = abs(xmax - xmin) - if xmin > xmax: - xmax = xmin - xmax -= 0.005 * delta - line._infoText = self.ax.text(xmax, y, text, - color=color, - horizontalalignment='right', - verticalalignment='top') + # X position will be updated in updateMarkerText call + textArtist = self.ax.text(1., y, " " + text, + color=color, + horizontalalignment='right', + verticalalignment='top') else: raise RuntimeError('A marker must at least have one coordinate') @@ -442,19 +489,29 @@ class BackendMatplotlib(BackendBase.BackendBase): if selectable or draggable: line.set_picker(5) - if overlay: - line.set_animated(True) - self._overlays.add(line) + # All markers are overlays + line.set_animated(True) + if textArtist is not None: + textArtist.set_animated(True) + + artists = [line] if textArtist is None else [line, textArtist] + container = _MarkerContainer(artists, x, y) + container.updateMarkerText(xmin, xmax, ymin, ymax) + self._overlays.add(container) - return line + return container + + def _updateMarkers(self): + xmin, xmax = self.ax.get_xbound() + ymin, ymax = self.ax.get_ybound() + for item in self._overlays: + if isinstance(item, _MarkerContainer): + item.updateMarkerText(xmin, xmax, ymin, ymax) # Remove methods def remove(self, item): # Warning: It also needs to remove extra stuff if added as for markers - if hasattr(item, "_infoText"): # For markers text - item._infoText.remove() - item._infoText = None self._overlays.discard(item) try: item.remove() @@ -562,6 +619,8 @@ class BackendMatplotlib(BackendBase.BackendBase): else: self.ax.set_ylim(max(ymin, ymax), min(ymin, ymax)) + self._updateMarkers() + def getGraphXLimits(self): if self._dirtyLimits and self.isKeepDataAspectRatio(): self.replot() # makes sure we get the right limits @@ -570,6 +629,7 @@ class BackendMatplotlib(BackendBase.BackendBase): def setGraphXLimits(self, xmin, xmax): self._dirtyLimits = True self.ax.set_xlim(min(xmin, xmax), max(xmin, xmax)) + self._updateMarkers() def getGraphYLimits(self, axis): assert axis in ('left', 'right') @@ -607,6 +667,8 @@ class BackendMatplotlib(BackendBase.BackendBase): else: ax.set_ylim(ymax, ymin) + self._updateMarkers() + # Graph axes def setXAxisLogarithmic(self, flag): @@ -814,7 +876,7 @@ class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib): self._picked.append({'kind': 'curve', 'legend': label, 'indices': event.ind}) - def pickItems(self, x, y): + def pickItems(self, x, y, kinds): self._picked = [] # Weird way to do an explicit picking: Simulate a button press event @@ -822,7 +884,8 @@ class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib): cid = self.mpl_connect('pick_event', self._onPick) self.fig.pick(mouseEvent) self.mpl_disconnect(cid) - picked = self._picked + + picked = [p for p in self._picked if p['kind'] in kinds] self._picked = None return picked @@ -882,6 +945,10 @@ class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib): xLimits, yLimits, yRightLimits = self._limitsBeforeResize self._limitsBeforeResize = None + if (xLimits != self.ax.get_xbound() or + yLimits != self.ax.get_ybound()): + self._updateMarkers() + if xLimits != self.ax.get_xbound(): self._plot.getXAxis()._emitLimitsChanged() if yLimits != self.ax.get_ybound(): @@ -889,6 +956,7 @@ class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib): if yRightLimits != self.ax2.get_ybound(): self._plot.getYAxis(axis='right')._emitLimitsChanged() + self._drawOverlays() def replot(self): -- cgit v1.2.3