_pylab_helpers.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. """
  2. Manage figures for the pyplot interface.
  3. """
  4. import atexit
  5. from collections import OrderedDict
  6. class Gcf:
  7. """
  8. Singleton to maintain the relation between figures and their managers, and
  9. keep track of and "active" figure and manager.
  10. The canvas of a figure created through pyplot is associated with a figure
  11. manager, which handles the interaction between the figure and the backend.
  12. pyplot keeps track of figure managers using an identifier, the "figure
  13. number" or "manager number" (which can actually be any hashable value);
  14. this number is available as the :attr:`number` attribute of the manager.
  15. This class is never instantiated; it consists of an `OrderedDict` mapping
  16. figure/manager numbers to managers, and a set of class methods that
  17. manipulate this `OrderedDict`.
  18. Attributes
  19. ----------
  20. figs : OrderedDict
  21. `OrderedDict` mapping numbers to managers; the active manager is at the
  22. end.
  23. """
  24. figs = OrderedDict()
  25. @classmethod
  26. def get_fig_manager(cls, num):
  27. """
  28. If manager number *num* exists, make it the active one and return it;
  29. otherwise return *None*.
  30. """
  31. manager = cls.figs.get(num, None)
  32. if manager is not None:
  33. cls.set_active(manager)
  34. return manager
  35. @classmethod
  36. def destroy(cls, num):
  37. """
  38. Destroy manager *num* -- either a manager instance or a manager number.
  39. In the interactive backends, this is bound to the window "destroy" and
  40. "delete" events.
  41. It is recommended to pass a manager instance, to avoid confusion when
  42. two managers share the same number.
  43. """
  44. if all(hasattr(num, attr) for attr in ["num", "destroy"]):
  45. manager = num
  46. if cls.figs.get(manager.num) is manager:
  47. cls.figs.pop(manager.num)
  48. else:
  49. try:
  50. manager = cls.figs.pop(num)
  51. except KeyError:
  52. return
  53. if hasattr(manager, "_cidgcf"):
  54. manager.canvas.mpl_disconnect(manager._cidgcf)
  55. manager.destroy()
  56. del manager, num
  57. @classmethod
  58. def destroy_fig(cls, fig):
  59. """Destroy figure *fig*."""
  60. num = next((manager.num for manager in cls.figs.values()
  61. if manager.canvas.figure == fig), None)
  62. if num is not None:
  63. cls.destroy(num)
  64. @classmethod
  65. def destroy_all(cls):
  66. """Destroy all figures."""
  67. for manager in list(cls.figs.values()):
  68. manager.canvas.mpl_disconnect(manager._cidgcf)
  69. manager.destroy()
  70. cls.figs.clear()
  71. @classmethod
  72. def has_fignum(cls, num):
  73. """Return whether figure number *num* exists."""
  74. return num in cls.figs
  75. @classmethod
  76. def get_all_fig_managers(cls):
  77. """Return a list of figure managers."""
  78. return list(cls.figs.values())
  79. @classmethod
  80. def get_num_fig_managers(cls):
  81. """Return the number of figures being managed."""
  82. return len(cls.figs)
  83. @classmethod
  84. def get_active(cls):
  85. """Return the active manager, or *None* if there is no manager."""
  86. return next(reversed(cls.figs.values())) if cls.figs else None
  87. @classmethod
  88. def _set_new_active_manager(cls, manager):
  89. """Adopt *manager* into pyplot and make it the active manager."""
  90. if not hasattr(manager, "_cidgcf"):
  91. manager._cidgcf = manager.canvas.mpl_connect(
  92. "button_press_event", lambda event: cls.set_active(manager))
  93. fig = manager.canvas.figure
  94. fig.number = manager.num
  95. label = fig.get_label()
  96. if label:
  97. manager.set_window_title(label)
  98. cls.set_active(manager)
  99. @classmethod
  100. def set_active(cls, manager):
  101. """Make *manager* the active manager."""
  102. cls.figs[manager.num] = manager
  103. cls.figs.move_to_end(manager.num)
  104. @classmethod
  105. def draw_all(cls, force=False):
  106. """
  107. Redraw all stale managed figures, or, if *force* is True, all managed
  108. figures.
  109. """
  110. for manager in cls.get_all_fig_managers():
  111. if force or manager.canvas.figure.stale:
  112. manager.canvas.draw_idle()
  113. atexit.register(Gcf.destroy_all)