|
""" |
|
Manage figures for the pyplot interface. |
|
""" |
|
|
|
import atexit |
|
from collections import OrderedDict |
|
|
|
|
|
class Gcf: |
|
""" |
|
Singleton to maintain the relation between figures and their managers, and |
|
keep track of and "active" figure and manager. |
|
|
|
The canvas of a figure created through pyplot is associated with a figure |
|
manager, which handles the interaction between the figure and the backend. |
|
pyplot keeps track of figure managers using an identifier, the "figure |
|
number" or "manager number" (which can actually be any hashable value); |
|
this number is available as the :attr:`number` attribute of the manager. |
|
|
|
This class is never instantiated; it consists of an `OrderedDict` mapping |
|
figure/manager numbers to managers, and a set of class methods that |
|
manipulate this `OrderedDict`. |
|
|
|
Attributes |
|
---------- |
|
figs : OrderedDict |
|
`OrderedDict` mapping numbers to managers; the active manager is at the |
|
end. |
|
""" |
|
|
|
figs = OrderedDict() |
|
|
|
@classmethod |
|
def get_fig_manager(cls, num): |
|
""" |
|
If manager number *num* exists, make it the active one and return it; |
|
otherwise return *None*. |
|
""" |
|
manager = cls.figs.get(num, None) |
|
if manager is not None: |
|
cls.set_active(manager) |
|
return manager |
|
|
|
@classmethod |
|
def destroy(cls, num): |
|
""" |
|
Destroy manager *num* -- either a manager instance or a manager number. |
|
|
|
In the interactive backends, this is bound to the window "destroy" and |
|
"delete" events. |
|
|
|
It is recommended to pass a manager instance, to avoid confusion when |
|
two managers share the same number. |
|
""" |
|
if all(hasattr(num, attr) for attr in ["num", "destroy"]): |
|
manager = num |
|
if cls.figs.get(manager.num) is manager: |
|
cls.figs.pop(manager.num) |
|
else: |
|
try: |
|
manager = cls.figs.pop(num) |
|
except KeyError: |
|
return |
|
if hasattr(manager, "_cidgcf"): |
|
manager.canvas.mpl_disconnect(manager._cidgcf) |
|
manager.destroy() |
|
del manager, num |
|
|
|
@classmethod |
|
def destroy_fig(cls, fig): |
|
"""Destroy figure *fig*.""" |
|
num = next((manager.num for manager in cls.figs.values() |
|
if manager.canvas.figure == fig), None) |
|
if num is not None: |
|
cls.destroy(num) |
|
|
|
@classmethod |
|
def destroy_all(cls): |
|
"""Destroy all figures.""" |
|
for manager in list(cls.figs.values()): |
|
manager.canvas.mpl_disconnect(manager._cidgcf) |
|
manager.destroy() |
|
cls.figs.clear() |
|
|
|
@classmethod |
|
def has_fignum(cls, num): |
|
"""Return whether figure number *num* exists.""" |
|
return num in cls.figs |
|
|
|
@classmethod |
|
def get_all_fig_managers(cls): |
|
"""Return a list of figure managers.""" |
|
return list(cls.figs.values()) |
|
|
|
@classmethod |
|
def get_num_fig_managers(cls): |
|
"""Return the number of figures being managed.""" |
|
return len(cls.figs) |
|
|
|
@classmethod |
|
def get_active(cls): |
|
"""Return the active manager, or *None* if there is no manager.""" |
|
return next(reversed(cls.figs.values())) if cls.figs else None |
|
|
|
@classmethod |
|
def _set_new_active_manager(cls, manager): |
|
"""Adopt *manager* into pyplot and make it the active manager.""" |
|
if not hasattr(manager, "_cidgcf"): |
|
manager._cidgcf = manager.canvas.mpl_connect( |
|
"button_press_event", lambda event: cls.set_active(manager)) |
|
fig = manager.canvas.figure |
|
fig.number = manager.num |
|
label = fig.get_label() |
|
if label: |
|
manager.set_window_title(label) |
|
cls.set_active(manager) |
|
|
|
@classmethod |
|
def set_active(cls, manager): |
|
"""Make *manager* the active manager.""" |
|
cls.figs[manager.num] = manager |
|
cls.figs.move_to_end(manager.num) |
|
|
|
@classmethod |
|
def draw_all(cls, force=False): |
|
""" |
|
Redraw all stale managed figures, or, if *force* is True, all managed |
|
figures. |
|
""" |
|
for manager in cls.get_all_fig_managers(): |
|
if force or manager.canvas.figure.stale: |
|
manager.canvas.draw_idle() |
|
|
|
|
|
atexit.register(Gcf.destroy_all) |
|
|