-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
ENH: Allow to register standalone figures with pyplot #29855
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
base: main
Are you sure you want to change the base?
Conversation
0e13c82
to
56f4ee1
Compare
56f4ee1
to
f97c262
Compare
I was hoping I could modify the figure and show again, but that does not seem to be the case import matplotlib.pyplot as plt
from matplotlib.figure import Figure
fig = Figure()
ax = fig.subplots()
ax.plot([0, 2])
plt.figure(fig)
plt.show()
ax.set_title('A cool line')
plt.figure(fig)
plt.show() No title is shown 😕 |
This would also close #19956. |
It may be fundamentally nice not to have to create the figure though pyplot to be able to use it in pyplot afterwards. You can now do ``` from matplotlib.figure import Figure import matplotlib.pyplot as plt fig = Figure() fig.subplots().plot([1, 3, 2]) plt.figure(fig) # fig is now tracked in pyplot plt.show() ``` This also opens up the possibility to more dynamically track and untrack figures in pyplot, which opens up the road to optimized figure tracking in pyplot (matplotlib#29849)
f97c262
to
d0958e4
Compare
Showing again now works properly. When destroying a figure manager, the figure's canvas is reset to a FigureCanvasBase. Technical questions:
|
Note: the failing tests are in matplotlib/lib/matplotlib/tests/test_backends_interactive.py Lines 222 to 235 in 011d12f
This is now no longer the case, as closing the figure resets the canvas to ![]() How should we handle this? I'm inclined to say that the previous expectation is no longer justified, and it makes sense that the canvas is reset. Should we try to fix this with tolerances? Or don't we need this image test at all anymore and instead it is sufficient to test that the figure has again a |
It's a bit strange that the end png result is not the same, as both should ultimately go through Agg for rasterizing... It would be nice to figure out what is going wrong, but I agree this isn't a blocker. |
Failing tests are
and the same again for toolmanager, which I left out for simplicity. So all cairocffi tests fail and addtionally the qtagg-PyQt5 test. But the agg-based tests for PyQt6, PySide2 and PySide6 pass. Interestingly, the qtagg-PyQt5 test passes on my local machine. The above diff image was from qtcairo-PyQt5. |
At least for the cairo tests this is understandable: if you reset to FigureCanvasBase, then they will now rasterize using agg, whereas they rasterized with cairo prior to that. I guess that's probably fine? |
I prefer adding doing this via super() calls: for the benefit of third-parties, it is relatively easier to document "one needs to call super().destroy() in subclasses" rather than "one needs to do this and that" (restore the default canvas, but who knows what else can change in the future). |
My thought was that the specific FigureManager* classes override the canvas in their |
When destroying a manager, replace the figure's canvas by a figure canvas base.
d0958e4
to
a38f9bb
Compare
There in now machinery for all of the public API that takes a renderer is input to get it from the current canvas on the root figure. Use that machinery instead.
I took the liberty of fixing one of the tests. We probably should purge all of the |
To expand, |
Flush the wx canvas, not the base canvas that is installed on close.
This goes up the chain to the GUI classes that do not have this method.
I'm still seeing failures locally but given that CI passes without the last commit, we may want to drop that one (I'm willing to push commits to Tim's branch, but not force push without explicit permission). I'm seeing timeout errors (including what looks like an infinite hang!) locally so I fear that I am the "lucky" developer who can reproduce our random hangs on CI locally. |
@tacaswell you are welcome to force-push |
49381a5
to
c578ec6
Compare
I think this is ready for review (the one failure so far is an invalid DISPLAY which I have seen at least once recently) |
My figure keeps growing... Python 3.12.5 | packaged by conda-forge | (main, Aug 8 2024, 18:36:51) [GCC 12.4.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.26.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import matplotlib.pyplot as plt
...: from matplotlib.figure import Figure
...:
...: fig = Figure()
...: ax = fig.subplots()
...: ax.plot([0, 2])
...: plt.figure(fig)
...: plt.show()
...:
In [2]: plt.figure(fig)
Out[2]: <Figure size 800x600 with 1 Axes>
In [3]: plt.show()
In [4]: plt.figure(fig)
Out[4]: <Figure size 1000x750 with 1 Axes>
In [5]: plt.show()
In [6]: plt.figure(fig)
Out[6]: <Figure size 1250x937.5 with 1 Axes>
In [7]: plt.show()
In [8]: plt.figure(fig)
Out[8]: <Figure size 1850x960 with 1 Axes>
In [9]: plt.show() It seems to be the |
@rcomer Do you have a hi-dpi screen an 125% scaling? |
670c664
to
06b97a8
Compare
Heads up / side-topic: In my example, I found it a bit annoying that I have to
Pyplot would still be used in the background but registering/deregistering would keep a clean state. But that discussion is for a followup. I'm mentioning this just in case you were also stumbling over |
I cannot repoduce the behavior here - figure size stays the same (pyqt 5.15.9; unscaled screen). Does it happen with other (Agg-based) backends as well, e.g. tkagg? |
It may be fundamentally nice not to have to create the figure though pyplot to be able to use it in pyplot afterwards. You can now do
This also opens up the possibility to more dynamically track and untrack figures in pyplot, which opens up the road to optimized figure tracking in pyplot (#29849)
Anybody, feel free to play around with this and try to break it.