Repository: silx-kit/silx
Issue: picking issue with matplotlib backend

They are regression in the plot picking with the matplotlib backend with triangles and images.
The OpenGL backend looks to work fine.

Changed files:
silx/gui/plot/backends/BackendBase.py, silx/gui/plot/backends/BackendMatplotlib.py, silx/gui/plot/backends/BackendOpenGL.py, silx/gui/plot/items/curve.py, silx/gui/plot/items/complex.py, silx/gui/plot/items/histogram.py, silx/gui/plot/items/marker.py, silx/gui/plot/items/image.py, silx/gui/plot/items/scatter.py

diff --git a/silx/gui/plot/backends/BackendBase.py b/silx/gui/plot/backends/BackendBase.py
index bfce7749a..75d999b37 100755
--- a/silx/gui/plot/backends/BackendBase.py
+++ b/silx/gui/plot/backends/BackendBase.py
@@ -100,7 +100,7 @@ class BackendBase(object):
     def addCurve(self, x, y,
                  color, symbol, linewidth, linestyle,
                  yaxis,
-                 xerror, yerror, z, selectable,
+                 xerror, yerror, z,
                  fill, alpha, symbolsize, baseline):
         """Add a 1D curve given by x an y to the graph.
 
@@ -135,7 +135,6 @@ class BackendBase(object):
         :param yerror: Values with the uncertainties on the y values
         :type yerror: numpy.ndarray or None
         :param int z: Layer on which to draw the cuve
-        :param bool selectable: indicate if the curve can be selected
         :param bool fill: True to fill the curve, False otherwise
         :param float alpha: Curve opacity, as a float in [0., 1.]
         :param float symbolsize: Size of the symbol (if any) drawn
@@ -146,7 +145,6 @@ class BackendBase(object):
 
     def addImage(self, data,
                  origin, scale, z,
-                 selectable, draggable,
                  colormap, alpha):
         """Add an image to the plot.
 
@@ -159,8 +157,6 @@ class BackendBase(object):
                        Default: (1., 1.)
         :type scale: 2-tuple of float
         :param int z: Layer on which to draw the image
-        :param bool selectable: indicate if the image can be selected
-        :param bool draggable: indicate if the image can be moved
         :param ~silx.gui.colors.Colormap colormap: Colormap object to use.
             Ignored if data is RGB(A).
         :param float alpha: Opacity of the image, as a float in range [0, 1].
@@ -169,7 +165,7 @@ class BackendBase(object):
         return object()
 
     def addTriangles(self, x, y, triangles,
-                     color, z, selectable, alpha):
+                     color, z, alpha):
         """Add a set of triangles.
 
         :param numpy.ndarray x: The data corresponding to the x axis
@@ -178,7 +174,6 @@ class BackendBase(object):
             as a (Ntriangle, 3) array
         :param numpy.ndarray color: color(s) as (npoints, 4) array
         :param int z: Layer on which to draw the cuve
-        :param bool selectable: indicate if the curve can be selected
         :param float alpha: Opacity as a float in [0., 1.]
         :returns: The triangles' unique identifier used by the backend
         """
@@ -214,7 +209,6 @@ class BackendBase(object):
         return object()
 
     def addMarker(self, x, y, text, color,
-                  selectable, draggable,
                   symbol, linestyle, linewidth, constraint, yaxis):
         """Add a point, vertical line or horizontal line marker to the plot.
 
@@ -224,8 +218,6 @@ class BackendBase(object):
                         If None, the marker is a vertical line.
         :param str text: Text associated to the marker (or None for no text)
         :param str color: Color to be used for instance 'blue', 'b', '#FF0000'
-        :param bool selectable: indicate if the marker can be selected
-        :param bool draggable: indicate if the marker can be moved
         :param str symbol: Symbol representing the marker.
             Only relevant for point markers where X and Y are not None.
             Value in:
@@ -252,7 +244,6 @@ class BackendBase(object):
                            dragging operations or None for no filter.
                            This function is called each time a marker is
                            moved.
-                           This parameter is only used if draggable is True.
         :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.
diff --git a/silx/gui/plot/backends/BackendMatplotlib.py b/silx/gui/plot/backends/BackendMatplotlib.py
index b3b1cfe3f..075f6aa7b 100755
--- a/silx/gui/plot/backends/BackendMatplotlib.py
+++ b/silx/gui/plot/backends/BackendMatplotlib.py
@@ -213,6 +213,14 @@ class _PickableContainer(Container):
         Container.__init__(self, *args, **kwargs)
         self.__zorder = None
 
+    @property
+    def axes(self):
+        """Mimin Artist.axes"""
+        for child in self.get_children():
+            if hasattr(child, 'axes'):
+                return child.axes
+        return None
+
     def draw(self, *args, **kwargs):
         """artist-like draw to broadcast draw to children"""
         for child in self.get_children():
@@ -477,10 +485,10 @@ class BackendMatplotlib(BackendBase.BackendBase):
     def addCurve(self, x, y,
                  color, symbol, linewidth, linestyle,
                  yaxis,
-                 xerror, yerror, z, selectable,
+                 xerror, yerror, z,
                  fill, alpha, symbolsize, baseline):
         for parameter in (x, y, color, symbol, linewidth, linestyle,
-                          yaxis, z, selectable, fill, alpha, symbolsize):
+                          yaxis, z, fill, alpha, symbolsize):
             assert parameter is not None
         assert yaxis in ('left', 'right')
 
@@ -494,7 +502,7 @@ class BackendMatplotlib(BackendBase.BackendBase):
         else:
             axes = self.ax
 
-        picker = 3 if selectable else None
+        picker = 3
 
         artists = []  # All the artists composing the curve
 
@@ -576,24 +584,22 @@ class BackendMatplotlib(BackendBase.BackendBase):
 
         return _PickableContainer(artists)
 
-    def addImage(self, data, origin, scale, z, selectable, draggable, colormap, alpha):
+    def addImage(self, data, origin, scale, z, colormap, alpha):
         # Non-uniform image
         # http://wiki.scipy.org/Cookbook/Histograms
         # Non-linear axes
         # http://stackoverflow.com/questions/11488800/non-linear-axes-for-imshow-in-matplotlib
-        for parameter in (data, origin, scale, z, selectable, draggable):
+        for parameter in (data, origin, scale, z):
             assert parameter is not None
 
         origin = float(origin[0]), float(origin[1])
         scale = float(scale[0]), float(scale[1])
         height, width = data.shape[0:2]
 
-        picker = (selectable or draggable)
-
         # All image are shown as RGBA image
         image = Image(self.ax,
                       interpolation='nearest',
-                      picker=picker,
+                      picker=True,
                       origin='lower')
 
         if alpha < 1:
@@ -626,13 +632,10 @@ class BackendMatplotlib(BackendBase.BackendBase):
         self.ax.add_artist(image)
         return image
 
-    def addTriangles(self, x, y, triangles, color, z, selectable, alpha):
-        for parameter in (x, y, triangles, color, z, selectable, alpha):
+    def addTriangles(self, x, y, triangles, color, z, alpha):
+        for parameter in (x, y, triangles, color, z, alpha):
             assert parameter is not None
 
-        # 0 enables picking on filled triangle
-        picker = 0 if selectable else None
-
         color = numpy.array(color, copy=False)
         assert color.ndim == 2 and len(color) == len(x)
 
@@ -642,7 +645,7 @@ class BackendMatplotlib(BackendBase.BackendBase):
         collection = TriMesh(
             Triangulation(x, y, triangles),
             alpha=alpha,
-            picker=picker)
+            picker=0)  # 0 enables picking on filled triangle
         collection.set_color(color)
         self.ax.add_collection(collection)
 
@@ -730,7 +733,6 @@ class BackendMatplotlib(BackendBase.BackendBase):
         return item
 
     def addMarker(self, x, y, text, color,
-                  selectable, draggable,
                   symbol, linestyle, linewidth, constraint, yaxis):
         textArtist = None
 
@@ -792,8 +794,7 @@ class BackendMatplotlib(BackendBase.BackendBase):
         else:
             raise RuntimeError('A marker must at least have one coordinate')
 
-        if selectable or draggable:
-            line.set_picker(5)
+        line.set_picker(5)
 
         # All markers are overlays
         line.set_animated(True)
@@ -1295,8 +1296,9 @@ class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib):
     # picking
 
     def pickItem(self, x, y, item):
-        y = self._mplQtYAxisCoordConversion(y)
-        mouseEvent = MouseEvent('button_press_event', self, x, y)
+        mouseEvent = MouseEvent(
+            'button_press_event', self, x, self._mplQtYAxisCoordConversion(y))
+        mouseEvent.inaxes = item.axes
         picked, info = item.contains(mouseEvent)
 
         if not picked:
@@ -1311,8 +1313,9 @@ class BackendMatplotlibQt(FigureCanvasQTAgg, BackendMatplotlib):
             # from furthest to closest to put closest point last
             # This is to be somewhat consistent with last scatter point
             # being the top one.
-            dists = ((triangulation.x[indices] - x) ** 2 +
-                     (triangulation.y[indices] - y) ** 2)
+            xdata, ydata = self.pixelToData(x, y, axis='left')
+            dists = ((triangulation.x[indices] - xdata) ** 2 +
+                     (triangulation.y[indices] - ydata) ** 2)
             return indices[numpy.flip(numpy.argsort(dists), axis=0)]
 
         else:  # Returns indices if any
diff --git a/silx/gui/plot/backends/BackendOpenGL.py b/silx/gui/plot/backends/BackendOpenGL.py
index bb90a866d..27f3894dd 100755
--- a/silx/gui/plot/backends/BackendOpenGL.py
+++ b/silx/gui/plot/backends/BackendOpenGL.py
@@ -96,7 +96,6 @@ class _ShapeItem(dict):
 
 class _MarkerItem(dict):
     def __init__(self, x, y, text, color,
-                 selectable, draggable,
                  symbol, linestyle, linewidth, constraint, yaxis):
         super(_MarkerItem, self).__init__()
 
@@ -104,7 +103,7 @@ class _MarkerItem(dict):
             symbol = '+'
 
         # Apply constraint to provided position
-        isConstraint = (draggable and constraint is not None and
+        isConstraint = (constraint is not None and
                         x is not None and y is not None)
         if isConstraint:
             x, y = constraint(x, y)
@@ -727,10 +726,10 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget):
     def addCurve(self, x, y,
                  color, symbol, linewidth, linestyle,
                  yaxis,
-                 xerror, yerror, z, selectable,
+                 xerror, yerror, z,
                  fill, alpha, symbolsize, baseline):
         for parameter in (x, y, color, symbol, linewidth, linestyle,
-                          yaxis, z, selectable, fill, symbolsize):
+                          yaxis, z, fill, symbolsize):
             assert parameter is not None
         assert yaxis in ('left', 'right')
 
@@ -848,10 +847,8 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget):
 
     def addImage(self, data,
                  origin, scale, z,
-                 selectable, draggable,
                  colormap, alpha):
-        for parameter in (data, origin, scale, z,
-                          selectable, draggable):
+        for parameter in (data, origin, scale, z):
             assert parameter is not None
 
         if data.ndim == 2:
@@ -902,7 +899,7 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget):
         return image
 
     def addTriangles(self, x, y, triangles,
-                     color, z, selectable, alpha):
+                     color, z, alpha):
         # Handle axes log scale: convert data
         if self._plotFrame.xAxis.isLog:
             x = numpy.log10(x)
@@ -930,10 +927,8 @@ class BackendOpenGL(BackendBase.BackendBase, glu.OpenGLWidget):
                           linestyle, linewidth, linebgcolor)
 
     def addMarker(self, x, y, text, color,
-                  selectable, draggable,
                   symbol, linestyle, linewidth, constraint, yaxis):
         return _MarkerItem(x, y, text, color,
-                           selectable, draggable,
                            symbol, linestyle, linewidth, constraint, yaxis)
 
     # Remove methods
diff --git a/silx/gui/plot/items/complex.py b/silx/gui/plot/items/complex.py
index aadeeca0e..722669553 100644
--- a/silx/gui/plot/items/complex.py
+++ b/silx/gui/plot/items/complex.py
@@ -164,8 +164,6 @@ class ImageComplexData(ImageBase, ColormapMixIn, ComplexMixIn):
                                 origin=self.getOrigin(),
                                 scale=self.getScale(),
                                 z=self.getZValue(),
-                                selectable=self.isSelectable(),
-                                draggable=self.isDraggable(),
                                 colormap=colormap,
                                 alpha=self.getAlpha())
 
diff --git a/silx/gui/plot/items/curve.py b/silx/gui/plot/items/curve.py
index 87ab5e391..5853ef58c 100644
--- a/silx/gui/plot/items/curve.py
+++ b/silx/gui/plot/items/curve.py
@@ -215,7 +215,6 @@ class Curve(PointsBase, ColorMixIn, YAxisMixIn, FillMixIn, LabelsMixIn,
                                 xerror=xerror,
                                 yerror=yerror,
                                 z=self.getZValue(),
-                                selectable=self.isSelectable(),
                                 fill=self.isFill(),
                                 alpha=self.getAlpha(),
                                 symbolsize=style.getSymbolSize(),
diff --git a/silx/gui/plot/items/histogram.py b/silx/gui/plot/items/histogram.py
index 95c3d530b..993c0f01b 100644
--- a/silx/gui/plot/items/histogram.py
+++ b/silx/gui/plot/items/histogram.py
@@ -171,7 +171,6 @@ class Histogram(Item, AlphaMixIn, ColorMixIn, FillMixIn,
                                 xerror=None,
                                 yerror=None,
                                 z=self.getZValue(),
-                                selectable=self.isSelectable(),
                                 fill=self.isFill(),
                                 alpha=self.getAlpha(),
                                 baseline=baseline,
diff --git a/silx/gui/plot/items/image.py b/silx/gui/plot/items/image.py
index 60a914f49..44cb70feb 100644
--- a/silx/gui/plot/items/image.py
+++ b/silx/gui/plot/items/image.py
@@ -305,8 +305,6 @@ class ImageData(ImageBase, ColormapMixIn):
                                 origin=self.getOrigin(),
                                 scale=self.getScale(),
                                 z=self.getZValue(),
-                                selectable=self.isSelectable(),
-                                draggable=self.isDraggable(),
                                 colormap=self.getColormap(),
                                 alpha=self.getAlpha())
 
@@ -437,8 +435,6 @@ class ImageRgba(ImageBase):
                                 origin=self.getOrigin(),
                                 scale=self.getScale(),
                                 z=self.getZValue(),
-                                selectable=self.isSelectable(),
-                                draggable=self.isDraggable(),
                                 colormap=None,
                                 alpha=self.getAlpha())
 
diff --git a/silx/gui/plot/items/marker.py b/silx/gui/plot/items/marker.py
index 87804a167..f5a1689a3 100755
--- a/silx/gui/plot/items/marker.py
+++ b/silx/gui/plot/items/marker.py
@@ -65,8 +65,6 @@ class MarkerBase(Item, DraggableMixIn, ColorMixIn, YAxisMixIn):
             y=self.getYPosition(),
             text=self.getText(),
             color=self.getColor(),
-            selectable=self.isSelectable(),
-            draggable=self.isDraggable(),
             symbol=symbol,
             linestyle=linestyle,
             linewidth=linewidth,
diff --git a/silx/gui/plot/items/scatter.py b/silx/gui/plot/items/scatter.py
index e2aaeb287..50cc694b5 100644
--- a/silx/gui/plot/items/scatter.py
+++ b/silx/gui/plot/items/scatter.py
@@ -408,7 +408,6 @@ class Scatter(PointsBase, ColormapMixIn, ScatterVisualizationMixIn):
                                     xerror=xerror,
                                     yerror=yerror,
                                     z=self.getZValue(),
-                                    selectable=self.isSelectable(),
                                     fill=False,
                                     alpha=self.getAlpha(),
                                     symbolsize=self.getSymbolSize(),
@@ -435,7 +434,6 @@ class Scatter(PointsBase, ColormapMixIn, ScatterVisualizationMixIn):
                                                 triangles,
                                                 color=rgbacolors,
                                                 z=self.getZValue(),
-                                                selectable=self.isSelectable(),
                                                 alpha=self.getAlpha())
 
             elif visualization is self.Visualization.REGULAR_GRID:
@@ -464,8 +462,6 @@ class Scatter(PointsBase, ColormapMixIn, ScatterVisualizationMixIn):
                     origin=gridInfo.origin,
                     scale=gridInfo.scale,
                     z=self.getZValue(),
-                    selectable=self.isSelectable(),
-                    draggable=False,
                     colormap=None,
                     alpha=self.getAlpha())
 
@@ -513,7 +509,6 @@ class Scatter(PointsBase, ColormapMixIn, ScatterVisualizationMixIn):
                                             indices,
                                             color=gridcolors,
                                             z=self.getZValue(),
-                                            selectable=self.isSelectable(),
                                             alpha=self.getAlpha())
             else:
                 _logger.error("Unhandled visualization %s", visualization)