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

Existing FigureCanvasQT objects destroyed by call to plt.figure · Issue #14426 · matplotlib/matplotlib · GitHub

Repository navigation

Existing FigureCanvasQT objects destroyed by call to plt.figure #14426

Closed

Description

Bug report

Bug summary

For a number of years, I have been maintaining an interactive application that embeds subclassed FigureCanvasQT objects within a PyQt application. Up until Matplotlib v3.0.3., it was possible to create standard Matplotlib PyQt figures, i.e., using plt.figure within an embedded IPython shell, that coexist with the subclassed canvas objects. Now in Matplotlib v3.1.0, a call to plt.figure() destroys all the other canvas objects.

Unfortunately, I cannot debug this within Visual Studio since I am currently unable to install Matplotlib from the source. By going through the new_figure_manager code line by line, I can confirm that the windows are destroyed when calling FigureCanvasQT.__init__(figure), but not when calling FigureCanvasBase.__init__(figure), but I can't work out what triggers the destruction of the other windows. I presume the subclassed canvasses are not being registered somewhere, and pyplot assumes they are no longer active, but I am hoping someone will show me where to look. This may not be a Matplotlib bug, but it's certainly an unwelcome side effect in my application.

Code for reproduction
If you have nexpy installed (pip install nexpy) and launch it, you can reproduce this from within the embedded IPython shell with the following, which creates a new subclassed window and then attempts to open a regular pyplot window.:

In [1]: new_window=NXPlotView()
In [2]: plt.get_fignums()
Out[2]: [1, 2]
In [3]: plt.figure()

There are two figure numbers, because NeXpy automatically creates a window with a figure number of 1 when it is launched.

Actual outcome

A new window with an updated figure number is created but all other windows not created by pyplot are destroyed.

In [4]: plt.get_fignums()
Out[4]: [3]

Expected outcome

In Matplotlib v3.0.3, a new pyplot window is created by the PyQt5 backend without destroying anything else.

In [4]: plt.get_fignums()
Out[4]: [1, 2, 3]

Matplotlib version

  • Operating system: Mac OS v10.14.5
  • Matplotlib version: 3.1.0
  • Matplotlib backend: Qt5Agg
  • Python version: 3.7.2
  • Jupyter version (if applicable): 1.0.0
  • Other libraries:

Activity

  1. anntzer commented on Jun 3, 2019

    Contributor

    This bisects to #12637, and is essentially due to the fact that we now initialize ipython/matplotlib support when the first canvas is created (here, by plt.figure()), that during initialization, ipython calls switch_backend, that switch_backend starts by calling close("all"), and that NXPlotView() is registered with pyplot and thus gets closed at that point.

    I think we can actually remove the close("all") (well, modulo backcompat, yada yada)? If there are conflicting event loops, closing figures won't help, if there aren't (like in this case), we don't need to close them.

    In the meantime you can probably get away with creating a figure and immediately closing it -- we only initialize ipython/matplotlib support once.

  2. added this to the v3.1.1 milestone on Jun 3, 2019
  3. added
    Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
    on Jun 3, 2019
  4. rayosborn commented on Jun 4, 2019

    Author

    Thanks for the information. I am subclassing FigureCanvasQT, so I would prefer to preempt the close("all") call, perhaps by callingswitch_backend myself for the first instance, but I can't see how it gets called during the first plt.figure() call. I'll look again tomorrow, but if you have any tips, I would appreciate them. I wondered if it was hidden in the switch_backend_decorator, but I don't see that being used.

  5. anntzer commented on Jun 4, 2019

    Contributor

    switch_backend is called via ip.enable_matplotlib(), which is called in FigureCanvasBase._fix_ipython_backend2gui, which is called in the FigureCanvasBase constructor.

  6. rayosborn commented on Jun 5, 2019

    Author

    I think removing that close("all") might be the only solution for me. I have tried endless permutations of preemptively setting mpl.rcParamsOrig('backend'), calling pt.enable_matplotlib or pt.activate_matplotlib, adding a dummy _fix_ipython_backend2gui to my FigureCanvasQT subclass, even defining my own subclass of _BackendQT5 using the _Backend.export decorator (which produces unhelpful side effects) , but none of them stop IPython from calling switch_backend, whose first line is close("all"). I need to make a new release of NeXpy in the next couple of days, but I am going to have to make Matplotlib v3.0.3 the highest allowed version, unless anyone has other tips for me to try.

  7. anntzer commented on Jun 5, 2019

    Contributor

    It looks like the following workaround does suffice: add a call to FigureCanvas(Figure()) at the toplevel of plotview.py (note that because the figure is not registered with pyplot, it will be immediately garbage collected anyways, but that'll arrange to call _fix_ipython_backend2gui properly).

    (I still do think that we should not call close("all"), but that may take longer to change...)

  8. rayosborn commented on Jun 5, 2019

    Author

    Thanks for the suggestion, which I think would work. In the past few minutes, I have just found an alternative solution. I just have to monkey-patch the IPython InteractiveShell class to do nothing when there is a call to enable_matplotlib. Everything else is already initialized so the call is redundant. It also seems to work.

    I haven't thought about this issue nearly as long as you, but I get the impression that the ultimate solution would have to come from IPython allowing alternative backends to be registered in some well-documented way.

  9. anntzer commented on Jun 5, 2019

    Contributor

    The way we are registering backends with IPython definitely borders API abuse, sorry for breaking things on your side. On the other hand, I would rather have as much of that code as possible living on Matplotlib's side, as cross-project coordination is a much harder problem...

  10. modified the milestones: v3.1.1, v3.2.0 on Jun 6, 2019
  11. removed
    Release criticalFor bugs that make the library unusable (segfaults, incorrect plots, etc) and major regressions.
    on Jun 6, 2019
  12. anntzer commented on Jun 6, 2019

    Contributor

    I remilestone to 3.2 as we have a workaround now, although I do think the API needs to be revisited on our side.

  13. rayosborn commented on Jun 6, 2019

    Author

    I think the simplest short-term fix would be to add an optional keyword argument to the FigureCanvasBase to allow instances to skip the call to _fix_ipython_backend2gui if it's unnecessary. If you are loath to change call signatures, the same thing could be achieved by adding a private class variable (_fix_ipython=True) that a subclass could override.

  14. anntzer commented on Jun 6, 2019

    Contributor

    That doesn't seem much better than just documenting "call FigureCanvas(Figure()) early"?

  15. 19 remaining items

  16. added this to the v3.3.0 milestone on Sep 5, 2019
  17. added a commit that references this issue on Jan 20, 2020
    59589ef
  18. modified the milestones: v3.3.0, v3.4.0 on May 22, 2020
  19. added a commit that references this issue on Aug 24, 2020
    3914e89
  20. modified the milestones: v3.4.0, v3.5.0 on Jan 27, 2021
  21. modified the milestones: v3.5.0, v3.6.0 on Sep 25, 2021
  22. modified the milestones: v3.6.0, v3.7.0 on Jul 8, 2022
  23. added a commit that references this issue on Sep 1, 2022
    893134f
  24. added a commit that references this issue on Dec 19, 2022
    7bcdaf3
  25. added a commit that references this issue on Apr 19, 2026
    8be0755
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions


      Back | FazBrowse Home | New Git URL