From cfd768e7cd09257297645cba7f22bc10eff76c89 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Tue, 23 Apr 2019 22:53:30 +0200 Subject: [PATCH] Simpler "pyplotless" use pattern. Right now, if one does not want to use pyplot (e.g., batch use), one can do from matplotlib.figure import Figure fig = Figure(); ... ...; fig.savefig(...) but it is impossible to show() the figure interactively (i.e. pyplot cannot "adopt" the figure) e.g. for debugging. This patch makes it possible: with it, one can do fig = plt.new_figure_manager(num).canvas.figure ... ... fig.savefig() plt.show(figures=[fig]) Note that this does *not* register the figure with pyplot; in particular *fig* is garbage-collected when it goes out of scope, does not participate in gcf(); etc. So this is "pyplotless" in the sense that there is no global figure registry anymore, but pyplot still stays in charge of the GUI integration. Obviously the `plt.new_figure_manager(num).canvas.figure` expression could / should be encapsulated in a helper function, up to bikeshedding. The `num` parameter is needed as matplotlib currently uses it to set the figure title, but that can easily be made optional too. Finally note that `plt.show([fig])` would be a nicer API, but right now the first parameter to plt.show is `block`, which is undergoing transition to kwonly. IOW the end-goal would be e.g. # intentionally terrible name as placeholder :) fig = plt.new_figure_not_globally_registered("some title") ... ... fig.savefig() plt.show([fig]) --- lib/matplotlib/backend_bases.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 7f2ab50a56b2..caffc3ea647b 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -2485,19 +2485,23 @@ def key_press_handler(event, canvas=None, toolbar=None): toggle_xscale_keys = rcParams['keymap.xscale'] all_keys = dict.__getitem__(rcParams, 'keymap.all_axes') - # toggle fullscreen mode ('f', 'ctrl + f') - if event.key in fullscreen_keys: - try: - canvas.manager.full_screen_toggle() - except AttributeError: - pass + manager = canvas.manager - # quit the figure (default key 'ctrl+w') - if event.key in quit_keys: - Gcf.destroy_fig(canvas.figure) + # quit all figures (no default) if event.key in quit_all_keys: Gcf.destroy_all() + if manager is not None: + # toggle fullscreen mode ('f', 'ctrl + f') + if event.key in fullscreen_keys: + canvas.manager.full_screen_toggle() + # quit the figure (default key 'ctrl+w') + if event.key in quit_keys: + if Gcf.get_fig_manager(canvas.manager.num): + Gcf.destroy_fig(canvas.figure) + else: + manager.destroy() + if toolbar is not None: # home or reset mnemonic (default key 'h', 'home' and 'r') if event.key in home_keys: @@ -3545,15 +3549,16 @@ def draw_if_interactive(cls): cls.trigger_manager_draw(manager) @classmethod - def show(cls, *, block=None): + def show(cls, figures=None, *, block=None): """ - Show all figures. + Show the *figures*, defaulting to all pyplot-managed figures. `show` blocks by calling `mainloop` if *block* is ``True``, or if it is ``None`` and we are neither in IPython's ``%pylab`` mode, nor in `interactive` mode. """ - managers = Gcf.get_all_fig_managers() + managers = ([figure.canvas.manager for figure in figures] + if figures is not None else Gcf.get_all_fig_managers()) if not managers: return for manager in managers: