mpldatacursor
mpldatacursor copied to clipboard
Update values in the cursor when plot changes (without re clicking)
Hi, First off, thanks a bunch for creating this. :)
I have one question regarding the use of the data cursor:
Say, I have a 10x10x10 data array which I want to display using imshow plot and a slider, where the plot updates when the slider is moved. After I click on the plot at say x=10, y=10, at slider position 1 (where the z value is 1000), I would like the data cursor to remain at the same x,y position but get updated as I move the slider.
Is it possible to do this? If so could you point me in the right direction on how to do so?
Regards, Adithya
@ajn1985 - Sorry I've taken so long to reply!
There probably is a way to have mpldatacursor
do this automatically, but for the moment, it's easiest to update the annotation when you update the image.
At present, it's easiest to re-fire the event that originally displayed the "popup". In the next release, I'll try to have a way of making this a bit easier.
In the meantime, here's a general example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from mpldatacursor import datacursor
def main():
data = np.random.random((10, 10, 10))
fig, ax = plt.subplots()
explorer = DataExplorer(data, ax)
updater = DatacursorUpdater(datacursor(axes=ax))
explorer.callback = updater
plt.show()
class DataExplorer(object):
"""
I imagine you have something similar to this class to "scroll" through your 3D array.
"""
def __init__(self, data, ax, callback=None):
self.data = data
self.ax = ax
self.im = self._plot_image()
self.callback = callback
self.sliderax = self.ax.figure.add_axes([0.27, 0.01, 0.5, 0.03])
self.slider = Slider(self.sliderax, 'Index', 0, self.data.shape[2],
valinit=0)
self.slider.on_changed(self.slider_update)
def _plot_image(self):
im = self.ax.imshow(self.data[:,:,0], interpolation='none', cmap='gray',
vmin=self.data.min(), vmax=self.data.max())
return im
def slider_update(self, val):
self.im.set_data(self.data[:,:,int(val)])
if self.callback is not None:
self.callback()
self.ax.figure.canvas.draw()
class DatacursorUpdater(object):
def __init__(self, dc):
self.orig_formatter = dc.formatter
dc.formatter = self.formatter
self.dc = dc
self.last_event = None
def formatter(self, **kwargs):
"""The sole purpose of this is to hold onto the last pick event."""
self.last_event = kwargs['event']
return self.orig_formatter(**kwargs)
def __call__(self):
"""Update the datacursor's displayed values by re-firing the last pick
event."""
if self.last_event is None:
return
canvas = self.last_event.artist.figure.canvas
canvas.callbacks.process('pick_event', self.last_event)
main()