Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3db41bd

Browse files
authored
Merge pull request#29855 from timhoffm/pyplot-register-figure
ENH: Allow to register standalone figures with pyplot
2 parents5b38f50 +623fe4d commit3db41bd

File tree

17 files changed

+174
-19
lines changed

17 files changed

+174
-19
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
Figures can be attached to and removed from pyplot
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
Figures can now be attached to and removed from management through pyplot, which in
4+
the background also means a less strict coupling to backends.
5+
6+
In particular, standalone figures (created with the `.Figure` constructor) can now be
7+
registered with the `.pyplot` module by calling ``plt.figure(fig)``. This allows to
8+
show them with ``plt.show()`` as you would do with any figure created with pyplot
9+
factory methods such as ``plt.figure()`` or ``plt.subplots()``.
10+
11+
When closing a shown figure window, the related figure is reset to the standalone
12+
state, i.e. it's not visible to pyplot anymore, but if you still hold a reference
13+
to it, you can continue to work with it (e.g. do ``fig.savefig()``, or re-add it
14+
to pyplot with ``plt.figure(fig)`` and then show it again).
15+
16+
The following is now possible - though the example is exaggerated to show what's
17+
possible. In practice, you'll stick with much simpler versions for better
18+
consistency ::
19+
20+
import matplotlib.pyplot as plt
21+
from matplotlib.figure import Figure
22+
23+
# Create a standalone figure
24+
fig = Figure()
25+
ax = fig.add_subplot()
26+
ax.plot([1, 2, 3], [4, 5, 6])
27+
28+
# Register it with pyplot
29+
plt.figure(fig)
30+
31+
# Modify the figure through pyplot
32+
plt.xlabel("x label")
33+
34+
# Show the figure
35+
plt.show()
36+
37+
# Close the figure window through the GUI
38+
39+
# Continue to work on the figure
40+
fig.savefig("my_figure.png")
41+
ax.set_ylabel("y label")
42+
43+
# Re-register the figure and show it again
44+
plt.figure(fig)
45+
plt.show()
46+
47+
Technical detail: Standalone figures use `.FigureCanvasBase` as canvas. This is
48+
replaced by a backend-dependent subclass when registering with pyplot, and is
49+
reset to `.FigureCanvasBase` when the figure is closed. `.Figure.savefig` uses
50+
the current canvas to save the figure (if possible). Since `.FigureCanvasBase`
51+
can not render the figure, when saving the figure, it will fallback to a suitable
52+
canvas subclass, e.g. `.FigureCanvasAgg` for raster outputs such as png.
53+
Any Agg-based backend will create the same file output. However, there may be
54+
slight differences for non-Agg backends; e.g. if you use "GTK4Cairo" as
55+
interactive backend, ``fig.savefig("file.png")`` may create a slightly different
56+
image depending on whether the figure is registered with pyplot or not. In
57+
general, you should not store a reference to the canvas, but rather always
58+
obtain it from the figure with ``fig.canvas``. This will return the current
59+
canvas, which is either the original `.FigureCanvasBase` or a backend-dependent
60+
subclass, depending on whether the figure is registered with pyplot or not.
61+
Additionally, the swapping of the canvas currently does not play well with
62+
blitting of matplotlib widgets; in such cases either deactivate blitting or do not
63+
continue to use the figure (e.g. saving it after closing the window).

‎lib/matplotlib/_pylab_helpers.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def destroy(cls, num):
5353
two managers share the same number.
5454
"""
5555
ifall(hasattr(num,attr)forattrin ["num","destroy"]):
56+
# num is a manager-like instance (not necessarily a
57+
# FigureManagerBase subclass)
5658
manager=num
5759
ifcls.figs.get(manager.num)ismanager:
5860
cls.figs.pop(manager.num)

‎lib/matplotlib/backend_bases.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2806,7 +2806,9 @@ def show(self):
28062806
f"shown")
28072807

28082808
defdestroy(self):
2809-
pass
2809+
# managers may have swapped the canvas to a GUI-framework specific one.
2810+
# restore the base canvas when the manager is destroyed.
2811+
self.canvas.figure._set_base_canvas()
28102812

28112813
deffull_screen_toggle(self):
28122814
pass

‎lib/matplotlib/backends/_backend_gtk.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ def destroy(self, *args):
195195
self._destroying=True
196196
self.window.destroy()
197197
self.canvas.destroy()
198+
super().destroy()
198199

199200
@classmethod
200201
defstart_main_loop(cls):

‎lib/matplotlib/backends/_backend_tk.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,7 @@ def delayed_destroy():
634634
else:
635635
self.window.update()
636636
delayed_destroy()
637+
super().destroy()
637638

638639
defget_window_title(self):
639640
returnself.window.wm_title()

‎lib/matplotlib/backends/backend_gtk3.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ def __init__(self, figure=None):
9696

9797
defdestroy(self):
9898
CloseEvent("close_event",self)._process()
99+
super().destroy()
99100

100101
defset_cursor(self,cursor):
101102
# docstring inherited

‎lib/matplotlib/backends/backend_nbagg.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ def destroy(self):
142142
forcomminlist(self.web_sockets):
143143
comm.on_close()
144144
self.clearup_closed()
145+
super().destroy()
145146

146147
defclearup_closed(self):
147148
"""Clear up any closed Comms."""

‎lib/matplotlib/backends/backend_qt.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,7 @@ def destroy(self, *args):
674674
ifself.toolbar:
675675
self.toolbar.destroy()
676676
self.window.close()
677+
super().destroy()
677678

678679
defget_window_title(self):
679680
returnself.window.windowTitle()

‎lib/matplotlib/backends/backend_wx.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,7 @@ def destroy(self, *args):
10121012
# As this can be called from non-GUI thread from plt.close use
10131013
# wx.CallAfter to ensure thread safety.
10141014
wx.CallAfter(frame.Close)
1015+
super().destroy()
10151016

10161017
deffull_screen_toggle(self):
10171018
# docstring inherited

‎lib/matplotlib/figure.py‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2642,7 +2642,7 @@ def __init__(self,
26422642
self._set_artist_props(self.patch)
26432643
self.patch.set_antialiased(False)
26442644

2645-
FigureCanvasBase(self)# Set self.canvas.
2645+
self._set_base_canvas()
26462646

26472647
ifsubplotparsisNone:
26482648
subplotpars=SubplotParams()
@@ -2996,6 +2996,20 @@ def get_constrained_layout_pads(self, relative=False):
29962996

29972997
returnw_pad,h_pad,wspace,hspace
29982998

2999+
def_set_base_canvas(self):
3000+
"""
3001+
Initialize self.canvas with a FigureCanvasBase instance.
3002+
3003+
This is used upon initialization of the Figure, but also
3004+
to reset the canvas when decoupling from pyplot.
3005+
"""
3006+
# check if we have changed the DPI due to hi-dpi screens
3007+
orig_dpi=getattr(self,'_original_dpi',self._dpi)
3008+
FigureCanvasBase(self)# Set self.canvas as a side-effect
3009+
# put it back to what it was
3010+
iforig_dpi!=self._dpi:
3011+
self.dpi=orig_dpi
3012+
29993013
defset_canvas(self,canvas):
30003014
"""
30013015
Set the canvas that contains the figure

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp