Thanks to visit codestin.com
Credit goes to github.com

Skip to content

MRG: Dont close figures until end of script #161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from

Conversation

larsoner
Copy link
Contributor

@larsoner larsoner commented Nov 8, 2016

Sometimes it is necessary to have figures that are not closed within a given code-block. This modifies the gallery behavior to leave the figures open until the script is done. This is useful e.g. when creating a figure in one code block, and querying some parameter from it later. Our particular use case came from creating a Mayavi figure in one code block, and needing to get the viewport transformation from it in another code-block.

@larsoner
Copy link
Contributor Author

larsoner commented Nov 8, 2016

An alternative solution would be to do everything that is needed for a given figure within a single code block, but that's not as flexible as this solution.

@@ -351,6 +363,7 @@ def save_figures(image_path, fig_count, gallery_conf):
current_fig = image_path.format(fig_count + fig_mngr.num)
fig.savefig(current_fig, **kwargs)
figure_list.append(current_fig)
fig._sg_captured = True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than this monkey-patching I think it would be slightly better to have an additional argument in save_figures, which is a set that remembers the already captured figures.

Alternatively we could have a matplotlib index and a mayavi index keeping track of the number of the last plot saved.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I could do that. I don't think indexing will work:

>> import matplotlib.pyplot as plt
>>> fig = plt.figure()
>>> fig.number
1
>>> plt.close('all')
>>> fig = plt.figure()
>>> fig.number
1

Instead I could maintain a list and check with is. The problem with that approach is that it might lead to memory issues, because the figures can't be garbage-collected even if they are closed by the user. So I'm actually starting to think that the monkey patching, although ugly, might be better.

@lesteve
Copy link
Member

lesteve commented Nov 9, 2016

It would be great to add tests, preferentially both with matplotlib and mayavi. Not a matplotlib expert, I could not find something that goes wrong because of closing all the figures at the end of a block, suggestions welcome!

@larsoner
Copy link
Contributor Author

I'm not sure about whether it causes problems with matplotlib but it definitely does for mayavi. And if we're going to do it for one, we might as well do it for both to be safe.

I'll look into the Travis failure and try to add tests soon.

@larsoner
Copy link
Contributor Author

Okay, tests added. I put in a bunch of fixes for Windows-related building because I'm on my Windows dev machine currently. It would be worth adding AppVeyor testing at some point and cleaning up Windows support properly.

@agramfort
Copy link
Contributor

travis is not happy :(

@larsoner
Copy link
Contributor Author

All better

@Titan-C
Copy link
Member

Titan-C commented Nov 14, 2016

I'll will start with a consistency check of how we want to present output, and how we should understand the notebook styled examples or code and outputs interleaved with prose.
Let's put this simple script to test

# -*- coding: utf-8 -*-
"""
The sin function with a phase shift
===================================

"""

# Code source: Óscar Nájera
# License: BSD 3 clause

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
y = np.sin(x)
plt.plot(x, y, label=r'$\sin(x)$')

###############################################################################
# now put an angle
y = np.sin(x + np.pi / 4)
plt.plot(x, y, lw=2, label=r'$\sin(x+\pi/4)$')

plt.legend(loc=0)
plt.xlabel('$x$')
plt.ylabel('$\sin(x)$')
plt.show()

If we run this directly from the command line we get a window to pop up with the 2 curves in the same figure. If I run this in the gallery at master I get 2 figures, each one holds 1 curve.
2016-11-14-220135_404x978_scrot

And the rendered Jupyter notebook produces a similar output, as indeed we have defined 2 blocks.
2016-11-14-220638_398x882_scrot

Running this PR I get a not consistent result, at least in my linux PC the second block does not show any output, and that I assume is totally unwanted. I would have expected the second figure to be a composition of the first and second plot instructions. Showing 2 curves in the figure.
2016-11-14-221010_403x857_scrot

Now to the core of my discussion. I do see the intention in a tutorial to show how every new line of code adds to the drawn figure and so to show each code block as an intermediate step. But I feel in a notebook styled example we should restrict ourselves to plot individually what each block is declaring. Especially since the examples are targeted for education purposes, that once the students try out the jupyter notebook file they shall get the same output. To me the easiest way to accomplish this is to just close all figures after saving them for each block.

Second thought, is that jupyter notebooks do allow to recover the plotted figure from the previous cell and compose on top of that, but the syntax is quite involved and I would be even more against to support that for our simple set of examples when just rewriting the plot instructions is so simple.

Third thought is that if we want to support accumulating the figures. I would rather first include a flag to tell sphinx-gallery to deviate from the behavior of making each block individual and stop closing figures after each block. Then make the author of the script be responsible for which figures are open(alive), and which he wants to close. In that case sphinx-gallery shall save and show for each block all active figures. I think this would also avoid the monkey patching of the figure object, simplify the numbering/naming of saved figures as is just a counter. And as put in the current PR comment, recapturing a picture in this case is trivial.

@lesteve
Copy link
Member

lesteve commented Nov 15, 2016

I do agree there is some tension between seeing the example as a Jupyter notebook and seing it as a script, for example when you do multiple plt.plot in different cells, it is not clear to me what the behaviour should be in this case.

In @Eric89GXL's case though, I think that we are not in such a tricky case. IIUC he plots two different figures in two different cells. The problem is that closing the first figure (let's call it fig) make the variable fig not usable any more (to be honest it could well be considered as a mayavi bug, but that may be a very naive interpretation of the problem given that I know close to nothing about mayavi). The second plot tries to use fig, not to plot on top of the first figure but to reuse its "viewport transformation" and that's where the problem happens.

I would be in favour of not closing the figures since that seems to be a useful work-around in this case, unless we can spot a real blocker in the fine case of trying to plot figures in different cells.

I would leave plotting on top of an existing figure and recapturing as undefined behaviour for now.

@Titan-C
Copy link
Member

Titan-C commented Nov 15, 2016

I would be in favour of not closing the figures since that seems to be a useful work-around in this case, unless we can spot a real blocker in the fine case of trying to plot figures in different cells.

But for mayavi or for matplotlib?

@lesteve
Copy link
Member

lesteve commented Nov 15, 2016

But for mayavi or for matplotlib?

I guess for both, be it only for consistency reasons.

@larsoner
Copy link
Contributor Author

larsoner commented Dec 9, 2016

Closing for now until consensus

@larsoner larsoner closed this Dec 9, 2016
@lesteve
Copy link
Member

lesteve commented Dec 19, 2016

I am +1 on closing the figures only at the end of the script.

@Eric89GXL what is your current work-around this in MNE-python?

@larsoner
Copy link
Contributor Author

In the example where we needed to modify an existing (mayavi) figure, we instead recreated it in the next block and modified it.

@lesteve
Copy link
Member

lesteve commented Dec 19, 2016

In the example where we needed to modify an existing (mayavi) figure, we instead recreated it in the next block and modified it.

So you have some duplication (the lines to create the figure) in your example?

@larsoner
Copy link
Contributor Author

Yeah, that's what we ended up going with I think

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants