ipympl icon indicating copy to clipboard operation
ipympl copied to clipboard

Follow-up to #343 - Embedding Static Plots

Open ianhi opened this issue 2 years ago • 11 comments

#343 was a huge step forward for saving the state of plots in notebooks. But it doesn't truly reproduce the state of the notebook that the author was looking at for these two cases:

  1. If widget state is saved then only a blank figure renders (https://github.com/matplotlib/ipympl/pull/343#issuecomment-920167467)
  2. Any updates to the figure after the initial render are not saved in the notebook (https://github.com/matplotlib/ipympl/pull/343#issuecomment-921008926)

I think part 2 is very important to be able to tell a story using a notebook. e.g. If I make some plots with a slider, get them just so and then run nbconvert I don't think I would faithfully reproduce the state of the notebook as I was looking at it.

Part 1 should be solvable if we include the PNG as part of the widget model

Part 2 I think a reasonable tradeoff would be to only update the static plot whenever the notebook is saved. This would require the frontend of ipywidgets (jupyterlab-manager, and widgetsnbextension) to listen to notebook save signals and pass them to the ipympl frontend, which would in turn let the backend know to update the mimebundle.

@martinRenou how hard do you think the second one (saving) would be to fix? To me that feels like a very important thing to hit.

ianhi avatar Sep 22 '21 17:09 ianhi

I don't think classic notebook widgets saved the widget state either? From the point of view of reproducibility, I would argue that having the state of the figure set by sliders is suboptimal. Slide away to explore the data, but "publish" with hard-coded values.

jklymak avatar Sep 22 '21 18:09 jklymak

  1. If widget state is saved then only a blank figure renders

I was planning on working on this next.

I guess the best approach for this is to save the image in the widget model but to not sync it with the JavaScript model, this way we don't send the data twice to the front-end, and animations are still decently fast.

  1. Any updates to the figure after the initial render are not saved in the notebook

Actually, whether or not we want to fix this is debatable. Do we want to save the state of the plot after interactions? Or do we want to save the plot that the Notebook generates when being executed? For the sake of reproducibility the second approach might be better. (edit, sorry @jklymak I see I'm repeating what you said, it's morning 😅 )

If we want to fix this, IPython's update_display might be our best shot. If we can get rid of the flickering this introduces. But it would make ipympl slower.

martinRenou avatar Sep 23 '21 07:09 martinRenou

Any updates to the figure after the initial render are not saved in the notebook

Actually, whether or not we want to fix this is debatable. Do we want to save the state of the plot after interactions? Or do we want to save the plot that the Notebook generates when being executed? For the sake of reproducibility the second approach might be better. (edit, sorry @jklymak I see I'm repeating what you said, it's morning sweat_smile )

If we want to fix this, IPython's update_display might be our best shot. If we can get rid of the flickering this introduces. But it would make ipympl slower.

We initially did use the display with a display id (essentially what martin describes wrt update_display) and thought that the current solution was preferable.

If we updated the static rendering of previous cells like this, the resulting notebook including only the static plots would be very different from what you would obtain with e.g. the inline backend, as it would be a snapshot of the widget backend. Someone looking at the resulting notebook may not understand why an output of a first cell would not match what is done in the first cell.

If widget state is saved then only a blank figure renders (Implement image/png repr #343 (comment))

That should be resolved in a later PR.

SylvainCorlay avatar Sep 23 '21 07:09 SylvainCorlay

From the point of view of reproducibility, I would argue that having the state of the figure set by sliders is suboptimal

Any updates to the figure after the initial render are not saved in the notebook Actually, whether or not we want to fix this is debatable. Do we want to save the state of the plot after interactions? Or do we want to save the plot that the Notebook generates when being executed? For the sake of reproducibility the second approach might be better. (edit, sorry @jklymak I see I'm repeating what you said, it's morning sweat_smile )

Someone looking at the resulting notebook may not understand why an output of a first cell would not match what is done in the first cell.

I definitely agree that for reproducibility the notebook author should not be using sliders to control the plots. But it seems to me that that decision should be left up to the notebook author. While looking at a saved notebook and seeing a cell output not matching up perfectly with it's code is surprising behavior (also possible already with out of order execution) I'd contend it is more surprising (and potentially damaging) to save a notebook and then having it look different when you (or someone else) opens it next. For example a student turning in a homework after having zoomed in on a region of an image, or similarly sending something to a colleague and asking what they think of a certain plot. In those case of course the authors should have made things fully reproducible, but I'd wager that people often don't do that.

Though, perhaps more importantly only saving the first draw of the figure is inconsistent with the behavior of the other notebook backends.

nbagg Static image is as it was when the notebook was last saved. Peek 2021-09-23 12-35

inline In all cases the saved notebook is as the author was looking at it (even when using interact)

inline-persistence

ipympl with saving widget state If we make it so that the widget also restores an image when it's full state is saved then it will be restoring the plot as last drawn.


(To be clear I definitely think as it stands this is already a great improvement :) )

ianhi avatar Sep 27 '21 04:09 ianhi

Ah I see. I agree that if you add something in a different cell to an existing figure, it should also be saved.

jklymak avatar Sep 27 '21 06:09 jklymak

Could you add to the possible functions of a follow up of the initial feature that the export should work as well?

Currently when exporting the png version in Jupyterlab of the figure is not always included. A quick test shows:

  • [ ] HTML export
  • [x] LaTeX export
  • [x] Markdown
  • [x] PDF (via LaTeX backend)
  • [ ] Reveal.js
  • [ ] WebPDF (via Chromium backend)

asteppke avatar Sep 30 '21 10:09 asteppke

~@ianhi your first point should be fixed by https://github.com/matplotlib/ipympl/pull/369~

martinRenou avatar Oct 07 '21 12:10 martinRenou

Partially fixed by https://github.com/matplotlib/ipympl/pull/376

martinRenou avatar Oct 19 '21 09:10 martinRenou

Some more bug fixes on the pdf export https://github.com/matplotlib/ipympl/pull/404

And on the widget state export: https://github.com/matplotlib/ipympl/pull/406

martinRenou avatar Dec 14 '21 13:12 martinRenou

After gh-376, gh-404, and gh-406, I'm wondering what's missing?

astrojuanlu avatar Jul 16 '22 22:07 astrojuanlu

With the recent 0.9.1 when exporting the png version in Jupyterlab of the figure is included:

  • [x] HTML export
  • [x] LaTeX export
  • [x] Markdown
  • [x] PDF (via LaTeX backend)
  • [x] Reveal.js
  • [ ] WebPDF (via Chromium backend, here nbconvert timed out, so probably not ipympl related)

What is not yet solved is the second part, i.e. when zooming or panning the state of the figure does not change in the saved static png. While maybe not essential this is something a user might assume when, for example, focusing on a certain region to emphasize something.

asteppke avatar Jul 16 '22 23:07 asteppke