FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

[ENH]: Make savefig close plot by default · Issue #29782 · matplotlib/matplotlib · GitHub

Repository navigation

[ENH]: Make savefig close plot by default #29782

Closed

Description

Problem

I have to always do:

plt.savefig(...)
plt.close()

this seems unnecessary, given that 99% of the time I need the plot in order to save it, once it is saved, I do not need it anymore. Wouldn't it be a better idea to just close the plot automatically, maybe add an option

plt.savefig(..., keep_open=True)

for those users who do not want it closed.

Proposed solution

No response

Activity

  1. dstansby commented on Mar 20, 2025

    Member

    I guess there's the use case where someone wants to save a plot, make some changes to it, and save it again? My two cents are that the complexity of changing the behavour here and introducing a new argument for savefig is not worth it to save having to explicitly call plt.close after saving if that's what you're after.

  2. jklymak commented on Mar 20, 2025

    Member

    ... or closing via the window manager, given this is clearly aimed at interactive use. For certain I would be against this as the default - I'm not aware of any program that closes a document when you print it.

  3. added
    API: changesChanges to the public API, typically requiring deprecation.
    on Mar 20, 2025
  4. anntzer commented on Mar 20, 2025

    Contributor

    I agree with @jklymak's argument and would strongly oppose that change.

  5. timhoffm commented on Mar 21, 2025

    Member

    this is clearly aimed at interactive use.

    I'm not clear about this. @acampove do you show the figure at all?

    I can see a typical use case for just saving figures. While you nowadays don't have to go through pyplot for that, it's quite a long way from a standard on screen

    import matplotlib.pyplot as plt
    
    fig, ax = plt.subplots()
    [...]
    plt.show()
    

    to saving that figure without having to care about closing

    from matplotlib.figure import Figure
    
    fig, = Figure()
    ax = fig.subplots()
    [...]
    fig.savefig()
    

    One issue with close() is that it does two things: (1) close the plot window and (2) deregister the figure in pyplot. (1) is obviously not needed for save-only. (2) would not be needed if the user keeps a reference to the figure and it's not tracked in pyplot. But that option does not exist.

    Maybe we have to think broader. One issue is the different responsibilities of pyplot. It can be used as the state-based functional interface, where figure tracking and explicit close are needed. But it's also the entry point to our object based API, and there tracking and closing is a nuisance. With the mpl-gui concept we are trying to separate these, but that's focused around event loop and window management. While there's the Figure() solution above for save-only, it's difficult to learn. It would be great if I could always just start with plt.figure() to logically create a figure and be less tied to / smarter about additional infrastructure needs in the background.

    Would it be an idea to opt out of pyplot figure tracking like fig, ax = plt.subplots(track=False). Maybe this helps untangling the different aspects of pyplot?

    Or - wilder thoughts:

    • Could we turn figure() into a context manager.
    • Could we "detect" that the user is not using the functional interface and therefore decide to no track the figure? (Something like: if the figure was not accessed through additional pyplot commands, show/savefig deregister the figure by default)
  6. rcomer commented on Mar 21, 2025

    Member

    I think it would be useful to have more information about the use-case. Possibly it is something like

    import matplotlib.pyplot as plt
    
    plt.plot(my_data)
    plt.savefig(...)
    
    plt.plot(my_other_data)
    plt.savefig(...)   # why are both sets of data showing in the second file?

    To solve this, you either need to explicitly close the first figure that you didn't explicitly create, or you need to explicitly create the second figure. If the use-case is something like this, then I doubt that building more options into plt.figure or plt.subplots would be that helpful.

    @acampove can you say more about your use-case and how you got here?

  7. acampove commented on Mar 27, 2025

    Author

    I think it would be useful to have more information about the use-case. Possibly it is something like

    import matplotlib.pyplot as plt

    plt.plot(my_data)
    plt.savefig(...)

    plt.plot(my_other_data)
    plt.savefig(...) # why are both sets of data showing in the second file?

    To solve this, you either need to explicitly close the first figure that you didn't explicitly create, or you need to explicitly create the second figure. If the use-case is something like this, then I doubt that building more options into plt.figure or plt.subplots would be that helpful.

    @acampove can you say more about your use-case and how you got here?

    Hello @jklymak @timhoffm

    Thank you for your replies. Yes, my usecase is me making multiple plots the way you show them. I have to always use the line:

    plt.savefig(...)
    plt.close()

    which seems unnecessary, given that I do not use that plot after I save it anyway. I never use plt.show, usually I run the script and make all the plots, then I look at them separately. This means that the savefig line happens multiple times and they all have to be followed by the close line, which seemed unnecessary. Of course, if I do not do that, then I will get two completely unrelated figures drawn on top of each other.

    Cheers.

  8. jklymak commented on Mar 27, 2025

    Member

    OK, but the solution to this is to make separate figures: plt.figure().

    If you are worried about too many figures being open, then yes you need to close the figures.

    There is zero chance in my mind that plt.savefig('boo.png') will default to closing the figure. Thats not how any other program that I know of works, and would be very surprising as a default.

    Doing this as an option: plt.savefig('boo.png', close=True) may be OK. But then its not much better than just explicitly calling plt.close() (11 characters versus 10, and most IDEs will let you tab complete "clo" to "close()"). If you are running this as a script I dont' see what the annoyance of typing plt.close() is, other than remembering to do it.

    If I had this annoyance, I would just do

    def my_print(fname):
        plt.savefig(fname);
        plt.close()

    and store that in a local package that I install via pip.

  9. tacaswell commented on Mar 27, 2025

    Member

    If you never do plt.show(), then I think the better solution is to not make use of pyplot at all!

    from matplotilb.figure import Figure
    
    fig = Figure()
    ax = fig.subplots()
    ax.plot()
    # and other plotting
    fig.savefig()

    If you do this in a function then the Figure instance will follow "normal" garbage collection (because we do not keep a reference to it).

    The problem is the global state that exists to make working interactively in a shell nice is getting in your way. I think it is better to just not use the global state rather then add epicycles to it to.

    [edited: fixed a typo noted by @jklymak ]

  10. jklymak commented on Mar 27, 2025

    Member

    @tacaswell I think you mean fig.subpots()? Or does plt.subplots() also keep the namespace unpolluted?

    BTW, aside from the massive change to the docs, is there anything holding up mpl_gui?

  11. tacaswell commented on Mar 27, 2025

    Member

    🤦🏻 fixed the typo. mpl_gui is held up on docs and then consensus that it makes sense / feel good to use.

  12. rcomer commented on Mar 27, 2025

    Member

    If you never do plt.show(), then I think the better solution is to not make use of pyplot at all!

    Do we have anything in the docs recommending this? It is news to me and I consider myself more informed than many users...

  13. timhoffm commented on Mar 27, 2025

    Member

    We have at least https://matplotlib.org/stable/gallery/user_interfaces/web_application_server_sgskip.html

    Describing the pattern. But granted it's in a narrower context. And the FAQ primarily recommends pyplot https://matplotlib.org/stable/users/faq.html#generate-images-without-having-a-window-appear 😐

  14. 13 remaining items

  15. jklymak commented on Mar 28, 2025

    Member

    I'm not aware of any program in wide use over the last 50 years that conflates saving a document to file with clearing the document from memory (eg closing the document). I'm curious to hear examples.

    I regularly save to file and continue to use the figure for subsequent saves in standalone scripts:

    • saving to multiple formats (png, pdf)
    • Saving, and then focusing the plot on a region of interest by changing the limits and saving again.

    I imagine many other people do as well, and I feel that it would be very unexpected for the figure to close if it is saved unless I asked it to.

    I think we must compare plt.savefig() with the (default) blocking plt.show()

    With plt.show(), the shown figure stays open until I explicitly close it with the GUI. Definitely the script has handed things off the GUI at that point, but I can do multiple saves, zoom, and annotate from the GUI before I explicitly close the figure. Comparing that to a single save that then immediately closes the figure doesn't make much sense to me.

    I am also not a fan of a new rcParams to fit every idiosyncratic use of the library. We have an explicit way of closing the figure (plt.close()), and we have an implicit way (bypassing pyplot: #29782 (comment)). Adding another switch to the block=True/False, plt.ion array of interactivity options doesn't appeal. But my objection to this is less strenuous - if someone wants to add yet another confusing knob to accommodate a specific workflow, then I won't approve it, but I wouldn't block it either.

  16. timhoffm commented on Mar 28, 2025

    Member

    As a first step, can we agree that there's an asymmetry between plt.savefig() and plt.show() (whether that's justified is a second step):

    • I can plot -> savefig -> modify the plot -> savefig ... but I cannot plot -> show -> modify the plot -> show again ...
    • I can plot -> savefig -> show but I cannot plot -> show -> savefig

    I regularly save to file and continue to use the figure for subsequent saves

    I regularly save to a file and be done with the figure. Both are valid use cases and will be supported either way. The question is which is the more common one and thus should be simpler, and on which one do we put the burden for explicitly handling the use case (i.e. explicitly stating that the figure should be dergistered from pyplot or not).

  17. jklymak commented on Mar 28, 2025

    Member

    As a first step, can we agree that there's an asymmetry between plt.savefig() and plt.show()

    Sure, because they are orthogonal things. plt.savefig() saves the figure to a file. plt.show() hands the figure from the script off to the a GUI for further manipulation, including saving to a file (in most of the GUIs). I'm not clear why we would expect them to be symmetric.

    • I can plot -> savefig -> modify the plot -> savefig ... but I cannot plot -> show -> modify the plot -> show again ...

    Typical in plt.show() you can indeed modify the plot with the GUI and see the results on screen.

    • I can plot -> savefig -> show but I cannot plot -> show -> savefig

    I'm not sure what GUI you are using, but all the ones I use have a disk icon than allows me to call savefig via the GUI.

  18. anntzer commented on Mar 28, 2025

    Contributor

    FWIW I spend a lot of time in the ipython console, effectively in interactive mode.

    Ok, let's have a look at that. How is your workflow? Do you use ipython --matplotlib or plt.ion() or ...?

    It's (effectively, because of course I do things weirdly...) ipython --matplotlib.

    I can plot -> savefig -> show but I cannot plot -> show -> savefig

    I can (and regularly do so, in fact), in interactive mode.

  19. jklymak commented on Mar 28, 2025

    Member

    The question is which is the more common one and thus should be simpler, and on which one do we put the burden for explicitly handling the use case (i.e. explicitly stating that the figure should be dergistered from pyplot or not).

    Again, I am curious for examples where, by default, saving a file also clears it from memory. "Simpler" to me means doing what is expected, not necessarily what leads to less typing.

  20. timhoffm commented on Mar 28, 2025

    Member

    I'm not clear why we would expect them to be symmetric.

    They are just two variants of the last step of the basic workflow (i) create the figure (ii) add content (iii) output the result.

    • I can plot -> savefig -> modify the plot -> savefig ... but I cannot plot -> show -> modify the plot -> show again ...

    Typical in plt.show() you can indeed modify the plot with the GUI and see the results on screen.

    • I can plot -> savefig -> show but I cannot plot -> show -> savefig

    I'm not sure what GUI you are using, but all the ones I use have a disk icon than allows me to call savefig via the GUI.

    I'm not talking about any possible operations in the GUI. I generate a visual output of the figure (file or gui), after that I can continue on the command line, but in one case the figure is gone whereas in the other it's not.

  21. anntzer commented on Mar 28, 2025

    Contributor

    This discussion is seriously running in circles, but once again, the figure is gone in neither case for me (in interactive mode).

  22. rcomer commented on Mar 28, 2025

    Member

    I regularly save to file and continue to use the figure for subsequent saves

    I regularly save to a file and be done with the figure. Both are valid use cases and will be supported either way. The question is which is the more common one

    I suspect that when working interactively saving (or showing) multiple times is a lot more common, because the first attempt is rarely exactly what you want. IIRC when my institution first moved to python for data analysis one of the most common questions was "how do I modify my plot after show?"

    I suspect that when running a script saving once is more common, but if it only makes one figure then you don't care whether that figure got closed before the end of the script. If the script generates many figures it is not much burden to add plt.close to keep the memory in check.

  23. timhoffm commented on Mar 28, 2025

    Member

    This discussion is seriously running in circles, but once again, the figure is gone in neither case for me (in interactive mode).

    Yes we are going in (different) circles. Again, I'm talking about the non-interactive behavior of pyplot, i.e. plt.savefig() when no figure is ever shown, or plt.show() in the default blocking mode. Please assume a standard python shell or executing a python script. In this discussion, I don't care about or want to modify the interactive behavior.

    Let's make an example: In my experience, switching between savefig() and show() is a typical debug strategy so that you don't have to manually open and inspect the generated figures. Say I have a script that generates 10 figures in a loop, and while developing/debugging I want to look at them, so I show():

    for i in range(10):
        plt.figure()
        plt.plot(data[i])
        plt.show()

    When done with developing, I want to save the figures to file rather than showing them. Why is it unreasonable to expect that I can replace plt.show() by plt.savefig() and get the same runtime / lifecycle behavior for the figures?

  24. anntzer commented on Mar 28, 2025

    Contributor

    In the concrete example you give, it means that show() blocks at every iteration, and you have to close the figure again and again to advance the loop, instead of being able to inspect all the figures at once and close("all") once done. Perhaps that's indeed your workflow, but I would suggest that just calling ion() (or activating interactive mode in some other way, e.g. via %matplotlib) so that all figures show up at once would make things more convenient.

  25. timhoffm commented on Mar 28, 2025

    Member

    I'm in a script, I don't have %matplotlib. When just calling ion() the figures don't show up because the nothing is blocking and the script runs through to terminate. With more effort I can do

    plt.ion()
    for i in range(10):
        plt.figure()
        plt.plot([1, 3, 2])
        plt.show()
    plt.pause(10)
    

    but that requires three lines change between show and savefig. Also the resulting window arrangement is unhelpful:

    I'd then rather have one window pop up at a time. Since the windows appear in the same spot, I only have to move the mouse to the close symbol once and then have a decently efficient -> loop.

    Is there a better way for this scenario? Well, I could simply always have the close in there becaue it doesn't have an effect after show():

    for i in range(10):
        plt.figure()
        plt.plot([1, 3, 2])
        plt.show()  # replace by savefig() when done
        plt.close()
    

    but that's really not intuitive for people who are mostly coming from using show(), which is exactly my point: Why do I need the plt.close() for plt.savefig(), but not for plt.show().

  26. anntzer commented on Mar 29, 2025

    Contributor

    If I'm in a script I would just put a single (blocking, noninteractive) show() at the end of the loop. If I really don't want all the figures to pop up together then show()ing them one at a time is indeed a solution, but I tend to quickly get away from that because this means you can't move backwards in the list of figures, which is often what I need, so I usually go towards more complex solutions like binding key_press_event to be able to switch datasets by pressing up/down.

  27. timhoffm commented on Mar 31, 2025

    Member

    I'm still very much convinced that our story for when the the pyplot state is cleared or not is inconsistent. I can accept the argument "save" does not nce mean "I'm done with the figure", and deregistering the figure should be explicit. But then I'd also expect that closing a (blocking) figure window show() does not mean "I'm dont with the figure" and hence it should not deregister as well, which it currently does. - Though an equally valid argument is that often figures are only output once and it's thus a reasonable convenience that they are deregistered after an output operation by default and users should explicitly state that they want to continue to use the figure.

    But I concede for now, even though I think the behavior is suboptimal.

    To the strong proponents of savefig not closing (@anntzer @jklymak): Please improve the docs to make intended behavior and usage more clear to users:

    • update https://matplotlib.org/stable/users/faq.html#generate-images-without-having-a-window-appear. It is misleading to do this without close(). While it can work if you do this once in a single script, the pattern is unclean and can lead to issues down the road; it's like leaving a file handle open.

    • plt.close() is documented a

      Close a figure window.

      which is incomplete at best. It must explain that it deregisters the figure from pyplot. In particular for the only-
      save-to-file savefig() case the terminology is awkward: you have to do plt.savefig(); plt.close() but there is never a window involved that could be closed. It therefore needs good explanation why you have to close.

  28. timhoffm commented on Dec 13, 2025

    Member

    Closing. The consensus for now is to not change behavior. The improved documentation both in https://matplotlib.org/devdocs/api/_as_gen/matplotlib.pyplot.close.html#matplotlib.pyplot.close and https://matplotlib.org/devdocs/users/faq.html#generate-images-without-having-a-window-appear should make behavior more clear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    API: changesChanges to the public API, typically requiring deprecation.New feature

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL