Cursor tracking using matplotlib and doublex

I would like to track mouse coordinates relative to data coordinates on two axes at the same time. I can accurately track the position of the mouse relative to one axis. The problem is this: when I add a second axis with twinx() , both Cursors determine the coordinates of the report data relative to the second axis.

For example, my cursors ( fern and muffy ) report a value of y - 7.93

 Fern: (1597.63, 7.93) Muffy: (1597.63, 7.93) 

If I use:

  inv = ax.transData.inverted() x, y = inv.transform((event.x, event.y)) 

I get an IndexError.

So the question is, how do I change the code to track the coordinates of the data with respect to both axes?


enter image description here

 import numpy as np import matplotlib.pyplot as plt import logging logger = logging.getLogger(__name__) class Cursor(object): def __init__(self, ax, name): self.ax = ax self.name = name plt.connect('motion_notify_event', self) def __call__(self, event): x, y = event.xdata, event.ydata ax = self.ax # inv = ax.transData.inverted() # x, y = inv.transform((event.x, event.y)) logger.debug('{n}: ({x:0.2f}, {y:0.2f})'.format(n=self.name,x=x,y=y)) logging.basicConfig(level=logging.DEBUG, format='%(message)s',) fig, ax = plt.subplots() x = np.linspace(1000, 2000, 500) y = 100*np.sin(20*np.pi*(x-1500)/2000.0) fern = Cursor(ax, 'Fern') ax.plot(x,y) ax2 = ax.twinx() z = x/200.0 muffy = Cursor(ax2, 'Muffy') ax2.semilogy(x,z) plt.show() 
+3
source share
2 answers

Due to the fact that callbacks work, the event always returns to the upper axes. You just need some logic to verify that if the event occurs on the axes we want:

 class Cursor(object): def __init__(self, ax, x, y, name): self.ax = ax self.name = name plt.connect('motion_notify_event', self) def __call__(self, event): if event.inaxes is None: return ax = self.ax if ax != event.inaxes: inv = ax.transData.inverted() x, y = inv.transform(np.array((event.x, event.y)).reshape(1, 2)).ravel() elif ax == event.inaxes: x, y = event.xdata, event.ydata else: return logger.debug('{n}: ({x:0.2f}, {y:0.2f})'.format(n=self.name,x=x,y=y)) 

It may be a subtle bug in the conversion stack (or it’s the right use, and by and large it worked with tuples before), but, in any case, this will make it work. The problem is that the code in line 1996 in transform.py expects to get a 2D ndarray back, but the identity transformation simply returns a tuple that is passed into it, which generates errors.

+3
source

You can track both axis coordinates with a single cursor (or event handler) as follows:

 import numpy as np import matplotlib.pyplot as plt import logging logger = logging.getLogger(__name__) class Cursor(object): def __init__(self): plt.connect('motion_notify_event', self) def __call__(self, event): if event.inaxes is None: return x, y1 = ax1.transData.inverted().transform((event.x,event.y)) x, y2 = ax2.transData.inverted().transform((event.x,event.y)) logger.debug('(x,y1,y2)=({x:0.2f}, {y1:0.2f}, {y2:0.2f})'.format(x=x,y1=y1,y2=y2)) logging.basicConfig(level=logging.DEBUG, format='%(message)s',) fig, ax1 = plt.subplots() x = np.linspace(1000, 2000, 500) y = 100*np.sin(20*np.pi*(x-1500)/2000.0) fern = Cursor() ax1.plot(x,y) ax2 = ax1.twinx() z = x/200.0 ax2.plot(x,z) plt.show() 

(I got "too many indexes" when I used ax2.semilogy(x,z) as an OP, but could not handle this problem.)

The code ax1.transData.inverted().transform((event.x,event.y)) performs the conversion from display to the data coordinates on the specified axis and can be used with any axis as desired.

0
source

All Articles