onpick on a 2 y plot ( via twinx() ) seems to only allow picking of second axes's artists

Good day,

I've hit an issue that may be a bug. In a previous version of
matplotlib (.98.x) I had a picker set for lines plotted on two axes.
This was working until I upgraded to version 0.99.0. Now the first
axes's pick events never seem to fire even though they respond true if
queried with pickable(). The second axes's pick events still fire.

Using the example below, clicking on the left y's line will never
print anything, but the sin from the right will if clicked on.

OS: Windows XP Pro
Python: 2.6
matplotlib version: 0.99.0 -- installed using the python2.6 windows
exe obtained from sourceforge

Code to reproduce:

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(111)
t = np.arange(0.01, 10.0, 0.01)
s1 = np.exp(t)
ax1.plot(t, s1, 'b-', picker = 4.0)
ax1.set_xlabel('time (s)')
# Make the y-axis label and tick labels match the line color.
ax1.set_ylabel('exp', color='b')
for tl in ax1.get_yticklabels():
    tl.set_color('b')

ax2 = ax1.twinx()
s2 = np.sin(2*np.pi*t)
ax2.plot(t, s2, 'r.', picker = 4.0)
ax2.set_ylabel('sin', color='r')
for tl in ax2.get_yticklabels():
    tl.set_color('r')

def onpick(event):
    thisline = event.artist
    xdata = thisline.get_xdata()
    ydata = thisline.get_ydata()
    ind = event.ind
    print 'onpick points:', zip(xdata[ind], ydata[ind])

fig.canvas.mpl_connect('pick_event', onpick)

plt.show()

Thanks for a wonderful plotting package!

I'm not sure why it worked before -- that surprises me. Only one axes
can receive the pick event currently, so if you have overlapping axes,
as you do with twinx, the one with the highest zorder will receive the
pick events. By default, the zorder by the Axes is 0, so you could
raise the twinx axes, by increaseing the zorder

  ax2.set_zorder(0.1)

and then the line on that axes will respond to the pick events. With
some work, we might be able to support multiple overlapping axes
handling a single pick event.

JDH

···

On Thu, Aug 13, 2009 at 12:51 PM, Erik Schweller<othererik@...287...> wrote:

Good day,

I've hit an issue that may be a bug. In a previous version of
matplotlib (.98.x) I had a picker set for lines plotted on two axes.
This was working until I upgraded to version 0.99.0. Now the first
axes's pick events never seem to fire even though they respond true if
queried with pickable(). The second axes's pick events still fire.

Using the example below, clicking on the left y's line will never
print anything, but the sin from the right will if clicked on.

John,

Thanks for the information. The solution worked so, I can now do a mouse
over over lines from either axes and have a nice little tooltip, etc. I
found that if I change the zorder of the axis right after attempting to
"pick" on the mouse event, it fails, but it's fine for my needs to just
toggle the "top" axes on mouse events when looking for a mouse hover.

                    if self.ax.get_zorder() == 0.1:
                        ax.set_zorder( 0 )
                        ax2.set_zorder( 0.1 )
                    else:
                        ax2.set_zorder( 0 )
                        ax.set_zorder( 0.1 )

                self.figure.pick( event )

This is perhaps a bit of hackery, but I've not seen any mouse over legends
of the sorts built in.

Thanks again,
-Erik

John Hunter-4 wrote:

···

I'm not sure why it worked before -- that surprises me. Only one axes
can receive the pick event currently, so if you have overlapping axes,
as you do with twinx, the one with the highest zorder will receive the
pick events.

--
View this message in context: http://www.nabble.com/onpick-on-a-2-y-plot-(-via-twinx()-)-seems-to-only-allow-picking-of-second-axes's-artists-tp25049128p25062402.html
Sent from the matplotlib - users mailing list archive at Nabble.com.

Hello!

I am also using two axes in a plot and want to be able to pick the lines of
both axes.
So far I used MPL 0.99.3 and a button on my interface to change the z-order
of the axes in order to be able to pick lines of the "active" axes and to
see the correct x/y data in the navigation toolbar. The callback code of my
button is basically the code from othererik.

Since MPL 1.0.0 I have the problem that lines of the second axes simply
disappear from the plot whenever the plot is redrawn and it's zorder is
higher.

Here is my example code:
http://old.nabble.com/file/p30717629/twinxtest.py twinxtest.py

···

-----------------------
import matplotlib
matplotlib.use('TkAgg')

from numpy import arange, sin, pi, cos
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,
NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import Tkinter as Tk

root = Tk.Tk()

f = Figure(figsize=(5,4), dpi=100)
ax1 = f.add_subplot(111)
ax2 = ax1.twinx()

t = arange(0.0,3.0,0.01)
s1 = sin(2*pi*t)
s2 = 2*cos(2*pi*t)

ax1.plot(t,s1,color='red', picker=True)
ax2.plot(t,s2,picker=True)

def pick_cb(event):
    if event.artist.get_lw() > 1:
        event.artist.set_lw(1)
    else:
        event.artist.set_lw(3)
    f.canvas.draw()
    
def toggle():
    if ax1.get_zorder() == 0:
        ax1.set_zorder(0.1)
        ax2.set_zorder(0)
    else:
        ax1.set_zorder(0)
        ax2.set_zorder(0.1)

canvas = FigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)

toolbar = NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)

canvas.mpl_connect('pick_event', pick_cb)

button = Tk.Button(master=root, text='Toggle', command=toggle)
button.pack(side=Tk.BOTTOM)

Tk.mainloop()
------------------

Right after start I can only pick the blue line and both lines are properly
shown even when I resize the plot. When I hit the "Toggle" button now I can
pick the red line but the pick event callback also calls canvas.draw() which
let's the blue line disappear. When I click "Toggle" and call canvas.draw()
again by resizing the window, the blue line is visible again.
In Matplotlib 0.99.3 everything worked as I expected with this code. Both
lines were always visible.

-Stephan

--
View this message in context: http://old.nabble.com/onpick-on-a-2-y-plot-(-via-twinx()-)-seems-to-only-allow-picking-of-second-axes's-artists-tp25049128p30717629.html
Sent from the matplotlib - users mailing list archive at Nabble.com.

Right now I use a customized FigureCanvasTkAgg and overwrite the draw() and
resize() methods to reset the z-order of the axes before drawing / resizing
the figure and to restore the desired z-order afterwards. This works quite
well but it would be nice to have the picking work like in version <=0.99.3.

···

-----------------------
import matplotlib
matplotlib.use('TkAgg')

from numpy import arange, sin, pi, cos
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,
NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import Tkinter as Tk

root = Tk.Tk()

f = Figure(figsize=(5,4), dpi=100)
ax0 = f.add_subplot(111)
ax1 = ax0.twinx()

t = arange(0.0,3.0,0.01)
s1 = sin(2*pi*t)
s2 = 2*cos(2*pi*t)

ax0.plot(t,s1,color='red', picker=True)
ax1.plot(t,s2,picker=True)
ax0.my_zorder=0
ax1.my_zorder=0.1

class MyFigureCanvasTkAgg(FigureCanvasTkAgg):
    def draw(self):
        ax0 = self.figure.axes[0]
        ax1 = self.figure.axes[1]
        ax0.set_zorder(0)
        ax1.set_zorder(0)
        FigureCanvasTkAgg.draw(self)
        ax0.set_zorder(ax0.my_zorder)
        ax1.set_zorder(ax1.my_zorder)
            
    def resize(self, event):
        ax0 = self.figure.axes[0]
        ax1 = self.figure.axes[1]
        ax0.set_zorder(0)
        ax1.set_zorder(0)
        FigureCanvasTkAgg.resize(self, event)
        ax0.set_zorder(ax0.my_zorder)
        ax1.set_zorder(ax1.my_zorder)

def pick_cb(event):
    if event.artist.get_lw() > 1:
        event.artist.set_lw(1)
    else:
        event.artist.set_lw(3)
    f.canvas.draw()
   
def toggle():
    if ax0.my_zorder == 0:
        ax0.my_zorder=0.1
        ax1.my_zorder=0
    else:
        ax0.my_zorder=0
        ax1.my_zorder=0.1
    ax0.set_zorder(ax0.my_zorder)
    ax1.set_zorder(ax1.my_zorder)

canvas = MyFigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)

toolbar = NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)

canvas.mpl_connect('pick_event', pick_cb)

button = Tk.Button(master=root, text='Toggle', command=toggle)
button.pack(side=Tk.BOTTOM)

Tk.mainloop()
-----------------------
--
View this message in context: http://old.nabble.com/onpick-on-a-2-y-plot-(-via-twinx()-)-seems-to-only-allow-picking-of-second-axes's-artists-tp25049128p30893570.html
Sent from the matplotlib - users mailing list archive at Nabble.com.

This thread, in which I asked a similar question and receive a
workable solution from JJ, might be helpful. I am now just
automatically moving all my lines over to the highest z-order axis so
that whatever is visible is pickable. (But see below for a gotcha)

http://old.nabble.com/unable-to-point-pick-2nd-axis-after-upgrade-to-mpl-1.0-td30400311.html

On this note, to the developers: This need to take into account the
z-order for picking lines has made my development more difficult. I
have had a week's delay due to a difficult-to-understand bug in my
code in which I prematurely moved the line over to the highest z-order
axis, and then tried to format the axis with a custom
formatter--causing an error. Simply moving the line only after all
formatting is done fixed the bug, but that wasn't obvious for a while.
The need to track which lines are on which z-order and to move them
only after all formatting/locating has been done on them strikes me as
a new "gotcha" that the simpler and more intuitive approach of "if you
can see it, you can pick it" didn't have. (I have a somewhat complex
case, though, in which there are three possible sorts of axes that
could wind up on either the right or left sides).

It also seems counter-intuitive that a line can "belong" to an axis
from the in Matplotlib and yet for the user it clearly is measured
using the other axis. Could "z-order mattering" be toggled? Anyway,
my 2 cents.

Thanks,
Che

···

On Thu, Jan 20, 2011 at 4:44 AM, Stephan Markus <zweid@...273...> wrote:

Hello!

I am also using two axes in a plot and want to be able to pick the lines of
both axes.
So far I used MPL 0.99.3 and a button on my interface to change the z-order
of the axes in order to be able to pick lines of the "active" axes and to
see the correct x/y data in the navigation toolbar. The callback code of my
button is basically the code from othererik.

Since MPL 1.0.0 I have the problem that lines of the second axes simply
disappear from the plot whenever the plot is redrawn and it's zorder is
higher.

That solution might be fine for static plots, but I my case I prefer my
solution.

My plot is quite interactive so using your solution causes many problems
with my code.

···

--
View this message in context: http://old.nabble.com/onpick-on-a-2-y-plot-(-via-twinx()-)-seems-to-only-allow-picking-of-second-axes's-artists-tp25049128p30919747.html
Sent from the matplotlib - users mailing list archive at Nabble.com.