Skip to content

Commit 7dba22f

Browse files
committed
Refactor Pass 2. Refactored Gcf out of all backend code.
1 parent 00a2252 commit 7dba22f

File tree

8 files changed

+283
-138
lines changed

8 files changed

+283
-138
lines changed

lib/matplotlib/_pylab_helpers.py

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

12+
from matplotlib import is_interactive
1213

1314
def error_msg(msg):
1415
print(msg, file=sys.stderr)
@@ -35,6 +36,16 @@ class Gcf(object):
3536
_activeQue = []
3637
figs = {}
3738

39+
@classmethod
40+
def add_figure_manager(cls, manager):
41+
cls.figs[manager.num] = manager
42+
try: # TODO remove once all backends converted to use the new manager.
43+
manager.mpl_connect('window_destroy_event', cls.destroy_cbk)
44+
except:
45+
pass
46+
47+
cls.set_active(manager)
48+
3849
@classmethod
3950
def get_fig_manager(cls, num):
4051
"""
@@ -46,6 +57,49 @@ def get_fig_manager(cls, num):
4657
cls.set_active(manager)
4758
return manager
4859

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

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

140196
atexit.register(Gcf.destroy_all)

lib/matplotlib/backend_bases.py

Lines changed: 33 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.
@@ -2460,7 +2486,10 @@ def key_press_handler(event, canvas, toolbar=None):
24602486

24612487
# quit the figure (defaut key 'ctrl+w')
24622488
if event.key in quit_keys:
2463-
Gcf.destroy_fig(canvas.figure)
2489+
if isinstance(canvas.manager.mainloop, MainLoopBase): # If new no Gcf.
2490+
canvas.manager._destroy('window_destroy_event')
2491+
else:
2492+
Gcf.destroy_fig(canvas.figure)
24642493

24652494
if toolbar is not None:
24662495
# home or reset mnemonic (default key 'h', 'home' and 'r')
@@ -2540,15 +2569,9 @@ def __init__(self, name, window):
25402569
self.name = name
25412570
self.window = window
25422571

2543-
class WindowBase(object):
2572+
class WindowBase(cbook.EventListener):
25442573
def __init__(self, title):
2545-
self._callbacks = cbook.CallbackRegistry()
2546-
2547-
def mpl_connect(self, s, func):
2548-
return self._callbacks.connect(s, func)
2549-
2550-
def mpl_disconnect(self, cid):
2551-
return self._callbacks.disconnect(cid)
2574+
cbook.EventListener.__init__(self)
25522575

25532576
def show(self):
25542577
"""
@@ -2589,112 +2612,12 @@ def add_element_to_window(self, element, expand, fill, padding, from_start=False
25892612
"""
25902613
pass
25912614

2592-
def terminate_backend(self):
2593-
"""Method to terminate the usage of the backend
2594-
"""
2595-
# TODO refactor me out on second pass
2596-
pass
2597-
25982615
def destroy_event(self, *args):
25992616
s = 'window_destroy_event'
26002617
event = WindowEvent(s, self)
26012618
self._callbacks.process(s, event)
26022619

26032620

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

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