142 lines
4.4 KiB
Python
142 lines
4.4 KiB
Python
|
"""
|
||
|
Manage figures for the pyplot interface.
|
||
|
"""
|
||
|
|
||
|
import atexit
|
||
|
from collections import OrderedDict
|
||
|
import gc
|
||
|
|
||
|
|
||
|
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", "_cidgcf", "destroy"]):
|
||
|
manager = num
|
||
|
if cls.figs.get(manager.num) is manager:
|
||
|
cls.figs.pop(manager.num)
|
||
|
else:
|
||
|
return
|
||
|
else:
|
||
|
try:
|
||
|
manager = cls.figs.pop(num)
|
||
|
except KeyError:
|
||
|
return
|
||
|
manager.canvas.mpl_disconnect(manager._cidgcf)
|
||
|
manager.destroy()
|
||
|
gc.collect(1)
|
||
|
|
||
|
@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."""
|
||
|
# Reimport gc in case the module globals have already been removed
|
||
|
# during interpreter shutdown.
|
||
|
import gc
|
||
|
for manager in list(cls.figs.values()):
|
||
|
manager.canvas.mpl_disconnect(manager._cidgcf)
|
||
|
manager.destroy()
|
||
|
cls.figs.clear()
|
||
|
gc.collect(1)
|
||
|
|
||
|
@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)
|