-
-
Notifications
You must be signed in to change notification settings - Fork 8k
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?
Changes from 19 commits
bc31177
c718eae
a38f9bb
eab4a89
2396e90
b7a5cfd
c578ec6
06b97a8
db30cbe
1ea856d
0e078a7
1784c73
782750e
8b77fff
5906a4f
545892a
6af9062
6b1797b
b42ce64
774d02c
ccf1994
623fe4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
Figures can be attached to and removed from pyplot | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
Figures can now be attached to and removed from management through pyplot, which in | ||
the background also means a less strict coupling to backends. | ||
|
||
In particular, standalone figures (created with the `.Figure` constructor) can now be | ||
registered with the `.pyplot` module by calling ``plt.figure(fig)``. This allows to | ||
show them with ``plt.show()`` as you would do with any figure created with pyplot | ||
factory methods such as ``plt.figure()`` or ``plt.subplots()``. | ||
|
||
When closing a shown figure window, the related figure is reset to the standalone | ||
state, i.e. it's not visible to pyplot anymore, but if you still hold a reference | ||
to it, you can continue to work with it (e.g. do ``fig.savefig()``, or re-add it | ||
to pyplot with ``plt.figure(fig)`` and then show it again). | ||
|
||
The following is now possible - though the example is exaggerated to show what's | ||
possible. In practice, you'll stick with much simpler versions for better | ||
consistency :: | ||
|
||
import matplotlib.pyplot as plt | ||
from matplotlib.figure import Figure | ||
|
||
# Create a standalone figure | ||
fig = Figure() | ||
ax = fig.add_subplot() | ||
ax.plot([1, 2, 3], [4, 5, 6]) | ||
|
||
# Register it with pyplot | ||
plt.figure(fig) | ||
|
||
# Modify the figure through pyplot | ||
plt.xlabel("x label") | ||
|
||
# Show the figure | ||
plt.show() | ||
|
||
# Close the figure window through the GUI | ||
|
||
# Continue to work on the figure | ||
fig.savefig("my_figure.png") | ||
ax.set_ylabel("y label") | ||
|
||
# Re-register the figure and show it again | ||
plt.figure(fig) | ||
plt.show() | ||
|
||
Technical detail: Standalone figures use `.FigureCanvasBase` as canvas. This is | ||
replaced by a backend-dependent subclass when registering with pyplot, and is | ||
reset to `.FigureCanvasBase` when the figure is closed. `.Figure.savefig` uses | ||
the current canvas to save the figure (if possible). Since `.FigureCanvasBase` | ||
can not render the figure, when saving the figure, it will fallback to a suitable | ||
canvas subclass, e.g. `.FigureCanvasAgg` for raster outputs such as png. | ||
Any Agg-based backend will create the same file output, however | ||
There may be slight differences for non-Agg backends; e.g. if you use "GTK4Cairo" as | ||
interactive backend, ``fig.savefig("file.png")`` may create a slightly different | ||
image depending on whether the figure is registered with pyplot or not. In | ||
general, you should not store a reference to the canvas, but rather always | ||
obtain it from the figure with ``fig.canvas``. This will return the current | ||
canvas, which is either the original `.FigureCanvasBase` or a backend-dependent | ||
subclass, depending on whether the figure is registered with pyplot or not. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe add a comment that there are currently known limitations wrt. blitting/widgets (#30503)? We can always edit that out once that issue is fixed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added
|
||
Additionally, the swapping of the canvas currently does not play well with | ||
blitting of matplotlib widgets; in such cases either deactivate blitting or do not | ||
continue to use the figure (e.g. saving it after closing the window). |
Original file line number | Diff line number | Diff line change | ||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -686,6 +686,7 @@ bool mpl_check_modifier(bool present, PyObject* list, char const* name) | |||||||||||||||
{ | ||||||||||||||||
[self->window close]; | ||||||||||||||||
self->window = NULL; | ||||||||||||||||
[super destroy]; | ||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I could need a bit of help here from someone with more Objective-C / Cpython knowledge. This is missing the equivalent of
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you need to go into the objective C layer or can you stay at the Python layer (have not looked at the updates here in detail). But, we do have some multiple inheritance going on here, so you may have to explicitly call which super you want to interact with at this place. (I think the super().destroy() currently on these lines goes to the objective C implementation, and you want to go the FigureManagerBase implementation [we want to travel to both it sounds like]) matplotlib/lib/matplotlib/backends/backend_macosx.py Lines 168 to 174 in 352b419
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. super() goes to the next class in MRO, and that covers the whole inheritance tree also in case of multiple inheritance if all classes in the hierarchy call it without explicitly stating the parent. That‘s what I want. I do not want to mess with calling any specific parent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My quick suggestion would be to avoid trying to call super() from the C-layer. To avoid the multiple inheritance issue, you can rename the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe something along the lines of https://stackoverflow.com/questions/42201881/python-c-api-how-to-call-base-type-method-from-subtype would work? |
||||||||||||||||
Py_RETURN_NONE; | ||||||||||||||||
} | ||||||||||||||||
|
||||||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.