2

Problem

I want to display a slider linked to a Play button from ipywidgets and then update an imshow plot from matplotlib. However I want the image to stay there and only draw the artists that changed and not have to clear the output to draw everything again. bqplot handles this situation quite well but the displaying a gridheatmap has revealed challenging for editing tick labels and other parts of the design that matplotlib handles so well. Some people have used interact but that approach limits my freedom to handle traitlets.

My code

slider=wd.IntSlider(value=0,min=0,max=49,continuous_update=False)
play_button=wd.Play(
    value=0,
    min=0,
    max=49,
    step=1,
    interval=850,
    description="Press play",
    disabled=False
)

def slider_update():
    global im
    global fig
    slider.value=change.new
    label.value="Time: {}".format(slider.value)
    
    with out:
        new_data=random_data[:,:,slider.value]
        im.set_data(new_data)
        im.set_clim(0,np.max(new_data))

        background = fig.canvas.copy_from_bbox(ax.bbox)
        fig.canvas.flush_events()
        fig.canvas.restore_region(background)
        ax.draw_artist(im)
        fig.canvas.blit(ax.bbox)
        
        plt.show(fig)
    
label=wd.Label("Time: 0")
# slider.observe(slider_update, "value")
wd.jslink((play_button,"value"),(slider,"value"))
widget=wd.interactive(slider_update,idx=slider)

out=wd.Output()
app=wd.VBox([wd.HBox([play_button,slider,label]),out])


display(app)

Output

enter image description here

Intended output

enter image description here

But the image updates only the artiosts so as to get a smooth animation (this last image was accomplished by having display(app,fig) which does not do what I want.

Can anyone give a hand?

Bidon
  • 221
  • 1
  • 12
  • You will need to use an interactive maptlotlib backend, either `%matplotlib notebook` or `%matplotlib ipympl` – Ianhi Oct 13 '20 at 16:47
  • Check out the examples here: https://github.com/matplotlib/ipympl/blob/master/examples/ipympl.ipynb – Ianhi Oct 13 '20 at 16:48
  • And finally fwiw I wrote a library that makes updating an imshow object easy, see https://mpl-interactions.readthedocs.io/en/latest/examples/imshow.html. And for the specific case of indexing through an array there is a dedicated function hyperslicer: https://mpl-interactions.readthedocs.io/en/latest/examples/hyperslicer.html – Ianhi Oct 13 '20 at 16:50
  • 1
    also you will need to display `fig.canvas` if you use ipympl as just the `fig` object will be a png, but `fig.canvas` is a jupyter widget that will update – Ianhi Oct 13 '20 at 16:51
  • @lanhi I'm still not able to get it to update... I changed the backend to the ipympl and also was able to display the fig.canvas as a widget inside a ipywidgets layout. But it isn't updating – Bidon Oct 13 '20 at 17:08
  • nvm. I had to remove the draw_artist(), flush_events, blit() and restore_region() for it to work. Turns out, just the `set_data()` method will do the trick and I don't have to explicitly tell it to draw the artists. I would still love an explanation or more details on why this is so. But its working now – Bidon Oct 13 '20 at 17:14
  • I think that blitting is not implemented in any of the web backends for matplotlib. see for example https://github.com/matplotlib/ipympl/issues/228 – Ianhi Oct 13 '20 at 17:38

0 Answers0