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

Commit12988c3

Browse files
committed
Standardize creation of FigureManager from a given FigureCanvas class.
The `new_manager` classmethod may appear to belong more naturally to theFigureManager class rather than the FigureCanvas class, but putting iton FigureCanvas has multiple advantages:- One may want to create managers at times where all one has is a FigureCanvas instance, which may not even have a corresponding manager (e.g. `subplot_tool`).- A given FigureManager class can be associated with many different FigureCanvas classes (indeed, FigureManagerQT can manage both FigureCanvasQTAgg and FigureCanvasQTCairo and also the mplcairo Qt canvas classes), whereas we don't have multiple FigureManager classes for a given FigureCanvas class.Still, put the *actual* logic in FigureManager.create_with_canvas andaccess it via some indirection, as suggested by timhoffm.
1 parent4e20f15 commit12988c3

File tree

9 files changed

+112
-85
lines changed

9 files changed

+112
-85
lines changed

‎lib/matplotlib/backend_bases.py‎

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,13 @@ class FigureCanvasBase:
15801580
# interactive framework is required, or None otherwise.
15811581
required_interactive_framework=None
15821582

1583+
# The manager class instantiated by new_manager.
1584+
# (This is defined as a classproperty because the manager class is
1585+
# currently defined *after* the canvas class, but one could also assign
1586+
# ``FigureCanvasBase.manager_class = FigureManagerBase``
1587+
# after defining both classes.)
1588+
manager_class=_api.classproperty(lambdacls:FigureManagerBase)
1589+
15831590
events= [
15841591
'resize_event',
15851592
'draw_event',
@@ -1662,6 +1669,13 @@ def _fix_ipython_backend2gui(cls):
16621669
if_is_non_interactive_terminal_ipython(ip):
16631670
ip.enable_gui(backend2gui_rif)
16641671

1672+
@classmethod
1673+
defnew_manager(cls,figure,num):
1674+
"""
1675+
Create a new figure manager for *figure*, using this canvas class.
1676+
"""
1677+
returncls.manager_class.create_with_canvas(cls,figure,num)
1678+
16651679
@contextmanager
16661680
def_idle_draw_cntx(self):
16671681
self._is_idle_drawing=True
@@ -2759,6 +2773,16 @@ def notify_axes_change(fig):
27592773
ifself.toolmanagerisNoneandself.toolbarisnotNone:
27602774
self.toolbar.update()
27612775

2776+
@classmethod
2777+
defcreate_with_canvas(cls,canvas_class,figure,num):
2778+
"""
2779+
Create a manager for a given *figure* using a specific *canvas_class*.
2780+
2781+
Backends should override this method if they have specific needs for
2782+
setting up the canvas or the manager.
2783+
"""
2784+
returncls(canvas_class(figure),num)
2785+
27622786
defshow(self):
27632787
"""
27642788
For GUI backends, show the figure window and redraw.
@@ -3225,11 +3249,10 @@ def configure_subplots(self, *args):
32253249
ifhasattr(self,"subplot_tool"):
32263250
self.subplot_tool.figure.canvas.manager.show()
32273251
return
3228-
plt=_safe_pyplot_import()
3252+
# This import needs to happen here due to circular imports.
3253+
frommatplotlib.figureimportFigure
32293254
withmpl.rc_context({"toolbar":"none"}):# No navbar for the toolfig.
3230-
# Use new_figure_manager() instead of figure() so that the figure
3231-
# doesn't get registered with pyplot.
3232-
manager=plt.new_figure_manager(-1, (6,3))
3255+
manager=type(self.canvas).new_manager(Figure(figsize=(6,3)),-1)
32333256
manager.set_window_title("Subplot configuration tool")
32343257
tool_fig=manager.canvas.figure
32353258
tool_fig.subplots_adjust(top=0.9)
@@ -3457,9 +3480,7 @@ def new_figure_manager(cls, num, *args, **kwargs):
34573480
@classmethod
34583481
defnew_figure_manager_given_figure(cls,num,figure):
34593482
"""Create a new figure manager instance for the given figure."""
3460-
canvas=cls.FigureCanvas(figure)
3461-
manager=cls.FigureManager(canvas,num)
3462-
returnmanager
3483+
returncls.FigureCanvas.new_manager(figure,num)
34633484

34643485
@classmethod
34653486
defdraw_if_interactive(cls):

‎lib/matplotlib/backends/_backend_tk.py‎

Lines changed: 38 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def _on_timer(self):
162162

163163
classFigureCanvasTk(FigureCanvasBase):
164164
required_interactive_framework="tk"
165+
manager_class=_api.classproperty(lambdacls:FigureManagerTk)
165166

166167
def__init__(self,figure=None,master=None):
167168
super().__init__(figure)
@@ -431,6 +432,43 @@ def __init__(self, canvas, num, window):
431432

432433
self._shown=False
433434

435+
@classmethod
436+
defcreate_with_canvas(cls,canvas_class,figure,num):
437+
# docstring inherited
438+
with_restore_foreground_window_at_end():
439+
ifcbook._get_running_interactive_framework()isNone:
440+
cbook._setup_new_guiapp()
441+
_c_internal_utils.Win32_SetProcessDpiAwareness_max()
442+
window=tk.Tk(className="matplotlib")
443+
window.withdraw()
444+
445+
# Put a Matplotlib icon on the window rather than the default tk
446+
# icon. See https://www.tcl.tk/man/tcl/TkCmd/wm.html#M50
447+
#
448+
# `ImageTk` can be replaced with `tk` whenever the minimum
449+
# supported Tk version is increased to 8.6, as Tk 8.6+ natively
450+
# supports PNG images.
451+
icon_fname=str(cbook._get_data_path(
452+
'images/matplotlib.png'))
453+
icon_img=ImageTk.PhotoImage(file=icon_fname,master=window)
454+
455+
icon_fname_large=str(cbook._get_data_path(
456+
'images/matplotlib_large.png'))
457+
icon_img_large=ImageTk.PhotoImage(
458+
file=icon_fname_large,master=window)
459+
try:
460+
window.iconphoto(False,icon_img_large,icon_img)
461+
exceptExceptionasexc:
462+
# log the failure (due e.g. to Tk version), but carry on
463+
_log.info('Could not load matplotlib icon: %s',exc)
464+
465+
canvas=canvas_class(figure,master=window)
466+
manager=cls(canvas,num,window)
467+
ifmpl.is_interactive():
468+
manager.show()
469+
canvas.draw_idle()
470+
returnmanager
471+
434472
def_update_window_dpi(self,*args):
435473
newdpi=self._window_dpi.get()
436474
self.window.call('tk','scaling',newdpi/72)
@@ -958,45 +996,6 @@ def trigger(self, *args):
958996
class_BackendTk(_Backend):
959997
FigureManager=FigureManagerTk
960998

961-
@classmethod
962-
defnew_figure_manager_given_figure(cls,num,figure):
963-
"""
964-
Create a new figure manager instance for the given figure.
965-
"""
966-
with_restore_foreground_window_at_end():
967-
ifcbook._get_running_interactive_framework()isNone:
968-
cbook._setup_new_guiapp()
969-
_c_internal_utils.Win32_SetProcessDpiAwareness_max()
970-
window=tk.Tk(className="matplotlib")
971-
window.withdraw()
972-
973-
# Put a Matplotlib icon on the window rather than the default tk
974-
# icon. See https://www.tcl.tk/man/tcl/TkCmd/wm.html#M50
975-
#
976-
# `ImageTk` can be replaced with `tk` whenever the minimum
977-
# supported Tk version is increased to 8.6, as Tk 8.6+ natively
978-
# supports PNG images.
979-
icon_fname=str(cbook._get_data_path(
980-
'images/matplotlib.png'))
981-
icon_img=ImageTk.PhotoImage(file=icon_fname,master=window)
982-
983-
icon_fname_large=str(cbook._get_data_path(
984-
'images/matplotlib_large.png'))
985-
icon_img_large=ImageTk.PhotoImage(
986-
file=icon_fname_large,master=window)
987-
try:
988-
window.iconphoto(False,icon_img_large,icon_img)
989-
exceptExceptionasexc:
990-
# log the failure (due e.g. to Tk version), but carry on
991-
_log.info('Could not load matplotlib icon: %s',exc)
992-
993-
canvas=cls.FigureCanvas(figure,master=window)
994-
manager=cls.FigureManager(canvas,num,window)
995-
ifmpl.is_interactive():
996-
manager.show()
997-
canvas.draw_idle()
998-
returnmanager
999-
1000999
@staticmethod
10011000
defmainloop():
10021001
managers=Gcf.get_all_fig_managers()

‎lib/matplotlib/backends/backend_gtk3.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def _mpl_to_gtk_cursor(mpl_cursor):
7171
classFigureCanvasGTK3(Gtk.DrawingArea,FigureCanvasBase):
7272
required_interactive_framework="gtk3"
7373
_timer_cls=TimerGTK3
74+
manager_class=_api.classproperty(lambdacls:FigureManagerGTK3)
7475
# Setting this as a static constant prevents
7576
# this resulting expression from leaking
7677
event_mask= (Gdk.EventMask.BUTTON_PRESS_MASK

‎lib/matplotlib/backends/backend_gtk4.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class FigureCanvasGTK4(Gtk.DrawingArea, FigureCanvasBase):
3333
required_interactive_framework="gtk4"
3434
supports_blit=False
3535
_timer_cls=TimerGTK4
36+
manager_class=_api.classproperty(lambdacls:FigureManagerGTK4)
3637
_context_is_scaled=False
3738

3839
def__init__(self,figure=None):

‎lib/matplotlib/backends/backend_macosx.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
importmatplotlibasmpl
2-
frommatplotlibimportcbook
2+
frommatplotlibimport_api,cbook
33
frommatplotlib._pylab_helpersimportGcf
44
from .import_macosx
55
from .backend_aggimportFigureCanvasAgg
@@ -25,6 +25,7 @@ class FigureCanvasMac(_macosx.FigureCanvas, FigureCanvasAgg):
2525

2626
required_interactive_framework="macosx"
2727
_timer_cls=TimerMac
28+
manager_class=_api.classproperty(lambdacls:FigureManagerMac)
2829

2930
def__init__(self,figure):
3031
FigureCanvasBase.__init__(self,figure)

‎lib/matplotlib/backends/backend_nbagg.py‎

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,21 @@ def __init__(self, canvas, num):
7878
self._shown=False
7979
super().__init__(canvas,num)
8080

81+
@classmethod
82+
defcreate_with_canvas(cls,canvas_class,figure,num):
83+
canvas=canvas_class(figure)
84+
manager=cls(canvas,num)
85+
ifis_interactive():
86+
manager.show()
87+
canvas.draw_idle()
88+
89+
defdestroy(event):
90+
canvas.mpl_disconnect(cid)
91+
Gcf.destroy(manager)
92+
93+
cid=canvas.mpl_connect('close_event',destroy)
94+
returnmanager
95+
8196
defdisplay_js(self):
8297
# XXX How to do this just once? It has to deal with multiple
8398
# browser instances using the same kernel (require.js - but the
@@ -143,7 +158,7 @@ def remove_comm(self, comm_id):
143158

144159

145160
classFigureCanvasNbAgg(FigureCanvasWebAggCore):
146-
pass
161+
manager_class=FigureManagerNbAgg
147162

148163

149164
classCommSocket:
@@ -228,21 +243,6 @@ class _BackendNbAgg(_Backend):
228243
FigureCanvas=FigureCanvasNbAgg
229244
FigureManager=FigureManagerNbAgg
230245

231-
@staticmethod
232-
defnew_figure_manager_given_figure(num,figure):
233-
canvas=FigureCanvasNbAgg(figure)
234-
manager=FigureManagerNbAgg(canvas,num)
235-
ifis_interactive():
236-
manager.show()
237-
figure.canvas.draw_idle()
238-
239-
defdestroy(event):
240-
canvas.mpl_disconnect(cid)
241-
Gcf.destroy(manager)
242-
243-
cid=canvas.mpl_connect('close_event',destroy)
244-
returnmanager
245-
246246
@staticmethod
247247
defshow(block=None):
248248
## TODO: something to do when keyword block==False ?

‎lib/matplotlib/backends/backend_qt.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ def _timer_stop(self):
232232
classFigureCanvasQT(QtWidgets.QWidget,FigureCanvasBase):
233233
required_interactive_framework="qt"
234234
_timer_cls=TimerQT
235+
manager_class=_api.classproperty(lambdacls:FigureManagerQT)
235236

236237
buttond= {
237238
getattr(_enum("QtCore.Qt.MouseButton"),k):vfork,vin [

‎lib/matplotlib/backends/backend_webagg.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ def run(self):
4848
webagg_server_thread=ServerThread()
4949

5050

51-
classFigureCanvasWebAgg(core.FigureCanvasWebAggCore):
52-
pass
53-
54-
5551
classFigureManagerWebAgg(core.FigureManagerWebAgg):
5652
_toolbar2_class=core.NavigationToolbar2WebAgg
5753

5854

55+
classFigureCanvasWebAgg(core.FigureCanvasWebAggCore):
56+
manager_class=FigureManagerWebAgg
57+
58+
5959
classWebAggApplication(tornado.web.Application):
6060
initialized=False
6161
started=False

‎lib/matplotlib/backends/backend_wx.py‎

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ def error_msg_wx(msg, parent=None):
6363
returnNone
6464

6565

66+
# lru_cache holds a reference to the App and prevents it from being gc'ed.
67+
@functools.lru_cache(1)
68+
def_create_wxapp():
69+
wxapp=wx.App(False)
70+
wxapp.SetExitOnFrameDelete(True)
71+
cbook._setup_new_guiapp()
72+
returnwxapp
73+
74+
6675
classTimerWx(TimerBase):
6776
"""Subclass of `.TimerBase` using wx.Timer events."""
6877

@@ -418,6 +427,7 @@ class _FigureCanvasWxBase(FigureCanvasBase, wx.Panel):
418427

419428
required_interactive_framework="wx"
420429
_timer_cls=TimerWx
430+
manager_class=_api.classproperty(lambdacls:FigureManagerWx)
421431

422432
keyvald= {
423433
wx.WXK_CONTROL:'control',
@@ -970,6 +980,17 @@ def __init__(self, canvas, num, frame):
970980
self.frame=self.window=frame
971981
super().__init__(canvas,num)
972982

983+
@classmethod
984+
defcreate_with_canvas(cls,canvas_class,figure,num):
985+
# docstring inherited
986+
wxapp=wx.GetApp()or_create_wxapp()
987+
frame=FigureFrameWx(num,figure,canvas_class=canvas_class)
988+
manager=figure.canvas.manager
989+
ifmpl.is_interactive():
990+
manager.frame.Show()
991+
figure.canvas.draw_idle()
992+
returnmanager
993+
973994
defshow(self):
974995
# docstring inherited
975996
self.frame.Show()
@@ -1344,24 +1365,6 @@ class _BackendWx(_Backend):
13441365
FigureCanvas=FigureCanvasWx
13451366
FigureManager=FigureManagerWx
13461367

1347-
@classmethod
1348-
defnew_figure_manager_given_figure(cls,num,figure):
1349-
# Create a wx.App instance if it has not been created so far.
1350-
wxapp=wx.GetApp()
1351-
ifwxappisNone:
1352-
wxapp=wx.App()
1353-
wxapp.SetExitOnFrameDelete(True)
1354-
cbook._setup_new_guiapp()
1355-
# Retain a reference to the app object so that it does not get
1356-
# garbage collected.
1357-
_BackendWx._theWxApp=wxapp
1358-
# Attaches figure.canvas, figure.canvas.manager.
1359-
frame=FigureFrameWx(num,figure,canvas_class=cls.FigureCanvas)
1360-
ifmpl.is_interactive():
1361-
frame.Show()
1362-
figure.canvas.draw_idle()
1363-
returnfigure.canvas.manager
1364-
13651368
@staticmethod
13661369
defmainloop():
13671370
ifnotwx.App.IsMainLoopRunning():

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp