Skip to content

Commit a422dca

Browse files
committed
Backend class to factor out common code.
1 parent 22457f3 commit a422dca

26 files changed

+470
-841
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 103 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@
5555
from matplotlib import rcParams
5656
from matplotlib import is_interactive
5757
from matplotlib import get_backend
58-
from matplotlib._pylab_helpers import Gcf
5958
from matplotlib import lines
59+
from matplotlib._pylab_helpers import Gcf
6060

6161
from matplotlib.transforms import Bbox, TransformedBbox, Affine2D
6262

@@ -141,60 +141,118 @@ def get_registered_canvas_class(format):
141141
return backend_class
142142

143143

144-
class ShowBase(object):
145-
"""
146-
Simple base class to generate a show() callable in backends.
144+
class _Backend(object):
145+
# A backend can be defined by using the following pattern:
146+
#
147+
# @_Backend.export
148+
# class FooBackend(_Backend):
149+
# # override the attributes and methods documented below.
147150

148-
Subclass must override mainloop() method.
149-
"""
150-
def __call__(self, block=None):
151+
# The following attributes and methods must be overridden by subclasses.
152+
153+
# The `FigureCanvas` and `FigureManager` classes must be defined.
154+
FigureCanvas = None
155+
FigureManager = None
156+
157+
# The following methods must be left as None for non-interactive backends.
158+
# For interactive backends, `trigger_manager_draw` should be a function
159+
# taking a manager as argument and triggering a canvas draw, and `mainloop`
160+
# should be a function taking no argument and starting the backend main
161+
# loop.
162+
trigger_manager_draw = None
163+
mainloop = None
164+
165+
# The following methods will be automatically defined and exported, but
166+
# can be overridden.
167+
168+
@classmethod
169+
def new_figure_manager(cls, num, *args, **kwargs):
170+
"""Create a new figure manager instance.
171+
"""
172+
# This import needs to happen here due to circular imports.
173+
from matplotlib.figure import Figure
174+
fig_cls = kwargs.pop('FigureClass', Figure)
175+
fig = fig_cls(*args, **kwargs)
176+
return cls.new_figure_manager_given_figure(num, fig)
177+
178+
@classmethod
179+
def new_figure_manager_given_figure(cls, num, figure):
180+
"""Create a new figure manager instance for the given figure.
151181
"""
152-
Show all figures. If *block* is not None, then
153-
it is a boolean that overrides all other factors
154-
determining whether show blocks by calling mainloop().
155-
The other factors are:
156-
it does not block if run inside ipython's "%pylab" mode
157-
it does not block in interactive mode.
182+
canvas = cls.FigureCanvas(figure)
183+
manager = cls.FigureManager(canvas, num)
184+
return manager
185+
186+
@classmethod
187+
def draw_if_interactive(cls):
188+
if cls.trigger_manager_draw is not None and is_interactive():
189+
manager = Gcf.get_active()
190+
if manager:
191+
cls.trigger_manager_draw(manager)
192+
193+
@classmethod
194+
def show(cls, block=None):
195+
"""Show all figures.
196+
197+
`show` blocks by calling `mainloop` if *block* is ``True``, or if it
198+
is ``None`` and we are neither in IPython's ``%pylab`` mode, nor in
199+
`interactive` mode.
158200
"""
201+
if cls.mainloop is None:
202+
return
159203
managers = Gcf.get_all_fig_managers()
160204
if not managers:
161205
return
162-
163206
for manager in managers:
164207
manager.show()
208+
if block is None:
209+
# Hack: Are we in IPython's pylab mode?
210+
from matplotlib import pyplot
211+
try:
212+
# IPython versions >= 0.10 tack the _needmain attribute onto
213+
# pyplot.show, and always set it to False, when in %pylab mode.
214+
ipython_pylab = not pyplot.show._needmain
215+
except AttributeError:
216+
ipython_pylab = False
217+
block = not ipython_pylab and not is_interactive()
218+
# TODO: The above is a hack to get the WebAgg backend working with
219+
# ipython's `%pylab` mode until proper integration is implemented.
220+
if get_backend() == "WebAgg":
221+
block = True
222+
if block:
223+
cls.mainloop()
224+
225+
# This method is the one actually exporting the required methods.
226+
227+
@staticmethod
228+
def export(cls):
229+
for name in ["FigureCanvas",
230+
"FigureManager",
231+
"new_figure_manager",
232+
"new_figure_manager_given_figure",
233+
"draw_if_interactive",
234+
"show"]:
235+
setattr(sys.modules[cls.__module__], name, getattr(cls, name))
236+
237+
# For back-compatibility, generate a shim `Show` class.
238+
239+
class Show(ShowBase):
240+
def mainloop(self):
241+
return cls.mainloop()
242+
243+
setattr(sys.modules[cls.__module__], "Show", Show)
244+
return cls
245+
246+
247+
class ShowBase(_Backend):
248+
"""
249+
Simple base class to generate a show() callable in backends.
165250
166-
if block is not None:
167-
if block:
168-
self.mainloop()
169-
return
170-
else:
171-
return
172-
173-
# Hack: determine at runtime whether we are
174-
# inside ipython in pylab mode.
175-
from matplotlib import pyplot
176-
try:
177-
ipython_pylab = not pyplot.show._needmain
178-
# IPython versions >= 0.10 tack the _needmain
179-
# attribute onto pyplot.show, and always set
180-
# it to False, when in %pylab mode.
181-
ipython_pylab = ipython_pylab and get_backend() != 'WebAgg'
182-
# TODO: The above is a hack to get the WebAgg backend
183-
# working with ipython's `%pylab` mode until proper
184-
# integration is implemented.
185-
except AttributeError:
186-
ipython_pylab = False
187-
188-
# Leave the following as a separate step in case we
189-
# want to control this behavior with an rcParam.
190-
if ipython_pylab:
191-
return
192-
193-
if not is_interactive() or get_backend() == 'WebAgg':
194-
self.mainloop()
251+
Subclass must override mainloop() method.
252+
"""
195253

196-
def mainloop(self):
197-
pass
254+
def __call__(self, block=None):
255+
return self.show(block=block)
198256

199257

200258
class RendererBase(object):

lib/matplotlib/backends/backend_agg.py

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
from collections import OrderedDict
3030
from math import radians, cos, sin
3131
from matplotlib import verbose, rcParams, __version__
32-
from matplotlib.backend_bases import (RendererBase, FigureManagerBase,
33-
FigureCanvasBase)
32+
from matplotlib.backend_bases import (
33+
_Backend, FigureCanvasBase, FigureManagerBase, RendererBase)
3434
from matplotlib.cbook import maxdict, restrict_dict
3535
from matplotlib.figure import Figure
3636
from matplotlib.font_manager import findfont, get_font
@@ -397,24 +397,6 @@ def post_processing(image, dpi):
397397
gc, l + ox, height - b - h + oy, img)
398398

399399

400-
def new_figure_manager(num, *args, **kwargs):
401-
"""
402-
Create a new figure manager instance
403-
"""
404-
FigureClass = kwargs.pop('FigureClass', Figure)
405-
thisFig = FigureClass(*args, **kwargs)
406-
return new_figure_manager_given_figure(num, thisFig)
407-
408-
409-
def new_figure_manager_given_figure(num, figure):
410-
"""
411-
Create a new figure manager instance for the given figure.
412-
"""
413-
canvas = FigureCanvasAgg(figure)
414-
manager = FigureManagerBase(canvas, num)
415-
return manager
416-
417-
418400
class FigureCanvasAgg(FigureCanvasBase):
419401
"""
420402
The canvas the figure renders into. Calls the draw and print fig
@@ -609,4 +591,7 @@ def print_tif(self, filename_or_obj, *args, **kwargs):
609591
print_tiff = print_tif
610592

611593

612-
FigureCanvas = FigureCanvasAgg
594+
@_Backend.export
595+
class _BackendAgg(_Backend):
596+
FigureCanvas = FigureCanvasAgg
597+
FigureManager = FigureManagerBase

lib/matplotlib/backends/backend_cairo.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,12 @@
5252
del _version_required
5353

5454
from matplotlib.backend_bases import (
55-
RendererBase, GraphicsContextBase, FigureManagerBase, FigureCanvasBase)
56-
from matplotlib.figure import Figure
57-
from matplotlib.mathtext import MathTextParser
58-
from matplotlib.path import Path
59-
from matplotlib.transforms import Bbox, Affine2D
55+
_Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase,
56+
RendererBase)
57+
from matplotlib.figure import Figure
58+
from matplotlib.mathtext import MathTextParser
59+
from matplotlib.path import Path
60+
from matplotlib.transforms import Bbox, Affine2D
6061
from matplotlib.font_manager import ttfFontProperty
6162

6263

@@ -549,4 +550,7 @@ def _save(self, fo, fmt, **kwargs):
549550
fo.close()
550551

551552

552-
FigureCanvas = FigureCanvasCairo
553+
@_Backend.export
554+
class _BackendCairo(_Backend):
555+
FigureCanvas = FigureCanvasCairo
556+
FigureManager = FigureManagerBase

lib/matplotlib/backends/backend_gdk.py

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
from matplotlib import rcParams
2525
from matplotlib._pylab_helpers import Gcf
2626
from matplotlib.backend_bases import (
27-
RendererBase, GraphicsContextBase, FigureManagerBase, FigureCanvasBase)
27+
_Backend, FigureCanvasBase, FigureManagerBase, GraphicsContextBase,
28+
RendererBase)
2829
from matplotlib.cbook import restrict_dict, warn_deprecated
2930
from matplotlib.figure import Figure
3031
from matplotlib.mathtext import MathTextParser
@@ -381,24 +382,6 @@ def set_linewidth(self, w):
381382
self.gdkGC.line_width = max(1, int(np.round(pixels)))
382383

383384

384-
def new_figure_manager(num, *args, **kwargs):
385-
"""
386-
Create a new figure manager instance
387-
"""
388-
FigureClass = kwargs.pop('FigureClass', Figure)
389-
thisFig = FigureClass(*args, **kwargs)
390-
return new_figure_manager_given_figure(num, thisFig)
391-
392-
393-
def new_figure_manager_given_figure(num, figure):
394-
"""
395-
Create a new figure manager instance for the given figure.
396-
"""
397-
canvas = FigureCanvasGDK(figure)
398-
manager = FigureManagerBase(canvas, num)
399-
return manager
400-
401-
402385
class FigureCanvasGDK (FigureCanvasBase):
403386
def __init__(self, figure):
404387
FigureCanvasBase.__init__(self, figure)
@@ -452,3 +435,9 @@ def _print_image(self, filename, format, *args, **kwargs):
452435
options['quality'] = str(options['quality'])
453436

454437
pixbuf.save(filename, format, options=options)
438+
439+
440+
@_Backend.export
441+
class _BackendGDK(_Backend):
442+
FigureCanvas = FigureCanvasGDK
443+
FigureManager = FigureManagerBase

lib/matplotlib/backends/backend_gtk.py

Lines changed: 19 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,14 @@
2828

2929
import matplotlib
3030
from matplotlib._pylab_helpers import Gcf
31-
from matplotlib.backend_bases import RendererBase, GraphicsContextBase, \
32-
FigureManagerBase, FigureCanvasBase, NavigationToolbar2, cursors, TimerBase
33-
from matplotlib.backend_bases import ShowBase
31+
from matplotlib.backend_bases import (
32+
_Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2,
33+
TimerBase, cursors)
3434

3535
from matplotlib.backends.backend_gdk import RendererGDK, FigureCanvasGDK
36-
from matplotlib.cbook import is_writable_file_like
36+
from matplotlib.cbook import is_writable_file_like, warn_deprecated
3737
from matplotlib.figure import Figure
3838
from matplotlib.widgets import SubplotTool
39-
from matplotlib.cbook import warn_deprecated
4039

4140
from matplotlib import (
4241
cbook, colors as mcolors, lines, markers, rcParams, verbose)
@@ -63,41 +62,6 @@ def GTK_WIDGET_DRAWABLE(w):
6362
return flags & gtk.VISIBLE != 0 and flags & gtk.MAPPED != 0
6463

6564

66-
def draw_if_interactive():
67-
"""
68-
Is called after every pylab drawing command
69-
"""
70-
if matplotlib.is_interactive():
71-
figManager = Gcf.get_active()
72-
if figManager is not None:
73-
figManager.canvas.draw_idle()
74-
75-
76-
class Show(ShowBase):
77-
def mainloop(self):
78-
if gtk.main_level() == 0:
79-
gtk.main()
80-
81-
show = Show()
82-
83-
def new_figure_manager(num, *args, **kwargs):
84-
"""
85-
Create a new figure manager instance
86-
"""
87-
FigureClass = kwargs.pop('FigureClass', Figure)
88-
thisFig = FigureClass(*args, **kwargs)
89-
return new_figure_manager_given_figure(num, thisFig)
90-
91-
92-
def new_figure_manager_given_figure(num, figure):
93-
"""
94-
Create a new figure manager instance for the given figure.
95-
"""
96-
canvas = FigureCanvasGTK(figure)
97-
manager = FigureManagerGTK(canvas, num)
98-
return manager
99-
100-
10165
class TimerGTK(TimerBase):
10266
'''
10367
Subclass of :class:`backend_bases.TimerBase` using GTK for timer events.
@@ -521,6 +485,7 @@ def stop_event_loop(self):
521485
FigureCanvasBase.stop_event_loop_default(self)
522486
stop_event_loop.__doc__=FigureCanvasBase.stop_event_loop_default.__doc__
523487

488+
524489
class FigureManagerGTK(FigureManagerBase):
525490
"""
526491
Attributes
@@ -866,6 +831,7 @@ def get_filename_from_user (self):
866831

867832
return filename, self.ext
868833

834+
869835
class DialogLineprops(object):
870836
"""
871837
A GUI dialog for controlling lineprops
@@ -1056,5 +1022,16 @@ def error_msg_gtk(msg, parent=None):
10561022
dialog.destroy()
10571023

10581024

1059-
FigureCanvas = FigureCanvasGTK
1060-
FigureManager = FigureManagerGTK
1025+
@_Backend.export
1026+
class _BackendGTK(_Backend):
1027+
FigureCanvas = FigureCanvasGTK
1028+
FigureManager = FigureManagerGTK
1029+
1030+
@staticmethod
1031+
def trigger_manager_draw(manager):
1032+
manager.canvas.draw_idle()
1033+
1034+
@staticmethod
1035+
def mainloop():
1036+
if gtk.main_level() == 0:
1037+
gtk.main()

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy