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

Matplotlib.pyplot.close() interacts with tk.Tk() and closes the tk.Tk() object. · Issue #13470 · matplotlib/matplotlib · GitHub

Repository navigation

Matplotlib.pyplot.close() interacts with tk.Tk() and closes the tk.Tk() object. #13470

Closed

Description

Bug report

Bug summary

The bug revolves around the interaction between tkinter and matplotlib, while using PdfPages. Specifically, somehow a call to pyplot.close() let's the current 'module' finish but afterwards the tk.Tk() instance is closed without warning/error (see the attached MVE code). I expect that this is a bug in the matplotlib domain after hearing that this occurs more often (https://stackoverflow.com/q/54772180/1093485).

Code for reproduction

import tkinter as tk
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.backends.backend_pdf import PdfPages
from pathlib import Path

class Pdf(object):
    def __init__(self, master):
        self.master = master
        pdf = PdfPages(Path.cwd() / 'demo.pdf')

        self.pdf = pdf

    def plot_initial(self):
        fig = plt.figure(figsize=(8,6))
        fig.add_subplot(111)

        mu, sigma = 0, 0.1
        s = np.random.normal(mu, sigma, 1000)
        count, bins, ignored = plt.hist(s, 30, density=True)
        plt.plot(bins, 1/(sigma * np.sqrt(2 * np.pi)) *
                 np.exp( - (bins - mu)**2 / (2 * sigma**2) ),
                 linewidth=2, color='r')
        plt.title('Overview')
        plt.xlabel('X')
        plt.ylabel('Y')
        self.pdf.savefig(fig)
        # THE CULPRIT
        plt.close(fig)

    def plot_extra(self):
        fig = plt.figure(figsize=(8,6))
        fig.add_subplot(111)

        mu, sigma = 0, 0.1
        s = np.random.normal(mu, sigma, 1000)
        count, bins, ignored = plt.hist(s, 30, density=True)
        plt.plot(bins, 1/(sigma * np.sqrt(2 * np.pi)) *
                 np.exp( - (bins - mu)**2 / (2 * sigma**2) ),
                 linewidth=2, color='r')
        plt.title('Extra')
        plt.xlabel('X')
        plt.ylabel('Y')
        self.pdf.savefig(fig)
        plt.close(fig)

    def close(self):
        self.pdf.close()

class MVE(object):
    @classmethod
    def run(cls):
        root = tk.Tk()
        MVE(root)
        root.mainloop()

    def __init__(self, master):
        self.root = master
        tk.Frame(master)

        menu = tk.Menu(master)
        master.config(menu=menu)

        test_menu = tk.Menu(menu, tearoff=0)
        menu.add_cascade(label='Bug', menu=test_menu)
        test_menu.add_command(label='PDF', command=
                              self.generate_pdf)

    def generate_pdf(self):
        pdf = Pdf(self)

        pdf.plot_initial()
        for i in range(0,3):
            pdf.plot_extra()
        pdf.close()

if __name__ == "__main__":
    MVE.run()

Expected outcome

I expect that the initial tk.Tk() window isn't closed once the PDF report has been generated.

Matplotlib version

  • Operating system: Tested on Win 7 and Win 10
  • Matplotlib version: 2.2.3 from pip
  • Matplotlib backend (print(matplotlib.get_backend())): TkAgg
  • Python version: 3.7.0
  • Jupyter version (if applicable):
  • Other libraries: Tkinter 8.6 from pip

Activity

  1. ImportanceOfBeingErnest commented on Feb 19, 2019

    Member

    This does not occur in matplotlib 3.0.2. Very likely it has been fixed by #12707. It has however not been backported to 2.2 Not sure what's going on, it sometimes works, sometimes not.

  2. Tarskin commented on Feb 19, 2019

    Author

    I am still encountering the issue after upgrading to 3.0.2 (TkAgg backend), with the above code snippet.

  3. ImportanceOfBeingErnest commented on Feb 19, 2019

    Member

    What is your exact version of Tk? With TK version 8.6.8 I cannot reproduce this, while it appears to fail with 8.6.7.

  4. Tarskin commented on Feb 19, 2019

    Author

    I think simply 8.6 as that's what tkinter.TkVersion lists, unless that lists only major versions somehow.

  5. ImportanceOfBeingErnest commented on Feb 19, 2019

    Member

    Now I'm really lost. I can reproduce this with any mpl version, and any of the two tk versions if this is being run from the command line. If however I run it from within Spyder with the setting "Use external system command" (which I would have thought to be identical to the CLI) it works as expected. (In both cases adding matplotlib.use("TkAgg"), as other backends don't show this issue)

  6. tacaswell commented on Feb 20, 2019

    Member

    The "problem" is the logic in

    def destroy(self, *args):
    if self.window is not None:
    #self.toolbar.destroy()
    if self.canvas._idle_callback:
    self.canvas._tkcanvas.after_cancel(self.canvas._idle_callback)
    self.window.destroy()
    if Gcf.get_num_fig_managers() == 0:
    if self.window is not None:
    self.window.quit()
    self.window = None
    which quits the application when all of the figures are closed. This is essential for plt.show(block=True) to work (so when you close all of the plots we return control to the terminal). Is there a way to ask tk "do you still have any windows open" to trigger that logic on instead?

    In this case do you want to show the figures as interactive tk windows or just save pdf? In the former I think you may have to do the embedding your self, if the later, picking a non-interactive backend is probably the correct solution.

    I have no guesses as to why in is working is spyder though...

  7. modified the milestones: v3.0.3, v3.2.0 on Feb 20, 2019
  8. tacaswell commented on Feb 20, 2019

    Member

    moved to 3.2 as this is not a regression.

  9. Tarskin commented on Feb 20, 2019

    Author

    The program should be able to have plots open in it's own GUI or create plots in a PDF report (depending on the user selection). Would it in any way be useful for you to see the full code of how I was using this?

    I think I may always have mis-interpreted the meaning of a non-interactive backend as using simply ('Agg') fixes this issue for my system, while still providing the option to interact with the plot inside the tkinter window.

  10. tacaswell commented on Feb 20, 2019

    Member

    See https://matplotlib.org/gallery/user_interfaces/embedding_in_tk_sgskip.html for how to do a 'full embedding'. If you are writing a stand-alone application, I strongly suggest going that route and not even importing pyplot.

    This is yet another of the points of tension in Matplotlib between providing a low-level library to be used by application developers and being a front-line user interface. It this case, the niceties we need to provide for the end-user (ex, exiting the GUI main loop when the last figure is closed) is conflicting with how @Tarskin wants to use Matplot as a low-level library.

    @Tarskin "interactive" means we hook up all the mouse / keyboard listeners + embedded the image of the plot in a native-GUI window + re-draw on changes + do some rudimentary hooking into the GUI main loop (basically, all the details that need to happen for plt.ion(); plt.plot(range(5)) to pop up a 'live' window you can pan / zoom / mutate and have it 'do the right thing')

  11. Tarskin commented on Feb 20, 2019

    Author

    The plotting was already embedded in the GUI part, and I was trying to be 'simple' in my Pdf class that created the PDF reports. I am now refactoring those to use Figure instead, which I didn't consider. I do want to check if I understood you correctly, in suggesting that one should never use pyplot in a stand-alone and instead resort to always working with the Figure class?

    I will admit that just using the Figure class directly, makes it much simpler as the underlying logic is then the same for the gui and the reports, I should have considered that to begin with. Thanks for the tip!

  12. tacaswell commented on Feb 20, 2019

    Member

    https://matplotlib.org/tutorials/introductory/usage.html#coding-styles may also be a useful link (but it sounds like you already figured out everything in there!).

    I am going to go ahead and close this as it sounds like this is primarily a conflict in usage and not a bug we can fix.

  13. anntzer commented on Feb 20, 2019

    Contributor

    I think we could at least try to not close the main Tk instance as long as there are windows (not managed by matplotlib) left open?

  14. added
    🌱 Good first issueOpen a pull request against these issues if there are no active ones!
    on Feb 25, 2019
  15. tacaswell commented on Feb 25, 2019

    Member

    That is a fair point, re-opening.

    Tagging as good first issue as this is primarily an issue with how we use Tk (rather than Matplotlib internals).

  16. sameshl commented on Aug 11, 2019

    Contributor

    @tacaswell Could you summarize what should be done here? I would love to contribute to this.

  17. richardsheridan commented on Jun 30, 2020

    Contributor

    @tacaswell This looks fixable but the fix is not pretty without help from upstream, see #17802.

  18. removed this from the future releases milestone on Oct 6, 2022
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

    GUI: tk🌱 Good first issueOpen a pull request against these issues if there are no active ones!

    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