Picking and events question

Hi,
I am trying to use matplotlib to visually explore some data. I started
from the "event_handling example code: data_browser.py" example, but
wanted to go a bit further. The idea is to have two plots and an image
linked together. The first plot represents a measure calculated from
an image region at different times of the day. Selecting one of this
measures shows the corresponding image, and some other measure calculated
from the individual pixels. Further on, selecting in the image or the
second plot should highlight the corresponding point in the plot or pixel
on the image, respectively (That is, the first plot is linked to the image,
and the image and the second plot are linked together).
My problem is that on the function being called upon and event, I always
get events generated by the image, even when the moused was clicked on one of
the other plots. The plots behave ok.
The coded below is a simplified functional version (the measures here are
trivial, and I haven't included the actions when the user selects the image or
the second plot), which exhibits this behavior. What I see when executing this
code is for example:

Actions (click on) Results (from the print statement on the
onpick() function)

plot1 plot1
                        image2

image2 image2

plot3 plot3
                        image2

Am I doing something wrong? I am new to both python and matplotlib, so if you
see something that could be done in a better way, please tell me.

Jorges

----- Test code -----
import os
import sys
import glob
import re
import fnmatch
import Image as im
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import scipy as sp
import scipy.stats

class LCRBrowser():
    """
    Permits to visualize log chromaticity ratios and related things for
    analysing static images taken at throughout a period of time
    """
    def __init__(self, data):
        # Initialize instance attributes
        self.lcr = np.asarray(map((lambda x: x['lcr']), data))
        self.lcr_ind = np.asarray(map((lambda x: x['lcr_ind']), data))
        self.img = np.asarray(map((lambda x: x['img']), data))
        self.lastind = 0
        # Initial drawing and picking init
        self.fig = plt.figure()
        self.ax1 = self.fig.add_subplot(311)
        self.ax2 = self.fig.add_subplot(312)
        self.ax3 = self.fig.add_subplot(313)
        self.plot1, = self.ax1.plot(self.lcr, 'o', picker=5)
        self.selected, = self.ax1.plot([self.lcr[0]], 'o', ms=12, alpha=0.4,
                                  color='yellow', visible=False)
        self.img2 = self.ax2.imshow(self.img[0], visible=True, picker=2,\
                                    interpolation='nearest')
        self.plot3, = self.ax3.plot(self.lcr_ind[0][:], 'o', visible=True, picker=5)
        print type(self.plot3)
        self.fig.canvas.mpl_connect('pick_event', self.onpick)
        self.fig.canvas.draw()

    def onpick(self, event):
        x2 = event.mouseevent.ydata
        if event.artist == self.plot1:
            print 'plot1\n'
            N = len(event.ind)
            if not N: return True
            distances = x2-self.lcr[event.ind]
            indmin = distances.argmin()
            dataind = event.ind[indmin]
            self.lastind = dataind
        elif event.artist == self.img2:
            print 'img2\n'
        elif event.artist == self.plot3:
            print 'plot3\n'
        else:
            print 'didn\'t detect artist'
            print str(event.artist)
            return True
        self.update()

    def update(self):
        if self.lastind is None: return

        ind = self.lastind
        self.img2.set_data(self.img[ind])
        a = self.lcr_ind[ind][:]
        self.plot3.set_data((range(len(a)), a))
        self.fig.canvas.draw()

def main():
    results = []
    for name in range(5):

        img = np.random.rand(5,4,3)
        lcr = sp.stats.gmean(np.mean(np.mean(img, 0), 0))
        lcr_ind = sp.stats.gmean(img, 2).reshape(-1,1)
        result = {'lcr': lcr, 'lcr_ind':lcr_ind, 'img':img}
        results.append(result)

    LCRBrowser(results)

if __name__ == '__main__':
    main()

----- code end -----

It looks like you found a pretty significant bug -- the Artist.pick
method forwards the event to all of it's children, whether or not the
pick event happened in the same Axes as the event being queried. Not
only is this inefficient, it can create false positives when the two
axes share a similar coord system. I just committed a fix to svn, to
make sure the artist axes instance is the same as the pick event
inaxes attribute before forwarding on the call. artist.Artist.pick
now reads:

        # Pick children
        for a in self.get_children():
            # make sure the event happened in the same axes
            ax = getattr(a, 'axes', None)
            if mouseevent.inaxes==ax:
                a.pick(mouseevent)

This seems to fix the problem you identified -- give svn r7141 or
later a test drive if you have access

  http://matplotlib.sourceforge.net/faq/installing_faq.html#install-from-svn

JDH

···

On Mon, May 25, 2009 at 7:16 AM, <jorgesmbox-ml@...1664...> wrote:

Hi,
I am trying to use matplotlib to visually explore some data. I started
from the "event_handling example code: data_browser.py" example, but
wanted to go a bit further. The idea is to have two plots and an image
linked together. The first plot represents a measure calculated from
an image region at different times of the day. Selecting one of this
measures shows the corresponding image, and some other measure calculated
from the individual pixels. Further on, selecting in the image or the
second plot should highlight the corresponding point in the plot or pixel
on the image, respectively (That is, the first plot is linked to the image,
and the image and the second plot are linked together).
My problem is that on the function being called upon and event, I always
get events generated by the image, even when the moused was clicked on one of
the other plots. The plots behave ok.
The coded below is a simplified functional version (the measures here are
trivial, and I haven't included the actions when the user selects the image or
the second plot), which exhibits this behavior. What I see when executing this
code is for example:

It looks like you found a pretty significant bug -- the Artist.pick
method forwards the event to all of it's children, whether or not the
pick event happened in the same Axes as the event being queried. Not
only is this inefficient, it can create false positives when the two
axes share a similar coord system. I just committed a fix to svn, to
make sure the artist axes instance is the same as the pick event
inaxes attribute before forwarding on the call. artist.Artist.pick
now reads:

        # Pick children
        for a in self.get_children():
            # make sure the event happened in the same axes
            ax = getattr(a, 'axes', None)
            if mouseevent.inaxes==ax:
                a.pick(mouseevent)

This seems to fix the problem you identified -- give svn r7141 or
later a test drive if you have access

  http://matplotlib.sourceforge.net/faq/installing_faq.html#install-from-svn

JDH

Thanks! Your solution works for me. I haven't actually tested the svn, only patched the current version provided by my distribution (0.98.5.2) with the modification you posted.

Jorges