mpldatacursor icon indicating copy to clipboard operation
mpldatacursor copied to clipboard

Update values in the cursor when plot changes (without re clicking)

Open ajn1985 opened this issue 9 years ago • 1 comments

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 avatar Mar 18 '15 03:03 ajn1985

@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()

joferkington avatar Mar 30 '15 03:03 joferkington