Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 526c2b4

Browse files
committed
Refactor Pass 2. Refactored Gcf out of all backend code.
1 parent cd30d5b commit 526c2b4

File tree

8 files changed

+306
-158
lines changed

8 files changed

+306
-158
lines changed

lib/matplotlib/_pylab_helpers.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import gc
1010
import atexit
1111

12+
from matplotlib import is_interactive
13+
1214

1315
def error_msg(msg):
1416
print(msg, file=sys.stderr)
@@ -35,6 +37,16 @@ class Gcf(object):
3537
_activeQue = []
3638
figs = {}
3739

40+
@classmethod
41+
def add_figure_manager(cls, manager):
42+
cls.figs[manager.num] = manager
43+
try: # TODO remove once all backends converted to use the new manager.
44+
manager.mpl_connect('window_destroy_event', cls.destroy_cbk)
45+
except:
46+
pass
47+
48+
cls.set_active(manager)
49+
3850
@classmethod
3951
def get_fig_manager(cls, num):
4052
"""
@@ -46,6 +58,49 @@ def get_fig_manager(cls, num):
4658
cls.set_active(manager)
4759
return manager
4860

61+
@classmethod
62+
def show_all(cls, block=None):
63+
"""
64+
Show all figures. If *block* is not None, then
65+
it is a boolean that overrides all other factors
66+
determining whether show blocks by calling mainloop().
67+
The other factors are:
68+
it does not block if run inside ipython's "%pylab" mode
69+
it does not block in interactive mode.
70+
"""
71+
managers = cls.get_all_fig_managers()
72+
if not managers:
73+
return
74+
75+
for manager in managers:
76+
manager.show()
77+
78+
if block is not None:
79+
if block:
80+
manager.mainloop()
81+
return
82+
83+
from matplotlib import pyplot
84+
try:
85+
ipython_pylab = not pyplot.show._needmain
86+
# IPython versions >= 0.10 tack the _needmain
87+
# attribute onto pyplot.show, and always set
88+
# it to False, when in %pylab mode.
89+
ipython_pylab = ipython_pylab and get_backend() != 'WebAgg'
90+
# TODO: The above is a hack to get the WebAgg backend
91+
# working with ipython's `%pylab` mode until proper
92+
# integration is implemented.
93+
except AttributeError:
94+
ipython_pylab = False
95+
96+
# Leave the following as a separate step in case we
97+
# want to control this behavior with an rcParam.
98+
if ipython_pylab:
99+
block = False
100+
101+
if not is_interactive() or get_backend() == 'WebAgg':
102+
manager.mainloop()
103+
49104
@classmethod
50105
def destroy(cls, num):
51106
"""
@@ -134,7 +189,9 @@ def set_active(cls, manager):
134189
if m != manager:
135190
cls._activeQue.append(m)
136191
cls._activeQue.append(manager)
137-
cls.figs[manager.num] = manager
138192

193+
@classmethod
194+
def destroy_cbk(cls, event):
195+
cls.destroy(event.figure_manager.num)
139196

140197
atexit.register(Gcf.destroy_all)

lib/matplotlib/backend_bases.py

Lines changed: 35 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import io
4141

4242
import numpy as np
43-
import matplotlib # temporary )assuming we refactor where marked below)
4443
import matplotlib.cbook as cbook
4544
import matplotlib.colors as colors
4645
import matplotlib.transforms as transforms
@@ -133,6 +132,33 @@ def get_registered_canvas_class(format):
133132
return backend_class
134133

135134

135+
class MainLoopBase(object):
136+
"""This gets used as a key maintaining the event loop.
137+
Backends should only need to override begin and end.
138+
It should not matter if this gets used as a singleton or not due to
139+
clever magic.
140+
"""
141+
_instance_count = {}
142+
def __init__(self):
143+
MainLoopBase._instance_count.setdefault(self.__class__, 0)
144+
MainLoopBase._instance_count[self.__class__] += 1
145+
146+
def begin(self):
147+
pass
148+
149+
def end(self):
150+
pass
151+
152+
def __call__(self):
153+
self.begin()
154+
155+
def __del__(self):
156+
MainLoopBase._instance_count[self.__class__] -= 1
157+
if (MainLoopBase._instance_count[self.__class__] <= 0 and
158+
not is_interactive()):
159+
self.end()
160+
161+
136162
class ShowBase(object):
137163
"""
138164
Simple base class to generate a show() callable in backends.
@@ -2462,7 +2488,10 @@ def key_press_handler(event, canvas, toolbar=None):
24622488

24632489
# quit the figure (defaut key 'ctrl+w')
24642490
if event.key in quit_keys:
2465-
Gcf.destroy_fig(canvas.figure)
2491+
if isinstance(canvas.manager.mainloop, MainLoopBase): # If new no Gcf.
2492+
canvas.manager._destroy('window_destroy_event')
2493+
else:
2494+
Gcf.destroy_fig(canvas.figure)
24662495

24672496
if toolbar is not None:
24682497
# home or reset mnemonic (default key 'h', 'home' and 'r')
@@ -2537,20 +2566,16 @@ def key_press_handler(event, canvas, toolbar=None):
25372566
class NonGuiException(Exception):
25382567
pass
25392568

2569+
25402570
class WindowEvent(object):
25412571
def __init__(self, name, window):
25422572
self.name = name
25432573
self.window = window
25442574

2545-
class WindowBase(object):
2546-
def __init__(self, title):
2547-
self._callbacks = cbook.CallbackRegistry()
25482575

2549-
def mpl_connect(self, s, func):
2550-
return self._callbacks.connect(s, func)
2551-
2552-
def mpl_disconnect(self, cid):
2553-
return self._callbacks.disconnect(cid)
2576+
class WindowBase(cbook.EventEmitter):
2577+
def __init__(self, title):
2578+
cbook.EventEmitter.__init__(self)
25542579

25552580
def show(self):
25562581
"""
@@ -2591,112 +2616,12 @@ def add_element_to_window(self, element, expand, fill, padding, from_start=False
25912616
"""
25922617
pass
25932618

2594-
def terminate_backend(self):
2595-
"""Method to terminate the usage of the backend
2596-
"""
2597-
# TODO refactor me out on second pass
2598-
pass
2599-
26002619
def destroy_event(self, *args):
26012620
s = 'window_destroy_event'
26022621
event = WindowEvent(s, self)
26032622
self._callbacks.process(s, event)
26042623

26052624

2606-
class FigureManager(object):
2607-
def __init__(self, canvas, num, classes):
2608-
self._classes = classes
2609-
self.canvas = canvas
2610-
canvas.manager = self
2611-
self.num = num
2612-
2613-
self.key_press_handler_id = self.canvas.mpl_connect('key_press_event',
2614-
self.key_press)
2615-
2616-
self.window = classes['Window']('Figure %d' % num)
2617-
self.window.mpl_connect('window_destroy_event', self._destroy)
2618-
2619-
w = int(self.canvas.figure.bbox.width)
2620-
h = int(self.canvas.figure.bbox.height)
2621-
2622-
self.window.add_element_to_window(self.canvas, True, True, 0, True)
2623-
2624-
self.toolbar = self._get_toolbar(canvas)
2625-
if self.toolbar is not None:
2626-
h += self.window.add_element_to_window(self.toolbar, False, False, 0)
2627-
2628-
self.window.set_default_size(w,h)
2629-
2630-
# Refactor this? If so, delete import matplotlib from above.
2631-
if matplotlib.is_interactive():
2632-
self.window.show()
2633-
2634-
def notify_axes_change(fig):
2635-
'this will be called whenever the current axes is changed'
2636-
if self.toolbar is not None: self.toolbar.update()
2637-
self.canvas.figure.add_axobserver(notify_axes_change)
2638-
2639-
self.canvas.grab_focus()
2640-
2641-
def key_press(self, event):
2642-
"""
2643-
Implement the default mpl key bindings defined at
2644-
:ref:`key-event-handling`
2645-
"""
2646-
key_press_handler(event, self.canvas, self.canvas.toolbar)
2647-
2648-
def _destroy(self, event):
2649-
Gcf.destroy(self.num) # TODO refactor me out of here on second pass!
2650-
2651-
def destroy(self, *args):
2652-
self.window.destroy()
2653-
self.canvas.destroy()
2654-
if self.toolbar:
2655-
self.toolbar.destroy()
2656-
2657-
# TODO refactor out on second pass
2658-
if Gcf.get_num_fig_managers()==0 and not matplotlib.is_interactive():
2659-
self.window.terminate_backend()
2660-
2661-
def show(self):
2662-
self.window.show()
2663-
2664-
def full_screen_toggle(self):
2665-
self._full_screen_flag = not self._full_screen_flag
2666-
self.window.set_fullscreen(self._full_screen_flag)
2667-
2668-
def resize(self, w, h):
2669-
self.window.resize(w,h)
2670-
2671-
def get_window_title(self):
2672-
"""
2673-
Get the title text of the window containing the figure.
2674-
Return None for non-GUI backends (e.g., a PS backend).
2675-
"""
2676-
return self.window.get_window_title()
2677-
2678-
def set_window_title(self, title):
2679-
"""
2680-
Set the title text of the window containing the figure. Note that
2681-
this has no effect for non-GUI backends (e.g., a PS backend).
2682-
"""
2683-
self.window.set_window_title(title)
2684-
2685-
def show_popup(self, msg):
2686-
"""
2687-
Display message in a popup -- GUI only
2688-
"""
2689-
pass
2690-
2691-
def _get_toolbar(self, canvas):
2692-
# must be inited after the window, drawingArea and figure
2693-
# attrs are set
2694-
if rcParams['toolbar'] == 'toolbar2':
2695-
toolbar = self._classes['Toolbar2'](canvas, self.window)
2696-
else:
2697-
toolbar = None
2698-
return toolbar
2699-
27002625
class FigureManagerBase(object):
27012626
"""
27022627
Helper class for pyplot mode, wraps everything up into a neat bundle

0 commit comments

Comments
 (0)